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); } }