netgescon-master/_BACKUP_OLD_netgescon-laravel_INACTIVE/app/Services/RipartizioneSpesaService.php

267 lines
9.6 KiB
PHP

<?php
namespace App\Services;
use App\Models\RipartizioneSpese;
use App\Models\DettaglioRipartizioneSpese;
use App\Models\PianoRateizzazione;
use App\Models\Rata;
use App\Models\VoceSpesa;
use App\Models\TabellaMillesimale;
use App\Models\UnitaImmobiliare;
use App\Models\AnagraficaCondominiale;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* Service per la gestione completa della ripartizione spese
* e generazione automatica di piani di rateizzazione
*/
class RipartizioneSpesaService
{
/**
* Calcola automaticamente la ripartizione spese per uno stabile
*
* @param VoceSpesa $voceSpesa
* @param float $importoTotale
* @param int|null $tabellaMillesimaleId
* @param array $opzioni
* @return RipartizioneSpese
*/
public function calcolaRipartizione(
VoceSpesa $voceSpesa,
float $importoTotale,
?int $tabellaMillesimaleId = null,
array $opzioni = []
): RipartizioneSpesa {
return DB::transaction(function () use ($voceSpesa, $importoTotale, $tabellaMillesimaleId, $opzioni) {
// Usa la tabella millesimale di default se non specificata
$tabellaId = $tabellaMillesimaleId ?? $voceSpesa->tabella_millesimale_default_id;
if (!$tabellaId) {
throw new \InvalidArgumentException('Nessuna tabella millesimale specificata o di default');
}
$tabellaMillesimale = TabellaMillesimale::findOrFail($tabellaId);
// Crea la ripartizione principale
$ripartizione = RipartizioneSpese::create([
'voce_spesa_id' => $voceSpesa->id,
'stabile_id' => $voceSpesa->stabile_id,
'tabella_millesimale_id' => $tabellaId,
'importo_totale' => $importoTotale,
'tipo_ripartizione' => $opzioni['tipo_ripartizione'] ?? 'millesimale',
'data_ripartizione' => $opzioni['data_ripartizione'] ?? now()->toDateString(),
'note' => $opzioni['note'] ?? null,
'creato_da' => auth()->id(),
]);
// Calcola i dettagli per ogni unità immobiliare
$this->calcolaDettagliRipartizione($ripartizione, $tabellaMillesimale, $importoTotale);
return $ripartizione;
});
}
/**
* Calcola i dettagli di ripartizione per ogni unità immobiliare
*/
protected function calcolaDettagliRipartizione(
RipartizioneSpese $ripartizione,
TabellaMillesimale $tabellaMillesimale,
float $importoTotale
): void {
// Ottieni le unità immobiliari dello stabile con i loro millesimi
$unitaConMillesimi = UnitaImmobiliare::where('stabile_id', $ripartizione->stabile_id)
->with(['dirittiReali.anagrafica'])
->get();
$totaleMillesimi = $tabellaMillesimale->totale_millesimi ?? 1000;
foreach ($unitaConMillesimi as $unita) {
// Ottieni i millesimi dell'unità per questa tabella
$millesimi = $this->getMillesimiUnita($unita, $tabellaMillesimale);
if ($millesimi <= 0) {
continue; // Salta unità senza millesimi
}
// Calcola l'importo proporzionale
$importoCalcolato = ($importoTotale * $millesimi) / $totaleMillesimi;
// Ottieni l'anagrafica proprietaria (primo diritto reale attivo)
$anagrafica = $unita->dirittiReali()
->where('attivo', true)
->with('anagrafica')
->first()?->anagrafica;
if (!$anagrafica) {
continue; // Salta unità senza proprietario
}
// Crea il dettaglio ripartizione
DettaglioRipartizioneSpese::create([
'ripartizione_spese_id' => $ripartizione->id,
'unita_immobiliare_id' => $unita->id,
'anagrafica_condominiale_id' => $anagrafica->id,
'millesimi' => $millesimi,
'importo_calcolato' => round($importoCalcolato, 2),
]);
}
// Aggiorna l'importo ripartito
$importoRipartito = $ripartizione->dettagli()->sum('importo_calcolato');
$ripartizione->update(['importo_ripartito' => $importoRipartito]);
}
/**
* Ottieni i millesimi di un'unità per una specifica tabella millesimale
*/
protected function getMillesimiUnita(UnitaImmobiliare $unita, TabellaMillesimale $tabella): float
{
// Logica per ottenere i millesimi specifici dalla tabella
// Per ora usiamo i millesimi di proprietà dell'unità
return $unita->millesimi_proprieta ?? 0;
}
/**
* Crea un piano di rateizzazione da una ripartizione spese
*/
public function creaPianoRateizzazione(
RipartizioneSpese $ripartizione,
int $numeroRate,
string $frequenza = 'mensile',
Carbon $dataPrimaRata = null,
array $opzioni = []
): PianoRateizzazione {
return DB::transaction(function () use ($ripartizione, $numeroRate, $frequenza, $dataPrimaRata, $opzioni) {
$dataPrimaRata = $dataPrimaRata ?? now()->addMonth()->startOfMonth();
$piano = PianoRateizzazione::create([
'ripartizione_spese_id' => $ripartizione->id,
'stabile_id' => $ripartizione->stabile_id,
'descrizione' => $opzioni['descrizione'] ?? "Piano rateizzazione per {$ripartizione->voceSpesa->descrizione}",
'tipo_piano' => $opzioni['tipo_piano'] ?? 'standard',
'importo_totale' => $ripartizione->importo_totale,
'numero_rate' => $numeroRate,
'data_prima_rata' => $dataPrimaRata,
'frequenza' => $frequenza,
'note' => $opzioni['note'] ?? null,
'creato_da' => auth()->id(),
]);
// Genera le rate automaticamente
$this->generaRate($piano, $dataPrimaRata, $frequenza);
return $piano;
});
}
/**
* Genera automaticamente le rate per un piano di rateizzazione
*/
protected function generaRate(PianoRateizzazione $piano, Carbon $dataPrimaRata, string $frequenza): void
{
$importoRata = round($piano->importo_totale / $piano->numero_rate, 2);
$dataScadenza = $dataPrimaRata->copy();
for ($i = 1; $i <= $piano->numero_rate; $i++) {
// Aggiusta l'ultima rata per eventuali arrotondamenti
$importoCorrente = ($i == $piano->numero_rate)
? $piano->importo_totale - (($piano->numero_rate - 1) * $importoRata)
: $importoRata;
Rata::create([
'piano_rateizzazione_id' => $piano->id,
'ripartizione_spese_id' => $piano->ripartizione_spese_id,
'numero_rata' => $i,
'importo_rata' => $importoCorrente,
'data_scadenza' => $dataScadenza->copy(),
]);
// Calcola la prossima scadenza
$dataScadenza = $this->calcolaProximaScadenza($dataScadenza, $frequenza);
}
}
/**
* Calcola la prossima data di scadenza in base alla frequenza
*/
protected function calcolaProximaScadenza(Carbon $dataCorrente, string $frequenza): Carbon
{
return match ($frequenza) {
'mensile' => $dataCorrente->addMonth(),
'bimestrale' => $dataCorrente->addMonths(2),
'trimestrale' => $dataCorrente->addMonths(3),
'semestrale' => $dataCorrente->addMonths(6),
default => $dataCorrente->addMonth(),
};
}
/**
* Approva una ripartizione spese
*/
public function approvaRipartizione(RipartizioneSpese $ripartizione): bool
{
return $ripartizione->update([
'stato' => 'approvata',
'approvato_at' => now(),
'approvato_da' => auth()->id(),
]);
}
/**
* Contabilizza una ripartizione spese
*/
public function contabilizzaRipartizione(RipartizioneSpese $ripartizione): bool
{
return $ripartizione->update([
'stato' => 'contabilizzata',
'contabilizzato_at' => now(),
'contabilizzato_da' => auth()->id(),
]);
}
/**
* Registra il pagamento di una rata
*/
public function registraPagamento(
Rata $rata,
float $importoPagato,
Carbon $dataPagamento = null,
string $modalitaPagamento = null,
string $riferimentoPagamento = null
): bool {
$dataPagamento = $dataPagamento ?? now();
return $rata->update([
'stato' => 'pagata',
'data_pagamento' => $dataPagamento,
'importo_pagato' => $importoPagato,
'modalita_pagamento' => $modalitaPagamento,
'riferimento_pagamento' => $riferimentoPagamento,
'registrato_da' => auth()->id(),
'registrato_at' => now(),
]);
}
/**
* Calcola le statistiche di una ripartizione
*/
public function calcolaStatistiche(RipartizioneSpese $ripartizione): array
{
$dettagli = $ripartizione->dettagli;
return [
'numero_unita' => $dettagli->count(),
'importo_totale' => $ripartizione->importo_totale,
'importo_ripartito' => $ripartizione->importo_ripartito,
'differenza' => $ripartizione->importo_totale - $ripartizione->importo_ripartito,
'importo_medio_unita' => $dettagli->avg('importo_calcolato'),
'importo_min_unita' => $dettagli->min('importo_calcolato'),
'importo_max_unita' => $dettagli->max('importo_calcolato'),
'millesimi_totali' => $dettagli->sum('millesimi'),
];
}
}