feat(amministratori-fornitori): import Cecilia Tordini, add 1-click impersonation, import Trib 1019/1020 & Regime Fiscale for 387 suppliers (task-d095b0e23b)

This commit is contained in:
michele 2026-08-27 18:44:34 +02:00
parent 8f6f929736
commit 4a1b582e03
4 changed files with 205 additions and 0 deletions

View File

@ -74,6 +74,12 @@ public function handle(): int
$cap = trim((string) ($row['Amm_770_CAP_resid'] ?? '')); $cap = trim((string) ($row['Amm_770_CAP_resid'] ?? ''));
$prov = trim((string) ($row['Amm_770_pr_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)); $key = $cf ?: strtoupper(trim($cognome . ' ' . $nome));
if ($key !== '' && ! isset($adminsFound[$key])) { if ($key !== '' && ! isset($adminsFound[$key])) {

View File

@ -0,0 +1,164 @@
<?php
namespace App\Console\Commands;
use App\Models\Fornitore;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class GesconImportFornitoriFiscalDetailsCommand extends Command
{
protected $signature = 'gescon:import-fornitori-fiscal-details {--mdb= : Percorso opzionale Fornitori.mdb}';
protected $description = 'Importa dati fiscali avanzati (Trib 1019/1020, Regime Fiscale, Cassa Prof, PEC, IBAN) da Fornitori.mdb';
public function handle(): int
{
$this->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;
}
}

View File

@ -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 dellamministratore selezionato. Tutte le azioni saranno tracciate nellaudit 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') Action::make('delete')
->label('Elimina') ->label('Elimina')
->icon('heroicon-o-trash') ->icon('heroicon-o-trash')

View File

@ -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`. - **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*). - **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.