fix(anagrafica): Anagrafica Unica canonica filtering, audit history filter, and protected CRUD guards (task-d095b0e23b)
This commit is contained in:
parent
4774ef5751
commit
ae8f54dc36
|
|
@ -148,6 +148,7 @@ public function handle(): int
|
|||
'cognome' => 'Seed',
|
||||
'user_id' => $userId,
|
||||
'codice_univoco' => 'ASEED001',
|
||||
'codice_amministratore' => 'ASEED001',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -128,7 +128,39 @@ public function table(Table $table): Table
|
|||
TextColumn::make('telefono_ufficio')->label('Tel.')->searchable()->toggleable(),
|
||||
TextColumn::make('telefono_cellulare')->label('Cell.')->searchable()->toggleable(),
|
||||
TextColumn::make('telefono_casa')->label('Tel. casa')->searchable()->toggleable(isToggledHiddenByDefault: true),
|
||||
TextColumn::make('stato')->label('Stato')->sortable()->toggleable(isToggledHiddenByDefault: true),
|
||||
TextColumn::make('stato')->label('Stato')
|
||||
->sortable()
|
||||
->badge()
|
||||
->color(fn (?string $state): string => match ($state) {
|
||||
'attivo' => 'success',
|
||||
'inattivo', 'archiviato_duplicato', 'inattivo_duplicato' => 'warning',
|
||||
default => 'gray',
|
||||
})
|
||||
->toggleable(),
|
||||
TextColumn::make('note')->label('Note / Audit')->wrap()->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
\Filament\Tables\Filters\SelectFilter::make('vista_stato')
|
||||
->label('Vista Anagrafica')
|
||||
->options([
|
||||
'attivi' => 'Contatti attivi (Canonica)',
|
||||
'storico' => 'Storico & bonifiche audit',
|
||||
'tutti' => 'Tutti i contatti',
|
||||
])
|
||||
->default('attivi')
|
||||
->query(function (Builder $query, array $data): Builder {
|
||||
$value = $data['value'] ?? 'attivi';
|
||||
if ($value === 'attivi' || empty($value)) {
|
||||
return $query->where(function (Builder $q): void {
|
||||
$q->whereNull('rubrica_universale.stato')
|
||||
->orWhereNotIn('rubrica_universale.stato', ['inattivo', 'inattivo_duplicato', 'archiviato_duplicato']);
|
||||
});
|
||||
}
|
||||
if ($value === 'storico') {
|
||||
return $query->whereIn('rubrica_universale.stato', ['inattivo', 'inattivo_duplicato', 'archiviato_duplicato']);
|
||||
}
|
||||
return $query;
|
||||
}),
|
||||
])
|
||||
->actions([
|
||||
Action::make('modifica')
|
||||
|
|
@ -180,6 +212,25 @@ public function table(Table $table): Table
|
|||
->label('Apri scheda')
|
||||
->icon('heroicon-o-identification')
|
||||
->url(fn(RubricaUniversale $record) => RubricaUniversaleScheda::getUrl(['record' => $record->id], panel: 'admin-filament')),
|
||||
|
||||
Action::make('elimina')
|
||||
->label('Elimina')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Conferma eliminazione contatto')
|
||||
->modalDescription('Cancellazione protetta: la rimozione fisica verrà bloccata se il contatto ha relazioni attive o storiche.')
|
||||
->action(function (RubricaUniversale $record): void {
|
||||
try {
|
||||
$record->delete();
|
||||
} catch (\Throwable $e) {
|
||||
\Filament\Notifications\Notification::make()
|
||||
->title('Operazione bloccata')
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
use Illuminate\Database\Eloquent\Relations\HasMany; // Aggiunto per condomini()
|
||||
use Illuminate\Database\Eloquent\SoftDeletes; // Aggiunto per soft deletes
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
|
|||
|
|
@ -333,6 +333,15 @@ protected static function boot()
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
static::deleting(function ($persona) {
|
||||
$hasRelazioni = \Illuminate\Support\Facades\DB::table('persone_unita_relazioni')->where('persona_id', $persona->id)->exists();
|
||||
$hasRappresentanti = \Illuminate\Support\Facades\DB::table('rappresentanti_legali')->where('persona_id', $persona->id)->orWhere('societa_id', $persona->id)->exists();
|
||||
|
||||
if ($hasRelazioni || $hasRappresentanti) {
|
||||
throw new \Exception("Cancellazione fisica bloccata: la persona ha relazioni attive o storiche con unità immobiliari o società.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -164,4 +164,24 @@ public function scopeRicerca($query, $termine)
|
|||
->orWhere('telefono_cellulare', 'like', "%{$termine}%");
|
||||
});
|
||||
}
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function ($rubrica) {
|
||||
$isForce = method_exists($rubrica, 'isForceDeleting') ? $rubrica->isForceDeleting() : true;
|
||||
|
||||
if ($isForce) {
|
||||
$hasRoles = \Illuminate\Support\Facades\DB::table('rubrica_ruoli')->where('rubrica_id', $rubrica->id)->exists();
|
||||
$hasFornitori = \Illuminate\Support\Facades\DB::table('fornitori')->where('rubrica_id', $rubrica->id)->exists();
|
||||
$hasStabili = \Illuminate\Support\Facades\Schema::hasColumn('stabili', 'rubrica_id') && \Illuminate\Support\Facades\DB::table('stabili')->where('rubrica_id', $rubrica->id)->exists();
|
||||
$hasDatiBancari = \Illuminate\Support\Facades\DB::table('dati_bancari')->where('contatto_id', $rubrica->id)->exists();
|
||||
|
||||
if ($hasRoles || $hasFornitori || $hasStabili || $hasDatiBancari) {
|
||||
throw new \Exception("Cancellazione fisica bloccata: il contatto ha relazioni attive o storiche (ruoli, stabili, fornitori o dati bancari).");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public function panel(Panel $panel): Panel
|
|||
->brandName('NetGescon')
|
||||
->spa()
|
||||
->login(\App\Filament\Auth\Login::class)
|
||||
->passwordReset()
|
||||
->registration()
|
||||
->colors([
|
||||
'primary' => Color::Amber,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public function up(): void
|
|||
return;
|
||||
}
|
||||
|
||||
DB::statement("ALTER TABLE `dati_bancari` MODIFY COLUMN `tipo_conto` ENUM('corrente','deposito','risparmio','cassa') NOT NULL DEFAULT 'corrente'");
|
||||
DB::statement("ALTER TABLE `dati_bancari` MODIFY COLUMN `tipo_conto` ENUM('corrente','deposito','risparmio','cassa','posta','postale') NOT NULL DEFAULT 'corrente'");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
145
tests/Feature/AnagraficaUnicaCanonicaTest.php
Normal file
145
tests/Feature/AnagraficaUnicaCanonicaTest.php
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
use App\Livewire\Gescon\RubricaUniversaleTable;
|
||||
use App\Models\Persona;
|
||||
use App\Models\PersonaUnitaRelazione;
|
||||
use App\Models\RubricaUniversale;
|
||||
use App\Models\User;
|
||||
use App\Models\Stabile;
|
||||
use App\Support\StabileContext;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Livewire;
|
||||
|
||||
beforeEach(function () {
|
||||
$user = User::first();
|
||||
if (! $user) {
|
||||
$user = User::factory()->create(['email' => 'cecilia.tordini@gmail.com']);
|
||||
}
|
||||
|
||||
try {
|
||||
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'amministratore', 'guard_name' => 'web']);
|
||||
$user->assignRole('amministratore');
|
||||
} catch (\Throwable $e) {}
|
||||
|
||||
$adminId = DB::table('amministratori')->insertGetId([
|
||||
'user_id' => $user->id,
|
||||
'nome' => 'Admin Test',
|
||||
'cognome' => 'Test',
|
||||
'codice_amministratore' => 'ADM00001',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$stabile = Stabile::firstOrCreate(['codice_stabile' => '0021'], [
|
||||
'denominazione' => 'SUPERCONDOMINIO MILIZIE 3',
|
||||
'amministratore_id' => $adminId,
|
||||
'indirizzo' => 'Viale delle Milizie 3',
|
||||
'cap' => '00192',
|
||||
'citta' => 'Roma',
|
||||
'provincia' => 'RM',
|
||||
]);
|
||||
if ($stabile->amministratore_id !== $adminId) {
|
||||
$stabile->amministratore_id = $adminId;
|
||||
$stabile->save();
|
||||
}
|
||||
|
||||
$canonical = RubricaUniversale::where('codice_fiscale', 'BNDDNL86M58H224O')->first();
|
||||
if (! $canonical) {
|
||||
RubricaUniversale::create([
|
||||
'amministratore_id' => $adminId,
|
||||
'codice_univoco' => '000000JC',
|
||||
'nome' => 'DANIELA',
|
||||
'cognome' => 'BENEDETTO',
|
||||
'codice_fiscale' => 'BNDDNL86M58H224O',
|
||||
'stato' => 'attivo',
|
||||
'note' => '[MDB_0021] CF Validato',
|
||||
]);
|
||||
RubricaUniversale::create([
|
||||
'amministratore_id' => $adminId,
|
||||
'codice_univoco' => '000000I2',
|
||||
'nome' => 'DANIELA',
|
||||
'cognome' => 'BENEDETTO',
|
||||
'stato' => 'inattivo',
|
||||
'note' => '[ARCHIVIATO_DUPLICATO] Soft archived verso Rubrica 697',
|
||||
]);
|
||||
RubricaUniversale::create([
|
||||
'amministratore_id' => $adminId,
|
||||
'codice_univoco' => '000000I3',
|
||||
'nome' => 'DANIELA',
|
||||
'cognome' => 'BENEDETTO',
|
||||
'stato' => 'inattivo',
|
||||
'note' => '[ARCHIVIATO_DUPLICATO] Soft archived verso Rubrica 697',
|
||||
]);
|
||||
} else {
|
||||
$canonical->amministratore_id = $adminId;
|
||||
$canonical->save();
|
||||
RubricaUniversale::where('cognome', 'LIKE', '%BENEDETTO%')->update(['amministratore_id' => $adminId]);
|
||||
}
|
||||
});
|
||||
|
||||
test('anagrafica unica default view shows only active canonical contacts', function () {
|
||||
$user = User::first();
|
||||
|
||||
$stabile = Stabile::where('codice_stabile', '0021')->first();
|
||||
Auth::login($user);
|
||||
StabileContext::setFromStabile($stabile, $user);
|
||||
|
||||
$canonical = RubricaUniversale::where('codice_fiscale', 'BNDDNL86M58H224O')->first();
|
||||
expect($canonical)->not->toBeNull();
|
||||
expect($canonical->stato)->toBe('attivo');
|
||||
|
||||
Livewire::test(RubricaUniversaleTable::class)
|
||||
->assertSee('DANIELA')
|
||||
->assertSee('BENEDETTO')
|
||||
->assertDontSee('000000I2');
|
||||
});
|
||||
|
||||
test('anagrafica unica storico filter shows archived duplicate records', function () {
|
||||
$user = User::first();
|
||||
|
||||
$stabile = Stabile::where('codice_stabile', '0021')->first();
|
||||
Auth::login($user);
|
||||
StabileContext::setFromStabile($stabile, $user);
|
||||
|
||||
Livewire::test(RubricaUniversaleTable::class)
|
||||
->filterTable('vista_stato', 'storico')
|
||||
->assertSee('BENEDETTO');
|
||||
});
|
||||
|
||||
test('physical delete blocked when relations exist', function () {
|
||||
$persona = Persona::where('codice_fiscale', 'BNDDNL86M58H224O')->first();
|
||||
if (! $persona) {
|
||||
$persona = Persona::create([
|
||||
'cognome' => 'BENEDETTO',
|
||||
'nome' => 'DANIELA',
|
||||
'codice_fiscale' => 'BNDDNL86M58H224O',
|
||||
'attivo' => true,
|
||||
]);
|
||||
PersonaUnitaRelazione::create([
|
||||
'persona_id' => $persona->id,
|
||||
'unita_id' => 1,
|
||||
'tipo_relazione' => 'proprietario',
|
||||
'data_inizio' => now(),
|
||||
'attivo' => true,
|
||||
]);
|
||||
} else {
|
||||
if (! PersonaUnitaRelazione::where('persona_id', $persona->id)->exists()) {
|
||||
PersonaUnitaRelazione::create([
|
||||
'persona_id' => $persona->id,
|
||||
'unita_id' => 1,
|
||||
'tipo_relazione' => 'proprietario',
|
||||
'data_inizio' => now(),
|
||||
'attivo' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$hasRelations = PersonaUnitaRelazione::where('persona_id', $persona->id)->exists();
|
||||
expect($hasRelations)->toBeTrue();
|
||||
|
||||
expect(function () use ($persona) {
|
||||
$persona->delete();
|
||||
})->toThrow(\Exception::class, 'Cancellazione fisica bloccata');
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user