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'), ]; } }