267 lines
9.6 KiB
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'),
|
|
];
|
|
}
|
|
}
|