diff --git a/app/Console/Commands/GesconImportAmministratoriLegacyCommand.php b/app/Console/Commands/GesconImportAmministratoriLegacyCommand.php index 5de0365..45eec5e 100644 --- a/app/Console/Commands/GesconImportAmministratoriLegacyCommand.php +++ b/app/Console/Commands/GesconImportAmministratoriLegacyCommand.php @@ -74,6 +74,12 @@ public function handle(): int $cap = trim((string) ($row['Amm_770_CAP_resid'] ?? '')); $prov = trim((string) ($row['Amm_770_pr_resid'] ?? '')); + if ($cf === 'TRDCCL74T52H501R') { + $cognome = 'Tordini'; + $nome = 'Cecilia'; + $email = 'cecilia.tordini@netgescon.it'; + } + $key = $cf ?: strtoupper(trim($cognome . ' ' . $nome)); if ($key !== '' && ! isset($adminsFound[$key])) { diff --git a/app/Console/Commands/GesconImportFornitoriFiscalDetailsCommand.php b/app/Console/Commands/GesconImportFornitoriFiscalDetailsCommand.php new file mode 100644 index 0000000..ce2b7cb --- /dev/null +++ b/app/Console/Commands/GesconImportFornitoriFiscalDetailsCommand.php @@ -0,0 +1,164 @@ +info('Avvio importazione dati fiscali fornitori (Trib 1019/1020 & Regime Fiscale)...'); + + $candidateMdbs = [ + $this->option('mdb'), + '/mnt/gescon-archives/gescon/dbc/Fornitori.mdb', + '/mnt/gescon-archives/id/temp/fornitori.mdb', + ]; + + $mdbPath = null; + foreach ($candidateMdbs as $p) { + if ($p && file_exists($p) && is_readable($p)) { + $mdbPath = $p; + break; + } + } + + if (! $mdbPath) { + $this->error('File Fornitori.mdb non trovato.'); + return self::FAILURE; + } + + $this->line("Utilizzo file MDB: {$mdbPath}"); + + $proc = new \Symfony\Component\Process\Process(['mdb-export', $mdbPath, 'Fornitori']); + $proc->run(); + + if (! $proc->isSuccessful()) { + $this->error('Impossibile eseguire mdb-export su Fornitori.mdb.'); + return self::FAILURE; + } + + $csvContent = $proc->getOutput(); + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $csvContent); + rewind($stream); + + $header = fgetcsv($stream); + if (! $header) { + fclose($stream); + $this->error('Header CSV non valido.'); + return self::FAILURE; + } + + $updated = 0; + while (($data = fgetcsv($stream)) !== false) { + if (count($data) === count($header)) { + $row = array_combine($header, $data); + $piva = trim((string) ($row['p_iva'] ?? '')); + $cf = trim((string) ($row['cod_fisc'] ?? '')); + $ragSoc = trim((string) ($row['Denominazione'] ?? '')) ?: trim(trim((string) ($row['cognome'] ?? '')) . ' ' . trim((string) ($row['nome'] ?? ''))); + $trib = trim((string) ($row['Trib_1019_1020'] ?? '')); + $regime = trim((string) ($row['Regime_fiscale'] ?? '')); + $cassaProf = trim((string) ($row['Perc_cassa_prof'] ?? '')); + $iban = trim((string) ($row['Cod_IBAN'] ?? '')); + $pec = trim((string) ($row['PEC_Fornitore'] ?? '')); + $intestCC = trim((string) ($row['Intestaz_CC_esatta'] ?? '')); + $oldId = (int) ($row['id_fornitore'] ?? 0); + $codForn = trim((string) ($row['cod_forn'] ?? '')); + + if (! $piva && ! $cf && ! $ragSoc) { + continue; + } + + $query = Fornitore::query(); + if ($piva !== '') { + $query->where('partita_iva', $piva); + } elseif ($cf !== '') { + $query->where('codice_fiscale', $cf); + } else { + $query->where('ragione_sociale', $ragSoc); + } + + $fornitore = $query->first(); + + if (! $fornitore && $ragSoc !== '') { + $admin = \App\Models\Amministratore::query()->first(); + $adminId = $admin?->id ?: 12; + + $cleanOldId = null; + if ($oldId > 0) { + $existsOldId = Fornitore::query() + ->where('amministratore_id', $adminId) + ->where('old_id', $oldId) + ->exists(); + if (! $existsOldId) { + $cleanOldId = $oldId; + } + } + + $fornitore = Fornitore::query()->create([ + 'amministratore_id' => $adminId, + 'codice_univoco' => strtoupper(\Illuminate\Support\Str::random(8)), + 'ragione_sociale' => $ragSoc, + 'partita_iva' => $piva, + 'codice_fiscale' => $cf, + 'email' => trim((string) ($row['Indir_Email'] ?? '')), + 'pec' => $pec, + 'telefono' => trim((string) ($row['Telef_1'] ?? '')), + 'cellulare' => trim((string) ($row['Cellulare'] ?? '')), + 'indirizzo' => trim((string) ($row['indirizzo'] ?? '')), + 'cap' => trim((string) ($row['cap'] ?? '')), + 'citta' => trim((string) ($row['citta'] ?? '')), + 'provincia' => trim((string) ($row['pr'] ?? '')), + 'old_id' => $cleanOldId, + ]); + } + + if ($fornitore) { + $existingData = is_array($fornitore->dati_fiscali_json ?? null) ? $fornitore->dati_fiscali_json : []; + $fiscalData = array_merge($existingData, [ + 'trib_1019_1020' => $trib, + 'regime_fiscale' => $regime, + 'perc_cassa_prof' => $cassaProf, + 'intestazione_cc_esatta'=> $intestCC, + 'cod_forn_legacy' => $codForn, + 'id_fornitore_legacy' => $oldId, + ]); + + $updatePayload = [ + 'pec' => $pec ?: $fornitore->pec, + 'iban' => $iban ?: $fornitore->iban, + 'dati_fiscali_json'=> $fiscalData, + ]; + + if ($oldId > 0 && ! $fornitore->old_id) { + $existsOldId = Fornitore::query() + ->where('amministratore_id', $fornitore->amministratore_id) + ->where('old_id', $oldId) + ->exists(); + if (! $existsOldId) { + $updatePayload['old_id'] = $oldId; + } + } + + $fornitore->update($updatePayload); + + $updated++; + $this->line(" ✓ Fornitore #{$fornitore->id} [{$fornitore->ragione_sociale}]: Trib={$trib}, Regime={$regime}, IBAN={$iban}"); + } + } + } + fclose($stream); + + $this->info("Importazione dati fiscali fornitori completata per {$updated} fornitori!"); + return self::SUCCESS; + } +} diff --git a/app/Filament/Pages/SuperAdmin/Amministratori.php b/app/Filament/Pages/SuperAdmin/Amministratori.php index 426357c..cce090d 100755 --- a/app/Filament/Pages/SuperAdmin/Amministratori.php +++ b/app/Filament/Pages/SuperAdmin/Amministratori.php @@ -276,6 +276,22 @@ public function table(Table $table): Table } }), + Action::make('impersonate') + ->label('Imperna') + ->icon('heroicon-o-user-plus') + ->color('warning') + ->requiresConfirmation() + ->modalHeading('Conferma Impersonificazione Amministratore') + ->modalDescription('Stai per accedere al sistema impersonando lo studio dell’amministratore selezionato. Tutte le azioni saranno tracciate nell’audit log.') + ->visible(fn(Amministratore $record): bool => (bool) $record->user_id && $record->user_id !== Auth::id()) + ->action(function (Amministratore $record): void { + if ($record->user) { + session(['impersonator_id' => Auth::id()]); + Auth::login($record->user); + redirect()->to('/admin-filament'); + } + }), + Action::make('delete') ->label('Elimina') ->icon('heroicon-o-trash') diff --git a/directives/ANAGRAFICHE_MAPPING_LEGACY.md b/directives/ANAGRAFICHE_MAPPING_LEGACY.md index 73be45d..f3be862 100644 --- a/directives/ANAGRAFICHE_MAPPING_LEGACY.md +++ b/directives/ANAGRAFICHE_MAPPING_LEGACY.md @@ -104,4 +104,23 @@ ## 16. Sistema Grafico Unificato per l'Anagrafica Fornitori (`/admin-filament/ge - **Wireframe ASCII di Riferimento**: `skill-netgescon/ui-wireframes/fornitori-anagrafica.md`. - **Filtri Tag & P.IVA**: Supporta la ricerca rapida per Ragione Sociale, Partita IVA, Codice Fiscale, recapiti e tag di categoria (es. *Idraulici, Elettricisti, Pulizie, Ascensori*). +## 17. Importazione Dati Fiscali Avanzati Fornitori (`Fornitori.mdb` & `Regime_fisclale.mdb`) +- **Comando di importazione**: `php artisan gescon:import-fornitori-fiscal-details` +- Estrae da `Fornitori.mdb` e `Regime_fisclale.mdb`: + - **Ritenuta Tributo**: `Trib_1019_1020` (es. `1019`, `1020`, `1040`). + - **Regime Fiscale**: `Regime_fiscale` (es. `RF01` Ordinario, `RF02` Minimi, `RF19` Forfettario). + - **Cassa Professionale**: `Perc_cassa_prof` (Aliquota Cassa 4%, 5%). + - **PEC & IBAN**: `PEC_Fornitore`, `Cod_IBAN`, `Intestaz_CC_esatta`. +- Collegamento diretto con la pipeline di importazione delle Fatture Elettroniche (FE) e la Certificazione Unica (CU/770). + +## 18. Audit Log Permanente delle Modifiche ("Chi fa cosa") +- Ogni inserimento, modifica o cancellazione sui dati anagrafici, amministratori o stabili viene registrato nella tabella `audit_logs` memorizzando l'ID utente operante, il timestamp, i campi modificati ed i valori pre/post modifica. + +## 19. Editing In-Place (Senza Modal Dialogs) +- Le maschere CRUD (es. Amministratori `/admin-filament/superadmin/amministratori` e Fornitori `/admin-filament/anagrafica/fornitori/{record}`) consentono l'editing diretto nella stessa pagina senza aprire popup o finestre modal. + +## 20. Amministratore Cecilia Tordini & Impersonificazione Istantanea +- Amministratore censito con CF `TRDCCL74T52H501R` ed account `cecilia.tordini@netgescon.it` (ID #13). +- Azione **Imperna** attiva nelle viste per consentire il login istantaneo contestuale da parte dei SuperAdmin senza richiedere digitazione di password. +