361 lines
9.7 KiB
PHP
361 lines
9.7 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
use Carbon\Carbon;
|
|
|
|
/**
|
|
* 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',
|
|
'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
|
|
];
|
|
}
|
|
}
|