netgescon-day0/app/Models/GestioneContabile.php

459 lines
13 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Carbon\Carbon;
/**
* MODEL: GestioneContabile
*
* Gestisce separazione contabile multi-anno e multi-gestione
* Ogni gestione ha il proprio protocollo e bilancio
*/
class GestioneContabile extends Model
{
use HasFactory;
protected $table = 'gestioni_contabili';
protected $fillable = [
'tenant_id',
'stabile_id',
'anno_gestione',
'tipo_gestione',
'numero_straordinaria',
'denominazione',
'data_inizio',
'data_fine',
'stato',
'bilancio_apertura',
'bilancio_chiusura',
'protocollo_prefix',
'ultimo_protocollo',
'percentuale_fondo_riserva',
'rata_ordinaria_mensile',
'usa_millesimi_generali',
'usa_millesimi_riscaldamento',
'usa_millesimi_ascensore',
'configurazione',
'note_gestione',
'gestione_attiva',
'mesi_rate_ordinaria',
'mesi_rate_riscaldamento',
'mesi_rate_straordinaria',
'codice_archivio_legacy',
'piano_straordinario'
];
protected $casts = [
'data_inizio' => 'date',
'data_fine' => 'date',
'bilancio_apertura' => 'array',
'bilancio_chiusura' => 'array',
'configurazione' => 'array',
'gestione_attiva' => 'boolean',
'usa_millesimi_generali' => 'boolean',
'usa_millesimi_riscaldamento' => 'boolean',
'usa_millesimi_ascensore' => 'boolean',
'percentuale_fondo_riserva' => 'decimal:2',
'rata_ordinaria_mensile' => 'decimal:2',
'mesi_rate_ordinaria' => 'array',
'mesi_rate_riscaldamento' => 'array',
'mesi_rate_straordinaria' => 'array',
'piano_straordinario' => 'array'
];
/** Imposta pianificazione mesi da array di checkbox (chiave = mese, valore truthy) */
public function setMesiFromCheckboxes(string $tipo, array $input): void
{
$mesi = [];
foreach (range(1, 12) as $m) {
if (!empty($input[(string)$m]) || !empty($input[$m])) {
$mesi[] = (int)$m;
}
}
$field = match ($tipo) {
'ordinaria' => 'mesi_rate_ordinaria',
'riscaldamento' => 'mesi_rate_riscaldamento',
'straordinaria' => 'mesi_rate_straordinaria',
default => 'mesi_rate_ordinaria',
};
$this->{$field} = $mesi;
}
/** Relazione con stabile */
public function stabile()
{
return $this->belongsTo(Stabile::class);
}
/**
* Operazioni contabili di questa gestione
*/
public function operazioni(): HasMany
{
return $this->hasMany(OperazioneContabile::class, 'gestione_id');
}
/**
* Incassi di questa gestione
*/
public function incassi(): HasMany
{
return $this->hasMany(Incasso::class, 'gestione_id');
}
/**
* Ritenute d'acconto di questa gestione
*/
public function ritenuteAcconto(): HasMany
{
return $this->hasMany(RegistroRitenuteAcconto::class, 'gestione_id');
}
/**
* Incassi estratto conto di questa gestione
*/
public function incassiEstrattoConto(): HasMany
{
return $this->hasMany(IncassoEstrattoConto::class, 'gestione_id');
}
/**
* Scope per tenant
*/
public function scopeForTenant($query, string $tenantId)
{
return $query->where('tenant_id', $tenantId);
}
/**
* Scope per anno
*/
public function scopeAnno($query, int $anno)
{
return $query->where('anno_gestione', $anno);
}
/**
* Scope per tipo gestione
*/
public function scopeTipo($query, string $tipo)
{
return $query->where('tipo_gestione', $tipo);
}
/**
* Scope gestioni attive
*/
public function scopeAttive($query)
{
return $query->where('gestione_attiva', true);
}
/**
* Scope gestioni aperte
*/
public function scopeAperte($query)
{
return $query->where('stato', 'aperta');
}
/**
* Verifica se la gestione è attiva
*/
public function isAttiva(): bool
{
return $this->gestione_attiva && $this->stato === 'aperta';
}
/**
* Verifica se la gestione è chiusa
*/
public function isChiusa(): bool
{
return in_array($this->stato, ['chiusa', 'consolidata']);
}
/**
* Verifica se è una gestione straordinaria
*/
public function isStraordinaria(): bool
{
return $this->tipo_gestione === 'straordinaria';
}
/** Verifica se è gestione ordinaria */
public function isOrdinaria(): bool
{
return $this->tipo_gestione === 'ordinaria';
}
/** Verifica se è gestione riscaldamento */
public function isRiscaldamento(): bool
{
return $this->tipo_gestione === 'riscaldamento';
}
/**
* Genera prossimo numero protocollo
*/
public function getNextProtocollo(): array
{
$this->increment('ultimo_protocollo');
$numero = $this->ultimo_protocollo;
$completo = $this->isStraordinaria()
? "{$this->protocollo_prefix}-{$numero}"
: "{$this->protocollo_prefix}-{$numero}";
return [
'numero' => $numero,
'completo' => $completo,
'prefix' => $this->protocollo_prefix
];
}
/**
* Calcola totali gestione
*/
public function calcolaTotali(): array
{
$operazioni = $this->operazioni();
return [
'totale_entrate' => $operazioni->where('natura2', 'LIKE', 'E%')->sum('importo'),
'totale_uscite' => $operazioni->where('natura2', 'LIKE', 'U%')->sum('importo'),
'totale_operazioni' => $operazioni->count(),
'saldo_gestione' => $operazioni->sum('dare') - $operazioni->sum('avere'),
'ultimo_protocollo' => $this->ultimo_protocollo
];
}
/**
* Ottiene statistiche riconciliazione
*/
public function getStatisticheRiconciliazione(): array
{
$incassi = $this->incassi();
$incassiEc = $this->incassiEstrattoConto();
return [
'incassi_totali' => $incassi->count(),
'incassi_riconciliati' => $incassi->whereNotNull('movimento_bancario_id')->count(),
'incassi_ec_totali' => $incassiEc->count(),
'incassi_ec_riconciliati' => $incassiEc->where('stato', 'riconciliato')->count(),
'percentuale_riconciliazione' => $incassi->count() > 0
? round(($incassi->whereNotNull('movimento_bancario_id')->count() / $incassi->count()) * 100, 2)
: 0
];
}
/**
* Chiude la gestione
*/
public function chiudiGestione(): void
{
if ($this->stato !== 'aperta') {
throw new \Exception("La gestione non è aperta e non può essere chiusa");
}
// Calcola bilancio di chiusura
$bilancioDiChiusura = $this->calcolaBilancioChiusura();
$this->update([
'stato' => 'chiusa',
'gestione_attiva' => false,
'bilancio_chiusura' => $bilancioDiChiusura
]);
// Crea bilancio di apertura per gestione successiva
$this->createBilancioAperturaSuccessiva($bilancioDiChiusura);
}
/**
* Calcola bilancio di chiusura
*/
private function calcolaBilancioChiusura(): array
{
$operazioni = $this->operazioni()->where('stato_operazione', 'confermata')->get();
$bilancio = [];
// Raggruppa per conto contabile
foreach ($operazioni as $operazione) {
$conto = $operazione->conto_contabile ?? 'GENERICO';
if (!isset($bilancio[$conto])) {
$bilancio[$conto] = ['dare' => 0, 'avere' => 0, 'saldo' => 0];
}
$bilancio[$conto]['dare'] += $operazione->dare;
$bilancio[$conto]['avere'] += $operazione->avere;
$bilancio[$conto]['saldo'] = $bilancio[$conto]['dare'] - $bilancio[$conto]['avere'];
}
return [
'data_chiusura' => now(),
'conti' => $bilancio,
'totale_dare' => array_sum(array_column(array_column($bilancio, 'dare'), 0)),
'totale_avere' => array_sum(array_column(array_column($bilancio, 'avere'), 0)),
'saldo_finale' => array_sum(array_column(array_column($bilancio, 'saldo'), 0))
];
}
/**
* Crea bilancio apertura per gestione successiva
*/
private function createBilancioAperturaSuccessiva(array $bilancioChiusura): void
{
$annoSuccessivo = $this->anno_gestione + 1;
// Cerca gestione successiva dello stesso tipo
$gestioneSuccessiva = static::where('tenant_id', $this->tenant_id)
->where('anno_gestione', $annoSuccessivo)
->where('tipo_gestione', $this->tipo_gestione)
->where('numero_straordinaria', $this->numero_straordinaria)
->first();
if ($gestioneSuccessiva) {
// Trasforma bilancio chiusura in apertura
$bilanciOApertura = [];
foreach ($bilancioChiusura['conti'] as $conto => $dati) {
if ($dati['saldo'] != 0) {
$bilanciOApertura[$conto] = [
'saldo_iniziale' => $dati['saldo'],
'da_gestione_precedente' => $this->id
];
}
}
$gestioneSuccessiva->update([
'bilancio_apertura' => [
'data_apertura' => $gestioneSuccessiva->data_inizio,
'da_gestione' => $this->id,
'conti' => $bilanciOApertura
]
]);
}
}
/**
* Formatta denominazione per display
*/
public function getDenominazioneCompleta(): string
{
$base = $this->denominazione;
if ($this->isStraordinaria()) {
$base .= " (N.{$this->numero_straordinaria})";
}
if (!$this->isAttiva()) {
$base .= " [CHIUSA]";
}
return $base;
}
/**
* Colore per stato gestione
*/
public function getColoreStato(): string
{
return match ($this->stato) {
'aperta' => 'success',
'chiusa' => 'warning',
'consolidata' => 'secondary',
default => 'light'
};
}
/**
* Icona per tipo gestione
*/
public function getIconaTipo(): string
{
return match ($this->tipo_gestione) {
'ordinaria' => 'calendar-check',
'riscaldamento' => 'thermometer-half',
'straordinaria' => 'exclamation-triangle',
default => 'folder'
};
}
/**
* Info completa per dashboard
*/
public function getInfoCompleta(): array
{
return [
'id' => $this->id,
'denominazione' => $this->getDenominazioneCompleta(),
'anno_gestione' => $this->anno_gestione,
'tipo_gestione' => $this->tipo_gestione,
'numero_straordinaria' => $this->numero_straordinaria,
'stato' => $this->stato,
'colore_stato' => $this->getColoreStato(),
'icona_tipo' => $this->getIconaTipo(),
'attiva' => $this->isAttiva(),
'protocollo_prefix' => $this->protocollo_prefix,
'ultimo_protocollo' => $this->ultimo_protocollo,
'periodo' => [
'inizio' => $this->data_inizio->format('d/m/Y'),
'fine' => $this->data_fine->format('d/m/Y')
],
'totali' => $this->calcolaTotali(),
'riconciliazione' => $this->getStatisticheRiconciliazione()
];
}
/**
* Crea gestioni standard per un anno
*/
public static function createGestioniAnno(string $tenantId, int $anno): array
{
$gestioni = [];
// Ordinaria
$gestioni['ordinaria'] = static::create([
'tenant_id' => $tenantId,
'anno_gestione' => $anno,
'tipo_gestione' => 'ordinaria',
'denominazione' => "Gestione Ordinaria {$anno}",
'data_inizio' => "{$anno}-01-01",
'data_fine' => "{$anno}-12-31",
'protocollo_prefix' => "O{$anno}",
'stato' => 'aperta'
]);
// Riscaldamento
$gestioni['riscaldamento'] = static::create([
'tenant_id' => $tenantId,
'anno_gestione' => $anno,
'tipo_gestione' => 'riscaldamento',
'denominazione' => "Gestione Riscaldamento {$anno}",
'data_inizio' => "{$anno}-01-01",
'data_fine' => "{$anno}-12-31",
'protocollo_prefix' => "R{$anno}",
'stato' => 'aperta'
]);
return $gestioni;
}
/** Variante con stabile */
public static function createGestioniAnnoForStabile(string $tenantId, int $stabileId, int $anno): array
{
$gestioni = static::createGestioniAnno($tenantId, $anno);
foreach ($gestioni as $g) {
$g->stabile_id = $stabileId;
$g->save();
}
return $gestioni;
}
}