netgescon-day0/app/Models/Incasso.php

360 lines
10 KiB
PHP
Executable File

<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* MODEL: Incasso
*
* Gestisce i pagamenti ricevuti dai condomini
* Source: incassi da Gescon + riconciliazione bancaria
*/
class Incasso extends Model
{
use HasFactory;
protected $table = 'incassi';
protected $fillable = [
'tenant_id',
'gestione_id',
'condominio_id',
'conto_bancario_id',
'condomino_id',
'movimento_bancario_id',
'id_incasso_gescon',
'cod_cond_gescon',
'cond_inquil',
'n_riferimento',
'anno_rif',
'da_ricev_diretto',
'n_ricevuta',
'posiz_riga',
'anno_ricev',
'n_mese',
'o_r_s',
'importo_pagato',
'importo_pagato_euro',
'd_p_e',
'dt_empag',
'data_pagamento',
'data_competenza',
'descrizione',
'note',
'stato_riconciliazione',
'scostamento_importo',
'giorni_scostamento',
'confidenza_match',
'rate_associate',
'tipo_incasso',
'cod_cassa_gescon',
'totale',
'str_orig',
'linea_sep_stp',
'proviene_ors',
'proviene_n_stra',
'proviene_eserc',
'metadati_gescon',
'log_riconciliazione',
];
protected $casts = [
'importo_pagato' => 'decimal:4',
'importo_pagato_euro' => 'decimal:4',
'scostamento_importo' => 'decimal:4',
'confidenza_match' => 'decimal:2',
'dt_empag' => 'datetime',
'data_pagamento' => 'date',
'data_competenza' => 'date',
'rate_associate' => 'array',
'metadati_gescon' => 'array',
'log_riconciliazione' => 'array',
];
/**
* Condominio di appartenenza
*/
public function condominio(): BelongsTo
{
return $this->belongsTo(Condominio::class);
}
/**
* Conto bancario utilizzato
*/
public function contoBancario(): BelongsTo
{
return $this->belongsTo(ContoBancario::class, 'conto_bancario_id');
}
/**
* Condomino che ha effettuato il pagamento
*/
public function condomino(): BelongsTo
{
return $this->belongsTo(Condomino::class);
}
/**
* Movimento bancario associato
*/
public function movimentoBancario(): BelongsTo
{
return $this->belongsTo(MovimentoBancario::class, 'movimento_bancario_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_rif', $anno);
}
/**
* Scope per tipo spesa
*/
public function scopeTipoSpesa($query, string $ors)
{
return $query->where('o_r_s', $ors);
}
/**
* Scope per stato riconciliazione
*/
public function scopeStatoRiconciliazione($query, string $stato)
{
return $query->where('stato_riconciliazione', $stato);
}
/**
* Scope per condominio
*/
public function scopeCondominio($query, int $condominioId)
{
return $query->where('condominio_id', $condominioId);
}
/**
* Scope riconciliati
*/
public function scopeRiconciliati($query)
{
return $query->whereIn('stato_riconciliazione', ['automatica', 'manuale']);
}
/**
* Scope sospesi
*/
public function scopeSospesi($query)
{
return $query->where('stato_riconciliazione', 'sospesa');
}
/**
* Scope per periodo
*/
public function scopePeriodo($query, Carbon $dataInizio, Carbon $dataFine)
{
return $query->whereBetween('data_pagamento', [$dataInizio, $dataFine]);
}
/**
* Verifica se è riconciliato
*/
public function isRiconciliato(): bool
{
return in_array($this->stato_riconciliazione, ['automatica', 'manuale']);
}
/**
* Verifica se è anomalo
*/
public function isAnomalo(): bool
{
return $this->stato_riconciliazione === 'anomala' ||
$this->scostamento_importo > 10 ||
$this->giorni_scostamento > 30;
}
/**
* Descrizione tipo spesa
*/
public function getDescrizioneTipoSpesa(): string
{
return match ($this->o_r_s) {
'O' => 'Ordinarie',
'R' => 'Riscaldamento',
'S' => 'Straordinarie',
default => 'Altro'
};
}
/**
* Colore stato riconciliazione
*/
public function getColoreStatoRiconciliazione(): string
{
return match ($this->stato_riconciliazione) {
'automatica' => 'success',
'manuale' => 'info',
'sospesa' => 'warning',
'anomala' => 'danger',
default => 'secondary'
};
}
/**
* Descrizione stato riconciliazione
*/
public function getDescrizioneStatoRiconciliazione(): string
{
return match ($this->stato_riconciliazione) {
'automatica' => 'Riconciliata automaticamente',
'manuale' => 'Riconciliata manualmente',
'sospesa' => 'In attesa di riconciliazione',
'anomala' => 'Anomalia da verificare',
default => 'Stato sconosciuto'
};
}
/**
* Formatta importo
*/
public function getImportoFormattato(): string
{
return '€ ' . number_format($this->importo_pagato_euro, 2, ',', '.');
}
/**
* Info per riconciliazione
*/
public function getInfoRiconciliazione(): array
{
return [
'stato' => $this->stato_riconciliazione,
'descrizione_stato' => $this->getDescrizioneStatoRiconciliazione(),
'colore_stato' => $this->getColoreStatoRiconciliazione(),
'confidenza_match' => $this->confidenza_match,
'scostamento_importo' => $this->scostamento_importo,
'giorni_scostamento' => $this->giorni_scostamento,
'movimento_associato' => $this->movimento_bancario_id ? true : false,
'anomalo' => $this->isAnomalo(),
];
}
/**
* Associa movimento bancario
*/
public function associaMovimentoBancario(MovimentoBancario $movimento, string $tipoRiconciliazione = 'manuale'): void
{
$this->update([
'movimento_bancario_id' => $movimento->id,
'stato_riconciliazione' => $tipoRiconciliazione,
'scostamento_importo' => abs($this->importo_pagato_euro - $movimento->importo),
'giorni_scostamento' => $this->data_pagamento
? abs($this->data_pagamento->diffInDays($movimento->data_operazione))
: 0,
'log_riconciliazione' => array_merge($this->log_riconciliazione ?? [], [[
'data' => now(),
'tipo' => $tipoRiconciliazione,
'movimento_id' => $movimento->id,
'user_id' => auth()->id(),
]])
]);
$movimento->update([
'incasso_id' => $this->id,
'stato_riconciliazione' => $tipoRiconciliazione,
]);
}
/**
* Rimuovi associazione movimento
*/
public function rimuoviAssociazioneMovimento(): void
{
if ($this->movimento_bancario_id) {
$movimento = $this->movimentoBancario;
$this->update([
'movimento_bancario_id' => null,
'stato_riconciliazione' => 'sospesa',
'scostamento_importo' => 0,
'giorni_scostamento' => 0,
'confidenza_match' => 0,
'log_riconciliazione' => array_merge($this->log_riconciliazione ?? [], [[
'data' => now(),
'tipo' => 'disassociazione',
'movimento_id' => $movimento->id,
'user_id' => auth()->id(),
]])
]);
$movimento->update([
'incasso_id' => null,
'stato_riconciliazione' => 'sospesa',
]);
}
}
/**
* Rate potenzialmente associate
*/
public function getRatePotenziali(): array
{
// Logic per trovare rate che potrebbero essere associate a questo incasso
// Basato su condominio, anno, mese, tipo spesa, importo
return [];
}
/**
* Genera riferimento univoco
*/
public function getRiferimentoUnico(): string
{
return sprintf(
'%s-%04d-%02d-%s-%d',
$this->cod_cond_gescon,
$this->anno_rif,
(int) $this->n_mese,
$this->o_r_s,
$this->n_ricevuta
);
}
/**
* Info completa per dashboard
*/
public function getInfoCompleta(): array
{
return [
'id' => $this->id,
'riferimento' => $this->getRiferimentoUnico(),
'condominio' => $this->condominio?->nome_condominio,
'condomino' => $this->condomino?->nome_completo,
'importo' => $this->importo_pagato_euro,
'importo_formattato' => $this->getImportoFormattato(),
'data_pagamento' => $this->data_pagamento?->format('d/m/Y'),
'tipo_spesa' => $this->getDescrizioneTipoSpesa(),
'anno_competenza' => $this->anno_rif,
'mese_competenza' => $this->n_mese,
'conto_bancario' => $this->contoBancario?->descrizione,
'riconciliazione' => $this->getInfoRiconciliazione(),
'note' => $this->note,
'descrizione' => $this->descrizione,
];
}
}