netgescon-day0/app/Filament/Pages/Gescon/FornitoriArchivio.php

714 lines
27 KiB
PHP

<?php
namespace App\Filament\Pages\Gescon;
use App\Models\Fornitore;
use App\Models\FornitoreDipendente;
use App\Models\RubricaUniversale;
use App\Models\User;
use App\Support\StabileContext;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use UnitEnum;
class FornitoriArchivio extends Page
{
public int $fornitoriCount = 0;
public string $fornitoriSearch = '';
public string $activeTab = 'elenco';
public ?int $selectedFornitoreId = null;
/** @var array<string, string> */
public array $automazioni = [
'fornitore_acqua' => '0',
'abilita_ticket' => '1',
'abilita_allegati_camera' => '1',
];
public ?string $contoIban = null;
public ?string $contoIntestazioneEsatta = null;
public string $dipendentiRubricaSearch = '';
public string $searchInput = '';
public string $editRagioneSociale = '';
public string $editTitolo = '';
public string $editNome = '';
public string $editCognome = '';
public string $editEmail = '';
public string $editPec = '';
public string $editTelefono = '';
public string $editCellulare = '';
public string $editPartitaIva = '';
public string $editCodiceFiscale = '';
public string $editSitoWeb = '';
public string $editTags = '';
public string $editNote = '';
public string $newFornitoreRagioneSociale = '';
public string $newFornitoreNome = '';
public string $newFornitoreCognome = '';
public string $newFornitoreEmail = '';
public string $newFornitoreTelefono = '';
public string $newFornitorePartitaIva = '';
public string $newFornitoreCodiceFiscale = '';
public string $newDipendenteNome = '';
public string $newDipendenteCognome = '';
public string $newDipendenteEmail = '';
public string $newDipendenteTelefono = '';
protected static ?string $navigationLabel = 'Fornitori';
protected static ?string $title = 'Fornitori';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-building-storefront';
protected static UnitEnum|string|null $navigationGroup = 'NetGescon';
protected static ?int $navigationSort = 12;
protected static ?string $slug = 'gescon/anagrafica/fornitori';
protected string $view = 'filament.pages.gescon.table';
public static function canAccess(): bool
{
$user = Auth::user();
return $user instanceof User
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
}
public function mount(): void
{
try {
$this->fornitoriCount = (int) $this->getTableQuery()->count();
} catch (\Throwable) {
$this->fornitoriCount = 0;
}
$this->searchInput = $this->fornitoriSearch;
}
protected function getTableQuery(): Builder
{
$user = Auth::user();
if (! $user instanceof User) {
return Fornitore::query()->whereRaw('1 = 0');
}
if ($user->hasAnyRole(['super-admin', 'admin'])) {
return Fornitore::query();
}
$amministratoreId = $user->amministratore?->id;
if (! $amministratoreId) {
$stabile = StabileContext::getActiveStabile($user);
$amministratoreId = $stabile?->amministratore_id;
}
if (! $amministratoreId) {
return Fornitore::query()->whereRaw('1 = 0');
}
return Fornitore::query()->where('amministratore_id', (int) $amministratoreId);
}
/**
* @return Collection<int, Fornitore>
*/
public function getFornitoriRowsProperty(): Collection
{
$query = $this->getTableQuery()
->withCount('dipendenti')
->orderBy('ragione_sociale')
->orderBy('cognome')
->orderBy('nome');
$term = trim($this->fornitoriSearch);
if ($term !== '') {
$hasTags = Schema::hasColumn('fornitori', 'tags');
$like = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $term) . '%';
$query->where(function (Builder $sub) use ($like, $hasTags): void {
$sub->where('ragione_sociale', 'like', $like)
->orWhere('nome', 'like', $like)
->orWhere('cognome', 'like', $like)
->orWhere('partita_iva', 'like', $like)
->orWhere('codice_fiscale', 'like', $like)
->orWhere('email', 'like', $like)
->orWhere('telefono', 'like', $like);
if ($hasTags) {
$sub->orWhere('tags', 'like', $like);
}
});
}
return $query->limit(400)->get();
}
public function updatedFornitoriSearch(): void
{}
public function getFornitoreLabel(Fornitore $fornitore): string
{
$label = trim((string) ($fornitore->ragione_sociale ?: trim(($fornitore->nome ?? '') . ' ' . ($fornitore->cognome ?? ''))));
return $label !== '' ? $label : ('Fornitore #' . $fornitore->id);
}
public function getFornitoreHubUrl(int $fornitoreId): string
{
return FornitoreScheda::getUrl(['record' => $fornitoreId], panel: 'admin-filament');
}
public function getFornitoreAnagraficaUrl(int $fornitoreId): string
{
return url('/admin-filament/anagrafica/fornitori/' . $fornitoreId);
}
public function getSelectedFornitoreProperty(): ?Fornitore
{
if ((int) ($this->selectedFornitoreId ?? 0) <= 0) {
return null;
}
return $this->getTableQuery()
->with(['rubrica', 'dipendenti.user'])
->withCount('dipendenti')
->find((int) $this->selectedFornitoreId);
}
/**
* @return array<int, string>
*/
public function getActiveSearchFiltersProperty(): array
{
return $this->extractSearchTokens((string) $this->fornitoriSearch);
}
/**
* @return Collection<int, FornitoreDipendente>
*/
public function getDipendentiRowsProperty(): Collection
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
return new Collection();
}
return FornitoreDipendente::query()
->with('user')
->where('fornitore_id', (int) $fornitore->id)
->orderByDesc('attivo')
->orderBy('cognome')
->orderBy('nome')
->get();
}
/**
* @return Collection<int, RubricaUniversale>
*/
public function getRubricaDipendentiCandidatesProperty(): Collection
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
return new Collection();
}
$query = RubricaUniversale::query()
->whereNull('deleted_at')
->where(function (Builder $q): void {
$q->where('tipo_contatto', 'persona_fisica')
->orWhereNull('tipo_contatto');
})
->where(function (Builder $q): void {
$q->whereNotNull('nome')
->where('nome', '!=', '')
->orWhereNotNull('cognome')
->where('cognome', '!=', '')
->orWhereNotNull('email')
->where('email', '!=', '');
});
if (Schema::hasColumn('rubrica_universale', 'amministratore_id')) {
$adminId = (int) ($fornitore->amministratore_id ?? 0);
if ($adminId > 0) {
$query->where(function (Builder $q) use ($adminId): void {
$q->where('amministratore_id', $adminId)
->orWhereNull('amministratore_id');
});
}
}
$term = trim($this->dipendentiRubricaSearch);
if ($term !== '') {
$tokens = $this->extractSearchTokens($term);
foreach ($tokens as $token) {
$like = '%' . $token . '%';
$query->where(function (Builder $sub) use ($like): void {
$sub->where('nome', 'like', $like)
->orWhere('cognome', 'like', $like)
->orWhere('ragione_sociale', 'like', $like)
->orWhere('email', 'like', $like)
->orWhere('telefono_cellulare', 'like', $like)
->orWhere('telefono_ufficio', 'like', $like)
->orWhere('codice_fiscale', 'like', $like);
});
}
}
return $query
->orderBy('cognome')
->orderBy('nome')
->limit(80)
->get(['id', 'nome', 'cognome', 'ragione_sociale', 'email', 'telefono_cellulare', 'telefono_ufficio', 'codice_fiscale']);
}
public function apriElenco(): void
{
$this->activeTab = 'elenco';
}
public function apriTabScheda(): void
{
if ((int) ($this->selectedFornitoreId ?? 0) <= 0) {
Notification::make()
->title('Seleziona prima un fornitore dalla Tab 1')
->warning()
->send();
$this->activeTab = 'elenco';
return;
}
$this->hydrateSchedaFromSelected();
$this->activeTab = 'scheda';
}
public function apriTabDipendenti(): void
{
if ((int) ($this->selectedFornitoreId ?? 0) <= 0) {
Notification::make()
->title('Seleziona prima un fornitore dalla Tab 1')
->warning()
->send();
$this->activeTab = 'elenco';
return;
}
$this->hydrateSchedaFromSelected();
$this->activeTab = 'dipendenti';
}
public function applicaRicerca(): void
{
$this->fornitoriSearch = trim($this->searchInput);
$this->activeTab = 'elenco';
}
public function pulisciRicerca(): void
{
$this->fornitoriSearch = '';
$this->searchInput = '';
$this->activeTab = 'elenco';
}
public function removeSearchFilter(string $token): void
{
$tokens = array_values(array_filter(
$this->extractSearchTokens((string) $this->fornitoriSearch),
static fn(string $value): bool => mb_strtolower($value) !== mb_strtolower($token)
));
$this->fornitoriSearch = implode(' e ', $tokens);
$this->searchInput = $this->fornitoriSearch;
$this->activeTab = 'elenco';
}
public function apriScheda(int $fornitoreId): void
{
$fornitore = $this->getTableQuery()->find($fornitoreId);
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Fornitore non trovato')->danger()->send();
return;
}
$this->selectedFornitoreId = (int) $fornitore->id;
$this->activeTab = 'scheda';
$this->loadSchedaState($fornitore);
}
public function updatedSelectedFornitoreId(): void
{
$this->hydrateSchedaFromSelected();
}
public function createFornitoreRapido(): void
{
$adminId = $this->resolveAmministratoreId();
if ($adminId <= 0) {
Notification::make()->title('Amministratore non risolto')->danger()->send();
return;
}
$data = Validator::make([
'ragione_sociale' => trim($this->newFornitoreRagioneSociale),
'nome' => trim($this->newFornitoreNome),
'cognome' => trim($this->newFornitoreCognome),
'email' => trim($this->newFornitoreEmail),
'telefono' => trim($this->newFornitoreTelefono),
'partita_iva' => trim($this->newFornitorePartitaIva),
'codice_fiscale' => trim($this->newFornitoreCodiceFiscale),
], [
'ragione_sociale' => ['nullable', 'string', 'max:255'],
'nome' => ['nullable', 'string', 'max:255'],
'cognome' => ['nullable', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'telefono' => ['nullable', 'string', 'max:64'],
'partita_iva' => ['nullable', 'string', 'max:32'],
'codice_fiscale' => ['nullable', 'string', 'max:32'],
])->validate();
if (($data['ragione_sociale'] ?? '') === '' && (($data['nome'] ?? '') === '' && ($data['cognome'] ?? '') === '')) {
Notification::make()->title('Inserisci almeno ragione sociale o nominativo')->warning()->send();
return;
}
$fornitore = Fornitore::query()->create([
'amministratore_id' => $adminId,
'ragione_sociale' => $this->cleanNullable($data['ragione_sociale'] ?? null),
'nome' => $this->cleanNullable($data['nome'] ?? null),
'cognome' => $this->cleanNullable($data['cognome'] ?? null),
'email' => $this->cleanNullable($data['email'] ?? null),
'telefono' => $this->cleanNullable($data['telefono'] ?? null),
'partita_iva' => $this->cleanNullable($data['partita_iva'] ?? null),
'codice_fiscale' => $this->cleanNullable($data['codice_fiscale'] ?? null),
]);
$this->newFornitoreRagioneSociale = '';
$this->newFornitoreNome = '';
$this->newFornitoreCognome = '';
$this->newFornitoreEmail = '';
$this->newFornitoreTelefono = '';
$this->newFornitorePartitaIva = '';
$this->newFornitoreCodiceFiscale = '';
$this->fornitoriCount = (int) $this->getTableQuery()->count();
$this->apriScheda((int) $fornitore->id);
Notification::make()->title('Fornitore creato')->success()->send();
}
public function saveSchedaAnagrafica(): void
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Seleziona un fornitore')->danger()->send();
return;
}
$data = Validator::make([
'ragione_sociale' => trim($this->editRagioneSociale),
'titolo' => trim($this->editTitolo),
'nome' => trim($this->editNome),
'cognome' => trim($this->editCognome),
'email' => trim($this->editEmail),
'pec' => trim($this->editPec),
'telefono' => trim($this->editTelefono),
'cellulare' => trim($this->editCellulare),
'partita_iva' => trim($this->editPartitaIva),
'codice_fiscale' => trim($this->editCodiceFiscale),
'sito_web' => trim($this->editSitoWeb),
'tags' => trim($this->editTags),
'note' => trim($this->editNote),
], [
'ragione_sociale' => ['nullable', 'string', 'max:255'],
'titolo' => ['nullable', 'string', 'max:50'],
'nome' => ['nullable', 'string', 'max:255'],
'cognome' => ['nullable', 'string', 'max:255'],
'email' => ['nullable', 'email', 'max:255'],
'pec' => ['nullable', 'email', 'max:255'],
'telefono' => ['nullable', 'string', 'max:64'],
'cellulare' => ['nullable', 'string', 'max:64'],
'partita_iva' => ['nullable', 'string', 'max:32'],
'codice_fiscale' => ['nullable', 'string', 'max:32'],
'sito_web' => ['nullable', 'string', 'max:255'],
'tags' => ['nullable', 'string', 'max:1000'],
'note' => ['nullable', 'string', 'max:5000'],
])->validate();
$fornitore->ragione_sociale = $this->cleanNullable($data['ragione_sociale'] ?? null);
if (Schema::hasColumn('fornitori', 'titolo')) {
$fornitore->titolo = $this->cleanNullable($data['titolo'] ?? null);
}
$fornitore->nome = $this->cleanNullable($data['nome'] ?? null);
$fornitore->cognome = $this->cleanNullable($data['cognome'] ?? null);
$fornitore->email = $this->cleanNullable($data['email'] ?? null);
$fornitore->pec = $this->cleanNullable($data['pec'] ?? null);
$fornitore->telefono = $this->cleanNullable($data['telefono'] ?? null);
$fornitore->cellulare = $this->cleanNullable($data['cellulare'] ?? null);
$fornitore->partita_iva = $this->cleanNullable($data['partita_iva'] ?? null);
$fornitore->codice_fiscale = $this->cleanNullable($data['codice_fiscale'] ?? null);
$fornitore->sito_web = $this->cleanNullable($data['sito_web'] ?? null);
$fornitore->tags = $this->cleanNullable($data['tags'] ?? null);
$fornitore->note = $this->cleanNullable($data['note'] ?? null);
$fornitore->save();
$this->loadSchedaState($fornitore->fresh());
Notification::make()->title('Anagrafica fornitore aggiornata')->success()->send();
}
public function addDipendenteManuale(): void
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Seleziona un fornitore')->warning()->send();
return;
}
$nome = trim($this->newDipendenteNome);
$email = trim($this->newDipendenteEmail);
if ($nome === '') {
Notification::make()->title('Nome dipendente obbligatorio')->warning()->send();
return;
}
if ($email !== '') {
$exists = FornitoreDipendente::query()
->where('fornitore_id', (int) $fornitore->id)
->whereRaw('LOWER(email) = ?', [mb_strtolower($email)])
->exists();
if ($exists) {
Notification::make()->title('Dipendente con stessa email gia presente')->warning()->send();
return;
}
}
FornitoreDipendente::query()->create([
'fornitore_id' => (int) $fornitore->id,
'nome' => $nome,
'cognome' => $this->cleanNullable($this->newDipendenteCognome),
'email' => $this->cleanNullable($email),
'telefono' => $this->cleanNullable($this->newDipendenteTelefono),
'attivo' => true,
'created_by_user_id' => Auth::id(),
'updated_by_user_id' => Auth::id(),
]);
$this->newDipendenteNome = '';
$this->newDipendenteCognome = '';
$this->newDipendenteEmail = '';
$this->newDipendenteTelefono = '';
Notification::make()->title('Dipendente aggiunto')->success()->send();
}
public function addDipendenteFromRubrica(int $rubricaId): void
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Seleziona prima un fornitore')->warning()->send();
$this->activeTab = 'elenco';
return;
}
$rubrica = RubricaUniversale::query()->find((int) $rubricaId);
if (! $rubrica instanceof RubricaUniversale) {
Notification::make()->title('Contatto rubrica non trovato')->danger()->send();
return;
}
$email = trim((string) ($rubrica->email ?? ''));
$nome = trim((string) ($rubrica->nome ?? ''));
$cognome = trim((string) ($rubrica->cognome ?? ''));
$telefono = trim((string) ($rubrica->telefono_cellulare ?: $rubrica->telefono_ufficio ?: ''));
if ($nome === '' && $cognome === '') {
$fromRs = trim((string) ($rubrica->ragione_sociale ?? ''));
if ($fromRs !== '') {
$nome = $fromRs;
} else {
Notification::make()->title('Rubrica senza nominativo utile')->warning()->send();
return;
}
}
$exists = FornitoreDipendente::query()
->where('fornitore_id', (int) $fornitore->id)
->where(function (Builder $q) use ($email, $nome, $cognome, $telefono): void {
if ($email !== '') {
$q->orWhereRaw('LOWER(email) = ?', [mb_strtolower($email)]);
}
$full = mb_strtolower(trim($nome . ' ' . $cognome));
if ($full !== '') {
$q->orWhereRaw("LOWER(TRIM(CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, '')))) = ?", [$full]);
}
if ($telefono !== '') {
$normalized = preg_replace('/\D+/', '', $telefono) ?? '';
if ($normalized !== '') {
$q->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono, ''), ' ', ''), '-', ''), '.', ''), '(', ''), ')', '') = ?", [$normalized]);
}
}
})
->exists();
if ($exists) {
Notification::make()->title('Dipendente gia collegato a questo fornitore')->warning()->send();
return;
}
$dipendente = new FornitoreDipendente();
$dipendente->fornitore_id = (int) $fornitore->id;
$dipendente->nome = $nome;
$dipendente->cognome = $cognome !== '' ? $cognome : null;
$dipendente->email = $email !== '' ? $email : null;
$dipendente->telefono = $telefono !== '' ? $telefono : null;
$dipendente->attivo = true;
$dipendente->created_by_user_id = Auth::id();
$dipendente->updated_by_user_id = Auth::id();
$existingNote = trim((string) ($rubrica->note ?? ''));
$linkNote = 'Agganciato da rubrica ID #' . (int) $rubrica->id;
$dipendente->note = Str::limit(trim($linkNote . ($existingNote !== '' ? (' | ' . $existingNote) : '')), 2000, '');
$dipendente->save();
Notification::make()->title('Dipendente collegato al fornitore')->success()->send();
$this->activeTab = 'dipendenti';
}
public function saveSchedaAutomazioni(): void
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Seleziona un fornitore')->danger()->send();
return;
}
$features = is_array($fornitore->fe_features ?? null) ? $fornitore->fe_features : [];
$features['automazioni'] = [
'fornitore_acqua' => ($this->automazioni['fornitore_acqua'] ?? '0') === '1',
'abilita_ticket' => ($this->automazioni['abilita_ticket'] ?? '0') === '1',
'abilita_allegati_camera' => ($this->automazioni['abilita_allegati_camera'] ?? '0') === '1',
];
$fornitore->fe_features = $features;
$fornitore->save();
Notification::make()->title('Automazioni aggiornate')->success()->send();
}
public function saveSchedaBancaria(): void
{
$fornitore = $this->selectedFornitore;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Seleziona un fornitore')->danger()->send();
return;
}
$fornitore->iban = $this->cleanNullable($this->contoIban);
$fornitore->intestazione_cc_esatta = $this->cleanNullable($this->contoIntestazioneEsatta);
$fornitore->save();
Notification::make()->title('Dati conto aggiornati')->success()->send();
}
private function loadSchedaState(Fornitore $fornitore): void
{
$features = is_array($fornitore->fe_features ?? null) ? $fornitore->fe_features : [];
$auto = (array) ($features['automazioni'] ?? []);
$this->automazioni = [
'fornitore_acqua' => ! empty($auto['fornitore_acqua']) ? '1' : '0',
'abilita_ticket' => ! empty($auto['abilita_ticket']) ? '1' : '0',
'abilita_allegati_camera' => ! empty($auto['abilita_allegati_camera']) ? '1' : '0',
];
$this->contoIban = is_string($fornitore->iban) ? $fornitore->iban : null;
$this->contoIntestazioneEsatta = is_string($fornitore->intestazione_cc_esatta ?? null)
? $fornitore->intestazione_cc_esatta
: null;
$this->editRagioneSociale = (string) ($fornitore->ragione_sociale ?? '');
$this->editTitolo = Schema::hasColumn('fornitori', 'titolo')
? (string) ($fornitore->titolo ?? '')
: '';
$this->editNome = (string) ($fornitore->nome ?? '');
$this->editCognome = (string) ($fornitore->cognome ?? '');
$this->editEmail = (string) ($fornitore->email ?? '');
$this->editPec = (string) ($fornitore->pec ?? '');
$this->editTelefono = (string) ($fornitore->telefono ?? '');
$this->editCellulare = (string) ($fornitore->cellulare ?? '');
$this->editPartitaIva = (string) ($fornitore->partita_iva ?? '');
$this->editCodiceFiscale = (string) ($fornitore->codice_fiscale ?? '');
$this->editSitoWeb = (string) ($fornitore->sito_web ?? '');
$this->editTags = (string) ($fornitore->tags ?? '');
$this->editNote = (string) ($fornitore->note ?? '');
}
private function resolveAmministratoreId(): int
{
$user = Auth::user();
if (! $user instanceof User) {
return 0;
}
$adminId = (int) ($user->amministratore?->id ?? 0);
if ($adminId > 0) {
return $adminId;
}
$stabile = StabileContext::getActiveStabile($user);
return (int) ($stabile?->amministratore_id ?? 0);
}
private function hydrateSchedaFromSelected(): void
{
if ((int) ($this->selectedFornitoreId ?? 0) <= 0) {
return;
}
$fornitore = $this->getTableQuery()->find((int) $this->selectedFornitoreId);
if (! $fornitore instanceof Fornitore) {
return;
}
$this->loadSchedaState($fornitore);
}
private function cleanNullable(?string $value): ?string
{
$value = trim((string) $value);
return $value !== '' ? $value : null;
}
/**
* @return array<int, string>
*/
private function extractSearchTokens(string $raw): array
{
$term = trim(preg_replace('/\s+/', ' ', $raw) ?? '');
if ($term === '') {
return [];
}
$hasLogicalSeparator = preg_match('/[,;|+]|\s(?:e|ed|and)\s/ui', $term) === 1;
if (! $hasLogicalSeparator) {
return [$term];
}
$parts = preg_split('/(?:[,;|+]|\s(?:e|ed|and)\s)+/ui', $term) ?: [];
$parts = array_values(array_filter(array_map(static fn(string $value): string => trim($value), $parts), static fn(string $value): bool => $value !== ''));
return $parts !== [] ? array_values(array_unique($parts)) : [$term];
}
}