296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Contabilita\Models;
|
|
|
|
use App\Models\GestioneContabile;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use RuntimeException;
|
|
|
|
class Registrazione extends Model
|
|
{
|
|
use SoftDeletes;
|
|
|
|
protected $table = 'contabilita_registrazioni';
|
|
public $timestamps = true;
|
|
|
|
protected $fillable = [
|
|
'stabile_id',
|
|
'gestione_id',
|
|
'data_registrazione',
|
|
'entry_date',
|
|
'description',
|
|
'descrizione',
|
|
'denominazione',
|
|
'note',
|
|
'document_type',
|
|
'document_number',
|
|
'external_reference',
|
|
'protocollo_prefix',
|
|
'protocollo_numero',
|
|
'protocollo_completo',
|
|
'created_by',
|
|
'updated_by',
|
|
'user_id',
|
|
];
|
|
|
|
protected $dates = ['data_registrazione', 'entry_date', 'deleted_at'];
|
|
|
|
protected static function booted(): void
|
|
{
|
|
static::creating(function (self $model): void {
|
|
$model->applyAutoProtocols();
|
|
});
|
|
}
|
|
|
|
public function movimenti()
|
|
{
|
|
return $this->hasMany(Movimento::class, 'registrazione_id');
|
|
}
|
|
|
|
public function user()
|
|
{
|
|
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
|
}
|
|
|
|
public function assertBilanciata(): void
|
|
{
|
|
if (! Schema::hasTable('contabilita_movimenti')) {
|
|
return;
|
|
}
|
|
|
|
$dare = (float) Movimento::query()
|
|
->where('registrazione_id', $this->id)
|
|
->where('tipo', 'dare')
|
|
->sum('importo');
|
|
|
|
$avere = (float) Movimento::query()
|
|
->where('registrazione_id', $this->id)
|
|
->where('tipo', 'avere')
|
|
->sum('importo');
|
|
|
|
if (abs($dare - $avere) > 0.005) {
|
|
throw new RuntimeException('Registrazione sbilanciata: Dare ' . number_format($dare, 2, ',', '.') . ' / Avere ' . number_format($avere, 2, ',', '.'));
|
|
}
|
|
}
|
|
|
|
public function applyAutoProtocols(): void
|
|
{
|
|
if (! Schema::hasTable('contabilita_registrazioni')) {
|
|
return;
|
|
}
|
|
|
|
$stabileId = (int) ($this->stabile_id ?? 0);
|
|
if ($stabileId <= 0) {
|
|
return;
|
|
}
|
|
|
|
$year = null;
|
|
$data = (string) ($this->data_registrazione ?? $this->entry_date ?? '');
|
|
if ($data !== '' && strlen($data) >= 4) {
|
|
$candidate = (int) substr($data, 0, 4);
|
|
if ($candidate > 0) {
|
|
$year = $candidate;
|
|
}
|
|
}
|
|
$year = $year ?: (int) now()->format('Y');
|
|
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'protocol')) {
|
|
$currentGlobal = trim((string) ($this->protocol ?? ''));
|
|
if ($currentGlobal === '') {
|
|
$this->protocol = $this->generateGlobalProtocol($stabileId, $year);
|
|
}
|
|
}
|
|
|
|
$prefix = trim((string) ($this->protocollo_prefix ?? ''));
|
|
if ($prefix === '') {
|
|
$prefix = $this->resolveProtocolPrefix($year);
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'protocollo_prefix')) {
|
|
$this->protocollo_prefix = $prefix;
|
|
}
|
|
}
|
|
|
|
$numero = (int) ($this->protocollo_numero ?? 0);
|
|
if ($numero <= 0) {
|
|
$numero = $this->nextProtocolNumber($stabileId, $prefix);
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'protocollo_numero')) {
|
|
$this->protocollo_numero = $numero;
|
|
}
|
|
}
|
|
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'protocollo_completo')) {
|
|
$current = trim((string) ($this->protocollo_completo ?? ''));
|
|
if ($current === '' && $prefix !== '' && $numero > 0) {
|
|
$this->protocollo_completo = $prefix . '-' . $numero;
|
|
}
|
|
}
|
|
}
|
|
|
|
private function resolveProtocolPrefix(int $year): string
|
|
{
|
|
$gestioneId = (int) ($this->gestione_id ?? 0);
|
|
if ($gestioneId > 0 && Schema::hasTable('gestioni_contabili')) {
|
|
$gestionePrefix = (string) GestioneContabile::query()
|
|
->where('id', $gestioneId)
|
|
->value('protocollo_prefix');
|
|
|
|
$gestionePrefix = trim($gestionePrefix);
|
|
if ($gestionePrefix !== '') {
|
|
return $gestionePrefix;
|
|
}
|
|
}
|
|
|
|
$documentType = strtoupper(trim((string) ($this->document_type ?? '')));
|
|
if (in_array($documentType, ['INC', 'INCASSO', 'INCASSI'], true)) {
|
|
return 'INC' . $year;
|
|
}
|
|
if (in_array($documentType, ['RDA', 'RITENUTA', 'F24'], true)) {
|
|
return 'RDA' . $year;
|
|
}
|
|
|
|
return 'PN' . $year;
|
|
}
|
|
|
|
private function nextProtocolNumber(int $stabileId, string $prefix): int
|
|
{
|
|
$max = (int) DB::table('contabilita_registrazioni')
|
|
->where('stabile_id', $stabileId)
|
|
->where('protocollo_prefix', $prefix)
|
|
->max('protocollo_numero');
|
|
|
|
return $max + 1;
|
|
}
|
|
|
|
private function generateGlobalProtocol(int $stabileId, int $year): string
|
|
{
|
|
$base = 'PN' . $year;
|
|
|
|
$max = DB::table('contabilita_registrazioni')
|
|
->where('stabile_id', $stabileId)
|
|
->where('protocol', 'like', $base . '-%')
|
|
->selectRaw('MAX(CAST(SUBSTRING_INDEX(protocol, "-", -1) AS UNSIGNED)) as mx')
|
|
->value('mx');
|
|
|
|
$next = ((int) $max) + 1;
|
|
|
|
return $base . '-' . str_pad((string) $next, 6, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
public static function resolveGestioneIdOrDefault(int $gestioneId, int $stabileId, ?string $data): ?int
|
|
{
|
|
if ($stabileId <= 0) {
|
|
return $gestioneId > 0 ? $gestioneId : null;
|
|
}
|
|
|
|
// Se esiste la tabella legacy gestioni, preferisci l'id_gestione (FK).
|
|
if (Schema::hasTable('gestioni')) {
|
|
if ($gestioneId > 0) {
|
|
$legacyExists = DB::table('gestioni')
|
|
->where('id_gestione', $gestioneId)
|
|
->exists();
|
|
if ($legacyExists) {
|
|
return $gestioneId;
|
|
}
|
|
|
|
if (Schema::hasTable('gestioni_contabili')) {
|
|
$contabile = GestioneContabile::query()
|
|
->where('stabile_id', $stabileId)
|
|
->find($gestioneId);
|
|
if ($contabile) {
|
|
$mappedTipo = match (strtolower((string) $contabile->tipo_gestione)) {
|
|
'riscaldamento' => 'Risc.',
|
|
'straordinaria' => 'Straord.',
|
|
default => 'Ord.',
|
|
};
|
|
|
|
$legacy = DB::table('gestioni')
|
|
->where('stabile_id', $stabileId)
|
|
->where('anno_gestione', (int) $contabile->anno_gestione)
|
|
->where('tipo_gestione', $mappedTipo)
|
|
->orderByDesc('id_gestione')
|
|
->first();
|
|
if ($legacy && isset($legacy->id_gestione)) {
|
|
return (int) $legacy->id_gestione;
|
|
}
|
|
|
|
$newId = DB::table('gestioni')->insertGetId([
|
|
'stabile_id' => $stabileId,
|
|
'anno_gestione' => (int) $contabile->anno_gestione,
|
|
'tipo_gestione' => $mappedTipo,
|
|
'data_inizio' => $contabile->data_inizio?->toDateString(),
|
|
'data_fine' => $contabile->data_fine?->toDateString(),
|
|
'stato' => $contabile->stato ?: 'aperta',
|
|
'descrizione' => $contabile->denominazione ?: null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
if (is_numeric($newId)) {
|
|
return (int) $newId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! Schema::hasTable('gestioni_contabili')) {
|
|
return $gestioneId > 0 ? $gestioneId : null;
|
|
}
|
|
|
|
$year = null;
|
|
if (is_string($data) && $data !== '') {
|
|
$year = (int) substr($data, 0, 4);
|
|
if ($year <= 0) {
|
|
$year = null;
|
|
}
|
|
}
|
|
|
|
$query = GestioneContabile::query()->where('stabile_id', $stabileId);
|
|
if ($year) {
|
|
$query->where('anno_gestione', $year);
|
|
}
|
|
|
|
$match = $query
|
|
->orderByDesc('anno_gestione')
|
|
->orderByDesc('numero_straordinaria')
|
|
->orderByDesc('id')
|
|
->first();
|
|
|
|
if ($match && Schema::hasTable('gestioni')) {
|
|
$mappedTipo = match (strtolower((string) $match->tipo_gestione)) {
|
|
'riscaldamento' => 'Risc.',
|
|
'straordinaria' => 'Straord.',
|
|
default => 'Ord.',
|
|
};
|
|
|
|
$legacy = DB::table('gestioni')
|
|
->where('stabile_id', $stabileId)
|
|
->when($year, fn($q) => $q->where('anno_gestione', $year))
|
|
->where('tipo_gestione', $mappedTipo)
|
|
->orderByDesc('id_gestione')
|
|
->first();
|
|
if ($legacy && isset($legacy->id_gestione)) {
|
|
return (int) $legacy->id_gestione;
|
|
}
|
|
|
|
$newId = DB::table('gestioni')->insertGetId([
|
|
'stabile_id' => $stabileId,
|
|
'anno_gestione' => (int) ($year ?: $match->anno_gestione),
|
|
'tipo_gestione' => $mappedTipo,
|
|
'data_inizio' => $match->data_inizio?->toDateString(),
|
|
'data_fine' => $match->data_fine?->toDateString(),
|
|
'stato' => $match->stato ?: 'aperta',
|
|
'descrizione' => $match->denominazione ?: null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
if (is_numeric($newId)) {
|
|
return (int) $newId;
|
|
}
|
|
}
|
|
|
|
return $match?->id ?: ($gestioneId > 0 ? $gestioneId : null);
|
|
}
|
|
}
|