hasAnyRole(['super-admin', 'admin'])) { return true; } return $user->can('contabilita.registro-ra'); } public function mount(): void { $tab = (string) request()->query('tab', 'ra'); if (in_array($tab, ['ra', 'f24', 'cu'], true)) { $this->viewTab = $tab; } $year = request()->query('anno'); $this->fiscalYear = is_numeric($year) ? (int) $year : (int) now()->format('Y'); $this->mountInteractsWithTable(); } public function setViewTab(string $tab): void { if (in_array($tab, ['ra', 'f24', 'cu'], true)) { $this->viewTab = $tab; } } public function setFiscalYear($year): void { if (is_numeric($year)) { $this->fiscalYear = (int) $year; } } public function setF24SubTab(string $tab): void { if (in_array($tab, ['riepilogo', 'dettaglio'], true)) { $this->f24SubTab = $tab; } } public function setF24DetailFilterState(string $state): void { if (in_array($state, ['tutti', 'versato', 'da_versare', 'ritardo'], true)) { $this->f24DetailFilterState = $state; } } public function setSelectedF24Fattura(?int $fatturaId = null): void { $this->selectedF24FatturaId = $fatturaId && $fatturaId > 0 ? $fatturaId : null; } public function setCuSubTab(string $tab): void { if (in_array($tab, ['riepilogo', 'dettaglio'], true)) { $this->cuSubTab = $tab; } } public function setSelectedCuFornitore(?int $fornitoreId = null): void { $this->selectedCuFornitoreId = $fornitoreId && $fornitoreId > 0 ? $fornitoreId : null; } protected function getHeaderActions(): array { return [ Action::make('sync_operazioni_ra') ->label('Sincronizza date da Operazioni') ->icon('heroicon-o-arrow-path') ->action(function (): void { $result = $this->syncRaDatesFromOperazioni(); Notification::make() ->title('Sincronizzazione completata') ->body("Fatture aggiornate: {$result['fatture_aggiornate']} · Registro RA aggiornato: {$result['registro_aggiornato']}") ->success() ->send(); }), ]; } protected function getTableQuery(): Builder { $user = Auth::user(); if (! $user instanceof User) { return RegistroRitenuteAcconto::query()->whereRaw('1 = 0'); } $stabileId = StabileContext::resolveActiveStabileId($user); if (! $stabileId) { return RegistroRitenuteAcconto::query()->whereRaw('1 = 0'); } return RegistroRitenuteAcconto::query() ->with(['fornitore', 'gestione']) ->whereHas('gestione', fn(Builder $q) => $q->where('stabile_id', (int) $stabileId)) ->when($this->fiscalYear, fn(Builder $q) => $q->where(function (Builder $sq): void { $sq->whereYear('data_competenza', (int) $this->fiscalYear) ->orWhere('anno_dichiarazione', (int) $this->fiscalYear); })) ->orderByDesc('data_competenza') ->orderByDesc('id'); } public function getAvailableFiscalYearsProperty(): array { $stabileId = StabileContext::resolveActiveStabileId(Auth::user()); if (! $stabileId) { return []; } $years = []; if (Schema::hasTable('contabilita_fatture_fornitori')) { $fattureYears = DB::table('contabilita_fatture_fornitori') ->where('stabile_id', (int) $stabileId) ->whereRaw('COALESCE(ritenuta_importo,0) <> 0') ->selectRaw('YEAR(data_documento) as y1, YEAR(data_pagamento) as y2, YEAR(data_versamento_ra) as y3') ->get(); foreach ($fattureYears as $y) { foreach ([(int) ($y->y1 ?? 0), (int) ($y->y2 ?? 0), (int) ($y->y3 ?? 0)] as $year) { if ($year > 0) { $years[$year] = $year; } } } } $registroYears = RegistroRitenuteAcconto::query() ->whereHas('gestione', fn(Builder $q) => $q->where('stabile_id', (int) $stabileId)) ->pluck('anno_dichiarazione') ->filter(fn($v) => is_numeric($v) && (int) $v > 0) ->map(fn($v) => (int) $v) ->all(); foreach ($registroYears as $year) { $years[$year] = $year; } if (empty($years)) { $current = (int) now()->format('Y'); return [$current => (string) $current]; } krsort($years); return collect($years)->mapWithKeys(fn($y) => [$y => (string) $y])->all(); } private function getRaDatasetRows(): array { $stabileId = StabileContext::resolveActiveStabileId(Auth::user()); if (! $stabileId || ! Schema::hasTable('contabilita_fatture_fornitori')) { return []; } $query = DB::table('contabilita_fatture_fornitori as f') ->leftJoin('fornitori as fr', 'fr.id', '=', 'f.fornitore_id') ->leftJoin('contabilita_movimenti_banca as mb', 'mb.id', '=', 'f.movimento_versamento_ra_id') ->leftJoin('dati_bancari as db', 'db.id', '=', 'mb.conto_id') ->where('f.stabile_id', (int) $stabileId) ->whereRaw('COALESCE(f.ritenuta_importo,0) <> 0'); if ($this->fiscalYear) { $year = (int) $this->fiscalYear; $query->where(function ($q) use ($year): void { $q->whereYear('f.data_documento', $year) ->orWhereYear('f.data_pagamento', $year) ->orWhereYear('f.data_versamento_ra', $year); }); } $rows = $query->get([ 'f.id', 'f.fornitore_id', 'f.numero_documento', 'f.data_documento', 'f.data_pagamento', 'f.data_versamento_ra', 'f.ritenuta_importo', 'f.ritenuta_codice_tributo', 'f.movimento_versamento_ra_id', 'f.imponibile', 'f.totale', 'fr.ragione_sociale as fornitore_ragione_sociale', 'fr.nome as fornitore_nome', 'fr.cognome as fornitore_cognome', 'db.denominazione_banca as banca_nome', 'db.abi as banca_abi', 'db.cab as banca_cab', 'mb.data as banca_data', 'mb.descrizione as banca_descrizione', 'mb.descrizione_estesa as banca_descrizione_estesa', ]); $fornitoreIds = $rows->pluck('fornitore_id') ->filter(fn($v) => is_numeric($v)) ->map(fn($v) => (int) $v) ->unique() ->values() ->all(); $tributoByFornitore = $this->resolveTributoByFornitoreIdMap($fornitoreIds); $registroMap = RegistroRitenuteAcconto::query() ->whereIn('fattura_id', $rows->pluck('id')->map(fn($v) => (int) $v)->all()) ->get(['fattura_id', 'sanzioni', 'interessi', 'inclusa_cu', 'comunicazione_fornitore_inviata', 'data_comunicazione_fornitore', 'anno_dichiarazione']) ->keyBy('fattura_id'); return $rows->map(function ($row) use ($tributoByFornitore, $registroMap) { $fornId = is_numeric($row->fornitore_id ?? null) ? (int) $row->fornitore_id : null; $tributo = strtoupper(trim((string) ($row->ritenuta_codice_tributo ?? ''))); if ($tributo === '' && $fornId && ! empty($tributoByFornitore[$fornId])) { $tributo = (string) $tributoByFornitore[$fornId]; } if ($tributo === '') { $tributo = '1040'; } $fornitore = trim((string) ($row->fornitore_ragione_sociale ?? '')); if ($fornitore === '') { $fornitore = trim((string) (($row->fornitore_nome ?? '') . ' ' . ($row->fornitore_cognome ?? ''))); } if ($fornitore === '') { $fornitore = '—'; } $registro = $registroMap[(int) $row->id] ?? null; $sanzioni = abs((float) ($registro->sanzioni ?? 0)); $interessi = abs((float) ($registro->interessi ?? 0)); $ra = abs((float) ($row->ritenuta_importo ?? 0)); $dataPagamento = ! empty($row->data_pagamento) ? Carbon::parse($row->data_pagamento)->format('Y-m-d') : null; $dataVersamento = ! empty($row->data_versamento_ra) ? Carbon::parse($row->data_versamento_ra)->format('Y-m-d') : null; $scadenza = $dataPagamento ? Carbon::parse($dataPagamento)->startOfMonth()->addMonthNoOverflow()->day(16)->format('Y-m-d') : null; $inRitardo = false; if ($scadenza && $dataVersamento) { $inRitardo = Carbon::parse($dataVersamento)->gt(Carbon::parse($scadenza)); } $giorniRitardo = $this->computeGiorniRitardo($scadenza, $dataVersamento); $sanzionePerc = $this->computeSanzionePercentuale($giorniRitardo); $rateLegale = $this->resolveTassoLegalePercent($scadenza ?: $dataPagamento ?: $dataVersamento); $sanzioneCalcolata = $this->computeSanzioneRavvedimento($ra, $giorniRitardo); $interessiCalcolati = $this->computeInteressiRavvedimento($ra, $giorniRitardo, $rateLegale); $sanzioniFinali = $sanzioni > 0 ? $sanzioni : $sanzioneCalcolata; $interessiFinali = $interessi > 0 ? $interessi : $interessiCalcolati; $fiscalYear = (int) (($registro->anno_dichiarazione ?? null) ?: ($dataVersamento ? Carbon::parse($dataVersamento)->format('Y') : ($dataPagamento ? Carbon::parse($dataPagamento)->format('Y') : 0))); return [ 'fattura_id' => (int) $row->id, 'fornitore_id' => $fornId, 'fornitore' => $fornitore, 'numero_documento' => (string) ($row->numero_documento ?? '—'), 'data_documento' => ! empty($row->data_documento) ? Carbon::parse($row->data_documento)->format('Y-m-d') : null, 'data_pagamento' => $dataPagamento, 'data_versamento' => $dataVersamento, 'scadenza_versamento' => $scadenza, 'tributo' => $tributo, 'importo_ra' => round($ra, 2), 'sanzioni' => round($sanzioniFinali, 2), 'interessi' => round($interessiFinali, 2), 'istituto_banca' => trim((string) ($row->banca_nome ?? '')) ?: '—', 'istituto_abi' => trim((string) ($row->banca_abi ?? '')), 'istituto_cab' => trim((string) ($row->banca_cab ?? '')), 'addebito_descrizione' => trim((string) ($row->banca_descrizione_estesa ?? $row->banca_descrizione ?? '')), 'banca_data' => ! empty($row->banca_data) ? Carbon::parse($row->banca_data)->format('Y-m-d') : null, 'versata' => $dataVersamento !== null, 'in_ritardo' => $inRitardo, 'giorni_ritardo' => $giorniRitardo, 'sanzione_percentuale' => $sanzionePerc, 'tasso_legale' => $rateLegale, 'tributo_sanzione' => '8906', 'tributo_interessi' => '1991', 'totale_f24' => round($ra + $sanzioniFinali + $interessiFinali, 2), 'fiscal_year' => $fiscalYear, 'inclusa_cu' => (bool) ($registro->inclusa_cu ?? false), 'comunicata_cu' => (bool) ($registro->comunicazione_fornitore_inviata ?? false), 'data_comunicazione_cu' => ! empty($registro?->data_comunicazione_fornitore) ? Carbon::parse($registro->data_comunicazione_fornitore)->format('Y-m-d') : null, ]; })->values()->all(); } private function computeGiorniRitardo(?string $scadenza, ?string $dataVersamento): int { if (! $scadenza) { return 0; } $scadenzaDate = Carbon::parse($scadenza)->startOfDay(); $targetDate = $dataVersamento ? Carbon::parse($dataVersamento)->startOfDay() : now()->startOfDay(); if ($targetDate->lte($scadenzaDate)) { return 0; } return (int) $scadenzaDate->diffInDays($targetDate); } private function computeSanzionePercentuale(int $giorniRitardo): float { if ($giorniRitardo <= 0) { return 0.0; } if ($giorniRitardo <= 14) { return round($giorniRitardo * 0.1, 4); } if ($giorniRitardo <= 30) { return 1.5; } if ($giorniRitardo <= 90) { return 1.67; } return 3.75; } private function computeSanzioneRavvedimento(float $imposta, int $giorniRitardo): float { if ($imposta <= 0 || $giorniRitardo <= 0) { return 0.0; } return round($imposta * $this->computeSanzionePercentuale($giorniRitardo) / 100, 2); } private function computeInteressiRavvedimento(float $imposta, int $giorniRitardo, float $tassoLegalePercent): float { if ($imposta <= 0 || $giorniRitardo <= 0 || $tassoLegalePercent <= 0) { return 0.0; } return round(($imposta * $tassoLegalePercent * $giorniRitardo) / 36500, 2); } private function resolveTassoLegalePercent(?string $dateRef): float { $fallback = 1.6; $year = $dateRef ? (int) Carbon::parse($dateRef)->format('Y') : (int) now()->format('Y'); if (array_key_exists($year, $this->tassoLegaleCache)) { return (float) $this->tassoLegaleCache[$year]; } if (! Schema::connection('gescon_import')->hasTable('ravv')) { $this->tassoLegaleCache[$year] = $fallback; return $fallback; } $columns = Schema::connection('gescon_import')->getColumnListing('ravv'); $rateColumn = collect(['tasso_legale', 'tasso', 'interesse', 'interessi']) ->first(fn(string $c) => in_array($c, $columns, true)); if (! $rateColumn) { $this->tassoLegaleCache[$year] = $fallback; return $fallback; } $yearColumn = collect(['anno', 'year', 'esercizio']) ->first(fn(string $c) => in_array($c, $columns, true)); $query = DB::connection('gescon_import')->table('ravv'); if ($yearColumn) { $query->where($yearColumn, '<=', $year)->orderByDesc($yearColumn); } $rate = $query->value($rateColumn); if (! is_numeric($rate)) { $this->tassoLegaleCache[$year] = $fallback; return $fallback; } $this->tassoLegaleCache[$year] = (float) $rate; return (float) $this->tassoLegaleCache[$year]; } public function getF24MonthlySummaryProperty(): array { $rows = $this->getRaDatasetRows(); $summary = []; $totVersato = 0.0; $totDaVersare = 0.0; foreach ($rows as $row) { $importo = abs((float) ($row['importo_ra'] ?? 0)); if ($importo <= 0) { continue; } $tributo = (string) ($row['tributo'] ?? '1040'); $periodDate = $row['data_versamento'] ?? $row['scadenza_versamento'] ?? $row['data_pagamento'] ?? $row['data_documento'] ?? null; if (! $periodDate) { continue; } $mese = Carbon::parse($periodDate)->format('Y-m'); $key = $mese . '|' . $tributo; if (! isset($summary[$key])) { $summary[$key] = [ 'mese' => $mese, 'tributo' => $tributo, 'versato' => 0.0, 'da_versare' => 0.0, 'righe' => 0, 'f24_compilati' => 0, ]; } $summary[$key]['righe']++; if (! empty($row['data_versamento'])) { $summary[$key]['f24_compilati']++; } if (! empty($row['versata'])) { $summary[$key]['versato'] += $importo; $totVersato += $importo; } else { $summary[$key]['da_versare'] += $importo; $totDaVersare += $importo; } } $rowsOut = collect($summary) ->map(function (array $row) { $row['versato'] = round((float) $row['versato'], 2); $row['da_versare'] = round((float) $row['da_versare'], 2); $row['delta'] = round($row['da_versare'] - $row['versato'], 2); return $row; }) ->sortBy(['mese', 'tributo']) ->values() ->all(); return [ 'rows' => $rowsOut, 'totale_versato' => round($totVersato, 2), 'totale_da_versare' => round($totDaVersare, 2), ]; } public function getF24DetailRowsProperty(): array { $rows = collect($this->getRaDatasetRows()) ->sortBy([ fn($a, $b) => strcmp((string) ($a['data_versamento'] ?? $a['scadenza_versamento'] ?? ''), (string) ($b['data_versamento'] ?? $b['scadenza_versamento'] ?? '')), fn($a, $b) => strcmp((string) ($a['tributo'] ?? ''), (string) ($b['tributo'] ?? '')), ]) ->values() ->all(); return $rows; } public function getFilteredF24DetailRowsProperty(): array { $rows = collect($this->f24DetailRows); $rows = match ($this->f24DetailFilterState) { 'versato' => $rows->filter(fn(array $r) => ! empty($r['versata'])), 'da_versare' => $rows->filter(fn(array $r) => empty($r['versata'])), 'ritardo' => $rows->filter(fn(array $r) => ! empty($r['in_ritardo']) || ((int) ($r['giorni_ritardo'] ?? 0) > 0)), default => $rows, }; return $rows->values()->all(); } public function getSelectedF24ModelProperty(): ?array { if (! $this->selectedF24FatturaId) { return null; } return collect($this->f24DetailRows) ->first(fn(array $row) => (int) ($row['fattura_id'] ?? 0) === (int) $this->selectedF24FatturaId); } public function getCuSummaryProperty(): array { $rows = $this->getRaDatasetRows(); $summary = []; $totaleRa = 0.0; foreach ($rows as $row) { $importo = abs((float) ($row['importo_ra'] ?? 0)); if ($importo <= 0) { continue; } $anno = (int) ($row['fiscal_year'] ?? 0); if ($anno <= 0) { continue; } $fornitoreId = (int) ($row['fornitore_id'] ?? 0); $fornitoreNome = (string) ($row['fornitore'] ?? 'Fornitore non associato'); $key = $anno . '|' . $fornitoreId; if (! isset($summary[$key])) { $summary[$key] = [ 'anno' => $anno, 'fornitore_id' => $fornitoreId, 'fornitore' => $fornitoreNome !== '' ? $fornitoreNome : '—', 'totale_ra' => 0.0, 'totale_versata' => 0.0, 'totale_da_versare' => 0.0, 'righe' => 0, 'inclusa_cu' => false, 'comunicata' => false, 'ultima_comunicazione' => null, ]; } $summary[$key]['totale_ra'] += $importo; $summary[$key]['righe']++; if (! empty($row['versata'])) { $summary[$key]['totale_versata'] += $importo; } else { $summary[$key]['totale_da_versare'] += $importo; } $summary[$key]['inclusa_cu'] = $summary[$key]['inclusa_cu'] || (bool) ($row['inclusa_cu'] ?? false); $summary[$key]['comunicata'] = $summary[$key]['comunicata'] || (bool) ($row['comunicata_cu'] ?? false); if (! empty($row['data_comunicazione_cu'])) { $dt = Carbon::parse((string) $row['data_comunicazione_cu'])->format('Y-m-d'); if (empty($summary[$key]['ultima_comunicazione']) || $dt > $summary[$key]['ultima_comunicazione']) { $summary[$key]['ultima_comunicazione'] = $dt; } } $totaleRa += $importo; } $rowsOut = collect($summary) ->map(function (array $row) { $row['totale_ra'] = round((float) $row['totale_ra'], 2); $row['totale_versata'] = round((float) $row['totale_versata'], 2); $row['totale_da_versare'] = round((float) $row['totale_da_versare'], 2); return $row; }) ->sortByDesc('anno') ->values() ->all(); return ['rows' => $rowsOut, 'totale_ra' => round($totaleRa, 2)]; } public function getCuDetailRowsProperty(): array { return collect($this->getRaDatasetRows()) ->sortBy([ fn($a, $b) => strcmp((string) ($a['fornitore'] ?? ''), (string) ($b['fornitore'] ?? '')), fn($a, $b) => strcmp((string) ($a['data_pagamento'] ?? ''), (string) ($b['data_pagamento'] ?? '')), ]) ->values() ->all(); } public function getFilteredCuDetailRowsProperty(): array { $rows = collect($this->cuDetailRows); if ($this->selectedCuFornitoreId) { $rows = $rows->filter(fn(array $row) => (int) ($row['fornitore_id'] ?? 0) === (int) $this->selectedCuFornitoreId); } return $rows->values()->all(); } 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']); $tributoByCod = []; 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; } $trib = strtoupper(trim((string) ($raw['trib_1019_1020'] ?? ''))); if ($trib !== '') { $tributoByCod[$cod] = $trib; $noZero = ltrim($cod, '0'); if ($noZero !== '') { $tributoByCod[$noZero] = $trib; } } } $out = []; foreach ($fornitoriRows as $forn) { $old = trim((string) ($forn->old_id ?? '')); if ($old === '') { continue; } $oldNoZero = ltrim($old, '0'); $trib = $tributoByCod[$old] ?? ($oldNoZero !== '' ? ($tributoByCod[$oldNoZero] ?? null) : null); if ($trib) { $out[(int) $forn->id] = $trib; } } return $out; } private function syncRaDatesFromOperazioni(): array { $user = Auth::user(); if (! $user instanceof User) { return ['fatture_aggiornate' => 0, 'registro_aggiornato' => 0]; } $stabileId = StabileContext::resolveActiveStabileId($user); if (! $stabileId || ! Schema::hasTable('contabilita_fatture_fornitori') || ! Schema::connection('gescon_import')->hasTable('operazioni')) { return ['fatture_aggiornate' => 0, 'registro_aggiornato' => 0]; } $stabile = DB::table('stabili')->where('id', (int) $stabileId)->first(['codice_stabile', 'cod_stabile', 'old_id']); $legacyCodes = array_values(array_unique(array_filter([ trim((string) ($stabile->codice_stabile ?? '')), trim((string) ($stabile->cod_stabile ?? '')), trim((string) ($stabile->old_id ?? '')), ], fn($v) => $v !== ''))); $ops = DB::connection('gescon_import')->table('operazioni') ->when(! empty($legacyCodes) && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'), fn($q) => $q->whereIn('cod_stabile', $legacyCodes)) ->get(['benef', 'rif_rda', 'dt_spe', 'cod_for', 'num_fat', 'netto_vers_rda', 'importo_euro_770', 'importo_euro', 'importo']); $rdaRowsByCode = []; $invoiceMap = []; foreach ($ops as $op) { $benef = trim((string) ($op->benef ?? '')); $isRda = $benef !== '' && str_starts_with(strtoupper($benef), 'RDA:'); $rdaCode = trim((string) ($op->rif_rda ?? '')); if ($rdaCode === '' && $isRda) { $rdaCode = trim(substr($benef, 4)); } if ($rdaCode === '') { continue; } if ($isRda) { $date = ! empty($op->dt_spe) ? Carbon::parse($op->dt_spe)->format('Y-m-d') : null; $amount = abs((float) ($op->netto_vers_rda ?? $op->importo_euro_770 ?? $op->importo_euro ?? $op->importo ?? 0)); $existing = $rdaRowsByCode[$rdaCode] ?? null; if (! $existing || ($date && $date >= (string) ($existing['data'] ?? ''))) { $rdaRowsByCode[$rdaCode] = ['data' => $date, 'importo' => $amount]; } continue; } $codFor = trim((string) ($op->cod_for ?? '')); $num = strtoupper((string) preg_replace('/[^A-Z0-9]/', '', (string) ($op->num_fat ?? ''))); $date = ! empty($op->dt_spe) ? Carbon::parse($op->dt_spe)->format('Y-m-d') : null; if ($codFor === '' || $num === '' || ! $date) { continue; } $fornCodes = array_values(array_unique(array_filter([$codFor, ltrim($codFor, '0')], fn($v) => $v !== ''))); foreach ($fornCodes as $fc) { $key = $fc . '|' . $num; $existing = $invoiceMap[$key] ?? null; if (! $existing || $date >= (string) ($existing['data_pagamento'] ?? '')) { $invoiceMap[$key] = ['data_pagamento' => $date, 'rda_code' => $rdaCode]; } } } $fornitori = DB::table('fornitori')->get(['id', 'old_id'])->keyBy('id'); $tributoByFornitore = $this->resolveTributoByFornitoreIdMap($fornitori->keys()->map(fn($v) => (int) $v)->all()); $fatture = DB::table('contabilita_fatture_fornitori') ->where('stabile_id', (int) $stabileId) ->get(['id', 'fornitore_id', 'numero_documento', 'data_pagamento', 'data_versamento_ra', 'ritenuta_importo', 'ritenuta_codice_tributo', 'imponibile', 'ritenuta_aliquota']); $fattureAggiornate = 0; $registroAggiornato = 0; foreach ($fatture as $fattura) { $forn = $fornitori[(int) ($fattura->fornitore_id ?? 0)] ?? null; $old = trim((string) ($forn->old_id ?? '')); $num = strtoupper((string) preg_replace('/[^A-Z0-9]/', '', (string) ($fattura->numero_documento ?? ''))); if ($old === '' || $num === '') { continue; } $map = $invoiceMap[$old . '|' . $num] ?? (($old !== ltrim($old, '0')) ? ($invoiceMap[ltrim($old, '0') . '|' . $num] ?? null) : null); if (! $map) { continue; } $update = []; if (empty($fattura->data_pagamento) && ! empty($map['data_pagamento'])) { $update['data_pagamento'] = $map['data_pagamento']; } $rdaCode = trim((string) ($map['rda_code'] ?? '')); if (empty($fattura->data_versamento_ra) && $rdaCode !== '' && ! empty($rdaRowsByCode[$rdaCode]['data'])) { $update['data_versamento_ra'] = $rdaRowsByCode[$rdaCode]['data']; } if ((float) ($fattura->ritenuta_importo ?? 0) == 0.0 && $rdaCode !== '' && ! empty($rdaRowsByCode[$rdaCode]['importo'])) { $update['ritenuta_importo'] = -abs((float) $rdaRowsByCode[$rdaCode]['importo']); } if (empty($fattura->ritenuta_codice_tributo) && ! empty($tributoByFornitore[(int) ($fattura->fornitore_id ?? 0)])) { $update['ritenuta_codice_tributo'] = $tributoByFornitore[(int) $fattura->fornitore_id]; } if (empty($update)) { continue; } $update['updated_at'] = now(); DB::table('contabilita_fatture_fornitori')->where('id', (int) $fattura->id)->update($update); $fattureAggiornate++; if (Schema::hasTable('registro_ritenute_acconto')) { $ra = abs((float) ($update['ritenuta_importo'] ?? $fattura->ritenuta_importo ?? 0)); if ($ra > 0) { $registroUpdate = [ 'data_pagamento_fattura' => $update['data_pagamento'] ?? $fattura->data_pagamento, 'data_versamento' => $update['data_versamento_ra'] ?? $fattura->data_versamento_ra, 'codice_tributo' => $update['ritenuta_codice_tributo'] ?? $fattura->ritenuta_codice_tributo ?? '1040', 'importo_ritenuta' => $ra, 'stato_versamento' => ! empty($update['data_versamento_ra'] ?? $fattura->data_versamento_ra) ? 'versata' : 'da_versare', 'updated_at' => now(), ]; $datePag = $registroUpdate['data_pagamento_fattura']; if (! empty($datePag)) { $registroUpdate['data_scadenza_versamento'] = Carbon::parse((string) $datePag)->startOfMonth()->addMonthNoOverflow()->day(16)->format('Y-m-d'); } $updated = DB::table('registro_ritenute_acconto') ->where('fattura_id', (int) $fattura->id) ->update($registroUpdate); if ($updated > 0) { $registroAggiornato += $updated; } } } } return ['fatture_aggiornate' => $fattureAggiornate, 'registro_aggiornato' => $registroAggiornato]; } public function table(Table $table): Table { return $table ->striped() ->filters([ SelectFilter::make('stato_versamento') ->label('Stato') ->options([ 'da_versare' => 'Da versare', 'versata' => 'Versata', 'compensata' => 'Compensata', 'annullata' => 'Annullata', ]) ->query(fn(Builder $query, array $data): Builder => isset($data['value']) && $data['value'] !== '' ? $query->where('stato_versamento', $data['value']) : $query ), SelectFilter::make('mese_scadenza') ->label('Mese scadenza') ->options([ '1' => 'Gennaio', '2' => 'Febbraio', '3' => 'Marzo', '4' => 'Aprile', '5' => 'Maggio', '6' => 'Giugno', '7' => 'Luglio', '8' => 'Agosto', '9' => 'Settembre', '10' => 'Ottobre', '11' => 'Novembre', '12' => 'Dicembre', ]) ->query(fn(Builder $query, array $data): Builder => isset($data['value']) && $data['value'] !== '' ? $query->whereMonth('data_scadenza_versamento', (int) $data['value']) : $query ), ]) ->columns([ TextColumn::make('numero_progressivo')->label('N.')->sortable(), TextColumn::make('data_competenza')->label('Competenza')->date('d/m/Y')->sortable(), TextColumn::make('data_pagamento_fattura')->label('Pag. fattura')->date('d/m/Y')->toggleable(), TextColumn::make('fornitore.ragione_sociale')->label('Fornitore')->wrap()->searchable(), TextColumn::make('imponibile')->label('Imponibile')->money('EUR')->toggleable(), TextColumn::make('aliquota_ritenuta')->label('%')->toggleable(), TextColumn::make('importo_ritenuta') ->label('RA') ->money('EUR') ->sortable() ->summarize( Sum::make() ->label('Totale') ->formatStateUsing(fn($state): string => '€ ' . number_format((float) $state, 2, ',', '.')) ), TextColumn::make('data_scadenza_versamento') ->label('Scad.') ->date('d/m/Y') ->toggleable() ->getStateUsing(function (RegistroRitenuteAcconto $record): ?string { return $record->data_pagamento_fattura ? $record->data_scadenza_versamento?->toDateString() : null; }), BadgeColumn::make('stato_versamento')->label('Stato')->colors([ 'warning' => 'da_versare', 'success' => 'versata', 'primary' => 'compensata', 'danger' => 'annullata', ]), ]) ->actions([ Action::make('imposta_pagamento_fattura') ->label('Imposta pagamento') ->icon('heroicon-o-calendar-days') ->visible(fn(RegistroRitenuteAcconto $record) => $record->stato_versamento === 'da_versare') ->form([ DatePicker::make('data_pagamento_fattura') ->label('Data pagamento fattura') ->required() ->default(fn(RegistroRitenuteAcconto $record) => $record->data_pagamento_fattura?->toDateString()), ]) ->action(function (RegistroRitenuteAcconto $record, array $data): void { $raw = $data['data_pagamento_fattura'] ?? null; if (! is_string($raw) || trim($raw) === '') { Notification::make()->title('Data non valida')->danger()->send(); return; } try { DB::transaction(function () use ($record, $raw) { $date = Carbon::parse($raw)->startOfDay(); $scadenza = $date->copy()->addMonthNoOverflow()->day(16); $record->data_pagamento_fattura = $date->toDateString(); $record->data_scadenza_versamento = $scadenza->toDateString(); $record->save(); }); } catch (\Throwable $e) { Notification::make()->title('Errore')->body($e->getMessage())->danger()->send(); return; } Notification::make()->title('Pagamento aggiornato')->success()->send(); }), Action::make('segna_versata') ->label('Segna versata') ->icon('heroicon-o-check') ->visible(fn(RegistroRitenuteAcconto $record) => $record->stato_versamento === 'da_versare') ->form([ DatePicker::make('data_versamento')->label('Data versamento')->required()->default(now()->toDateString()), TextInput::make('f24_riferimento')->label('Riferimento F24')->maxLength(100)->nullable(), TextInput::make('importo_versato')->label('Importo versato')->nullable(), ]) ->action(function (RegistroRitenuteAcconto $record, array $data): void { try { DB::transaction(function () use ($record, $data) { $record->stato_versamento = 'versata'; $record->data_versamento = $data['data_versamento'] ?? null; $record->f24_riferimento = $data['f24_riferimento'] ?? null; if (isset($data['importo_versato']) && $data['importo_versato'] !== null && $data['importo_versato'] !== '') { $record->importo_versato = (float) str_replace(',', '.', str_replace('.', '', (string) $data['importo_versato'])); } $record->save(); }); } catch (\Throwable $e) { Notification::make()->title('Errore')->body($e->getMessage())->danger()->send(); return; } Notification::make()->title('Ritenuta aggiornata')->success()->send(); }), ]); } }