hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']); } public ?string $mastrinoTabella = null; public ?string $mastrinoCodSpe = null; public array $mastrinoRows = []; public array $acquaRipartoRows = []; public function mount(): void { $user = Auth::user(); if ($user instanceof User && $this->filterAnno === null) { $this->filterAnno = AnnoGestioneContext::resolveActiveAnno($user); } $tab = request()->query('tab'); if (in_array($tab, ['operazioni', 'consuntivo', 'straordinarie', 'acqua', 'incassi', 'fatture'], true)) { $this->viewTab = $tab; } if ($this->onlyStraordinarieMode) { $straord = request()->query('straord'); if (is_numeric($straord)) { $this->selectedStraordinaria = (int) $straord; } if (! in_array($this->viewTab, ['operazioni', 'consuntivo'], true)) { $this->viewTab = 'operazioni'; } } if ($this->netgesconWorkflowMode) { $this->sortField = 'dt_spe'; $this->sortDirection = 'asc'; } } public function getStraordinarieGestioniProperty(): array { if (! $this->onlyStraordinarieMode) { return []; } if (! Schema::connection('gescon_import')->hasTable('operazioni')) { return []; } $query = DB::connection('gescon_import') ->table('operazioni') ->where('gestione', 'S') ->whereNotNull('n_stra'); $legacyCode = $this->resolveLegacyCodiceStabile(); if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) { $query->where('cod_stabile', $legacyCode); } if ($this->filterAnno) { $query->whereYear('dt_spe', $this->filterAnno); } $rows = $query ->selectRaw('n_stra, MIN(dt_spe) as data_inizio, MAX(dt_spe) as data_fine, COUNT(*) as totale') ->groupBy('n_stra') ->orderBy('n_stra') ->get(); $out = []; foreach ($rows as $row) { $nStra = is_numeric($row->n_stra ?? null) ? (int) $row->n_stra : null; if (! $nStra) { continue; } $periodo = null; if (! empty($row->data_inizio) || ! empty($row->data_fine)) { $start = ! empty($row->data_inizio) ? (string) \Carbon\Carbon::parse($row->data_inizio)->format('d/m/Y') : '—'; $end = ! empty($row->data_fine) ? (string) \Carbon\Carbon::parse($row->data_fine)->format('d/m/Y') : '—'; $periodo = $start . ' → ' . $end; } $out[] = [ 'n_stra' => $nStra, 'label' => 'Gestione straordinaria #' . $nStra, 'periodo' => $periodo, 'totale' => (int) ($row->totale ?? 0), ]; } if ($this->selectedStraordinaria === null && ! empty($out)) { $this->selectedStraordinaria = (int) ($out[0]['n_stra'] ?? null); } return $out; } private function resolveLegacyCodiceStabile(): ?string { $user = Auth::user(); $stabile = StabileContext::getActiveStabile($user); return StabileContext::preferredLegacyCode($stabile); } private function resolveLegacyYearForRiparto(?string $codStabile): ?string { if (! $codStabile) { return null; } return (string) (DB::connection('gescon_import') ->table('dett_tab') ->where('cod_stabile', $codStabile) ->max('legacy_year') ?: ''); } public function getLegacyOperazioniScopedByStabileProperty(): bool { return Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'); } public function getLegacyGestioneLabelProperty(): ?string { $legacyCode = $this->resolveLegacyCodiceStabile(); if (! $legacyCode) { return null; } $query = DB::connection('gescon_import') ->table('anni_gestione') ->where('anni_gestione.cod_stabile', $legacyCode); if ($this->filterAnno) { $query->where('anni_gestione.anno', (int) $this->filterAnno); } $row = $query ->leftJoin('stabili', 'stabili.cod_stabile', '=', 'anni_gestione.cod_stabile') ->orderByDesc('anni_gestione.anno') ->first([ 'anni_gestione.anno as anno', 'stabili.codice_directory as codice_directory', ]); if (! $row) { return null; } $dir = trim((string) ($row->codice_directory ?? '')); $anno = trim((string) ($row->anno ?? '')); if ($dir === '' && $anno === '') { return null; } $dirLabel = $dir !== '' ? $dir : '—'; $annoLabel = $anno !== '' ? $anno : '—'; return 'Gestione ' . $dirLabel . ' → ' . $annoLabel; } public function getLegacyGestioneAnnoProperty(): ?int { if ($this->filterAnno) { return (int) $this->filterAnno; } $legacyCode = $this->resolveLegacyCodiceStabile(); if (! $legacyCode) { return null; } $row = DB::connection('gescon_import') ->table('anni_gestione') ->where('anni_gestione.cod_stabile', $legacyCode) ->orderByDesc('anni_gestione.anno') ->first(['anni_gestione.anno as anno']); return $row && is_numeric($row->anno ?? null) ? (int) $row->anno : null; } public function getAcquaSummaryProperty(): array { $legacyCode = $this->resolveLegacyCodiceStabile(); $legacyYear = $this->resolveLegacyYearForRiparto($legacyCode); $rowsQuery = DB::connection('gescon_import') ->table('operazioni') ->where('gestione', 'O') ->whereIn('cod_spe', ['AC1', 'AC2']); if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) { $rowsQuery->where('cod_stabile', $legacyCode); } $rows = $rowsQuery ->select('cod_spe') ->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale') ->groupBy('cod_spe') ->get(); $byCode = []; foreach ($rows as $r) { $byCode[(string) $r->cod_spe] = (float) ($r->totale ?? 0); } $riparto = DB::connection('gescon_import') ->table('dett_tab') ->where('cod_tab', 'ACQUA') ->when($legacyCode, fn($q) => $q->where('cod_stabile', $legacyCode)) ->when($legacyYear, fn($q) => $q->where('legacy_year', $legacyYear)) ->sum(DB::raw('COALESCE(cons_euro, 0)')); $totaleFatture = array_sum($byCode); return [ 'voci' => $byCode, 'totale' => $totaleFatture, 'riparto_totale' => (float) $riparto, 'riparto_delta' => (float) ($riparto - $totaleFatture), ]; } public function getAcquaRipartoProperty(): array { $legacyCode = $this->resolveLegacyCodiceStabile(); $legacyYear = $this->resolveLegacyYearForRiparto($legacyCode); if (! $legacyCode) { return ['rows' => [], 'totale' => 0.0]; } $condQuery = DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $legacyCode); if ($legacyYear) { $condQuery->where('legacy_year', $legacyYear); } $condRows = $condQuery ->get([ 'id_cond', 'scala', 'interno', 'piano', 'cognome', 'nome', 'proprietario_denominazione', 'inquilino_denominazione', 'inquil_nome', ]) ->keyBy('id_cond'); $ripartoQuery = DB::connection('gescon_import') ->table('dett_tab') ->where('cod_tab', 'ACQUA') ->where('cod_stabile', $legacyCode); if ($legacyYear) { $ripartoQuery->where('legacy_year', $legacyYear); } $ripartoRows = $ripartoQuery ->orderBy('id_cond') ->orderBy('cond_inquil') ->get(['id_cond', 'cond_inquil', 'cons_euro']); $out = []; $totale = 0.0; foreach ($ripartoRows as $r) { $cond = $condRows[$r->id_cond] ?? null; $ruolo = strtoupper((string) ($r->cond_inquil ?? '')); $nominativo = ''; if ($cond) { if ($ruolo === 'I') { $nominativo = trim((string) ($cond->inquilino_denominazione ?? '')); if ($nominativo === '') { $nominativo = trim((string) ($cond->inquil_nome ?? '')); } } else { $nominativo = trim((string) ($cond->proprietario_denominazione ?? '')); if ($nominativo === '') { $nominativo = trim((string) (($cond->cognome ?? '') . ' ' . ($cond->nome ?? ''))); } } } if ($ruolo === 'I' && $nominativo === '') { continue; } if ($nominativo === '') { $nominativo = '—'; } $val = (float) ($r->cons_euro ?? 0); $totale += $val; $out[] = [ 'id_cond' => $r->id_cond, 'ruolo' => $ruolo !== '' ? $ruolo : 'C', 'nominativo' => $nominativo, 'scala' => $cond->scala ?? null, 'interno' => $cond->interno ?? null, 'piano' => $cond->piano ?? null, 'cons_euro' => $val, ]; } return ['rows' => $out, 'totale' => $totale]; } public ?int $dettPersNSpe = null; public array $dettPersRows = []; public float $dettPersTotale = 0.0; public float $dettPersImportoOperazione = 0.0; public float $dettPersDelta = 0.0; public bool $dettPersQuadraturaOk = true; public string $dettPersRipartoMode = 'legacy'; public ?string $dettPersRipartoTabella = null; public ?string $dettPersRipartoTarget = null; public array $dettPersRipartoTabellaOptions = []; public array $dettPersRipartoTargetOptions = []; public array $dettPersRecipients = []; public array $dettPersLegacyAmounts = []; public ?string $dettPersLegacyCode = null; public ?string $dettPersLegacyYear = null; public array $dettPersLegacyUnico = []; public function getOperazioniProperty() { $base = $this->buildOperazioniQuery(); $paginator = $this->applyOperazioniSorting($base) ->paginate($this->perPage); $items = $paginator->getCollection(); $rdaByCode = []; foreach ($items as $row) { $rdaCode = null; if (! empty($row->rif_rda)) { $rdaCode = $row->rif_rda; } elseif (is_string($row->benef ?? null) && str_starts_with(strtoupper(trim((string) $row->benef)), 'RDA:')) { $rdaCode = trim(substr(trim((string) $row->benef), 4)); } if ($rdaCode) { $benef = trim((string) ($row->benef ?? '')); $isRdaLine = $benef !== '' && str_starts_with(strtoupper($benef), 'RDA:'); if ($isRdaLine) { $rdaByCode[$rdaCode] = [ 'importo' => $this->computeSignedAmount($row), 'data' => $row->dt_spe ?? null, ]; } } } $codSpe = $items->pluck('cod_spe')->filter()->unique()->values()->all(); $codFor = $items ->pluck('cod_for') ->filter() ->map(fn($v) => trim((string) $v)) ->filter() ->unique() ->values() ->all(); $codForNoZero = array_values(array_filter(array_unique(array_map(fn($v) => ltrim((string) $v, '0'), $codFor)))); $codFor = array_values(array_unique(array_merge($codFor, $codForNoZero))); $codTab = $items->pluck('tabella')->filter()->unique()->values()->all(); $vociMap = []; $vociTabellaMap = []; if (! empty($codSpe)) { $vociRows = DB::connection('gescon_import') ->table('voc_spe') ->whereIn('cod', $codSpe) ->get(['cod', 'descriz', 'tabella']); $vociMap = $vociRows ->mapWithKeys(fn($v) => [$v->cod => $v->descriz]) ->all(); $vociTabellaMap = $vociRows ->mapWithKeys(fn($v) => [$v->cod => $v->tabella]) ->all(); } $tabFromVoci = array_values(array_filter(array_unique(array_map('strval', $vociTabellaMap)))); if (! empty($tabFromVoci)) { $codTab = array_values(array_unique(array_merge($codTab, $tabFromVoci))); } $tabelleMap = []; if (! empty($codTab)) { $tabelleMap = DB::connection('gescon_import') ->table('tabelle_millesimali') ->whereIn('cod_tabella', $codTab) ->pluck('denominazione', 'cod_tabella') ->all(); } $fornitoriMap = []; $fornitoriIdMap = []; $codFornToLegacyId = []; $legacyIds = []; if (! empty($codFor)) { $useStg = Schema::hasTable('stg_fornitori_gescon'); $legacyRows = $useStg ? DB::table('stg_fornitori_gescon') ->whereIn('cod_forn', $codFor) ->get(['cod_forn', 'legacy_id_fornitore', 'denominazione', 'cognome', 'nome']) : DB::connection('gescon_import') ->table('mdb_fornitori') ->whereIn('cod_forn', $codFor) ->get(['cod_forn', 'id_fornitore', 'denominazione', 'cognome', 'nome']); $fornitoriMap = $legacyRows ->mapWithKeys(function ($f) { $label = $f->denominazione ?: trim(($f->nome ?? '') . ' ' . ($f->cognome ?? '')); return [trim((string) $f->cod_forn) => $label]; }) ->all(); $codFornToLegacyId = $legacyRows ->mapWithKeys(fn($f) => [ trim((string) $f->cod_forn) => $useStg ? $f->cod_forn : ($f->id_fornitore ?? null), ]) ->all(); foreach (array_keys($fornitoriMap) as $code) { $trimmed = trim((string) $code); $noZero = ltrim($trimmed, '0'); if ($trimmed !== '' && ! isset($fornitoriMap[$trimmed])) { $fornitoriMap[$trimmed] = $fornitoriMap[$code]; } if ($noZero !== '' && ! isset($fornitoriMap[$noZero])) { $fornitoriMap[$noZero] = $fornitoriMap[$code]; } if ($trimmed !== '' && ! isset($codFornToLegacyId[$trimmed]) && isset($codFornToLegacyId[$code])) { $codFornToLegacyId[$trimmed] = $codFornToLegacyId[$code]; } if ($noZero !== '' && ! isset($codFornToLegacyId[$noZero]) && isset($codFornToLegacyId[$code])) { $codFornToLegacyId[$noZero] = $codFornToLegacyId[$code]; } } $legacyIds = $legacyRows ->map(fn($f) => $useStg ? $f->cod_forn : ($f->id_fornitore ?? null)) ->filter() ->unique() ->values() ->all(); } $activeStabileId = null; $authUser = Auth::user(); if ($authUser) { $activeStabileId = StabileContext::resolveActiveStabileId($authUser); } if (! empty($legacyIds) && Schema::hasTable('fornitori') && $this->netgesconWorkflowMode) { $activeAmministratoreId = null; if ($activeStabileId && Schema::hasTable('stabili')) { $activeAmministratoreId = DB::table('stabili') ->where('id', (int) $activeStabileId) ->value('amministratore_id'); } $fornitoriRows = DB::table('fornitori') ->whereIn('old_id', $legacyIds) ->get(['id', 'old_id', 'amministratore_id']); $fornitoriByOldId = $fornitoriRows ->groupBy('old_id') ->map(function ($rows) use ($activeAmministratoreId) { if ($activeAmministratoreId) { $match = $rows->first(fn($row) => (int) ($row->amministratore_id ?? 0) === (int) $activeAmministratoreId); if ($match) { return (int) $match->id; } } return (int) ($rows->first()->id ?? 0); }) ->filter(fn($value) => $value > 0) ->all(); $fornitoriIdMap = $fornitoriByOldId; } elseif (! empty($legacyIds)) { $fornitoriIdMap = DB::table('fornitori') ->whereIn('old_id', $legacyIds) ->pluck('id', 'old_id') ->all(); } $fattureByFornitore = []; $fattureByFornitoreDocumento = []; $fattureByDocumentoGlobale = []; $fornitoriLocaliById = []; $vociLocaliByCode = []; $fornitoriIds = array_values(array_filter($fornitoriIdMap)); if (! empty($fornitoriIds) && Schema::hasTable('fornitori')) { $fornitoriRows = DB::table('fornitori') ->whereIn('id', $fornitoriIds) ->get(['id', 'ragione_sociale', 'nome', 'cognome']); foreach ($fornitoriRows as $fornitoreRow) { $label = trim((string) ($fornitoreRow->ragione_sociale ?? '')); if ($label === '') { $label = trim((string) (($fornitoreRow->nome ?? '') . ' ' . ($fornitoreRow->cognome ?? ''))); } $fornitoriLocaliById[(int) $fornitoreRow->id] = $label !== '' ? $label : null; } } if ($activeStabileId && Schema::hasTable('voci_spesa')) { $vociRows = DB::table('voci_spesa') ->where('stabile_id', (int) $activeStabileId) ->get(['codice', 'legacy_codice', 'descrizione', 'tabella_millesimale_default_id']); $tabellaIds = $vociRows ->pluck('tabella_millesimale_default_id') ->filter(fn($value) => is_numeric($value)) ->map(fn($value) => (int) $value) ->unique() ->values() ->all(); $tabellaLocaleMap = []; if (! empty($tabellaIds) && Schema::hasTable('tabelle_millesimali')) { $tabellaLocaleMap = DB::table('tabelle_millesimali') ->whereIn('id', $tabellaIds) ->get(['id', 'codice_tabella', 'nome_tabella']) ->mapWithKeys(function ($tabellaRow) { $name = trim((string) ($tabellaRow->nome_tabella ?? '')); $code = trim((string) ($tabellaRow->codice_tabella ?? '')); return [(int) $tabellaRow->id => ($name !== '' ? $name : $code)]; }) ->all(); } foreach ($vociRows as $voceRow) { $codes = array_filter([ strtoupper(trim((string) ($voceRow->codice ?? ''))), strtoupper(trim((string) ($voceRow->legacy_codice ?? ''))), ]); $voceData = [ 'descrizione' => trim((string) ($voceRow->descrizione ?? '')), 'tabella' => $tabellaLocaleMap[(int) ($voceRow->tabella_millesimale_default_id ?? 0)] ?? null, ]; foreach ($codes as $code) { $vociLocaliByCode[$code] = $voceData; } } } if (! empty($fornitoriIds) && DB::getSchemaBuilder()->hasTable('contabilita_fatture_fornitori')) { $fatture = DB::table('contabilita_fatture_fornitori') ->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId)) ->whereIn('fornitore_id', $fornitoriIds) ->select([ 'id', 'fornitore_id', 'numero_documento', 'data_documento', 'totale', 'netto_da_pagare', 'data_pagamento', 'ritenuta_importo', 'data_versamento_ra', 'stato', ]) ->get(); foreach ($fatture as $f) { $fattureByFornitore[$f->fornitore_id][] = $f; $docNumber = $this->normalizeInvoiceNumber($f->numero_documento ?? null); $docDate = ! empty($f->data_documento) ? (string) \Carbon\Carbon::parse($f->data_documento)->format('Y-m-d') : null; if ($docNumber !== null && $docDate !== null) { $key = (int) $f->fornitore_id . '|' . $docNumber . '|' . $docDate; $fattureByFornitoreDocumento[$key][] = $f; $globalKey = $docNumber . '|' . $docDate; $fattureByDocumentoGlobale[$globalKey][] = $f; } } } $mapped = $items->map(function ($row) use ($vociMap, $vociTabellaMap, $tabelleMap, $fornitoriMap, $fornitoriIdMap, $fattureByFornitore, $fattureByFornitoreDocumento, $fattureByDocumentoGlobale, $fornitoriLocaliById, $vociLocaliByCode, $codFornToLegacyId, $rdaByCode) { $row->voce_descrizione = $vociMap[$row->cod_spe ?? ''] ?? null; if (empty($row->tabella) && ! empty($vociTabellaMap[$row->cod_spe ?? ''])) { $row->tabella = $vociTabellaMap[$row->cod_spe ?? '']; } $row->tabella_descrizione = $tabelleMap[$row->tabella ?? ''] ?? null; $codFor = trim((string) ($row->cod_for ?? '')); $codForNoZero = ltrim($codFor, '0'); $row->fornitore_nome = $fornitoriMap[$codFor] ?? ($codForNoZero !== '' ? ($fornitoriMap[$codForNoZero] ?? null) : null); $row->fornitore_id = null; if ($codFor !== '') { $legacyId = $codFornToLegacyId[$codFor] ?? ($codForNoZero !== '' ? ($codFornToLegacyId[$codForNoZero] ?? null) : null); if ($legacyId && isset($fornitoriIdMap[$legacyId])) { $row->fornitore_id = (int) $fornitoriIdMap[$legacyId]; } } $row->fornitore_locale_nome = null; $row->voce_spesa_locale = null; $row->tabella_locale = null; if ($this->netgesconWorkflowMode) { $row->fornitore_locale_nome = $row->fornitore_id ? ($fornitoriLocaliById[$row->fornitore_id] ?? null) : null; $voceLocale = $vociLocaliByCode[strtoupper((string) ($row->cod_spe ?? ''))] ?? null; $row->voce_spesa_locale = $voceLocale['descrizione'] ?? null; $row->tabella_locale = $voceLocale['tabella'] ?? null; } $row->rda_code = null; $row->is_rda_line = false; if (! empty($row->rif_rda)) { $row->rda_code = $row->rif_rda; } elseif (is_string($row->benef ?? null) && str_starts_with(strtoupper(trim((string) $row->benef)), 'RDA:')) { $row->rda_code = trim(substr(trim((string) $row->benef), 4)); $row->is_rda_line = true; } $row->ra_importo = $row->netto_vers_rda ?? $row->importo_euro_770 ?? null; $row->ra_data = null; $row->fe_numero = null; $row->fe_totale = null; $row->fe_fattura_id = null; $row->fe_ricevuta_id = null; $row->fe_display = $row->fe_uid ?? null; if ($row->ra_importo !== null) { $raw = (string) $row->ra_importo; $raw = str_replace(['.', ' '], ['', ''], $raw); $raw = str_replace(',', '.', $raw); $row->ra_importo = is_numeric($raw) ? (float) $raw : null; } $amount = (float) ($row->importo_euro ?? $row->importo ?? 0); $row->importo_signed = $this->computeSignedAmount($row); $row->rda_row_importo = null; $row->rda_row_data = null; if (! $row->is_rda_line && ! empty($row->rda_code) && isset($rdaByCode[$row->rda_code])) { $row->rda_row_importo = $rdaByCode[$row->rda_code]['importo'] ?? null; $row->rda_row_data = $rdaByCode[$row->rda_code]['data'] ?? null; } $fornId = $row->fornitore_id; $best = null; $docNumber = $this->normalizeInvoiceNumber($row->num_fat ?? null); $docDate = ! empty($row->dt_fat) ? (string) \Carbon\Carbon::parse($row->dt_fat)->format('Y-m-d') : null; if ($this->netgesconWorkflowMode && $fornId && $docNumber !== null && $docDate !== null) { $strictKey = $fornId . '|' . $docNumber . '|' . $docDate; if (! empty($fattureByFornitoreDocumento[$strictKey])) { $best = $fattureByFornitoreDocumento[$strictKey][0]; } } if ($this->netgesconWorkflowMode && ! $best && $docNumber !== null && $docDate !== null) { $globalKey = $docNumber . '|' . $docDate; $globalMatches = $fattureByDocumentoGlobale[$globalKey] ?? []; if (count($globalMatches) === 1) { $best = $globalMatches[0]; } } if (! $best && $fornId && isset($fattureByFornitore[$fornId])) { foreach ($fattureByFornitore[$fornId] as $f) { $tot = (float) ($f->totale ?? 0); $net = (float) ($f->netto_da_pagare ?? 0); $matchTot = abs(abs($amount) - $tot) <= 0.01; $matchNet = abs(abs($amount) - $net) <= 0.01; if ($matchTot || $matchNet) { $best = $f; break; } } } if ($best) { $row->fe_fattura_id = $best->id ?? null; $row->fe_ricevuta_id = $best->fattura_elettronica_id ?? null; $row->fe_numero = $best->numero_documento ?? null; $row->fe_totale = $best->totale ?? $best->netto_da_pagare ?? null; $row->fe_data = $best->data_documento ?? null; $row->fe_netto = $best->netto_da_pagare ?? null; $row->fe_stato = $best->stato ?? null; $row->bonifico_data = $best->data_pagamento ?? null; $row->ra_importo = $row->ra_importo ?? ($best->ritenuta_importo ?? null); $row->ra_data = $best->data_versamento_ra ?? null; } if ($this->netgesconWorkflowMode && empty($row->bonifico_data) && ! empty($row->dt_spe)) { $row->bonifico_data = $row->dt_spe; } $row->rda_importo_calcolata = null; $row->rda_scadenza = null; $row->rda_stato = null; $row->protocollo_logico = null; if ($this->netgesconWorkflowMode) { $row->rda_importo_calcolata = $row->rda_row_importo ?? $row->ra_importo ?? null; $row->rda_scadenza = $this->computeRdaDueDate($row->bonifico_data ?? null); if (! empty($row->rda_importo_calcolata) && $row->rda_scadenza) { if (! empty($row->rda_row_data)) { $row->rda_stato = (\Carbon\Carbon::parse($row->rda_row_data)->lte(\Carbon\Carbon::parse($row->rda_scadenza))) ? 'versata in termine' : 'versata in ritardo'; } else { $row->rda_stato = 'da versare'; } } $row->protocollo_logico = 'debito'; if (! empty($row->rda_importo_calcolata)) { $row->protocollo_logico = 'bonifico+rda'; } } if (! empty($row->rda_code) && empty($row->fe_display) && ! empty($row->ra_importo)) { $row->fe_display = 'RA € ' . number_format((float) $row->ra_importo, 2, ',', '.'); } if (! empty($row->rda_code) && $row->fe_fattura_id && ! empty($row->dt_spe) && ! empty($row->importo_spese) && empty($row->ra_data)) { try { $dt = \Carbon\Carbon::parse($row->dt_spe)->format('Y-m-d'); DB::table('contabilita_fatture_fornitori') ->where('id', $row->fe_fattura_id) ->update([ 'data_versamento_ra' => $dt, 'ritenuta_importo' => $row->ra_importo, 'updated_at' => now(), ]); $row->ra_data = $dt; } catch (\Throwable) { } } $legacyCode = $this->resolveLegacyCodiceStabile(); if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) { $query->where('cod_stabile', $legacyCode); } $row->tipo_movimento = null; if (! empty($row->importo_spese)) { $row->tipo_movimento = 'Spesa'; } elseif (! empty($row->importo_entrate)) { $row->tipo_movimento = 'Entrata'; } elseif (! empty($row->importo_debiti)) { $row->tipo_movimento = 'Debito'; } elseif (! empty($row->importo_crediti)) { $row->tipo_movimento = 'Credito'; } return $row; }); $paginator->setCollection( $mapped->filter(fn($row) => empty($row->is_rda_line))->values() ); $indivRows = $items->filter(fn($r) => in_array(strtoupper((string) ($r->cod_spe ?? '')), ['I05', 'I06'], true) && ! empty($r->n_spe)); if ($indivRows->isNotEmpty()) { $nSpeList = $indivRows->pluck('n_spe')->filter()->unique()->values()->all(); $dettMap = DB::connection('gescon_import') ->table('dett_pers') ->whereIn('n_spe', $nSpeList) ->select('n_spe') ->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale') ->groupBy('n_spe') ->pluck('totale', 'n_spe') ->all(); $paginator->setCollection( $paginator->getCollection()->map(function ($row) use ($dettMap) { if (in_array(strtoupper((string) ($row->cod_spe ?? '')), ['I05', 'I06'], true) && ! empty($row->n_spe)) { $detTot = $this->normalizeMoney($dettMap[$row->n_spe] ?? 0); $imp = $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0); $row->dett_pers_totale = $detTot; $row->dett_pers_delta = $imp - $detTot; $row->dett_pers_ok = abs($row->dett_pers_delta) <= 0.01; } return $row; }) ); } return $paginator; } public function getConsuntivoProperty(): array { $base = $this->buildOperazioniQuery(); $excludeCods = ['Z01', 'Z02', 'I05']; if (DB::connection('gescon_import')->getSchemaBuilder()->hasTable('cre_deb_preced')) { $creDebCodes = DB::connection('gescon_import') ->table('cre_deb_preced') ->pluck('cod_voc') ->filter() ->unique() ->values() ->all(); if (! empty($creDebCodes)) { $excludeCods = array_values(array_unique(array_merge($excludeCods, $creDebCodes))); } } $rows = $base ->whereNotIn('cod_spe', $excludeCods) ->select('tabella', 'cod_spe') ->selectRaw("SUM( CASE WHEN (COALESCE(importo_spese, 0) + COALESCE(importo_entrate, 0) + COALESCE(importo_debiti, 0) + COALESCE(importo_crediti, 0)) = 0 THEN CASE WHEN cod_spe = 'RB1' THEN -1 * COALESCE(importo_euro, importo, 0) WHEN natura2 IN ('E', 'ENTRATA', 'ENTRATE', 'CREDITO') THEN -1 * COALESCE(importo_euro, importo, 0) ELSE COALESCE(importo_euro, importo, 0) END ELSE (COALESCE(importo_spese, 0) + COALESCE(importo_debiti, 0) - COALESCE(importo_entrate, 0) - COALESCE(importo_crediti, 0)) END ) as totale") ->groupBy('tabella', 'cod_spe') ->get(); $frazRows = collect(); if (Schema::connection('gescon_import')->hasTable('fraz_dett') && Schema::connection('gescon_import')->hasTable('fraz_gen')) { $parteCol = null; if (Schema::connection('gescon_import')->hasColumn('fraz_dett', 'parte')) { $parteCol = 'parte'; } elseif (Schema::connection('gescon_import')->hasColumn('fraz_dett', 'spe_parte')) { $parteCol = 'spe_parte'; } $frazQuery = DB::connection('gescon_import') ->table('fraz_dett as fd') ->join('fraz_gen as fg', 'fg.Progressivo', '=', 'fd.rif_progressivo') ->select([ 'fg.Progressivo as progressivo', 'fg.cod_voce as cod_voce_gen', 'fg.Importo_voce as importo_voce', 'fd.cod_voce as cod_voce_det', 'fd.Importo_parte as importo_parte', ]); if ($parteCol) { $frazQuery->addSelect("fd.$parteCol as parte"); } if ($this->filterAnno) { $frazQuery->where('fg.legacy_year', (string) $this->filterAnno); } $frazItems = $frazQuery->get(); if ($frazItems->isNotEmpty()) { $sumParteByProg = []; if ($parteCol) { foreach ($frazItems as $r) { $prog = (string) ($r->progressivo ?? ''); if ($prog === '') { continue; } $parteVal = is_numeric($r->parte ?? null) ? (float) $r->parte : 0.0; $sumParteByProg[$prog] = ($sumParteByProg[$prog] ?? 0.0) + $parteVal; } } $totByVoce = []; foreach ($frazItems as $r) { $codVoceDet = trim((string) ($r->cod_voce_det ?? '')); $codVoceGen = trim((string) ($r->cod_voce_gen ?? '')); $codVoce = $codVoceDet !== '' ? $codVoceDet : $codVoceGen; if ($codVoce === '') { continue; } $impParte = is_numeric($r->importo_parte ?? null) ? (float) $r->importo_parte : 0.0; $impVoce = is_numeric($r->importo_voce ?? null) ? (float) $r->importo_voce : 0.0; if (abs($impParte) < 0.00001 && $parteCol) { $prog = (string) ($r->progressivo ?? ''); $base = $sumParteByProg[$prog] ?? 0.0; $parteVal = is_numeric($r->parte ?? null) ? (float) $r->parte : 0.0; if ($base > 0 && $impVoce != 0.0) { $impParte = $impVoce * ($parteVal / $base); } } if (abs($impParte) < 0.00001) { continue; } $totByVoce[$codVoce] = ($totByVoce[$codVoce] ?? 0.0) + $impParte; } $frazRows = collect($totByVoce)->map(function ($totale, $codVoce) { return (object) [ 'tabella' => null, 'cod_spe' => $codVoce, 'totale' => $totale, 'origin' => 'fraz', ]; })->values(); } } $i05Operazioni = DB::connection('gescon_import') ->table('operazioni') ->where('cod_spe', 'I05') ->when( $this->resolveLegacyCodiceStabile() && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'), fn($q) => $q->where('cod_stabile', $this->resolveLegacyCodiceStabile()) ) ->when($this->filterAnno, fn($q) => $q->whereYear('dt_spe', $this->filterAnno)) ->get(['n_spe', 'importo_euro', 'importo']); $nSpeI05 = $i05Operazioni ->pluck('n_spe') ->filter(fn($value) => is_numeric($value)) ->map(fn($value) => (int) $value) ->unique() ->values() ->all(); $dettByNSpe = []; if (! empty($nSpeI05) && Schema::connection('gescon_import')->hasTable('dett_pers')) { $dettByNSpe = DB::connection('gescon_import') ->table('dett_pers') ->whereIn('n_spe', $nSpeI05) ->select('n_spe') ->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale') ->groupBy('n_spe') ->pluck('totale', 'n_spe') ->all(); } $i05 = $i05Operazioni->sum(function ($row) use ($dettByNSpe) { $nSpe = is_numeric($row->n_spe ?? null) ? (int) $row->n_spe : null; if ($nSpe !== null && array_key_exists($nSpe, $dettByNSpe)) { return $this->normalizeMoney($dettByNSpe[$nSpe]); } return $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0); }); $allRows = $rows ->map(function ($r) { $r->origin = 'base'; return $r; }) ->concat($frazRows) ->when($i05 !== null, function ($c) use ($i05) { return $c->push((object) [ 'tabella' => 'INDIV', 'cod_spe' => 'I05', 'totale' => $i05, 'origin' => 'i05', ]); }); $codes = $allRows->pluck('cod_spe')->filter()->unique()->values()->all(); $vociMap = []; $tabellaByCod = []; if (! empty($codes)) { $vocRows = DB::connection('gescon_import') ->table('voc_spe') ->whereIn('cod', $codes) ->get(['cod', 'descriz', 'tabella']); $vociMap = $vocRows->mapWithKeys(fn($v) => [$v->cod => $v->descriz])->all(); $tabellaByCod = $vocRows->mapWithKeys(fn($v) => [$v->cod => $v->tabella])->all(); } $tabCodes = $allRows->map(function ($r) use ($tabellaByCod) { return $r->tabella ?: ($tabellaByCod[$r->cod_spe ?? ''] ?? null); })->filter()->unique()->values()->all(); $tabellaMap = []; $tabellaInfo = []; if (! empty($tabCodes)) { $tabellaMap = DB::connection('gescon_import') ->table('tabelle_millesimali') ->whereIn('cod_tabella', $tabCodes) ->pluck('denominazione', 'cod_tabella') ->all(); $tabellaInfo = DB::connection('gescon_import') ->table('tabelle_millesimali') ->whereIn('cod_tabella', $tabCodes) ->get(['cod_tabella', 'denominazione', 'descrizione', 'tipologia', 'nord']) ->keyBy('cod_tabella') ->all(); } $rows = $allRows->map(function ($r) use ($vociMap, $tabellaByCod, $tabellaMap, $tabellaInfo) { $tabella = $r->tabella ?: ($tabellaByCod[$r->cod_spe ?? ''] ?? null); $forcedHeader = null; $codSpe = strtoupper((string) ($r->cod_spe ?? '')); if (in_array($codSpe, ['I05', 'I06'], true)) { $tabella = 'INDIV.'; $forcedHeader = 'INDIV. ADDEBITI INDIVIDUALI'; } $info = $tabella && isset($tabellaInfo[$tabella]) ? $tabellaInfo[$tabella] : null; $denom = $info?->denominazione ?? ($tabellaMap[$tabella ?? ''] ?? null); $descr = $info?->descrizione ?? null; $header = trim(($tabella ?? '—') . ' ' . ($denom ?? $descr ?? '')); if ($denom && $descr && $denom !== $descr) { $header = trim(($tabella ?? '—') . ' ' . $denom . ' (' . $descr . ')'); } if ($forcedHeader) { $header = $forcedHeader; } return [ 'tabella' => $tabella ?? '—', 'tabella_descrizione' => $tabellaMap[$tabella ?? ''] ?? null, 'tabella_header' => $header, 'tabella_tipologia' => $info?->tipologia ?? null, 'tabella_nord' => is_numeric($info?->nord ?? null) ? (int) $info->nord : 9999, 'cod_spe' => $r->cod_spe ?? '—', 'voce' => $vociMap[$r->cod_spe ?? ''] ?? null, 'totale' => (float) ($r->totale ?? 0), ]; }) ->filter(function (array $r): bool { $tot = (float) ($r['totale'] ?? 0); if ($tot !== 0.0) { return true; } $tab = trim((string) ($r['tabella'] ?? '')); $voce = trim((string) ($r['voce'] ?? '')); if ($tab === '—') { $tab = ''; } if ($voce === '—') { $voce = ''; } return $tab !== '' || $voce !== ''; }) ->values(); $rows = $rows->sortBy(function ($r) { return sprintf('%05d|%s|%s', (int) ($r['tabella_nord'] ?? 9999), $r['tabella'] ?? '', $r['cod_spe'] ?? ''); })->values(); $groups = [ 'ordinarie' => [], 'straordinarie' => [], 'acqua' => [], ]; foreach ($rows as $r) { $tab = (string) ($r['tabella'] ?? ''); $tip = strtoupper((string) ($r['tabella_tipologia'] ?? '')); $groupKey = 'ordinarie'; if ($tab === 'ACQUA' || str_starts_with($tab, 'AC')) { $groupKey = 'acqua'; } elseif (in_array($tip, ['S', 'R'], true)) { $groupKey = 'straordinarie'; } if (! isset($groups[$groupKey][$tab])) { $groups[$groupKey][$tab] = [ 'header' => $r['tabella_header'] ?? $tab, 'nord' => $r['tabella_nord'] ?? 9999, 'rows' => [], 'subtotal' => 0.0, ]; } $groups[$groupKey][$tab]['rows'][] = $r; $groups[$groupKey][$tab]['subtotal'] += (float) ($r['totale'] ?? 0); } foreach ($groups as $k => $tabs) { uasort($tabs, function ($a, $b) { return ($a['nord'] ?? 9999) <=> ($b['nord'] ?? 9999); }); $groups[$k] = $tabs; } return $groups; } public function getIncassiProperty() { if (! Schema::connection('gescon_import')->hasTable('incassi')) { return null; } $q = DB::connection('gescon_import')->table('incassi'); $hasCondomin = Schema::connection('gescon_import')->hasTable('condomin'); if ($hasCondomin) { $hasIncassiCodStabile = Schema::connection('gescon_import')->hasColumn('incassi', 'cod_stabile'); $hasCondominCodStabile = Schema::connection('gescon_import')->hasColumn('condomin', 'cod_stabile'); $hasCondominLegacyYear = Schema::connection('gescon_import')->hasColumn('condomin', 'legacy_year'); $q->leftJoin('condomin', function ($join) use ($hasIncassiCodStabile, $hasCondominCodStabile, $hasCondominLegacyYear): void { $join->on('condomin.cod_cond', '=', 'incassi.cod_cond'); if ($hasIncassiCodStabile && $hasCondominCodStabile) { $join->on('condomin.cod_stabile', '=', 'incassi.cod_stabile'); } if ($hasCondominLegacyYear) { $join->where('condomin.legacy_year', '=', '0001'); } }); } $this->applyIncassiFilters($q); $codStabile = $this->resolveLegacyCodiceStabile(); $orderCol = Schema::connection('gescon_import')->hasColumn('incassi', 'dt_empag') ? 'dt_empag' : 'id'; $paginator = $q ->select([ 'incassi.*', 'condomin.scala as cond_scala', 'condomin.interno as cond_interno', 'condomin.cognome as cond_cognome', 'condomin.nome as cond_nome', 'condomin.proprietario_denominazione as cond_proprietario_denominazione', 'condomin.inquilino_denominazione as cond_inquilino_denominazione', 'condomin.inquil_nome as cond_inquil_nome', ]) ->orderByDesc($orderCol) ->orderByDesc('id') ->paginate($this->perPage); $activeStabileId = StabileContext::resolveActiveStabileId(Auth::user()); $personaByCond = []; $rateRowsByCond = []; $codConds = $paginator->getCollection() ->pluck('cod_cond') ->filter(fn($v) => is_numeric($v)) ->map(fn($v) => (int) $v) ->unique() ->values() ->all(); if (! empty($codConds) && Schema::connection('gescon_import')->hasTable('rate')) { $rateQuery = DB::connection('gescon_import') ->table('rate') ->whereIn('cod_cond', $codConds); if ($codStabile && Schema::connection('gescon_import')->hasColumn('rate', 'cod_stabile')) { $rateQuery->where('cod_stabile', $codStabile); } if ($this->filterAnno) { $year = (int) $this->filterAnno; $rateQuery->where(function ($q) use ($year): void { if (Schema::connection('gescon_import')->hasColumn('rate', 'data_emissione')) { $q->orWhereYear('data_emissione', $year); } if (Schema::connection('gescon_import')->hasColumn('rate', 'data_scadenza')) { $q->orWhereYear('data_scadenza', $year); } if (Schema::connection('gescon_import')->hasColumn('rate', 'data_pagamento')) { $q->orWhereYear('data_pagamento', $year); } }); } $rateRows = $rateQuery->get([ 'cod_cond', 'n_emissione', 'data_emissione', 'data_scadenza', 'importo', 'importo_euro', 'causale', 'tipo_rata', 'pagata', 'data_pagamento', ]); $rateRowsByCond = $rateRows ->groupBy(fn($r) => (int) ($r->cod_cond ?? 0)) ->map(fn($rows) => $rows->values()->all()) ->all(); } if ($activeStabileId && Schema::hasTable('unita_immobiliari') && Schema::hasTable('persone_unita_relazioni') && Schema::hasTable('persone')) { if (! empty($codConds) && Schema::hasColumn('unita_immobiliari', 'legacy_cond_id')) { $unitaRows = DB::table('unita_immobiliari') ->where('stabile_id', (int) $activeStabileId) ->whereIn('legacy_cond_id', $codConds) ->get(['id', 'legacy_cond_id']); $unitaIds = $unitaRows->pluck('id')->map(fn($v) => (int) $v)->all(); $legacyByUnita = $unitaRows->mapWithKeys(fn($r) => [(int) $r->id => (int) $r->legacy_cond_id])->all(); if (! empty($unitaIds)) { $relRows = DB::table('persone_unita_relazioni as pur') ->join('persone as p', 'p.id', '=', 'pur.persona_id') ->whereIn('pur.unita_id', $unitaIds) ->where('pur.attivo', true) ->where(function ($q): void { $q->whereNull('pur.data_fine')->orWhereDate('pur.data_fine', '>=', now()->toDateString()); }) ->select(['pur.persona_id', 'pur.unita_id', 'pur.tipo_relazione', 'pur.data_inizio']) ->orderByDesc('pur.data_inizio') ->orderByDesc('pur.id') ->get(); foreach ($relRows as $rel) { $unitaId = (int) ($rel->unita_id ?? 0); $codCond = (int) ($legacyByUnita[$unitaId] ?? 0); if ($codCond <= 0) { continue; } $tipo = strtolower(trim((string) ($rel->tipo_relazione ?? ''))); $pid = (int) ($rel->persona_id ?? 0); if ($pid <= 0) { continue; } $roleClass = 'C'; if (in_array($tipo, ['inquilino', 'conduttore', 'affittuario', 'locatario'], true)) { $roleClass = 'I'; } $score = $this->relationScoreForIncasso($tipo, $roleClass); $current = $personaByCond[$codCond][$roleClass] ?? null; if (! $current || $score > ($current['score'] ?? -1)) { $personaByCond[$codCond][$roleClass] = [ 'persona_id' => $pid, 'score' => $score, ]; } } } } } $paginator->setCollection( $paginator->getCollection()->map(function ($row) use ($personaByCond, $rateRowsByCond) { $tipo = strtoupper(trim((string) ($row->cond_inq ?? $row->cond_inquil ?? ''))); $nominativo = ''; if ($tipo === 'I') { $nominativo = trim((string) ($row->cond_inquilino_denominazione ?? '')); if ($nominativo === '') { $nominativo = trim((string) ($row->cond_inquil_nome ?? '')); } } else { $nominativo = trim((string) ($row->cond_proprietario_denominazione ?? '')); if ($nominativo === '') { $nominativo = trim((string) (($row->cond_cognome ?? '') . ' ' . ($row->cond_nome ?? ''))); } } $row->nominativo_pagante = $nominativo !== '' ? $nominativo : '—'; $row->scala_interno_short = $this->formatScalaInternoShort($row->cond_scala ?? null, $row->cond_interno ?? null); $row->data_visuale = $row->dt_empag ?? $row->data_incasso ?? null; $row->riferimento_visuale = $row->riferimento_pagamento ?? $row->n_riferimento ?? $row->n_rata_riferimento ?? $row->n_ricevuta ?? $row->n_mese ?? null; $row->importo_visualizzato = $this->resolveIncassoImporto($row); $codCond = is_numeric($row->cod_cond ?? null) ? (int) $row->cod_cond : 0; $personaRef = null; if ($codCond > 0) { $personaRef = $personaByCond[$codCond][$tipo] ?? ($personaByCond[$codCond]['C'] ?? null); } $row->persona_id = $personaRef['persona_id'] ?? null; $row->persona_url = $row->persona_id ? \App\Filament\Pages\Gescon\PersonaScheda::getUrl(['record' => (int) $row->persona_id], panel : 'admin-filament') : null; $row->rata_match = null; $row->rata_match_source = null; if ($codCond > 0 && ! empty($rateRowsByCond[$codCond] ?? [])) { $match = $this->findRateMatchForIncasso($row, $rateRowsByCond[$codCond]); if ($match) { $row->rata_match = $match; $row->rata_match_source = 'rate'; } } return $row; }) ); return $paginator; } public function getIncassiRifSummaryProperty(): array { if (! Schema::connection('gescon_import')->hasTable('incassi')) { return ['rows' => [], 'totale' => 0.0, 'totale_ccb' => 0.0, 'count' => 0]; } $query = DB::connection('gescon_import')->table('incassi'); $this->applyIncassiFilters($query); $select = ['id']; foreach (['riferimento_pagamento', 'n_riferimento', 'n_rata_riferimento', 'n_ricevuta', 'n_mese', 'importo_euro', 'importo', 'importo_pagato_euro', 'dt_empag', 'data_incasso', 'cod_cassa'] as $col) { if (Schema::connection('gescon_import')->hasColumn('incassi', $col)) { $select[] = $col; } } $rows = $query->get($select); $summary = []; $totale = 0.0; $totaleCcb = 0.0; foreach ($rows as $row) { $rif = trim((string) ($row->riferimento_pagamento ?? $row->n_riferimento ?? $row->n_rata_riferimento ?? $row->n_ricevuta ?? $row->n_mese ?? '')); if ($rif === '') { $rif = '—'; } $importo = $this->resolveIncassoImporto($row); $totale += $importo; $isCcb = strtoupper(trim((string) ($row->cod_cassa ?? ''))) === 'CCB'; if ($isCcb) { $totaleCcb += $importo; } if (! isset($summary[$rif])) { $summary[$rif] = [ 'rif' => $rif, 'count' => 0, 'totale' => 0.0, 'totale_ccb' => 0.0, 'ultima_data' => null, 'casse' => [], ]; } $summary[$rif]['count']++; $summary[$rif]['totale'] += $importo; if ($isCcb) { $summary[$rif]['totale_ccb'] += $importo; } $cassa = trim((string) ($row->cod_cassa ?? '')); if ($cassa !== '') { $summary[$rif]['casse'][$cassa] = true; } $data = $row->dt_empag ?? $row->data_incasso ?? null; if (! empty($data)) { try { $ymd = (string) \Carbon\Carbon::parse($data)->format('Y-m-d'); if (empty($summary[$rif]['ultima_data']) || $ymd > (string) $summary[$rif]['ultima_data']) { $summary[$rif]['ultima_data'] = $ymd; } } catch (\Throwable) { } } } $rowsOut = collect($summary) ->map(function (array $row) { $row['totale'] = round((float) ($row['totale'] ?? 0), 2); $row['totale_ccb'] = round((float) ($row['totale_ccb'] ?? 0), 2); $row['casse'] = implode(', ', array_keys($row['casse'] ?? [])); return $row; }) ->sortByDesc('totale') ->values() ->all(); return [ 'rows' => $rowsOut, 'totale' => round($totale, 2), 'totale_ccb' => round($totaleCcb, 2), 'count' => (int) $rows->count(), ]; } public function getRaMonthlyTributoSummaryProperty(): array { if (! Schema::hasTable('contabilita_fatture_fornitori')) { return ['rows' => [], 'totale_fatta' => 0.0, 'totale_versata' => 0.0, 'totale_delta' => 0.0]; } $activeStabileId = StabileContext::resolveActiveStabileId(Auth::user()); $query = DB::table('contabilita_fatture_fornitori') ->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId)) ->whereRaw('COALESCE(ritenuta_importo, 0) <> 0'); if ($this->filterAnno) { $year = (int) $this->filterAnno; $query->where(function ($q) use ($year): void { $q->whereYear('data_documento', $year) ->orWhereYear('data_versamento_ra', $year); }); } $rows = $query->get([ 'fornitore_id', 'data_documento', 'data_versamento_ra', 'ritenuta_importo', 'ritenuta_codice_tributo', ]); $fornitoreIds = $rows ->pluck('fornitore_id') ->filter(fn($v) => is_numeric($v)) ->map(fn($v) => (int) $v) ->unique() ->values() ->all(); $tributoByFornitore = $this->resolveTributoByFornitoreIdMap($fornitoreIds); $summary = []; $totFatta = 0.0; $totVersata = 0.0; foreach ($rows as $row) { $importo = abs($this->normalizeMoney($row->ritenuta_importo ?? 0)); if ($importo <= 0) { continue; } $tributo = strtoupper(trim((string) ($row->ritenuta_codice_tributo ?? ''))); $fornitoreId = is_numeric($row->fornitore_id ?? null) ? (int) $row->fornitore_id : null; if ($tributo === '' && $fornitoreId && ! empty($tributoByFornitore[$fornitoreId])) { $tributo = (string) $tributoByFornitore[$fornitoreId]; } if ($tributo === '') { $tributo = '1040'; } if (! empty($row->data_documento)) { try { $mese = (string) \Carbon\Carbon::parse($row->data_documento)->format('Y-m'); $key = $mese . '|' . $tributo; if (! isset($summary[$key])) { $summary[$key] = ['mese' => $mese, 'tributo' => $tributo, 'fatta' => 0.0, 'versata' => 0.0]; } $summary[$key]['fatta'] += $importo; $totFatta += $importo; } catch (\Throwable) { } } if (! empty($row->data_versamento_ra)) { try { $mese = (string) \Carbon\Carbon::parse($row->data_versamento_ra)->format('Y-m'); $key = $mese . '|' . $tributo; if (! isset($summary[$key])) { $summary[$key] = ['mese' => $mese, 'tributo' => $tributo, 'fatta' => 0.0, 'versata' => 0.0]; } $summary[$key]['versata'] += $importo; $totVersata += $importo; } catch (\Throwable) { } } } $rowsOut = collect($summary) ->map(function (array $row) { $fatta = round((float) ($row['fatta'] ?? 0), 2); $versata = round((float) ($row['versata'] ?? 0), 2); $row['fatta'] = $fatta; $row['versata'] = $versata; $row['delta'] = round($fatta - $versata, 2); return $row; }) ->sortBy(['mese', 'tributo']) ->values() ->all(); return [ 'rows' => $rowsOut, 'totale_fatta' => round($totFatta, 2), 'totale_versata' => round($totVersata, 2), 'totale_delta' => round($totFatta - $totVersata, 2), ]; } public function getFattureRicevuteConsolidateProperty() { if (! Schema::hasTable('contabilita_fatture_fornitori')) { return null; } $activeStabileId = StabileContext::resolveActiveStabileId(Auth::user()); if (! $activeStabileId) { return null; } $query = DB::table('contabilita_fatture_fornitori as ff') ->leftJoin('fornitori as f', 'f.id', '=', 'ff.fornitore_id') ->leftJoin('fatture_elettroniche as fe', 'fe.id', '=', 'ff.fattura_elettronica_id') ->where('ff.stabile_id', (int) $activeStabileId); if ($this->filterAnno) { $query->whereYear('ff.data_documento', (int) $this->filterAnno); } if (! empty($this->filterCodForn)) { $needle = trim((string) $this->filterCodForn); $query->where(function ($q) use ($needle): void { $q->where('f.old_id', $needle) ->orWhere('f.old_id', ltrim($needle, '0')); }); } if (! empty($this->search)) { $needle = '%' . trim((string) $this->search) . '%'; $query->where(function ($q) use ($needle): void { $q->orWhere('ff.numero_documento', 'like', $needle) ->orWhere('ff.descrizione', 'like', $needle) ->orWhere('f.ragione_sociale', 'like', $needle) ->orWhere('fe.sdi_file', 'like', $needle); }); } return $query ->select([ 'ff.id', 'ff.data_documento', 'ff.numero_documento', 'ff.totale', 'ff.netto_da_pagare', 'ff.stato', 'ff.data_pagamento', 'ff.fattura_elettronica_id', 'ff.ritenuta_importo', 'ff.data_versamento_ra', 'f.old_id as cod_fornitore_legacy', 'f.ragione_sociale as fornitore_nome', 'fe.sdi_file as fe_sdi_file', ]) ->orderByDesc('ff.data_documento') ->orderByDesc('ff.id') ->paginate($this->perPage, ['*'], 'fattureRicevutePage'); } public function getFattureLegacyProperty() { $sourceTable = null; if (Schema::hasTable('stg_fatture_gescon')) { $sourceTable = 'stg_fatture_gescon'; } elseif (Schema::connection('gescon_import')->hasTable('fatture')) { $sourceTable = 'fatture'; } if (! $sourceTable) { return null; } if ($sourceTable === 'stg_fatture_gescon') { $query = DB::table('stg_fatture_gescon as f'); } else { $query = DB::connection('gescon_import')->table('fatture as f'); } $activeStabileId = StabileContext::resolveActiveStabileId(Auth::user()); $activeTenantId = null; $legacyStabileIdCandidates = []; $legacyCodiceCandidates = []; $legacyCode = $this->resolveLegacyCodiceStabile(); if ($legacyCode) { if (Schema::hasTable('stabili')) { $stabileRow = DB::table('stabili') ->where('codice_stabile', $legacyCode) ->first(['id', 'old_id', 'cod_stabile', 'codice_stabile', 'amministratore_id']); if ($stabileRow && ! empty($stabileRow->amministratore_id)) { $activeTenantId = (string) $stabileRow->amministratore_id; } foreach ([(string) ($stabileRow->old_id ?? ''), (string) ($stabileRow->cod_stabile ?? ''), (string) ($stabileRow->codice_stabile ?? $legacyCode)] as $value) { $trim = trim($value); if ($trim === '') { continue; } $legacyCodiceCandidates[] = $trim; $noZero = ltrim($trim, '0'); if ($noZero !== '') { $legacyCodiceCandidates[] = $noZero; } if (is_numeric($trim)) { $legacyStabileIdCandidates[] = (int) $trim; $normalized = ltrim($trim, '0'); if ($normalized !== '' && is_numeric($normalized)) { $legacyStabileIdCandidates[] = (int) $normalized; } } } } else { $legacyCodiceCandidates[] = $legacyCode; $noZero = ltrim($legacyCode, '0'); if ($noZero !== '') { $legacyCodiceCandidates[] = $noZero; } if (is_numeric($legacyCode)) { $legacyStabileIdCandidates[] = (int) $legacyCode; } } $legacyStabileIdCandidates = array_values(array_unique(array_filter($legacyStabileIdCandidates, fn($v) => is_int($v) && $v > 0))); $legacyCodiceCandidates = array_values(array_unique(array_filter($legacyCodiceCandidates, fn($v) => is_string($v) && $v !== ''))); if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'legacy_id_stabile') && ! empty($legacyStabileIdCandidates)) { $query->whereIn('f.legacy_id_stabile', $legacyStabileIdCandidates); } elseif ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'cod_stabile') && ! empty($legacyCodiceCandidates)) { $query->whereIn('f.cod_stabile', $legacyCodiceCandidates); } } if ($sourceTable === 'stg_fatture_gescon' && $activeTenantId !== null && Schema::hasColumn('stg_fatture_gescon', 'tenant_id')) { $query->where('f.tenant_id', $activeTenantId); } if ($this->filterAnno) { $year = (int) $this->filterAnno; if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'data_fattura')) { $query->whereYear('f.data_fattura', $year); } elseif ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'data_fattura')) { $query->whereYear('f.data_fattura', $year); } } if (! empty($this->filterCodForn)) { if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'cod_fornitore')) { $query->where('f.cod_fornitore', trim((string) $this->filterCodForn)); } if ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'cod_fornitore')) { $query->where('f.cod_fornitore', trim((string) $this->filterCodForn)); } } if (! empty($this->search)) { $needle = '%' . trim((string) $this->search) . '%'; $query->where(function ($q) use ($needle, $sourceTable): void { $fields = ['numero_fattura', 'descrizione', 'descrizione_sintetica', 'descrizione_corpo', 'note']; if ($sourceTable === 'stg_fatture_gescon') { $fields[] = 'cod_fornitore'; } else { $fields[] = 'cod_fornitore'; } foreach ($fields as $field) { if ($sourceTable === 'stg_fatture_gescon' && ! Schema::hasColumn('stg_fatture_gescon', $field)) { continue; } if ($sourceTable === 'fatture' && ! Schema::connection('gescon_import')->hasColumn('fatture', $field)) { continue; } $q->orWhere('f.' . $field, 'like', $needle); } }); } if ($sourceTable === 'stg_fatture_gescon') { $select = [ 'f.id as row_id', 'f.legacy_id_fatture', 'f.cod_fornitore', 'f.numero_fattura', 'f.data_fattura', 'f.data_pagamento', 'f.descrizione_sintetica', 'f.descrizione_corpo', 'f.note', 'f.imponibile', 'f.importo_iva', 'f.totale_fattura', 'f.importo_netto', 'f.importo_rda', 'f.riferimento', 'f.reg_gestione', 'f.reg_nstra', ]; } else { $select = [ 'f.id as row_id', 'f.id as legacy_id_fatture', 'f.cod_fornitore', 'f.numero_fattura', 'f.data_fattura', 'f.data_pagamento', 'f.descrizione', DB::raw('NULL as descrizione_corpo'), 'f.note', DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as imponibile'), 'f.iva as importo_iva', DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as totale_fattura'), DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as importo_netto'), 'f.ritenuta as importo_rda', DB::raw('NULL as riferimento'), DB::raw('NULL as reg_gestione'), DB::raw('NULL as reg_nstra'), ]; } $paginator = $query ->select($select) ->orderByDesc('f.data_fattura') ->orderByDesc('f.id') ->paginate($this->perPage); $codFornitori = $paginator->getCollection() ->pluck('cod_fornitore') ->filter(fn($v) => $v !== null && trim((string) $v) !== '') ->map(fn($v) => trim((string) $v)) ->unique() ->values() ->all(); $fornitoriMap = []; $fornitoriIdMap = []; $fornitoriTributoMap = []; if (! empty($codFornitori)) { if (Schema::hasTable('stg_fornitori_gescon')) { $fornRows = DB::table('stg_fornitori_gescon') ->whereIn('cod_forn', $codFornitori) ->get(['cod_forn', 'denominazione', 'cognome', 'nome', 'raw']); } else { $fornRows = DB::connection('gescon_import') ->table('mdb_fornitori') ->whereIn('cod_forn', $codFornitori) ->get(['cod_forn', 'denominazione', 'cognome', 'nome']); } foreach ($fornRows as $row) { $code = trim((string) ($row->cod_forn ?? '')); if ($code === '') { continue; } $label = trim((string) ($row->denominazione ?? '')); if ($label === '') { $label = trim((string) (($row->nome ?? '') . ' ' . ($row->cognome ?? ''))); } $fornitoriMap[$code] = $label !== '' ? $label : null; $rawTributo = null; if (isset($row->raw)) { $raw = json_decode((string) $row->raw, true); if (is_array($raw)) { $rawTributo = strtoupper(trim((string) ($raw['trib_1019_1020'] ?? ''))); } } if ($rawTributo !== '') { $fornitoriTributoMap[$code] = $rawTributo; $codeNoZero = ltrim($code, '0'); if ($codeNoZero !== '') { $fornitoriTributoMap[$codeNoZero] = $rawTributo; } } } if (Schema::hasTable('fornitori')) { $localRows = DB::table('fornitori') ->whereIn('old_id', $codFornitori) ->get(['id', 'old_id']); foreach ($localRows as $local) { $fornitoriIdMap[(string) $local->old_id] = (int) $local->id; } } } $fattureLocali = []; $fattureFeByDoc = []; $fattureFeByFornitoreDoc = []; if (! empty($fornitoriIdMap) && Schema::hasTable('contabilita_fatture_fornitori')) { $fattureRows = DB::table('contabilita_fatture_fornitori') ->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId)) ->whereIn('fornitore_id', array_values($fornitoriIdMap)) ->get(['id', 'fornitore_id', 'fattura_elettronica_id', 'numero_documento', 'data_documento', 'totale', 'stato', 'data_versamento_ra', 'ritenuta_importo', 'ritenuta_codice_tributo']); foreach ($fattureRows as $fattura) { $numero = $this->normalizeInvoiceNumber($fattura->numero_documento ?? null); $data = ! empty($fattura->data_documento) ? (string) \Carbon\Carbon::parse($fattura->data_documento)->format('Y-m-d') : null; if (! $numero || ! $data) { continue; } $key = (int) $fattura->fornitore_id . '|' . $numero . '|' . $data; $fattureLocali[$key][] = $fattura; $globalKey = $numero . '|' . $data; $fattureFeByDoc[$globalKey][] = $fattura; $fattureFeByFornitoreDoc[$key][] = $fattura; } } $feGlobalByDoc = []; $feByFornitoreDoc = []; if ($activeStabileId && Schema::hasTable('fatture_elettroniche')) { $feRows = DB::table('fatture_elettroniche') ->where('stabile_id', (int) $activeStabileId) ->get(['id', 'fornitore_id', 'numero_fattura', 'data_fattura']); foreach ($feRows as $fe) { $numero = $this->normalizeInvoiceNumber($fe->numero_fattura ?? null); $data = ! empty($fe->data_fattura) ? (string) \Carbon\Carbon::parse($fe->data_fattura)->format('Y-m-d') : null; if (! $numero || ! $data) { continue; } $globalKey = $numero . '|' . $data; $feGlobalByDoc[$globalKey][] = $fe; $fornitoreId = is_numeric($fe->fornitore_id ?? null) ? (int) $fe->fornitore_id : 0; if ($fornitoreId > 0) { $key = $fornitoreId . '|' . $numero . '|' . $data; $feByFornitoreDoc[$key][] = $fe; } } } $legacyRitenutaById = []; if ($sourceTable === 'stg_fatture_gescon' && Schema::connection('gescon_import')->hasTable('fatture')) { $legacyIds = $paginator->getCollection() ->pluck('legacy_id_fatture') ->filter(fn($v) => is_numeric($v)) ->map(fn($v) => (int) $v) ->unique() ->values() ->all(); if (! empty($legacyIds)) { $legacyRitenutaById = DB::connection('gescon_import') ->table('fatture') ->whereIn('id', $legacyIds) ->get(['id', 'ritenuta']) ->mapWithKeys(fn($r) => [(int) $r->id => $this->normalizeMoney($r->ritenuta ?? 0)]) ->all(); } } $rdaCodeByInvoice = []; $rdaRowsByCode = []; if (Schema::connection('gescon_import')->hasTable('operazioni')) { $opQuery = DB::connection('gescon_import')->table('operazioni'); if (! empty($legacyCodiceCandidates) && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) { $opQuery->whereIn('cod_stabile', $legacyCodiceCandidates); } if ($this->filterAnno && Schema::connection('gescon_import')->hasColumn('operazioni', 'dt_spe')) { $opQuery->whereYear('dt_spe', (int) $this->filterAnno); } $opSelect = ['benef']; foreach (['rif_rda', 'dt_spe', 'cod_for', 'num_fat', 'netto_vers_rda', 'importo_euro_770', 'importo_euro', 'importo'] as $column) { if (Schema::connection('gescon_import')->hasColumn('operazioni', $column)) { $opSelect[] = $column; } } $opRows = $opQuery->get(array_values(array_unique($opSelect))); foreach ($opRows as $opRow) { $benef = trim((string) ($opRow->benef ?? '')); $benefUpper = strtoupper($benef); $isRdaLine = $benefUpper !== '' && str_starts_with($benefUpper, 'RDA:'); $rdaCode = trim((string) ($opRow->rif_rda ?? '')); if ($rdaCode === '' && $isRdaLine) { $rdaCode = trim(substr($benef, 4)); } if ($rdaCode === '') { continue; } if ($isRdaLine) { $rdaAmount = abs($this->normalizeMoney( $opRow->netto_vers_rda ?? $opRow->importo_euro_770 ?? $opRow->importo_euro ?? $opRow->importo ?? 0 )); $existingDate = $rdaRowsByCode[$rdaCode]['data'] ?? null; $currentDate = ! empty($opRow->dt_spe) ? (string) \Carbon\Carbon::parse($opRow->dt_spe)->format('Y-m-d') : null; if (! isset($rdaRowsByCode[$rdaCode])) { $rdaRowsByCode[$rdaCode] = [ 'importo' => $rdaAmount, 'data' => $currentDate, ]; } elseif ($currentDate && ($existingDate === null || $currentDate >= (string) $existingDate)) { $rdaRowsByCode[$rdaCode]['data'] = $currentDate; if ($rdaAmount > 0) { $rdaRowsByCode[$rdaCode]['importo'] = $rdaAmount; } } elseif (($rdaRowsByCode[$rdaCode]['importo'] ?? 0) <= 0 && $rdaAmount > 0) { $rdaRowsByCode[$rdaCode]['importo'] = $rdaAmount; } continue; } $numeroNorm = $this->normalizeInvoiceNumber($opRow->num_fat ?? null); if ($numeroNorm === null) { continue; } $codForRaw = trim((string) ($opRow->cod_for ?? '')); if ($codForRaw === '') { continue; } $fornCandidates = array_values(array_unique(array_filter([ $codForRaw, ltrim($codForRaw, '0'), ], fn($v) => is_string($v) && $v !== ''))); foreach ($fornCandidates as $fornCode) { $rdaCodeByInvoice[$fornCode . '|' . $numeroNorm] = $rdaCode; } } } $paginator->setCollection( $paginator->getCollection()->map(function ($row) use ($fornitoriMap, $fornitoriIdMap, $fornitoriTributoMap, $fattureLocali, $fattureFeByDoc, $fattureFeByFornitoreDoc, $legacyRitenutaById, $rdaCodeByInvoice, $rdaRowsByCode, $feGlobalByDoc, $feByFornitoreDoc) { $code = trim((string) ($row->cod_fornitore ?? '')); $row->fornitore_nome = $fornitoriMap[$code] ?? null; $row->fornitore_id = $fornitoriIdMap[$code] ?? null; $tributoFromFornitore = null; $codeNoZero = ltrim($code, '0'); if ($code !== '' && ! empty($fornitoriTributoMap[$code])) { $tributoFromFornitore = $fornitoriTributoMap[$code]; } elseif ($codeNoZero !== '' && ! empty($fornitoriTributoMap[$codeNoZero])) { $tributoFromFornitore = $fornitoriTributoMap[$codeNoZero]; } $descrizione = trim((string) ($row->descrizione_sintetica ?? '')); if ($descrizione === '') { $descrizione = trim((string) ($row->descrizione ?? '')); } if ($descrizione === '') { $descrizione = trim((string) ($row->descrizione_corpo ?? '')); } $row->descrizione_view = $descrizione !== '' ? $descrizione : '—'; $row->totale_view = $this->normalizeMoney($row->totale_fattura ?? $row->importo_netto ?? $row->imponibile ?? 0); $row->ra_fatta_importo = abs($this->normalizeMoney($row->importo_rda ?? 0)); $row->ra_versata_data = null; $row->ra_tributo = null; $row->ra_riferimento = null; $legacyId = is_numeric($row->legacy_id_fatture ?? null) ? (int) $row->legacy_id_fatture : null; if ($row->ra_fatta_importo <= 0 && $legacyId && isset($legacyRitenutaById[$legacyId])) { $row->ra_fatta_importo = abs($this->normalizeMoney($legacyRitenutaById[$legacyId])); } $row->local_match_id = null; $row->local_match_url = null; $row->local_match_stato = null; $row->local_match_fe_id = null; $row->local_match_fe_url = null; $numero = $this->normalizeInvoiceNumber($row->numero_fattura ?? null); $data = ! empty($row->data_fattura) ? (string) \Carbon\Carbon::parse($row->data_fattura)->format('Y-m-d') : null; if ($numero !== null) { $fornCandidates = array_values(array_unique(array_filter([ $code, ltrim($code, '0'), ], fn($v) => is_string($v) && $v !== ''))); foreach ($fornCandidates as $fornCode) { $key = $fornCode . '|' . $numero; if (isset($rdaCodeByInvoice[$key])) { $row->ra_riferimento = $rdaCodeByInvoice[$key]; break; } } } if (empty($row->ra_riferimento) && ! empty($row->riferimento)) { $row->ra_riferimento = trim((string) $row->riferimento); } if (! empty($row->ra_riferimento) && isset($rdaRowsByCode[$row->ra_riferimento])) { $rdaData = $rdaRowsByCode[$row->ra_riferimento]; if ($row->ra_fatta_importo <= 0) { $row->ra_fatta_importo = abs($this->normalizeMoney($rdaData['importo'] ?? 0)); } if (empty($row->ra_versata_data) && ! empty($rdaData['data'])) { $row->ra_versata_data = $rdaData['data']; } } if ($row->fornitore_id && $numero && $data) { $key = (int) $row->fornitore_id . '|' . $numero . '|' . $data; $match = $fattureLocali[$key][0] ?? null; if ($match) { $row->local_match_id = (int) ($match->id ?? 0); $row->local_match_stato = $match->stato ?? null; $row->ra_versata_data = $match->data_versamento_ra ?? null; $row->ra_tributo = $match->ritenuta_codice_tributo ?? null; if ($row->ra_fatta_importo <= 0) { $row->ra_fatta_importo = abs($this->normalizeMoney($match->ritenuta_importo ?? 0)); } $row->local_match_url = \App\Filament\Pages\Contabilita\FatturaFornitoreScheda::getUrl([ 'record' => (int) $match->id, ], panel: 'admin-filament'); $feId = is_numeric($match->fattura_elettronica_id ?? null) ? (int) $match->fattura_elettronica_id : 0; if ($feId > 0) { $row->local_match_fe_id = $feId; $row->local_match_fe_url = \App\Filament\Pages\Contabilita\FatturaElettronicaScheda::getUrl([ 'record' => $feId, ], panel: 'admin-filament'); } } } // Fallback: FE presente ma fattura contabile non ancora agganciata. if (empty($row->local_match_url) && $numero && $data) { $feMatch = null; if ($row->fornitore_id) { $strictKey = (int) $row->fornitore_id . '|' . $numero . '|' . $data; $strict = $feByFornitoreDoc[$strictKey] ?? []; if (count($strict) === 1) { $feMatch = $strict[0]; } } if (! $feMatch) { $globalKey = $numero . '|' . $data; $global = $feGlobalByDoc[$globalKey] ?? []; if (count($global) === 1) { $feMatch = $global[0]; } } if ($feMatch && is_numeric($feMatch->id ?? null)) { $row->local_match_fe_id = (int) $feMatch->id; $row->local_match_fe_url = \App\Filament\Pages\Contabilita\FatturaElettronicaScheda::getUrl([ 'record' => (int) $feMatch->id, ], panel: 'admin-filament'); } } if (empty($row->ra_tributo) && ! empty($tributoFromFornitore)) { $row->ra_tributo = $tributoFromFornitore; } if ($row->ra_fatta_importo > 0 && empty($row->ra_tributo)) { $row->ra_tributo = '1040'; } return $row; }) ); return $paginator; } private function resolveTributoByFornitoreIdMap(array $fornitoreIds): array { if (empty($fornitoreIds) || ! Schema::hasTable('fornitori') || ! Schema::hasTable('stg_fornitori_gescon')) { return []; } $fornitoriRows = DB::table('fornitori') ->whereIn('id', $fornitoreIds) ->get(['id', 'old_id']); $oldIds = $fornitoriRows ->pluck('old_id') ->filter(fn($v) => $v !== null && trim((string) $v) !== '') ->map(fn($v) => trim((string) $v)) ->unique() ->values() ->all(); if (empty($oldIds)) { return []; } $stgRows = DB::table('stg_fornitori_gescon') ->whereIn('cod_forn', $oldIds) ->get(['cod_forn', 'raw']); $tributoByCodForn = []; foreach ($stgRows as $stg) { $cod = trim((string) ($stg->cod_forn ?? '')); if ($cod === '') { continue; } $raw = json_decode((string) ($stg->raw ?? ''), true); if (! is_array($raw)) { continue; } $tributo = strtoupper(trim((string) ($raw['trib_1019_1020'] ?? ''))); if ($tributo !== '') { $tributoByCodForn[$cod] = $tributo; $codNoZero = ltrim($cod, '0'); if ($codNoZero !== '') { $tributoByCodForn[$codNoZero] = $tributo; } } } $out = []; foreach ($fornitoriRows as $fornitore) { $oldId = trim((string) ($fornitore->old_id ?? '')); if ($oldId === '') { continue; } $oldIdNoZero = ltrim($oldId, '0'); $tributo = $tributoByCodForn[$oldId] ?? ($oldIdNoZero !== '' ? ($tributoByCodForn[$oldIdNoZero] ?? null) : null); if ($tributo) { $out[(int) $fornitore->id] = $tributo; } } return $out; } public function importaFattureLegacy(): void { $user = Auth::user(); if (! $user) { abort(403); } if (! Schema::hasTable('contabilita_fatture_fornitori')) { Notification::make() ->title('Tabella fatture fornitori non disponibile') ->danger() ->send(); return; } $activeStabileId = StabileContext::resolveActiveStabileId($user); if (! $activeStabileId) { Notification::make() ->title('Seleziona uno stabile attivo') ->warning() ->send(); return; } $tenantId = null; if (Schema::hasTable('stabili')) { $tenantId = DB::table('stabili') ->where('id', (int) $activeStabileId) ->value('amministratore_id'); } if (! $tenantId && isset($user->amministratore_id) && is_numeric($user->amministratore_id)) { $tenantId = (int) $user->amministratore_id; } if (! $tenantId) { Notification::make() ->title('Amministratore non risolto') ->body('Impossibile risolvere il tenant per l\'import fatture.') ->danger() ->send(); return; } $params = [ 'tenant' => (string) $tenantId, '--stabile-id' => (string) $activeStabileId, ]; $legacyCode = $this->resolveLegacyCodiceStabile(); if (! empty($legacyCode)) { $params['--stabile-code'] = $legacyCode; } if ($this->filterAnno) { $params['--anno'] = (string) $this->filterAnno; } try { $exit = Artisan::call('gescon:sync-fatture-fornitori', $params); $output = trim((string) Artisan::output()); } catch (\Throwable $e) { Notification::make() ->title('Import fatture fallito') ->body($e->getMessage()) ->danger() ->send(); return; } if ($exit === 0 && str_contains($output, 'Nessuna fattura da sincronizzare.')) { $mdbPath = $this->resolveLegacyGeneraleMdbPath((int) $activeStabileId); if (! empty($mdbPath)) { try { $loadExit = Artisan::call('import:gescon-fatture-legacy', [ 'tenant' => (string) $tenantId, '--mdb' => $mdbPath, ]); $loadOutput = trim((string) Artisan::output()); } catch (\Throwable $e) { Notification::make() ->title('Import da generale_stabile.mdb fallito') ->body($e->getMessage()) ->danger() ->send(); return; } if ($loadExit !== 0) { $snippet = $loadOutput !== '' ? mb_substr($loadOutput, 0, 800) : 'Errore durante caricamento MDB.'; Notification::make() ->title('Import da generale_stabile.mdb: errore') ->body($snippet) ->danger() ->send(); return; } try { $exit = Artisan::call('gescon:sync-fatture-fornitori', $params); $output = trim((string) Artisan::output()); } catch (\Throwable $e) { Notification::make() ->title('Sync post-import fallita') ->body($e->getMessage()) ->danger() ->send(); return; } } } if ($exit !== 0) { $snippet = $output !== '' ? mb_substr($output, 0, 800) : 'Errore durante la sincronizzazione.'; Notification::make() ->title('Import fatture: errore') ->body($snippet) ->danger() ->send(); return; } $tail = $output !== '' ? trim((string) collect(preg_split('/\R/', $output) ?: [])->last()): null; Notification::make() ->title('Import fatture completato') ->body($tail ?: 'Sincronizzazione eseguita correttamente.') ->success() ->send(); $this->resetPage(); } private function resolveLegacyGeneraleMdbPath(int $stabileId): ?string { $base = (string) UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon'); $base = rtrim($base, '/'); if ($base === '') { $base = '/mnt/gescon-archives/gescon'; } $stabile = Schema::hasTable('stabili') ? DB::table('stabili')->where('id', $stabileId)->first(['codice_stabile', 'cod_stabile', 'old_id']) : null; $codes = []; foreach ([(string) ($stabile->codice_stabile ?? ''), (string) ($stabile->cod_stabile ?? ''), (string) ($stabile->old_id ?? '')] as $raw) { $value = trim($raw); if ($value === '') { continue; } $codes[] = $value; $codes[] = ltrim($value, '0'); if (is_numeric($value)) { $num = (int) $value; $codes[] = (string) $num; $codes[] = str_pad((string) $num, 4, '0', STR_PAD_LEFT); } } $codes = array_values(array_unique(array_filter($codes, fn($v) => is_string($v) && trim($v) !== ''))); foreach ($codes as $code) { $path = $base . '/' . $code . '/generale_stabile.mdb'; if (is_file($path)) { return $path; } } return null; } private function applyIncassiFilters($query): void { $codStabile = $this->resolveLegacyCodiceStabile(); if ($codStabile && Schema::connection('gescon_import')->hasColumn('incassi', 'cod_stabile')) { $query->where('incassi.cod_stabile', $codStabile); } if ($this->onlyStraordinarieMode) { if (Schema::connection('gescon_import')->hasColumn('incassi', 'o_r_s')) { $query->where('incassi.o_r_s', 'S'); } if ($this->selectedStraordinaria !== null && Schema::connection('gescon_import')->hasColumn('incassi', 'n_stra')) { $query->where('incassi.n_stra', $this->selectedStraordinaria); } } else { if (Schema::connection('gescon_import')->hasColumn('incassi', 'o_r_s')) { $query->where(function ($qq): void { $qq->where('incassi.o_r_s', 'O') ->orWhereNull('incassi.o_r_s') ->orWhere('incassi.o_r_s', ''); }); } } $anno = $this->legacyGestioneAnno; if ($anno) { $annoCol = Schema::connection('gescon_import')->hasColumn('incassi', 'anno') ? 'anno' : (Schema::connection('gescon_import')->hasColumn('incassi', 'anno_rif') ? 'anno_rif' : null); $annoShort = (int) substr((string) $anno, -2); $query->where(function ($qq) use ($annoCol, $anno, $annoShort): void { if ($annoCol) { $qq->where($annoCol, (string) $anno) ->orWhere($annoCol, (string) $annoShort) ->orWhere($annoCol, str_pad((string) $annoShort, 2, '0', STR_PAD_LEFT)); } if (Schema::connection('gescon_import')->hasColumn('incassi', 'dt_empag')) { $qq->orWhereYear('dt_empag', (int) $anno); } }); } if (! empty($this->search)) { $needle = trim((string) $this->search); $query->where(function ($qq) use ($needle): void { foreach (['cod_cond', 'descrizione', 'cod_cassa', 'n_ricevuta', 'n_riferimento', 'riferimento_pagamento'] as $col) { if (Schema::connection('gescon_import')->hasColumn('incassi', $col)) { $qq->orWhere($col, 'like', '%' . $needle . '%'); } } }); } } private function resolveIncassoImporto(object $row): float { $importoEuro = $this->normalizeMoney($row->importo_euro ?? null); if (abs($importoEuro) > 0.00001) { return round($importoEuro, 2); } $importo = $this->normalizeMoney($row->importo ?? null); if (abs($importo) > 0.00001) { return round($importo, 2); } $pagato = $this->normalizeMoney($row->importo_pagato_euro ?? null); $abs = abs($pagato); if ($abs >= 10000 && fmod($abs, 1.0) === 0.0) { return round($pagato / 10000, 2); } if ($abs >= 1000 && fmod($abs, 1.0) === 0.0 && ((int) $abs % 100) === 0) { return round($pagato / 100, 2); } return round($pagato, 2); } private function relationScoreForIncasso(string $tipoRelazione, string $ruolo): int { $tipo = strtolower(trim($tipoRelazione)); if ($ruolo === 'I') { return match ($tipo) { 'inquilino', 'conduttore', 'affittuario', 'locatario' => 100, default => 10, }; } return match ($tipo) { 'proprietario' => 100, 'comproprietario' => 90, 'usufruttuario' => 80, 'delegato_assemblea' => 70, 'referente' => 60, default => 10, }; } private function findRateMatchForIncasso(object $incasso, array $rateRows): ?array { $refs = []; foreach (['n_rata_riferimento', 'riferimento_pagamento', 'n_riferimento', 'n_ricevuta'] as $field) { $v = $incasso->{$field} ?? null; if (is_numeric($v)) { $refs[] = (int) $v; } elseif (is_string($v)) { $trim = trim($v); if (ctype_digit($trim)) { $refs[] = (int) $trim; } } } $refs = array_values(array_unique($refs)); foreach ($rateRows as $rate) { $nEmissione = is_numeric($rate->n_emissione ?? null) ? (int) $rate->n_emissione : null; if ($nEmissione !== null && in_array($nEmissione, $refs, true)) { return [ 'n_emissione' => $nEmissione, 'importo' => (float) ($rate->importo_euro ?? $rate->importo ?? 0), 'data_emissione' => $rate->data_emissione ?? null, 'causale' => $rate->causale ?? null, 'tipo_rata' => $rate->tipo_rata ?? null, 'pagata' => (bool) ($rate->pagata ?? false), ]; } } $incassoImporto = abs($this->resolveIncassoImporto($incasso)); if ($incassoImporto <= 0.0) { return null; } $best = null; foreach ($rateRows as $rate) { $rateImporto = abs($this->normalizeMoney($rate->importo_euro ?? $rate->importo ?? 0)); if (abs($incassoImporto - $rateImporto) > 0.01) { continue; } $score = 10; $incassoDate = ! empty($incasso->data_visuale) ? (string) $incasso->data_visuale : null; $rateDate = ! empty($rate->data_scadenza) ? (string) $rate->data_scadenza : (! empty($rate->data_emissione) ? (string) $rate->data_emissione : null); if ($incassoDate && $rateDate) { try { $d1 = \Carbon\Carbon::parse($incassoDate); $d2 = \Carbon\Carbon::parse($rateDate); $days = abs($d1->diffInDays($d2)); $score += max(0, 30 - min($days, 30)); } catch (\Throwable) { } } if (! $best || $score > ($best['score'] ?? -1)) { $best = [ 'score' => $score, 'n_emissione' => is_numeric($rate->n_emissione ?? null) ? (int) $rate->n_emissione : null, 'importo' => (float) ($rate->importo_euro ?? $rate->importo ?? 0), 'data_emissione' => $rate->data_emissione ?? null, 'causale' => $rate->causale ?? null, 'tipo_rata' => $rate->tipo_rata ?? null, 'pagata' => (bool) ($rate->pagata ?? false), ]; } } if (! $best) { return null; } unset($best['score']); return $best; } private function formatScalaInternoShort($scala, $interno): string { $s = trim((string) ($scala ?? '')); $i = trim((string) ($interno ?? '')); if ($s !== '') { $s = preg_replace('/^S/i', '', $s); } if ($i !== '') { $i = preg_replace('/^I/i', '', $i); } if ($s !== '' && $i !== '') { return $s . '-' . $i; } if ($s !== '') { return $s; } if ($i !== '') { return $i; } return '—'; } public function openDettPersModal(int $nSpe): void { $legacyCode = $this->resolveLegacyCodiceStabile(); $operazione = DB::connection('gescon_import') ->table('operazioni') ->where('n_spe', $nSpe) ->whereIn('cod_spe', ['I05', 'I06']) ->when( $legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'), fn($q) => $q->where('cod_stabile', $legacyCode) ) ->select(['importo_euro', 'importo', 'dt_spe', 'unico', 'gestione', 'n_stra']) ->first(); $dettCols = ['id_cond', 'cond_inq', 'importo', 'importo_euro']; foreach (['unico', 'n_spe', 'n_stra', 'tipo_gestione', 'legacy_year'] as $optionalCol) { if (Schema::connection('gescon_import')->hasColumn('dett_pers', $optionalCol)) { $dettCols[] = $optionalCol; } } $dettQuery = DB::connection('gescon_import') ->table('dett_pers'); $hasNspe = Schema::connection('gescon_import')->hasColumn('dett_pers', 'n_spe'); $hasNstra = Schema::connection('gescon_import')->hasColumn('dett_pers', 'n_stra'); $hasUnico = Schema::connection('gescon_import')->hasColumn('dett_pers', 'unico'); if ($hasNspe || $hasNstra || ($hasUnico && ! empty($operazione?->unico))) { $dettQuery->where(function ($q) use ($nSpe, $operazione, $hasNspe, $hasNstra, $hasUnico): void { if ($hasNspe) { $q->orWhere('n_spe', $nSpe); } if ($hasNstra) { $q->orWhere('n_stra', $nSpe); } if ($hasUnico && ! empty($operazione?->unico)) { $q->orWhere('unico', $operazione->unico); } }); } else { $dettQuery->whereRaw('1 = 0'); } if (Schema::connection('gescon_import')->hasColumn('dett_pers', 'tipo_gestione') && ! empty($operazione?->gestione)) { $dettQuery->where('tipo_gestione', $operazione->gestione); } if (Schema::connection('gescon_import')->hasColumn('dett_pers', 'legacy_year')) { $dettQuery->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]); } $dettRows = $dettQuery->get($dettCols); $this->dettPersNSpe = $nSpe; $this->dettPersImportoOperazione = $this->normalizeMoney($operazione->importo_euro ?? $operazione->importo ?? 0); $this->dettPersLegacyCode = $this->resolveLegacyCodiceStabile(); $this->dettPersLegacyYear = '0001'; $legacyAmounts = []; $legacyUnico = []; foreach ($dettRows as $dett) { $idCond = is_numeric($dett->id_cond ?? null) ? (int) $dett->id_cond : 0; $role = strtoupper(trim((string) ($dett->cond_inq ?? 'C'))); $role = $role === 'I' ? 'I' : 'C'; if ($idCond <= 0) { continue; } $key = $idCond . '|' . $role; $legacyAmounts[$key] = ($legacyAmounts[$key] ?? 0.0) + $this->normalizeMoney($dett->importo_euro ?? $dett->importo ?? 0); if (isset($dett->unico) && is_numeric($dett->unico)) { $legacyUnico[(int) $dett->unico] = true; } } $this->dettPersLegacyAmounts = $legacyAmounts; $this->dettPersLegacyUnico = array_values(array_map('intval', array_keys($legacyUnico))); sort($this->dettPersLegacyUnico); $recipients = []; $targetOptions = []; $legacyCode = $this->dettPersLegacyCode; if ($legacyCode) { $condQuery = DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $legacyCode); $condColumns = ['cod_cond', 'scala', 'interno', 'piano', 'cognome', 'nome', 'denominazione', 'proprietario_denominazione', 'inquilino_denominazione', 'inquil_nome']; if (Schema::connection('gescon_import')->hasColumn('condomin', 'legacy_year') && $this->dettPersLegacyYear) { $condQuery->whereIn('legacy_year', [$this->dettPersLegacyYear, '0001']); $condColumns[] = 'legacy_year'; } $condRows = $condQuery->get($condColumns); $byId = []; foreach ($condRows as $cond) { $idCond = is_numeric($cond->cod_cond ?? null) ? (int) $cond->cod_cond : null; if (! $idCond || isset($byId[$idCond])) { continue; } $byId[$idCond] = $cond; } foreach ($byId as $idCond => $cond) { $unita = trim(implode(' ', array_filter([ ! empty($cond->scala) ? 'Scala ' . $cond->scala : null, ! empty($cond->interno) ? 'Int. ' . $cond->interno : null, ! empty($cond->piano) ? 'P. ' . $cond->piano : null, ]))); $propName = trim((string) ($cond->proprietario_denominazione ?? '')); if ($propName === '') { $propName = trim((string) (($cond->cognome ?? '') . ' ' . ($cond->nome ?? ''))); } if ($propName === '') { $propName = trim((string) ($cond->denominazione ?? '')); } $inquName = trim((string) ($cond->inquilino_denominazione ?? '')); if ($inquName === '') { $inquName = trim((string) ($cond->inquil_nome ?? '')); } $keyC = $idCond . '|C'; $recipients[$keyC] = [ 'key' => $keyC, 'id_cond' => $idCond, 'cond_inq' => 'C', 'soggetto' => 'Condomino: ' . ($propName !== '' ? $propName : ('Cond. ' . $idCond)), 'unita' => $unita !== '' ? $unita : '—', ]; $targetOptions[$keyC] = ($unita !== '' ? ($unita . ' · ') : '') . 'Condomino ' . ($propName !== '' ? $propName : ('Cond. ' . $idCond)); if ($inquName !== '') { $keyI = $idCond . '|I'; $recipients[$keyI] = [ 'key' => $keyI, 'id_cond' => $idCond, 'cond_inq' => 'I', 'soggetto' => 'Inquilino: ' . $inquName, 'unita' => $unita !== '' ? $unita : '—', ]; $targetOptions[$keyI] = ($unita !== '' ? ($unita . ' · ') : '') . 'Inquilino ' . $inquName; } } } foreach ($legacyAmounts as $key => $amount) { if (isset($recipients[$key])) { continue; } [$idCond, $role] = array_pad(explode('|', $key), 2, 'C'); $idCond = is_numeric($idCond) ? (int) $idCond : 0; $role = strtoupper($role) === 'I' ? 'I' : 'C'; $recipients[$key] = [ 'key' => $key, 'id_cond' => $idCond, 'cond_inq' => $role, 'soggetto' => ($role === 'I' ? 'Inquilino' : 'Condomino') . ': Cond. ' . ($idCond > 0 ? $idCond : '—'), 'unita' => '—', ]; $targetOptions[$key] = 'Cond. ' . ($idCond > 0 ? $idCond : '—') . ' (' . ($role === 'I' ? 'Inquilino' : 'Condomino') . ')'; } ksort($recipients); $this->dettPersRecipients = array_values($recipients); $this->dettPersRipartoTargetOptions = $targetOptions; $this->dettPersRipartoTarget = array_key_first($targetOptions); $tabOptions = []; $activeStabileId = StabileContext::resolveActiveStabileId(Auth::user()); if ($activeStabileId && Schema::hasTable('tabelle_millesimali') && Schema::hasTable('dettaglio_millesimi')) { $localRows = DB::table('tabelle_millesimali') ->where('stabile_id', (int) $activeStabileId) ->orderBy('codice_tabella') ->get(['id', 'codice_tabella', 'nome_tabella', 'denominazione']); foreach ($localRows as $localRow) { $key = 'LOCAL:' . (int) $localRow->id; $label = trim((string) ($localRow->codice_tabella ?? 'TAB')); $name = trim((string) ($localRow->denominazione ?? $localRow->nome_tabella ?? '')); if ($name !== '') { $label .= ' · ' . $name; } $sum = (float) DB::table('dettaglio_millesimi') ->where('tabella_millesimale_id', (int) $localRow->id) ->where('partecipa', true) ->sum('millesimi'); $tabOptions[$key] = $label . ' (totale ' . number_format($sum, 3, ',', '.') . ')'; } } if ($legacyCode && Schema::connection('gescon_import')->hasTable('dett_tab')) { $tabQuery = DB::connection('gescon_import') ->table('dett_tab') ->where('cod_stabile', $legacyCode) ->select('cod_tab') ->distinct(); if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) { $tabQuery->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]); } $tabs = $tabQuery->orderBy('cod_tab')->pluck('cod_tab')->all(); foreach ($tabs as $tab) { $code = strtoupper(trim((string) $tab)); if ($code === '') { continue; } $tabOptions[$code] = ($tabOptions[$code] ?? $code); } } $this->dettPersRipartoTabellaOptions = $tabOptions; $this->dettPersRipartoTabella = array_key_first($tabOptions); $this->dettPersRipartoMode = 'legacy'; $this->rebuildDettPersRows(); } public function updatedDettPersRipartoMode(): void { $this->rebuildDettPersRows(); } public function updatedDettPersRipartoTabella(): void { $this->rebuildDettPersRows(); } public function updatedDettPersRipartoTarget(): void { $this->rebuildDettPersRows(); } public function updatedDettPersRows($value = null, $key = null): void { if ($this->dettPersRipartoMode !== 'riparto_libero') { return; } $this->recalculateDettPersTotalsFromRows(); } private function rebuildDettPersRows(): void { $recipients = $this->dettPersRecipients; $total = $this->dettPersImportoOperazione; $amounts = []; foreach ($recipients as $recipient) { $key = (string) ($recipient['key'] ?? ''); if ($key !== '') { $amounts[$key] = 0.0; } } $mode = $this->dettPersRipartoMode; if ($mode === 'parti_uguali') { $keys = array_keys($amounts); $distributed = $this->distributeEqually($total, $keys); foreach ($distributed as $key => $value) { $amounts[$key] = $value; } } elseif ($mode === 'riparto_libero') { foreach ($this->dettPersRows as $row) { $idCond = is_numeric($row['id_cond'] ?? null) ? (int) $row['id_cond'] : 0; $role = strtoupper((string) ($row['cond_inq'] ?? 'C')) === 'I' ? 'I' : 'C'; if ($idCond <= 0) { continue; } $key = $idCond . '|' . $role; if (! array_key_exists($key, $amounts)) { continue; } $amounts[$key] = round($this->normalizeMoney($row['importo'] ?? 0), 2); } } elseif ($mode === 'unita_singola') { $target = (string) ($this->dettPersRipartoTarget ?? ''); if ($target !== '' && array_key_exists($target, $amounts)) { $amounts[$target] = round($total, 2); } } elseif ($mode === 'millesimi_tabella') { $weights = $this->resolveMillesimiWeights(); if (! empty($weights)) { $distributed = $this->distributeByWeights($total, $weights); foreach ($distributed as $key => $value) { if (array_key_exists($key, $amounts)) { $amounts[$key] = $value; } } } else { foreach ($this->dettPersLegacyAmounts as $key => $value) { if (array_key_exists($key, $amounts)) { $amounts[$key] = round((float) $value, 2); } } } } else { foreach ($this->dettPersLegacyAmounts as $key => $value) { if (array_key_exists($key, $amounts)) { $amounts[$key] = round((float) $value, 2); } } } $rows = []; $totale = 0.0; foreach ($recipients as $recipient) { $key = (string) ($recipient['key'] ?? ''); $value = round((float) ($amounts[$key] ?? 0), 2); $totale += $value; $rows[] = [ 'id_cond' => $recipient['id_cond'] ?? null, 'soggetto' => $recipient['soggetto'] ?? '—', 'unita' => $recipient['unita'] ?? '—', 'cond_inq' => $recipient['cond_inq'] ?? 'C', 'importo' => $value, ]; } $this->dettPersRows = $rows; $this->dettPersTotale = round($totale, 2); $this->dettPersDelta = round($this->dettPersImportoOperazione - $this->dettPersTotale, 2); $this->dettPersQuadraturaOk = abs($this->dettPersDelta) <= 0.01; } private function distributeEqually(float $total, array $keys): array { $out = []; if (empty($keys)) { return $out; } $count = count($keys); $base = round($total / $count, 2); foreach ($keys as $key) { $out[$key] = $base; } $sum = array_sum($out); $delta = round($total - $sum, 2); if ($delta !== 0.0) { $firstKey = $keys[0]; $out[$firstKey] = round(($out[$firstKey] ?? 0) + $delta, 2); } return $out; } private function distributeByWeights(float $total, array $weights): array { $sumWeights = array_sum($weights); if ($sumWeights <= 0) { return []; } $out = []; foreach ($weights as $key => $weight) { $share = ($total * ((float) $weight / $sumWeights)); $out[$key] = round($share, 2); } $sum = array_sum($out); $delta = round($total - $sum, 2); if ($delta !== 0.0) { $firstKey = array_key_first($out); if ($firstKey !== null) { $out[$firstKey] = round(($out[$firstKey] ?? 0) + $delta, 2); } } return $out; } private function resolveMillesimiWeights(): array { $tabKey = trim((string) ($this->dettPersRipartoTabella ?? '')); if ($tabKey === '') { return []; } if (str_starts_with($tabKey, 'LOCAL:') && Schema::hasTable('dettaglio_millesimi') && Schema::hasTable('unita_immobiliari')) { $tabellaId = (int) str_replace('LOCAL:', '', $tabKey); if ($tabellaId <= 0) { return []; } $rows = DB::table('dettaglio_millesimi as dm') ->join('unita_immobiliari as ui', 'ui.id', '=', 'dm.unita_immobiliare_id') ->where('dm.tabella_millesimale_id', $tabellaId) ->where('dm.partecipa', true) ->get(['ui.legacy_cond_id', 'dm.millesimi']); $weights = []; foreach ($rows as $row) { $idCond = is_numeric($row->legacy_cond_id ?? null) ? (int) $row->legacy_cond_id : 0; if ($idCond <= 0) { continue; } $w = $this->normalizeMoney($row->millesimi ?? 0); if ($w <= 0) { continue; } $key = $idCond . '|C'; $weights[$key] = ($weights[$key] ?? 0.0) + $w; } return $weights; } $legacyCode = $this->dettPersLegacyCode; $codTab = strtoupper($tabKey); if (! $legacyCode || ! Schema::connection('gescon_import')->hasTable('dett_tab')) { return []; } $query = DB::connection('gescon_import') ->table('dett_tab') ->where('cod_stabile', $legacyCode) ->where('cod_tab', $codTab); if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) { $query->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]); } $rows = $query->get(['id_cond', 'cond_inquil', 'cons_euro', 'mm', 'prev_euro']); $weights = []; foreach ($rows as $row) { $idCond = is_numeric($row->id_cond ?? null) ? (int) $row->id_cond : 0; $role = strtoupper(trim((string) ($row->cond_inquil ?? 'C'))); $role = $role === 'I' ? 'I' : 'C'; if ($idCond <= 0) { continue; } $weight = $this->normalizeMoney($row->cons_euro ?? 0); if ($weight <= 0) { $weight = $this->normalizeMoney($row->mm ?? 0); } if ($weight <= 0) { $weight = $this->normalizeMoney($row->prev_euro ?? 0); } if ($weight <= 0) { continue; } $key = $idCond . '|' . $role; $weights[$key] = ($weights[$key] ?? 0.0) + $weight; } return $weights; } private function recalculateDettPersTotalsFromRows(): void { $totale = 0.0; foreach ($this->dettPersRows as $idx => $row) { $value = round($this->normalizeMoney($row['importo'] ?? 0), 2); $this->dettPersRows[$idx]['importo'] = $value; $totale += $value; } $this->dettPersTotale = round($totale, 2); $this->dettPersDelta = round($this->dettPersImportoOperazione - $this->dettPersTotale, 2); $this->dettPersQuadraturaOk = abs($this->dettPersDelta) <= 0.01; } private function normalizeMoney($value): float { if ($value === null || $value === '') { return 0.0; } $s = trim((string) $value); $s = str_replace([' ', "\r", "\n", "\t"], '', $s); if (str_contains($s, ',') && str_contains($s, '.')) { $s = str_replace('.', '', $s); $s = str_replace(',', '.', $s); } elseif (str_contains($s, ',')) { $s = str_replace(',', '.', $s); } return is_numeric($s) ? (float) $s : 0.0; } private function normalizeInvoiceNumber($value): ?string { $raw = strtoupper(trim((string) ($value ?? ''))); if ($raw === '') { return null; } $normalized = preg_replace('/[^A-Z0-9]/', '', $raw); if (! is_string($normalized) || $normalized === '') { return null; } return $normalized; } private function computeRdaDueDate(?string $paymentDate): ?string { if (! $paymentDate) { return null; } try { $due = \Carbon\Carbon::parse($paymentDate) ->startOfMonth() ->addMonthNoOverflow() ->day(16) ->startOfDay(); while ($this->isNonWorkingDay($due)) { $due->addDay(); } return $due->format('Y-m-d'); } catch (\Throwable) { return null; } } private function isNonWorkingDay(\Carbon\Carbon $day): bool { if ($day->isWeekend()) { return true; } $fixed = [ '01-01', '01-06', '04-25', '05-01', '06-02', '08-15', '11-01', '12-08', '12-25', '12-26', ]; if (in_array($day->format('m-d'), $fixed, true)) { return true; } $year = (int) $day->format('Y'); $easter = \Carbon\Carbon::createFromTimestamp(easter_date($year))->startOfDay(); $easterMonday = $easter->copy()->addDay(); return $day->isSameDay($easter) || $day->isSameDay($easterMonday); } private function buildOperazioniQuery() { $query = DB::connection('gescon_import')->table('operazioni'); $legacyCode = $this->resolveLegacyCodiceStabile(); if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) { $query->where('cod_stabile', $legacyCode); } if ($this->onlyStraordinarieMode) { $query->where('gestione', 'S'); if ($this->selectedStraordinaria !== null) { $query->where('n_stra', $this->selectedStraordinaria); } } elseif ($this->netgesconWorkflowMode) { $query->where(function ($q) { $q->where('gestione', 'O') ->orWhereNull('gestione') ->orWhere('gestione', ''); }); } if ($this->search) { $s = '%' . $this->search . '%'; $query->where(function ($q) use ($s) { $q->where('cod_for', 'like', $s) ->orWhere('cod_spe', 'like', $s) ->orWhere('benef', 'like', $s) ->orWhere('num_fat', 'like', $s) ->orWhere('fe_uid', 'like', $s) ->orWhere('rif_rda', 'like', $s) ->orWhere('id_operaz', 'like', $s) ->orWhere('n_spe', 'like', $s) ->orWhere('n_stra', 'like', $s); }); } if ($this->filterCodForn) { $query->where('cod_for', $this->filterCodForn); } if ($this->filterCodSpe) { $query->where('cod_spe', $this->filterCodSpe); } if ($this->filterTabella) { $query->where('tabella', $this->filterTabella); } if ($this->filterBenef) { $query->where('benef', 'like', '%' . $this->filterBenef . '%'); } if ($this->filterRdaOnly) { $query->where(function ($q) { $q->whereNotNull('rif_rda') ->orWhere('benef', 'like', 'RDA:%'); }); } if ($this->filterAnno) { $query->whereYear('dt_spe', $this->filterAnno); } return $query; } private function computeSignedAmount($row): float { $base = $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0); $spese = $this->normalizeMoney($row->importo_spese ?? 0); $entrate = $this->normalizeMoney($row->importo_entrate ?? 0); $debiti = $this->normalizeMoney($row->importo_debiti ?? 0); $crediti = $this->normalizeMoney($row->importo_crediti ?? 0); if (($spese + $entrate + $debiti + $crediti) > 0) { return ($spese + $debiti) - ($entrate + $crediti); } $natura = strtoupper(trim((string) ($row->natura2 ?? ''))); if ($natura !== '' && in_array($natura, ['E', 'ENTRATA', 'ENTRATE', 'CREDITO'], true)) { return -1 * $base; } if (strtoupper((string) ($row->cod_spe ?? '')) === 'RB1') { return -1 * $base; } return $base; } public function openMastrino(string $tabella, string $codSpe): void { $this->mastrinoTabella = $tabella; $this->mastrinoCodSpe = $codSpe; $query = $this->buildOperazioniQuery() ->where('cod_spe', $codSpe) ->orderBy('dt_spe') ->orderBy('id_operaz'); if (trim($tabella) !== '') { $query->where('tabella', $tabella); } if ($this->filterAnno) { $query->whereYear('dt_spe', $this->filterAnno); } $rows = $query->get(); $this->mastrinoRows = $rows->map(function ($row) { return [ 'id' => $row->id_operaz ?? null, 'data' => $row->dt_spe ?? null, 'descrizione' => $row->des_oper ?? $row->benef ?? null, 'importo' => $this->computeSignedAmount($row), 'n_spe' => $row->n_spe ?? null, 'n_stra' => $row->n_stra ?? null, 'tabella' => $row->tabella ?? null, 'fe_uid' => $row->fe_uid ?? null, 'rif_rda' => $row->rif_rda ?? null, 'ra_importo' => $row->netto_vers_rda ?? $row->importo_euro_770 ?? null, ]; })->all(); } public function sortBy(string $field): void { $allowed = [ 'id_operaz', 'dt_spe', 'cod_spe', 'tabella', 'cod_for', 'benef', 'importo_euro', 'num_fat', 'fe_uid', 'rif_rda', ]; if (! in_array($field, $allowed, true)) { return; } if ($this->sortField === $field) { $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; } else { $this->sortField = $field; $this->sortDirection = 'asc'; } } public function getSpecialCodesSummaryProperty(): array { $base = $this->buildOperazioniQuery(); $rows = $base ->whereIn('cod_spe', ['Z01', 'Z02']) ->select('cod_spe') ->selectRaw("SUM( CASE WHEN (COALESCE(importo_spese, 0) + COALESCE(importo_entrate, 0) + COALESCE(importo_debiti, 0) + COALESCE(importo_crediti, 0)) = 0 THEN CASE WHEN natura2 IN ('E', 'ENTRATA', 'ENTRATE', 'CREDITO') THEN -1 * COALESCE(importo_euro, importo, 0) ELSE COALESCE(importo_euro, importo, 0) END ELSE (COALESCE(importo_spese, 0) + COALESCE(importo_debiti, 0) - COALESCE(importo_entrate, 0) - COALESCE(importo_crediti, 0)) END ) as totale") ->groupBy('cod_spe') ->get(); $map = $rows->mapWithKeys(fn($row) => [strtoupper((string) $row->cod_spe) => (float) ($row->totale ?? 0)])->all(); return [ 'Z01' => (float) ($map['Z01'] ?? 0), 'Z02' => (float) ($map['Z02'] ?? 0), 'totale' => (float) (($map['Z01'] ?? 0) + ($map['Z02'] ?? 0)), ]; } private function applyOperazioniSorting($query) { $dir = strtolower($this->sortDirection) === 'asc' ? 'asc' : 'desc'; $field = $this->sortField; switch ($field) { case 'importo_euro': $query->orderByRaw('COALESCE(importo_euro, importo, 0) ' . $dir); break; default: $query->orderBy($field, $dir); break; } if ($field !== 'id_operaz') { $query->orderBy('id_operaz', 'desc'); } return $query; } }