hasRole('super-admin'); } public function mount(): void { $this->mountInteractsWithTable(); } protected function getTableQuery(): Builder { return FatturaElettronica::query() ->with(['stabile', 'fornitore']) ->whereNotNull('allegato_pdf_path') ->where('allegato_pdf_path', '!=', ''); } public function table(Table $table): Table { return $table ->striped() ->defaultSort('data_fattura', 'desc') ->columns([ TextColumn::make('stabile.denominazione') ->label('Stabile') ->searchable() ->sortable(), TextColumn::make('fornitore_denominazione') ->label('Fornitore') ->searchable() ->sortable() ->description(fn (FatturaElettronica $record): string => "P.IVA: " . $record->fornitore_piva), TextColumn::make('numero_fattura') ->label('Numero') ->searchable() ->sortable(), TextColumn::make('data_fattura') ->label('Data') ->date('d/m/Y') ->sortable(), TextColumn::make('totale') ->label('Totale') ->money('EUR') ->sortable(), IconColumn::make('has_ocr') ->label('OCR Ready') ->boolean() ->getStateUsing(function (FatturaElettronica $record): bool { $doc = Documento::query() ->where('documentable_type', FatturaElettronica::class) ->where('documentable_id', $record->id) ->first(); return $doc && !empty($doc->contenuto_ocr); }), TextColumn::make('dati_utenza') ->label('Dati Utenza Estratti') ->html() ->getStateUsing(function (FatturaElettronica $record): string { $pdr = $record->acqua_contatore_matricola ?: '-'; $cliente = $record->acqua_codice_cliente ?: '-'; $contratto = $record->acqua_codice_contratto ?: '-'; $consumo = $record->consumo_valore !== null ? number_format((float) $record->consumo_valore, 2, ',', '.') . ' ' . $record->consumo_unita : '-'; // Controlla se c'è un payload salvato in consumo_raw $raw = is_string($record->consumo_raw) ? json_decode($record->consumo_raw, true) : null; if ($raw && isset($raw['codici'])) { $pdr = $raw['codici']['pdr'] ?? ($raw['codici']['pod'] ?? ($raw['codici']['utenza'] ?? $pdr)); $cliente = $raw['codici']['cliente'] ?? $cliente; $contratto = $raw['codici']['contratto'] ?? $contratto; } return "
" . "
Matr/POD/PDR: $pdr
" . "
Cod. Cliente: $cliente
" . "
Contratto: $contratto
" . "
Consumo: $consumo
" . "
"; }), ]) ->actions([ TableAction::make('esegui_ocr') ->label('Esegui OCR') ->icon('heroicon-o-cpu-chip') ->action(function (FatturaElettronica $record): void { $doc = Documento::query() ->where('documentable_type', FatturaElettronica::class) ->where('documentable_id', $record->id) ->first(); if (! $doc) { $doc = Documento::query()->create([ 'documentable_type' => FatturaElettronica::class, 'documentable_id' => $record->id, 'nome_file' => $record->allegato_pdf_nome ?: 'allegato.pdf', 'path_file' => $record->allegato_pdf_path, 'mime_type' => $record->allegato_pdf_mime ?: 'application/pdf', 'stabile_id' => $record->stabile_id, 'nome' => 'Allegato PDF Fattura #' . $record->numero_fattura, ]); } try { $res = app(PdfTextExtractionService::class)->extractAndStore($doc, true); if (($res['status'] ?? null) === 'ok') { Notification::make() ->title('OCR completato con successo') ->success() ->send(); } else { Notification::make() ->title('Errore OCR') ->body($res['error'] ?? 'Impossibile estrarre il testo dal PDF') ->danger() ->send(); } } catch (\Throwable $e) { Notification::make() ->title('Errore OCR') ->body($e->getMessage()) ->danger() ->send(); } }), TableAction::make('estrai_dati') ->label('Estrai Utenza') ->icon('heroicon-o-magnifying-glass-circle') ->color('info') ->form([ Select::make('tipo_utenza') ->label('Tipo Utenza / Parser') ->options([ 'gas_luce' => 'Gas / Luce (Riscaldamento)', 'acqua' => 'Acqua (Idrico)', ]) ->required() ->default(function (FatturaElettronica $record) { // Auto-detect $piva = $record->fornitore_piva; $op = \App\Models\AreraOperatore::query()->where('partita_iva_normalizzata', $piva)->first(); if ($op) { $settori = $op->settori->pluck('nome')->map('mb_strtolower')->all(); foreach ($settori as $s) { if (str_contains($s, 'idric') || str_contains($s, 'acqua')) { return 'acqua'; } } } return 'gas_luce'; }), ]) ->action(function (FatturaElettronica $record, array $data): void { $doc = Documento::query() ->where('documentable_type', FatturaElettronica::class) ->where('documentable_id', $record->id) ->first(); if (! $doc || empty($doc->contenuto_ocr)) { Notification::make() ->title('Testo OCR non disponibile') ->body('Esegui prima l\'azione OCR sul documento.') ->warning() ->send(); return; } $text = (string) $doc->contenuto_ocr; $userId = (int) Auth::id(); if ($data['tipo_utenza'] === 'acqua') { $parsed = app(AcquaPdfTextParser::class)->parse($text); $ingestion = app(ConsumiAcquaIngestionService::class)->ingest( $record, $parsed, null, $userId, true ); $record->update([ 'acqua_codice_utenza' => $parsed['codici']['utenza'] ?? null, 'acqua_codice_cliente' => $parsed['codici']['cliente'] ?? null, 'acqua_codice_contratto' => $parsed['codici']['contratto'] ?? null, 'acqua_contatore_matricola' => $parsed['contatore']['matricola'] ?? null, 'consumo_valore' => isset($parsed['consumi'][0]['valore']) ? $parsed['consumi'][0]['valore'] : null, 'consumo_unita' => 'mc', 'consumo_raw' => json_encode($parsed), ]); } else { $parsed = app(GasLucePdfTextParser::class)->parse($text); $ingestion = app(ConsumiRiscaldamentoIngestionService::class)->ingest( $record, $parsed, null, $userId, true ); $record->update([ 'acqua_codice_utenza' => $parsed['codici']['pdr'] ?? ($parsed['codici']['pod'] ?? null), 'acqua_codice_cliente' => $parsed['codici']['cliente'] ?? null, 'acqua_codice_contratto' => $parsed['codici']['contratto'] ?? null, 'acqua_contatore_matricola' => $parsed['codici']['pdr'] ?? ($parsed['codici']['pod'] ?? null), 'consumo_valore' => isset($parsed['consumi'][0]['valore']) ? $parsed['consumi'][0]['valore'] : null, 'consumo_unita' => $parsed['codici']['pod'] ? 'kWh' : 'Smc', 'consumo_raw' => json_encode($parsed), ]); } Notification::make() ->title('Dati estratti e allineati') ->body('Servizio: ' . ($ingestion['status'] ?? 'n/d') . ' - Letture create: ' . ($ingestion['created_letture'] ?? 0)) ->success() ->send(); }), TableAction::make('modifica_utenza') ->label('Modifica Dati') ->icon('heroicon-o-pencil') ->color('success') ->mountUsing(function (TextInput $codice_cliente, TextInput $codice_contratto, TextInput $pdr_pod, TextInput $consumo_valore, Select $consumo_unita, FatturaElettronica $record) { $raw = is_string($record->consumo_raw) ? json_decode($record->consumo_raw, true) : null; $pdrVal = $record->acqua_contatore_matricola; $cliVal = $record->acqua_codice_cliente; $conVal = $record->acqua_codice_contratto; if ($raw && isset($raw['codici'])) { $pdrVal = $raw['codici']['pdr'] ?? ($raw['codici']['pod'] ?? ($raw['codici']['utenza'] ?? $pdrVal)); $cliVal = $raw['codici']['cliente'] ?? $cliVal; $conVal = $raw['codici']['contratto'] ?? $conVal; } $codice_cliente->state($cliVal); $codice_contratto->state($conVal); $pdr_pod->state($pdrVal); $consumo_valore->state($record->consumo_valore); $consumo_unita->state($record->consumo_unita ?: 'Smc'); }) ->form([ TextInput::make('codice_cliente')->label('Codice Cliente'), TextInput::make('codice_contratto')->label('Codice Contratto'), TextInput::make('pdr_pod')->label('Matricola / POD / PDR'), TextInput::make('consumo_valore')->label('Valore Consumo')->numeric(), Select::make('consumo_unita') ->label('Unità di Misura') ->options([ 'Smc' => 'Smc (Gas)', 'mc' => 'mc (Acqua)', 'kWh' => 'kWh (Energia)', ]), ]) ->action(function (FatturaElettronica $record, array $data): void { $raw = is_string($record->consumo_raw) ? json_decode($record->consumo_raw, true) : []; if (!is_array($raw)) { $raw = []; } $raw['codici'] = [ 'cliente' => $data['codice_cliente'], 'contratto' => $data['codice_contratto'], 'pdr' => $data['consumo_unita'] === 'Smc' ? $data['pdr_pod'] : null, 'pod' => $data['consumo_unita'] === 'kWh' ? $data['pdr_pod'] : null, 'utenza' => $data['consumo_unita'] === 'mc' ? $data['pdr_pod'] : null, ]; $record->update([ 'acqua_codice_cliente' => $data['codice_cliente'], 'acqua_codice_contratto' => $data['codice_contratto'], 'acqua_codice_utenza' => $data['pdr_pod'], 'acqua_contatore_matricola' => $data['pdr_pod'], 'consumo_valore' => $data['consumo_valore'], 'consumo_unita' => $data['consumo_unita'], 'consumo_raw' => json_encode($raw), ]); // Forza sincronizzazione della lettura collegata $userId = (int) Auth::id(); if ($data['consumo_unita'] === 'mc') { app(ConsumiAcquaIngestionService::class)->ingest($record, $raw, null, $userId, true); } else { app(ConsumiRiscaldamentoIngestionService::class)->ingest($record, $raw, null, $userId, true); } Notification::make() ->title('Dati utenza aggiornati e sincronizzati con successo') ->success() ->send(); }), ]); } }