2521 lines
103 KiB
PHP
2521 lines
103 KiB
PHP
<?php
|
|
namespace App\Filament\Pages\Gescon;
|
|
|
|
use App\Models\Fornitore;
|
|
use App\Models\FornitoreDipendente;
|
|
use App\Models\PbxClickToCallRequest;
|
|
use App\Models\Persona;
|
|
use App\Models\PersonaEmailMultipla;
|
|
use App\Models\RubricaContattoCanale;
|
|
use App\Models\RubricaRuolo;
|
|
use App\Models\RubricaUniversale;
|
|
use App\Models\Stabile;
|
|
use App\Models\Titolo;
|
|
use App\Models\UnitaImmobiliare;
|
|
use App\Models\User;
|
|
use App\Support\StabileContext;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\DatePicker;
|
|
use Filament\Forms\Components\Hidden;
|
|
use Filament\Forms\Components\Repeater;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Toggle;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilderContract;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Support\Str;
|
|
|
|
class RubricaUniversaleScheda extends Page
|
|
{
|
|
protected static ?string $title = 'Scheda anagrafica (Rubrica)';
|
|
|
|
protected static ?string $slug = 'gescon/anagrafica/rubrica/{record}';
|
|
|
|
protected static bool $shouldRegisterNavigation = false;
|
|
|
|
protected string $view = 'filament.pages.gescon.rubrica-universale-scheda';
|
|
|
|
public RubricaUniversale $rubrica;
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $fornitori = [];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $stabili = [];
|
|
|
|
/** @var array<string, mixed> */
|
|
public array $collegamentoSuggerito = [];
|
|
|
|
public ?int $collegamentoStabileId = null;
|
|
|
|
public ?int $collegamentoUnitaId = null;
|
|
|
|
public string $collegamentoRuolo = 'altro';
|
|
|
|
public string $collegamentoRuoloCustom = '';
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $contattiPrincipali = [];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $ruoliAttivi = [];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $cronistoriaRuoli = [];
|
|
|
|
public int $adminId = 0;
|
|
|
|
public ?int $prevRecordId = null;
|
|
|
|
public ?int $nextRecordId = null;
|
|
|
|
public int $navPerPage = 25;
|
|
|
|
public int $navPage = 1;
|
|
|
|
public int $navPages = 1;
|
|
|
|
/** @var array<int, int> */
|
|
public array $adminStabileIds = [];
|
|
|
|
public bool $isInlineEditing = false;
|
|
|
|
public string $sideTab = 'collegamenti';
|
|
|
|
/** @var array<int,array<string,mixed>> */
|
|
public array $fornitoriCollegati = [];
|
|
|
|
/** @var array<int,array<string,mixed>> */
|
|
public array $dipendentiFornitoreRows = [];
|
|
|
|
/** @var array<int,int|string> */
|
|
public array $dipendenteRubricaSelect = [];
|
|
|
|
/** @var array<int,array<string,string>> */
|
|
public array $dipendenteManualDraft = [];
|
|
|
|
/** @var array<int,string> */
|
|
public array $dipendentePbxExtension = [];
|
|
|
|
/** @var array<int,string> */
|
|
public array $studioCollaboratoreStabileIds = [];
|
|
|
|
/** @var array<int,string> */
|
|
public array $studioCollaboratoreStabileOptions = [];
|
|
|
|
public ?int $studioCollaboratoreUserId = null;
|
|
|
|
public ?string $studioCollaboratoreUserEmail = null;
|
|
|
|
public ?string $studioCollaboratoreUserName = null;
|
|
|
|
public ?string $studioCollaboratoreGeneratedPassword = null;
|
|
|
|
public string $studioCollaboratorePbxExtension = '';
|
|
|
|
/** @var array<int,string> */
|
|
public array $studioCollaboratoreRoles = [];
|
|
|
|
/** @var array<string,mixed> */
|
|
public array $inlineForm = [];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $emailMultiple = [];
|
|
|
|
public function mount(int | string $record): void
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
if (! $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore'])) {
|
|
abort(403);
|
|
}
|
|
|
|
$this->rubrica = RubricaUniversale::query()->findOrFail((int) $record);
|
|
$this->applyLegacyIdentityDefaults();
|
|
|
|
$activeStabile = StabileContext::getActiveStabile($user);
|
|
$adminId = (int) ($activeStabile?->amministratore_id ?: 0);
|
|
$this->adminId = $adminId;
|
|
|
|
$stabiliAdminIds = [];
|
|
if (! $user->hasRole('super-admin') && $adminId > 0) {
|
|
$stabiliAdminIds = Stabile::query()
|
|
->where('amministratore_id', $adminId)
|
|
->pluck('id')
|
|
->map(fn($id) => (int) $id)
|
|
->all();
|
|
}
|
|
|
|
$this->adminStabileIds = $stabiliAdminIds;
|
|
|
|
$this->studioCollaboratoreStabileOptions = Stabile::query()
|
|
->when($adminId > 0, fn(Builder $q) => $q->where('amministratore_id', $adminId))
|
|
->when($adminId <= 0 && ! empty($stabiliAdminIds), fn(Builder $q) => $q->whereIn('id', $stabiliAdminIds))
|
|
->orderBy('codice_stabile')
|
|
->orderBy('denominazione')
|
|
->get(['id', 'codice_stabile', 'denominazione'])
|
|
->mapWithKeys(fn(Stabile $stabile) => [
|
|
(int) $stabile->id => trim((string) ($stabile->codice_stabile ?: ('#' . $stabile->id)) . ' - ' . (string) ($stabile->denominazione ?: 'Stabile')),
|
|
])
|
|
->all();
|
|
|
|
$this->initArchivioNavigation();
|
|
|
|
$this->fornitori = Fornitore::query()
|
|
->where('amministratore_id', $adminId)
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->orderBy('ragione_sociale')
|
|
->limit(50)
|
|
->get(['id', 'codice_univoco', 'ragione_sociale', 'partita_iva', 'codice_fiscale'])
|
|
->map(fn(Fornitore $f) => [
|
|
'id' => (int) $f->id,
|
|
'codice' => (string) ($f->codice_univoco ?? ''),
|
|
'nome' => (string) ($f->ragione_sociale ?: trim((string) ($f->cognome ?? '') . ' ' . (string) ($f->nome ?? ''))),
|
|
'piva' => (string) ($f->partita_iva ?? ''),
|
|
'cf' => (string) ($f->codice_fiscale ?? ''),
|
|
])
|
|
->all();
|
|
|
|
$ruoliQuery = RubricaRuolo::query()
|
|
->with(['stabile', 'unitaImmobiliare.stabile'])
|
|
->where('rubrica_id', (int) $this->rubrica->id);
|
|
|
|
if (! $user->hasRole('super-admin') && ! empty($stabiliAdminIds)) {
|
|
$ruoliQuery->where(function ($q) use ($stabiliAdminIds) {
|
|
$q->whereNull('stabile_id')
|
|
->orWhereIn('stabile_id', $stabiliAdminIds);
|
|
});
|
|
}
|
|
|
|
$ruoli = $ruoliQuery
|
|
->orderByDesc('is_attivo')
|
|
->orderByDesc('is_preferito')
|
|
->orderByDesc('data_inizio')
|
|
->orderByDesc('id')
|
|
->limit(200)
|
|
->get();
|
|
|
|
$labels = self::getRuoliLabels();
|
|
|
|
$this->ruoliAttivi = $ruoli
|
|
->filter(fn(RubricaRuolo $r) => (bool) $r->is_attivo)
|
|
->map(function (RubricaRuolo $r) use ($labels) {
|
|
$stabile = $r->stabile ?: $r->unitaImmobiliare?->stabile;
|
|
$ruoloKey = (string) ($r->ruolo_standard ?? 'altro');
|
|
$ruoloLabel = (string) ($labels[$ruoloKey] ?? $ruoloKey);
|
|
if (! empty($r->ruolo_custom)) {
|
|
$ruoloLabel .= ' (' . (string) $r->ruolo_custom . ')';
|
|
}
|
|
|
|
return [
|
|
'ruolo' => $ruoloLabel,
|
|
'stabile_codice' => (string) ($stabile?->codice_operatore ?? $stabile?->codice_stabile ?? ''),
|
|
'stabile_nome' => (string) ($stabile?->denominazione ?? ''),
|
|
'unita_id' => (int) ($r->unitaImmobiliare?->id ?? 0),
|
|
'unita_codice' => (string) ($r->unitaImmobiliare?->codice_unita ?? ''),
|
|
'unita_nome' => (string) ($r->unitaImmobiliare?->denominazione ?? ''),
|
|
'preferito' => (bool) ($r->is_preferito ?? false),
|
|
];
|
|
})
|
|
->values()
|
|
->all();
|
|
|
|
$this->cronistoriaRuoli = $ruoli
|
|
->map(function (RubricaRuolo $r) use ($labels) {
|
|
$stabile = $r->stabile ?: $r->unitaImmobiliare?->stabile;
|
|
$unita = $r->unitaImmobiliare;
|
|
|
|
$ruoloKey = (string) ($r->ruolo_standard ?? 'altro');
|
|
$ruoloLabel = (string) ($labels[$ruoloKey] ?? $ruoloKey);
|
|
if (! empty($r->ruolo_custom)) {
|
|
$ruoloLabel .= ' (' . (string) $r->ruolo_custom . ')';
|
|
}
|
|
|
|
return [
|
|
'ruolo' => $ruoloLabel,
|
|
'data_inizio' => optional($r->data_inizio)->format('d/m/Y'),
|
|
'data_fine' => $r->data_fine ? optional($r->data_fine)->format('d/m/Y') : null,
|
|
'attivo' => (bool) ($r->is_attivo ?? true),
|
|
'preferito' => (bool) ($r->is_preferito ?? false),
|
|
'stabile_codice' => (string) ($stabile?->codice_operatore ?? $stabile?->codice_stabile ?? ''),
|
|
'stabile_nome' => (string) ($stabile?->denominazione ?? ''),
|
|
'unita_id' => (int) ($unita?->id ?? 0),
|
|
'unita_codice' => (string) ($unita?->codice_unita ?? ''),
|
|
'unita_nome' => (string) ($unita?->denominazione ?? ''),
|
|
];
|
|
})
|
|
->all();
|
|
|
|
// Collegamenti stabili: includi sia stabili con rubrica_id diretto sia stabili legati tramite ruoli.
|
|
$stabiliIds = [];
|
|
foreach ($ruoli as $r) {
|
|
$sid = (int) ($r->stabile_id ?? 0);
|
|
if ($sid <= 0) {
|
|
$sid = (int) ($r->unitaImmobiliare?->stabile_id ?? 0);
|
|
}
|
|
if ($sid > 0) {
|
|
$stabiliIds[] = $sid;
|
|
}
|
|
}
|
|
$stabiliIds = array_values(array_unique($stabiliIds));
|
|
|
|
$stabiliQuery = Stabile::query()->where('amministratore_id', $adminId);
|
|
if (! empty($stabiliIds)) {
|
|
$stabiliQuery->where(function (Builder $q) use ($stabiliIds): void {
|
|
$q->whereIn('id', $stabiliIds)
|
|
->orWhere('rubrica_id', (int) $this->rubrica->id);
|
|
});
|
|
} else {
|
|
$stabiliQuery->where('rubrica_id', (int) $this->rubrica->id);
|
|
}
|
|
|
|
$this->stabili = $stabiliQuery
|
|
->orderBy('codice_stabile')
|
|
->limit(100)
|
|
->get(['id', 'codice_stabile', 'cod_stabile', 'denominazione', 'codice_fiscale'])
|
|
->map(fn(Stabile $s) => [
|
|
'id' => (int) $s->id,
|
|
'codice' => (string) ($s->codice_operatore ?? $s->codice_stabile ?? ''),
|
|
'nome' => (string) ($s->denominazione ?? ''),
|
|
'cf' => (string) ($s->codice_fiscale ?? ''),
|
|
])
|
|
->all();
|
|
|
|
// Contatti principali (per mostrarli velocemente nella scheda): prende canali principali + fallback ai campi telefono/email.
|
|
if (Schema::hasTable('rubrica_contatto_canali')) {
|
|
$principali = RubricaContattoCanale::query()
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->orderByDesc('is_principale')
|
|
->orderByDesc('data_inizio')
|
|
->orderByDesc('id')
|
|
->limit(20)
|
|
->get();
|
|
|
|
$this->contattiPrincipali = $principali
|
|
->map(fn(RubricaContattoCanale $c) => [
|
|
'tipo' => (string) ($c->tipo ?? 'altro'),
|
|
'etichetta' => (string) ($c->etichetta ?? ''),
|
|
'valore' => (string) ($c->valore ?? ''),
|
|
'principale' => (bool) ($c->is_principale ?? false),
|
|
])
|
|
->all();
|
|
} else {
|
|
$this->contattiPrincipali = [];
|
|
}
|
|
|
|
$this->loadEmailMultiple();
|
|
$this->fillInlineForm();
|
|
$this->hydrateCollegamentoWorkspace();
|
|
$this->hydrateFornitoriWorkspace();
|
|
$this->hydrateStudioCollaboratoreWorkspace();
|
|
}
|
|
|
|
private function hydrateCollegamentoWorkspace(): void
|
|
{
|
|
$suggestion = $this->resolveCollegamentoSuggerito();
|
|
|
|
$this->collegamentoSuggerito = $suggestion;
|
|
$this->collegamentoStabileId = ! empty($suggestion['stabile_id']) ? (int) $suggestion['stabile_id'] : null;
|
|
$this->collegamentoUnitaId = ! empty($suggestion['unita_id']) ? (int) $suggestion['unita_id'] : null;
|
|
$this->collegamentoRuolo = ! empty($suggestion['role_standard']) ? (string) $suggestion['role_standard'] : 'altro';
|
|
$this->collegamentoRuoloCustom = ! empty($suggestion['role_custom']) ? (string) $suggestion['role_custom'] : '';
|
|
}
|
|
|
|
public function collegaCollegamentoSuggerito(): void
|
|
{
|
|
if ($this->collegamentoSuggerito === []) {
|
|
Notification::make()
|
|
->title('Nessun collegamento suggerito disponibile')
|
|
->warning()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$this->persistCollegamentoRubrica(
|
|
(int) ($this->collegamentoSuggerito['stabile_id'] ?? 0),
|
|
! empty($this->collegamentoSuggerito['unita_id']) ? (int) $this->collegamentoSuggerito['unita_id'] : null,
|
|
(string) ($this->collegamentoSuggerito['role_standard'] ?? 'altro'),
|
|
(string) ($this->collegamentoSuggerito['role_custom'] ?? ''),
|
|
(string) ($this->collegamentoSuggerito['source'] ?? 'suggerimento_scheda')
|
|
);
|
|
}
|
|
|
|
public function salvaCollegamentoManuale(): void
|
|
{
|
|
$data = validator([
|
|
'stabile_id' => $this->collegamentoStabileId,
|
|
'unita_immobiliare_id' => $this->collegamentoUnitaId,
|
|
'ruolo_standard' => $this->collegamentoRuolo,
|
|
'ruolo_custom' => $this->collegamentoRuoloCustom,
|
|
], [
|
|
'stabile_id' => ['required', 'integer'],
|
|
'unita_immobiliare_id' => ['nullable', 'integer'],
|
|
'ruolo_standard' => ['required', 'string', 'max:120'],
|
|
'ruolo_custom' => ['nullable', 'string', 'max:255'],
|
|
])->validate();
|
|
|
|
$this->persistCollegamentoRubrica(
|
|
(int) $data['stabile_id'],
|
|
! empty($data['unita_immobiliare_id']) ? (int) $data['unita_immobiliare_id'] : null,
|
|
(string) $data['ruolo_standard'],
|
|
(string) ($data['ruolo_custom'] ?? ''),
|
|
'manuale_scheda'
|
|
);
|
|
}
|
|
|
|
private function persistCollegamentoRubrica(
|
|
int $stabileId,
|
|
?int $unitaId,
|
|
string $ruoloStandard,
|
|
string $ruoloCustom = '',
|
|
string $source = 'manuale_scheda'
|
|
): void {
|
|
$stabile = Stabile::query()->find($stabileId);
|
|
if (! $stabile) {
|
|
Notification::make()
|
|
->title('Stabile non trovato')
|
|
->warning()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$user = Auth::user();
|
|
$isSuperAdmin = $user instanceof User && method_exists($user, 'hasRole')
|
|
? (bool) $user->hasRole('super-admin')
|
|
: false;
|
|
|
|
if (! $isSuperAdmin && $this->adminId > 0 && (int) $stabile->amministratore_id !== $this->adminId) {
|
|
Notification::make()
|
|
->title('Stabile fuori dal tenant attivo')
|
|
->body('Il collegamento può essere creato solo sugli stabili dell\'amministratore corrente.')
|
|
->danger()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$unita = null;
|
|
if ($unitaId !== null && $unitaId > 0) {
|
|
$unita = UnitaImmobiliare::query()
|
|
->where('id', $unitaId)
|
|
->where('stabile_id', (int) $stabile->id)
|
|
->first();
|
|
|
|
if (! $unita) {
|
|
Notification::make()
|
|
->title('Unità non coerente con lo stabile')
|
|
->warning()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
$ruoloStandard = trim($ruoloStandard) !== '' ? trim($ruoloStandard) : 'altro';
|
|
$ruoloCustom = trim($ruoloCustom);
|
|
|
|
$attributes = [
|
|
'rubrica_id' => (int) $this->rubrica->id,
|
|
'ruolo_standard' => $ruoloStandard,
|
|
'stabile_id' => (int) $stabile->id,
|
|
'unita_immobiliare_id' => $unita?->id,
|
|
];
|
|
|
|
if ($ruoloCustom !== '') {
|
|
$attributes['ruolo_custom'] = $ruoloCustom;
|
|
}
|
|
|
|
$existing = RubricaRuolo::query()
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->where('ruolo_standard', $ruoloStandard)
|
|
->where('stabile_id', (int) $stabile->id)
|
|
->where('unita_immobiliare_id', $unita?->id)
|
|
->when($ruoloCustom !== '', fn(Builder $query) => $query->where('ruolo_custom', $ruoloCustom))
|
|
->first();
|
|
|
|
$payload = [
|
|
'ruolo_custom' => $ruoloCustom !== '' ? $ruoloCustom : null,
|
|
'is_attivo' => true,
|
|
'is_preferito' => $existing ? (bool) ($existing->is_preferito ?? false) : count($this->ruoliAttivi) === 0,
|
|
'data_inizio' => $existing?->data_inizio ?: now()->toDateString(),
|
|
'meta' => array_filter([
|
|
'source' => $source,
|
|
'linked_from' => 'rubrica_scheda',
|
|
], fn($value) => $value !== null && $value !== ''),
|
|
];
|
|
|
|
if ($existing) {
|
|
$existing->fill($payload);
|
|
$existing->save();
|
|
} else {
|
|
RubricaRuolo::query()->create(array_merge($attributes, $payload));
|
|
}
|
|
|
|
$this->rubrica->riferimento_stabile = $this->buildLocalStabileReference($stabile);
|
|
$this->rubrica->riferimento_unita = $unita ? $this->buildLocalUnitReference($unita) : $this->rubrica->riferimento_unita;
|
|
if (in_array($ruoloStandard, ['condomino', 'inquilino', 'fornitore', 'amministratore', 'altro'], true)) {
|
|
$this->rubrica->tipo_utenza_call = $ruoloStandard;
|
|
}
|
|
$this->rubrica->data_ultima_modifica = now()->toDateString();
|
|
$this->rubrica->modificato_da = Auth::id();
|
|
$this->rubrica->save();
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Collegamento aggiornato')
|
|
->body('La scheda rubrica è ora agganciata a stabile e unità selezionati.')
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function resolveCollegamentoSuggerito(): array
|
|
{
|
|
$legacy = $this->parseCollegamentoLegacyMeta((string) ($this->rubrica->note ?? ''));
|
|
$stabile = $this->resolveCollegamentoStabile($legacy['cod_stabile'] ?? null, (string) ($this->rubrica->riferimento_stabile ?? ''));
|
|
if (empty($stabile['id'])) {
|
|
return [];
|
|
}
|
|
|
|
$unit = $this->resolveCollegamentoUnita(
|
|
(int) $stabile['id'],
|
|
$legacy,
|
|
(string) ($this->rubrica->riferimento_unita ?? '')
|
|
);
|
|
|
|
return array_filter([
|
|
'stabile_id' => (int) $stabile['id'],
|
|
'stabile_label' => $this->buildCollegamentoStabileReference($stabile),
|
|
'unita_id' => (int) ($unit['unit_id'] ?? 0) ?: null,
|
|
'unita_label' => (string) ($unit['unit_label'] ?? ''),
|
|
'unita_reference' => (string) ($unit['unit_reference'] ?? ''),
|
|
'role_standard' => $this->mapCollegamentoRole($legacy['ruolo'] ?? $this->rubrica->tipo_utenza_call),
|
|
'role_custom' => null,
|
|
'source' => (string) ($unit['source'] ?? 'riferimento_stabile'),
|
|
'requires_review' => (bool) ($unit['source'] ?? false),
|
|
], fn($value) => $value !== null && $value !== '');
|
|
}
|
|
|
|
/** @return array{cod_stabile:?string,cod_cond:?string,ruolo:?string} */
|
|
private function parseCollegamentoLegacyMeta(string $note): array
|
|
{
|
|
$payload = [
|
|
'cod_stabile' => null,
|
|
'cod_cond' => null,
|
|
'ruolo' => null,
|
|
];
|
|
|
|
foreach (array_keys($payload) as $key) {
|
|
if (preg_match('/' . preg_quote($key, '/') . '=([^|\n\r]+)/i', $note, $matches) === 1) {
|
|
$payload[$key] = trim((string) ($matches[1] ?? '')) ?: null;
|
|
}
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
/** @return array{id:int|null,codice_stabile:?string,cod_stabile:?string,denominazione:?string} */
|
|
private function resolveCollegamentoStabile(?string $legacyCode, string $reference): array
|
|
{
|
|
$blank = ['id' => null, 'codice_stabile' => null, 'cod_stabile' => null, 'denominazione' => null];
|
|
|
|
foreach (array_filter([$legacyCode, ...$this->extractReferenceCandidates($reference)]) as $candidate) {
|
|
$code = trim((string) $candidate);
|
|
if ($code === '') {
|
|
continue;
|
|
}
|
|
|
|
$trimmed = ltrim($code, '0');
|
|
$row = Stabile::query()
|
|
->when($this->adminId > 0, fn(Builder $query) => $query->where('amministratore_id', $this->adminId))
|
|
->where(function (Builder $query) use ($code, $trimmed): void {
|
|
$query->where('codice_stabile', $code)
|
|
->orWhere('cod_stabile', $code);
|
|
|
|
if ($trimmed !== '' && $trimmed !== $code) {
|
|
$query->orWhere('codice_stabile', $trimmed)
|
|
->orWhere('cod_stabile', $trimmed);
|
|
}
|
|
})
|
|
->orderBy('id')
|
|
->first(['id', 'codice_stabile', 'cod_stabile', 'denominazione']);
|
|
|
|
if ($row) {
|
|
return [
|
|
'id' => (int) $row->id,
|
|
'codice_stabile' => $row->codice_stabile ? (string) $row->codice_stabile : null,
|
|
'cod_stabile' => $row->cod_stabile ? (string) $row->cod_stabile : null,
|
|
'denominazione' => $row->denominazione ? (string) $row->denominazione : null,
|
|
];
|
|
}
|
|
}
|
|
|
|
$name = $this->extractReferenceName($reference);
|
|
if ($name !== '') {
|
|
$rows = Stabile::query()
|
|
->when($this->adminId > 0, fn(Builder $query) => $query->where('amministratore_id', $this->adminId))
|
|
->whereRaw("LOWER(COALESCE(denominazione, '')) = ?", [mb_strtolower($name)])
|
|
->limit(2)
|
|
->get(['id', 'codice_stabile', 'cod_stabile', 'denominazione']);
|
|
|
|
if ($rows->count() === 1) {
|
|
$row = $rows->first();
|
|
|
|
return [
|
|
'id' => (int) $row->id,
|
|
'codice_stabile' => $row->codice_stabile ? (string) $row->codice_stabile : null,
|
|
'cod_stabile' => $row->cod_stabile ? (string) $row->cod_stabile : null,
|
|
'denominazione' => $row->denominazione ? (string) $row->denominazione : null,
|
|
];
|
|
}
|
|
}
|
|
|
|
return $blank;
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function resolveCollegamentoUnita(int $stabileId, array $legacy, string $reference): array
|
|
{
|
|
$reference = trim($reference);
|
|
if ($reference !== '') {
|
|
$code = trim((string) Str::before($reference, ' - '));
|
|
if ($code !== '') {
|
|
$rows = UnitaImmobiliare::query()
|
|
->where('stabile_id', $stabileId)
|
|
->where('codice_unita', $code)
|
|
->limit(2)
|
|
->get(['id', 'codice_unita', 'denominazione', 'scala', 'interno']);
|
|
|
|
if ($rows->count() === 1) {
|
|
$row = $rows->first();
|
|
|
|
return [
|
|
'unit_id' => (int) $row->id,
|
|
'unit_label' => $this->buildLocalUnitLabel($row),
|
|
'unit_reference' => $this->buildLocalUnitReference($row),
|
|
'source' => 'riferimento_unita',
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! empty($legacy['cod_stabile']) && ! empty($legacy['cod_cond']) && $this->hasGesconImportCondomin()) {
|
|
$rows = DB::connection('gescon_import')
|
|
->table('condomin')
|
|
->where('cod_stabile', trim((string) $legacy['cod_stabile']))
|
|
->where('cod_cond', trim((string) $legacy['cod_cond']))
|
|
->select(['nom_cond', 'scala', 'interno'])
|
|
->get();
|
|
|
|
$candidates = [];
|
|
foreach ($rows as $row) {
|
|
$unit = $this->resolveUnitFromImportRow($stabileId, [
|
|
'nom_cond' => (string) ($row->nom_cond ?? ''),
|
|
'scala' => (string) ($row->scala ?? ''),
|
|
'interno' => (string) ($row->interno ?? ''),
|
|
]);
|
|
|
|
if (! $unit) {
|
|
continue;
|
|
}
|
|
|
|
$candidates[(int) $unit->unit_id] = [
|
|
'unit_id' => (int) $unit->unit_id,
|
|
'unit_label' => $this->buildLocalUnitLabel($unit),
|
|
'unit_reference' => $this->buildLocalUnitReference($unit),
|
|
'source' => 'gescon_import',
|
|
];
|
|
}
|
|
|
|
if (count($candidates) === 1) {
|
|
return array_values($candidates)[0];
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
private function extractReferenceCandidates(string $reference): array
|
|
{
|
|
$parts = preg_split('/\s*-\s*/', trim($reference)) ?: [];
|
|
|
|
return array_values(array_filter(array_map(static function (string $part): string {
|
|
$value = trim($part);
|
|
return preg_match('/^[0-9]+$/', $value) === 1 ? $value : '';
|
|
}, $parts)));
|
|
}
|
|
|
|
private function extractReferenceName(string $reference): string
|
|
{
|
|
$parts = preg_split('/\s*-\s*/', trim($reference)) ?: [];
|
|
$parts = array_values(array_filter(array_map(static fn(string $part): string => trim($part), $parts), static fn(string $part): bool => $part !== '' && preg_match('/^[0-9]+$/', $part) !== 1));
|
|
|
|
return $parts !== [] ? (string) end($parts) : '';
|
|
}
|
|
|
|
private function buildLocalStabileReference(Stabile $stabile): string
|
|
{
|
|
$parts = array_values(array_filter([
|
|
trim((string) ($stabile->cod_stabile ?? '')),
|
|
trim((string) ($stabile->codice_stabile ?? '')),
|
|
trim((string) ($stabile->denominazione ?? '')),
|
|
]));
|
|
|
|
return $parts !== [] ? implode(' - ', $parts) : ('Stabile #' . (int) $stabile->id);
|
|
}
|
|
|
|
private function buildCollegamentoStabileReference(array $stabile): string
|
|
{
|
|
$parts = array_values(array_filter([
|
|
trim((string) ($stabile['cod_stabile'] ?? '')),
|
|
trim((string) ($stabile['codice_stabile'] ?? '')),
|
|
trim((string) ($stabile['denominazione'] ?? '')),
|
|
]));
|
|
|
|
return $parts !== [] ? implode(' - ', $parts) : ('Stabile #' . (int) ($stabile['id'] ?? 0));
|
|
}
|
|
|
|
private function buildLocalUnitReference(object $unit): string
|
|
{
|
|
$code = trim((string) data_get($unit, 'codice_unita', ''));
|
|
$label = trim((string) data_get($unit, 'denominazione', ''));
|
|
|
|
return trim($code . ($label !== '' ? ' - ' . $label : ''));
|
|
}
|
|
|
|
private function buildLocalUnitLabel(object $unit): string
|
|
{
|
|
$bits = array_values(array_filter([
|
|
trim((string) data_get($unit, 'codice_unita', '')),
|
|
trim((string) data_get($unit, 'scala', '')),
|
|
trim((string) data_get($unit, 'interno', '')),
|
|
trim((string) data_get($unit, 'denominazione', '')),
|
|
]));
|
|
|
|
return implode(' / ', $bits);
|
|
}
|
|
|
|
private function mapCollegamentoRole(?string $value): ?string
|
|
{
|
|
$normalized = strtolower(trim((string) $value));
|
|
|
|
return match ($normalized) {
|
|
'c', 'condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'titolare' => 'condomino',
|
|
'i', 'inquilino', 'conduttore', 'locatario' => 'inquilino',
|
|
'fornitore' => 'fornitore',
|
|
'amministratore' => 'amministratore',
|
|
'collaboratore' => 'collaboratore',
|
|
'altro' => 'altro',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
private function resolveUnitFromImportRow(int $stabileId, array $row): ?object
|
|
{
|
|
$query = DB::table('unita_immobiliari')
|
|
->where('stabile_id', $stabileId)
|
|
->select(['id as unit_id', 'codice_unita', 'denominazione', 'scala', 'interno']);
|
|
|
|
$scala = trim((string) ($row['scala'] ?? ''));
|
|
$interno = trim((string) ($row['interno'] ?? ''));
|
|
|
|
if ($scala !== '' || $interno !== '') {
|
|
$strict = (clone $query)
|
|
->when($scala !== '', fn($inner) => $inner->where('scala', $scala))
|
|
->when($interno !== '', fn($inner) => $inner->where('interno', $interno))
|
|
->get();
|
|
if ($strict->count() === 1) {
|
|
return $strict->first();
|
|
}
|
|
}
|
|
|
|
$normalizedName = $this->normalizeCollegamentoText((string) ($row['nom_cond'] ?? ''));
|
|
if ($normalizedName === '') {
|
|
return null;
|
|
}
|
|
|
|
$matches = (clone $query)
|
|
->get()
|
|
->filter(function ($unit) use ($normalizedName): bool {
|
|
$haystack = $this->normalizeCollegamentoText((string) ($unit->denominazione ?? ''));
|
|
|
|
return $haystack !== '' && ($haystack === $normalizedName || str_contains($haystack, $normalizedName) || str_contains($normalizedName, $haystack));
|
|
})
|
|
->values();
|
|
|
|
return $matches->count() === 1 ? $matches->first() : null;
|
|
}
|
|
|
|
private function hasGesconImportCondomin(): bool
|
|
{
|
|
try {
|
|
return Schema::connection('gescon_import')->hasTable('condomin');
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function normalizeCollegamentoText(string $value): string
|
|
{
|
|
$upper = mb_strtoupper(trim($value));
|
|
$normalized = preg_replace('/[^A-Z0-9]+/u', '', $upper);
|
|
|
|
return is_string($normalized) ? $normalized : '';
|
|
}
|
|
|
|
private function applyLegacyIdentityDefaults(): void
|
|
{
|
|
$legacyIdentity = $this->extractLegacyIdentityFromNote($this->rubrica->note ?? null);
|
|
|
|
if (! $this->rubrica->titolo_id && ! empty($legacyIdentity['titolo_id'])) {
|
|
$this->rubrica->titolo_id = (int) $legacyIdentity['titolo_id'];
|
|
$this->rubrica->saveQuietly();
|
|
$this->rubrica->refresh();
|
|
}
|
|
}
|
|
|
|
public function startInlineEdit(): void
|
|
{
|
|
$this->fillInlineForm();
|
|
$this->isInlineEditing = true;
|
|
}
|
|
|
|
public function cancelInlineEdit(): void
|
|
{
|
|
$this->fillInlineForm();
|
|
$this->isInlineEditing = false;
|
|
}
|
|
|
|
public function saveInlineEdit(): void
|
|
{
|
|
$legacyIdentity = $this->extractLegacyIdentityFromNote($this->inlineForm['note'] ?? null);
|
|
$resolvedAddress = $this->parseAddressComponents($this->inlineForm['indirizzo_completo'] ?? null);
|
|
|
|
$data = validator($this->inlineForm, [
|
|
'nome' => ['nullable', 'string', 'max:255'],
|
|
'cognome' => ['nullable', 'string', 'max:255'],
|
|
'ragione_sociale' => ['nullable', 'string', 'max:255'],
|
|
'tipo_contatto' => ['nullable', 'string', 'max:120'],
|
|
'categoria' => ['nullable', 'string', 'max:120'],
|
|
'codice_fiscale' => ['nullable', 'string', 'max:32'],
|
|
'partita_iva' => ['nullable', 'string', 'max:32'],
|
|
'sesso' => ['nullable', 'string', 'max:1'],
|
|
'data_nascita' => ['nullable', 'date'],
|
|
'luogo_nascita' => ['nullable', 'string', 'max:100'],
|
|
'provincia_nascita' => ['nullable', 'string', 'max:2'],
|
|
'email' => ['nullable', 'string', 'max:255'],
|
|
'pec' => ['nullable', 'string', 'max:255'],
|
|
'telefono_ufficio' => ['nullable', 'string', 'max:64'],
|
|
'telefono_cellulare' => ['nullable', 'string', 'max:64'],
|
|
'telefono_casa' => ['nullable', 'string', 'max:64'],
|
|
'indirizzo' => ['nullable', 'string', 'max:255'],
|
|
'civico' => ['nullable', 'string', 'max:32'],
|
|
'cap' => ['nullable', 'string', 'max:5'],
|
|
'citta' => ['nullable', 'string', 'max:100'],
|
|
'provincia' => ['nullable', 'string', 'max:2'],
|
|
'nazione' => ['nullable', 'string', 'max:50'],
|
|
'indirizzo_completo' => ['nullable', 'string', 'max:255'],
|
|
'tipo_utenza_call' => ['nullable', 'string', 'max:120'],
|
|
'riferimento_stabile' => ['nullable', 'string', 'max:255'],
|
|
'riferimento_unita' => ['nullable', 'string', 'max:255'],
|
|
'note' => ['nullable', 'string', 'max:5000'],
|
|
'note_segreteria' => ['nullable', 'string', 'max:5000'],
|
|
])->validate();
|
|
|
|
$normalizedBirthDate = $this->normalizeLegacyDateValue($data['data_nascita'] ?? $legacyIdentity['data_nascita'] ?? null);
|
|
$cleanNote = $this->cleanNullable($legacyIdentity['note_clean'] ?? ($data['note'] ?? null));
|
|
$sesso = $this->normalizeSessoValue($data['sesso'] ?? $legacyIdentity['sesso'] ?? null);
|
|
$provinciaNascita = $this->normalizeProvince($data['provincia_nascita'] ?? $legacyIdentity['provincia_nascita'] ?? null);
|
|
|
|
$indirizzo = $this->cleanNullable($data['indirizzo'] ?? null) ?: ($resolvedAddress['indirizzo'] ?? null);
|
|
$civico = $this->cleanNullable($data['civico'] ?? null) ?: ($resolvedAddress['civico'] ?? null);
|
|
$cap = $this->cleanNullable($data['cap'] ?? null) ?: ($resolvedAddress['cap'] ?? null);
|
|
$citta = $this->cleanNullable($data['citta'] ?? null) ?: ($resolvedAddress['citta'] ?? null);
|
|
$provincia = $this->normalizeProvince($data['provincia'] ?? null) ?: ($resolvedAddress['provincia'] ?? null);
|
|
|
|
$this->rubrica->fill([
|
|
'nome' => $this->cleanNullable($data['nome'] ?? null),
|
|
'cognome' => $this->cleanNullable($data['cognome'] ?? null),
|
|
'ragione_sociale' => $this->cleanNullable($data['ragione_sociale'] ?? null),
|
|
'tipo_contatto' => $this->cleanNullable($data['tipo_contatto'] ?? null),
|
|
'categoria' => $this->cleanNullable($data['categoria'] ?? null),
|
|
'codice_fiscale' => $this->cleanNullable($data['codice_fiscale'] ?? null),
|
|
'partita_iva' => $this->cleanNullable($data['partita_iva'] ?? null),
|
|
'sesso' => $sesso,
|
|
'data_nascita' => $normalizedBirthDate,
|
|
'luogo_nascita' => $this->cleanNullable($data['luogo_nascita'] ?? $legacyIdentity['luogo_nascita'] ?? null),
|
|
'provincia_nascita' => $provinciaNascita,
|
|
'email' => $this->cleanNullable($data['email'] ?? null),
|
|
'pec' => $this->cleanNullable($data['pec'] ?? null),
|
|
'telefono_ufficio' => $this->cleanNullable($data['telefono_ufficio'] ?? null),
|
|
'telefono_cellulare' => $this->cleanNullable($data['telefono_cellulare'] ?? null),
|
|
'telefono_casa' => $this->cleanNullable($data['telefono_casa'] ?? null),
|
|
'indirizzo' => $indirizzo,
|
|
'civico' => $civico,
|
|
'cap' => $cap,
|
|
'citta' => $citta,
|
|
'provincia' => $provincia,
|
|
'nazione' => $this->cleanNullable($data['nazione'] ?? null) ?: $this->rubrica->nazione,
|
|
'tipo_utenza_call' => $this->cleanNullable($data['tipo_utenza_call'] ?? null),
|
|
'riferimento_stabile' => $this->cleanNullable($data['riferimento_stabile'] ?? null),
|
|
'riferimento_unita' => $this->cleanNullable($data['riferimento_unita'] ?? null),
|
|
'note' => $cleanNote,
|
|
'note_segreteria' => $this->cleanNullable($data['note_segreteria'] ?? null),
|
|
'data_ultima_modifica' => now()->toDateString(),
|
|
'modificato_da' => Auth::id(),
|
|
]);
|
|
|
|
if (! $this->rubrica->titolo_id && ! empty($legacyIdentity['titolo_id'])) {
|
|
$this->rubrica->titolo_id = (int) $legacyIdentity['titolo_id'];
|
|
}
|
|
|
|
$this->rubrica->save();
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
$this->isInlineEditing = false;
|
|
|
|
Notification::make()
|
|
->title('Contatto aggiornato')
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
private function initArchivioNavigation(): void
|
|
{
|
|
$pp = (int) request()->query('pp', 25);
|
|
if ($pp <= 0) {
|
|
$pp = 25;
|
|
}
|
|
if (! in_array($pp, [10, 25, 50, 100], true)) {
|
|
$pp = 25;
|
|
}
|
|
|
|
$this->navPerPage = $pp;
|
|
|
|
if ($this->adminId <= 0) {
|
|
$this->prevRecordId = null;
|
|
$this->nextRecordId = null;
|
|
$this->navPage = 1;
|
|
$this->navPages = 1;
|
|
return;
|
|
}
|
|
|
|
$base = RubricaUniversale::query()->where('amministratore_id', $this->adminId);
|
|
|
|
$total = (clone $base)->count();
|
|
$this->navPages = max(1, (int) ceil($total / $this->navPerPage));
|
|
|
|
$before = (clone $base)
|
|
->where($this->whereBeforeCurrent())
|
|
->count();
|
|
$position = $before + 1;
|
|
$this->navPage = max(1, (int) ceil($position / $this->navPerPage));
|
|
|
|
$this->prevRecordId = (clone $base)
|
|
->where($this->whereBeforeCurrent())
|
|
->tap(fn(Builder $q) => $this->applySort($q, 'desc'))
|
|
->value('id');
|
|
|
|
$this->nextRecordId = (clone $base)
|
|
->where($this->whereAfterCurrent())
|
|
->tap(fn(Builder $q) => $this->applySort($q, 'asc'))
|
|
->value('id');
|
|
}
|
|
|
|
private function applySort(Builder $query, string $direction): void
|
|
{
|
|
$dir = strtolower($direction) === 'desc' ? 'DESC' : 'ASC';
|
|
|
|
$query
|
|
->orderByRaw("COALESCE(ragione_sociale, '') {$dir}")
|
|
->orderByRaw("COALESCE(cognome, '') {$dir}")
|
|
->orderByRaw("COALESCE(nome, '') {$dir}")
|
|
->orderBy('id', $dir);
|
|
}
|
|
|
|
private function whereBeforeCurrent(): \Closure
|
|
{
|
|
$ragione = (string) ($this->rubrica->ragione_sociale ?? '');
|
|
$cognome = (string) ($this->rubrica->cognome ?? '');
|
|
$nome = (string) ($this->rubrica->nome ?? '');
|
|
$id = (int) $this->rubrica->id;
|
|
|
|
return function (Builder $q) use ($ragione, $cognome, $nome, $id): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') < ?", [$ragione])
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') < ?", [$cognome]);
|
|
})
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome, $nome): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') = ?", [$cognome])
|
|
->whereRaw("COALESCE(nome, '') < ?", [$nome]);
|
|
})
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome, $nome, $id): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') = ?", [$cognome])
|
|
->whereRaw("COALESCE(nome, '') = ?", [$nome])
|
|
->where('id', '<', $id);
|
|
});
|
|
};
|
|
}
|
|
|
|
private function whereAfterCurrent(): \Closure
|
|
{
|
|
$ragione = (string) ($this->rubrica->ragione_sociale ?? '');
|
|
$cognome = (string) ($this->rubrica->cognome ?? '');
|
|
$nome = (string) ($this->rubrica->nome ?? '');
|
|
$id = (int) $this->rubrica->id;
|
|
|
|
return function (Builder $q) use ($ragione, $cognome, $nome, $id): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') > ?", [$ragione])
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') > ?", [$cognome]);
|
|
})
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome, $nome): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') = ?", [$cognome])
|
|
->whereRaw("COALESCE(nome, '') > ?", [$nome]);
|
|
})
|
|
->orWhere(function (Builder $q) use ($ragione, $cognome, $nome, $id): void {
|
|
$q->whereRaw("COALESCE(ragione_sociale, '') = ?", [$ragione])
|
|
->whereRaw("COALESCE(cognome, '') = ?", [$cognome])
|
|
->whereRaw("COALESCE(nome, '') = ?", [$nome])
|
|
->where('id', '>', $id);
|
|
});
|
|
};
|
|
}
|
|
|
|
public function getPrevUrl(): ?string
|
|
{
|
|
if (! $this->prevRecordId) {
|
|
return null;
|
|
}
|
|
return self::getUrl(['record' => $this->prevRecordId], panel: 'admin-filament')
|
|
. '?pp=' . $this->navPerPage;
|
|
}
|
|
|
|
public function getNextUrl(): ?string
|
|
{
|
|
if (! $this->nextRecordId) {
|
|
return null;
|
|
}
|
|
return self::getUrl(['record' => $this->nextRecordId], panel: 'admin-filament')
|
|
. '?pp=' . $this->navPerPage;
|
|
}
|
|
|
|
public function getPrevPageUrl(): ?string
|
|
{
|
|
$target = $this->navPage - 1;
|
|
if ($target < 1) {
|
|
return null;
|
|
}
|
|
return $this->getPageUrl($target);
|
|
}
|
|
|
|
public function getNextPageUrl(): ?string
|
|
{
|
|
$target = $this->navPage + 1;
|
|
if ($target > $this->navPages) {
|
|
return null;
|
|
}
|
|
return $this->getPageUrl($target);
|
|
}
|
|
|
|
public function getPageUrl(int $page): ?string
|
|
{
|
|
if ($this->adminId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$page = max(1, min($page, $this->navPages));
|
|
$offset = ($page - 1) * $this->navPerPage;
|
|
|
|
$base = RubricaUniversale::query()
|
|
->where('amministratore_id', $this->adminId);
|
|
|
|
$this->applySort($base, 'asc');
|
|
$id = (int) ($base->skip($offset)->take(1)->value('id') ?? 0);
|
|
if ($id <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return self::getUrl(['record' => $id], panel: 'admin-filament')
|
|
. '?pp=' . $this->navPerPage;
|
|
}
|
|
|
|
public function getPerPageUrl(int $perPage): string
|
|
{
|
|
if (! in_array($perPage, [10, 25, 50, 100], true)) {
|
|
$perPage = 25;
|
|
}
|
|
|
|
return self::getUrl(['record' => (int) $this->rubrica->id], panel: 'admin-filament')
|
|
. '?pp=' . $perPage;
|
|
}
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Action::make('titolo')
|
|
->label('Titolo')
|
|
->icon('heroicon-o-tag')
|
|
->form([
|
|
Select::make('titolo_id')
|
|
->label('Titolo')
|
|
->native(false)
|
|
->searchable()
|
|
->options(fn() => $this->getTitoliOptions()),
|
|
])
|
|
->fillForm(fn(): array=> [
|
|
'titolo_id' => $this->rubrica->titolo_id ? (int) $this->rubrica->titolo_id : null,
|
|
])
|
|
->action(function (array $data): void {
|
|
$titoloId = isset($data['titolo_id']) && is_numeric($data['titolo_id'])
|
|
? (int) $data['titolo_id']
|
|
: null;
|
|
|
|
$this->rubrica->titolo_id = $titoloId;
|
|
$this->rubrica->data_ultima_modifica = now()->toDateString();
|
|
$this->rubrica->modificato_da = Auth::id();
|
|
$this->rubrica->save();
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Titolo aggiornato')
|
|
->success()
|
|
->send();
|
|
}),
|
|
Action::make('contatti')
|
|
->label('Contatti')
|
|
->icon('heroicon-o-phone')
|
|
->action(function (): void {
|
|
$this->startInlineEdit();
|
|
}),
|
|
|
|
Action::make('email_multiple')
|
|
->label('Email aggiuntive')
|
|
->icon('heroicon-o-envelope')
|
|
->modalWidth('4xl')
|
|
->form([
|
|
Repeater::make('emails')
|
|
->label('Email aggiuntive')
|
|
->defaultItems(0)
|
|
->reorderable(true)
|
|
->schema([
|
|
Hidden::make('id'),
|
|
TextInput::make('email')
|
|
->label('Email')
|
|
->email()
|
|
->required()
|
|
->maxLength(255)
|
|
->columnSpan(6),
|
|
Select::make('tipo_email')
|
|
->label('Tipo')
|
|
->native(false)
|
|
->options([
|
|
'secondaria' => 'Secondaria',
|
|
'lavoro' => 'Lavoro',
|
|
'personale' => 'Personale',
|
|
'pec' => 'PEC',
|
|
'altro' => 'Altro',
|
|
])
|
|
->default('secondaria')
|
|
->columnSpan(4),
|
|
Toggle::make('attiva')
|
|
->label('Attiva')
|
|
->default(true)
|
|
->columnSpan(2),
|
|
])
|
|
->columns(12),
|
|
])
|
|
->fillForm(fn(): array=> ['emails' => $this->getAdditionalEmailsFormRows()])
|
|
->action(function (array $data): void {
|
|
$this->saveAdditionalEmails($data['emails'] ?? []);
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Email aggiuntive aggiornate')
|
|
->success()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('contatti_avanzati')
|
|
->label('Contatti avanzati')
|
|
->icon('heroicon-o-rectangle-stack')
|
|
->visible(fn(): bool => Schema::hasTable('rubrica_contatto_canali'))
|
|
->modalWidth('7xl')
|
|
->form([
|
|
Repeater::make('canali')
|
|
->label('Contatti aggiuntivi')
|
|
->defaultItems(0)
|
|
->reorderable(true)
|
|
->schema([
|
|
Hidden::make('id'),
|
|
|
|
Select::make('tipo')
|
|
->label('Tipo')
|
|
->options([
|
|
'telefono' => 'Telefono',
|
|
'cellulare' => 'Cellulare',
|
|
'email' => 'Email',
|
|
'pec' => 'PEC',
|
|
'fax' => 'Fax',
|
|
'sito_web' => 'Sito web',
|
|
'altro' => 'Altro',
|
|
])
|
|
->native(false)
|
|
->required()
|
|
->columnSpan(2),
|
|
|
|
TextInput::make('etichetta')
|
|
->label('Etichetta')
|
|
->maxLength(50)
|
|
->columnSpan(2),
|
|
|
|
TextInput::make('valore')
|
|
->label('Valore')
|
|
->required()
|
|
->maxLength(255)
|
|
->columnSpan(4),
|
|
|
|
Toggle::make('is_principale')
|
|
->label('Principale')
|
|
->default(false)
|
|
->columnSpan(2),
|
|
|
|
Select::make('stabile_id')
|
|
->label('Stabile (opzionale)')
|
|
->native(false)
|
|
->searchable()
|
|
->options(fn() => $this->getStabiliOptions())
|
|
->reactive()
|
|
->afterStateUpdated(fn($set) => $set('unita_immobiliare_id', null))
|
|
->columnSpan(3),
|
|
|
|
Select::make('unita_immobiliare_id')
|
|
->label('Unità (opzionale)')
|
|
->native(false)
|
|
->searchable()
|
|
->options(fn($get) => $this->getUnitaOptions($get('stabile_id')))
|
|
->columnSpan(3),
|
|
|
|
DatePicker::make('data_inizio')
|
|
->label('Data inizio')
|
|
->columnSpan(2),
|
|
|
|
DatePicker::make('data_fine')
|
|
->label('Data fine')
|
|
->columnSpan(2),
|
|
|
|
TextInput::make('note')
|
|
->label('Note')
|
|
->maxLength(255)
|
|
->columnSpan(12),
|
|
])
|
|
->columns(12),
|
|
])
|
|
->fillForm(function (): array {
|
|
$canali = RubricaContattoCanale::query()
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->orderByDesc('is_principale')
|
|
->orderByDesc('data_inizio')
|
|
->orderByDesc('id')
|
|
->limit(200)
|
|
->get();
|
|
|
|
return [
|
|
'canali' => $canali->map(fn(RubricaContattoCanale $c) => [
|
|
'id' => (int) $c->id,
|
|
'tipo' => (string) ($c->tipo ?? 'altro'),
|
|
'etichetta' => (string) ($c->etichetta ?? ''),
|
|
'valore' => (string) ($c->valore ?? ''),
|
|
'is_principale' => (bool) ($c->is_principale ?? false),
|
|
'stabile_id' => $c->stabile_id ? (int) $c->stabile_id : null,
|
|
'unita_immobiliare_id' => $c->unita_immobiliare_id ? (int) $c->unita_immobiliare_id : null,
|
|
'data_inizio' => $c->data_inizio?->format('Y-m-d'),
|
|
'data_fine' => $c->data_fine?->format('Y-m-d'),
|
|
'note' => (string) (($c->meta['note'] ?? null) ?: ''),
|
|
])->all(),
|
|
];
|
|
})
|
|
->action(function (array $data): void {
|
|
/** @var array<int, array<string, mixed>> $rows */
|
|
$rows = $data['canali'] ?? [];
|
|
$rows = is_array($rows) ? array_values($rows) : [];
|
|
|
|
DB::transaction(function () use ($rows): void {
|
|
$keepIds = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$id = (int) ($row['id'] ?? 0);
|
|
$tipo = (string) ($row['tipo'] ?? 'altro');
|
|
if ($tipo === '') {
|
|
$tipo = 'altro';
|
|
}
|
|
|
|
$attrs = [
|
|
'rubrica_id' => (int) $this->rubrica->id,
|
|
'tipo' => $tipo,
|
|
'etichetta' => ! empty($row['etichetta']) ? (string) $row['etichetta'] : null,
|
|
'valore' => ! empty($row['valore']) ? (string) $row['valore'] : null,
|
|
'is_principale' => (bool) ($row['is_principale'] ?? false),
|
|
'stabile_id' => ! empty($row['stabile_id']) ? (int) $row['stabile_id'] : null,
|
|
'unita_immobiliare_id' => ! empty($row['unita_immobiliare_id']) ? (int) $row['unita_immobiliare_id'] : null,
|
|
'data_inizio' => $row['data_inizio'] ?? null,
|
|
'data_fine' => $row['data_fine'] ?? null,
|
|
'meta' => [
|
|
'note' => ! empty($row['note']) ? (string) $row['note'] : null,
|
|
],
|
|
];
|
|
|
|
if ($id > 0) {
|
|
$existing = RubricaContattoCanale::query()
|
|
->where('id', $id)
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$existing->fill($attrs);
|
|
$existing->save();
|
|
$keepIds[] = (int) $existing->id;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$created = RubricaContattoCanale::query()->create($attrs);
|
|
$keepIds[] = (int) $created->id;
|
|
}
|
|
|
|
$deleteQuery = RubricaContattoCanale::query()->where('rubrica_id', (int) $this->rubrica->id);
|
|
if (! empty($keepIds)) {
|
|
$deleteQuery->whereNotIn('id', $keepIds);
|
|
}
|
|
$deleteQuery->delete();
|
|
});
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Contatti aggiornati')
|
|
->success()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('riferimenti_chiamata')
|
|
->label('Riferimenti chiamata')
|
|
->icon('heroicon-o-phone-arrow-up-right')
|
|
->form([
|
|
Select::make('tipo_utenza_call')
|
|
->label('Profilo chiamante')
|
|
->native(false)
|
|
->options([
|
|
'condomino' => 'Condomino',
|
|
'inquilino' => 'Inquilino',
|
|
'fornitore' => 'Fornitore',
|
|
'amministratore' => 'Amministratore',
|
|
'altro' => 'Altro',
|
|
]),
|
|
TextInput::make('riferimento_stabile')
|
|
->label('Riferimento stabile')
|
|
->maxLength(255),
|
|
TextInput::make('riferimento_unita')
|
|
->label('Riferimento unità')
|
|
->maxLength(255),
|
|
TextInput::make('note_segreteria')
|
|
->label('Note segreteria')
|
|
->maxLength(2000),
|
|
])
|
|
->fillForm(fn(): array=> [
|
|
'tipo_utenza_call' => $this->rubrica->tipo_utenza_call,
|
|
'riferimento_stabile' => $this->rubrica->riferimento_stabile,
|
|
'riferimento_unita' => $this->rubrica->riferimento_unita,
|
|
'note_segreteria' => $this->rubrica->note_segreteria,
|
|
])
|
|
->action(function (array $data): void {
|
|
$this->rubrica->tipo_utenza_call = ! empty($data['tipo_utenza_call']) ? (string) $data['tipo_utenza_call'] : null;
|
|
$this->rubrica->riferimento_stabile = ! empty($data['riferimento_stabile']) ? (string) $data['riferimento_stabile'] : null;
|
|
$this->rubrica->riferimento_unita = ! empty($data['riferimento_unita']) ? (string) $data['riferimento_unita'] : null;
|
|
$this->rubrica->note_segreteria = ! empty($data['note_segreteria']) ? (string) $data['note_segreteria'] : null;
|
|
$this->rubrica->data_ultima_modifica = now()->toDateString();
|
|
$this->rubrica->modificato_da = Auth::id();
|
|
$this->rubrica->save();
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Riferimenti chiamata aggiornati')
|
|
->success()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('ruoli_legami')
|
|
->label('Ruoli / legami')
|
|
->icon('heroicon-o-link')
|
|
->modalWidth('7xl')
|
|
->form([
|
|
Repeater::make('ruoli')
|
|
->label('Ruoli e legami')
|
|
->defaultItems(0)
|
|
->reorderable(true)
|
|
->schema([
|
|
Hidden::make('id'),
|
|
|
|
Select::make('ruolo_standard')
|
|
->label('Ruolo')
|
|
->options(self::getRuoliLabels())
|
|
->required()
|
|
->native(false)
|
|
->default('altro')
|
|
->columnSpan(2),
|
|
|
|
TextInput::make('ruolo_custom')
|
|
->label('Ruolo custom')
|
|
->maxLength(255)
|
|
->columnSpan(2),
|
|
|
|
Select::make('stabile_id')
|
|
->label('Stabile')
|
|
->native(false)
|
|
->searchable()
|
|
->options(fn() => $this->getStabiliOptions())
|
|
->reactive()
|
|
->afterStateUpdated(fn($set) => $set('unita_immobiliare_id', null))
|
|
->columnSpan(3),
|
|
|
|
Select::make('unita_immobiliare_id')
|
|
->label('Unità')
|
|
->native(false)
|
|
->searchable()
|
|
->options(fn($get) => $this->getUnitaOptions($get('stabile_id')))
|
|
->columnSpan(3),
|
|
|
|
DatePicker::make('data_inizio')
|
|
->label('Data inizio')
|
|
->columnSpan(2),
|
|
|
|
DatePicker::make('data_fine')
|
|
->label('Data fine')
|
|
->columnSpan(2),
|
|
|
|
Toggle::make('is_preferito')
|
|
->label('Preferito')
|
|
->default(false)
|
|
->columnSpan(2),
|
|
|
|
Toggle::make('is_attivo')
|
|
->label('Attivo')
|
|
->default(true)
|
|
->columnSpan(2),
|
|
])
|
|
->columns(12),
|
|
])
|
|
->fillForm(function (): array {
|
|
$ruoli = RubricaRuolo::query()
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->orderByDesc('is_attivo')
|
|
->orderByDesc('is_preferito')
|
|
->orderByDesc('data_inizio')
|
|
->orderByDesc('id')
|
|
->limit(200)
|
|
->get();
|
|
|
|
return [
|
|
'ruoli' => $ruoli->map(fn(RubricaRuolo $r) => [
|
|
'id' => (int) $r->id,
|
|
'ruolo_standard' => (string) ($r->ruolo_standard ?? 'altro'),
|
|
'ruolo_custom' => (string) ($r->ruolo_custom ?? ''),
|
|
'stabile_id' => $r->stabile_id ? (int) $r->stabile_id : null,
|
|
'unita_immobiliare_id' => $r->unita_immobiliare_id ? (int) $r->unita_immobiliare_id : null,
|
|
'data_inizio' => $r->data_inizio?->format('Y-m-d'),
|
|
'data_fine' => $r->data_fine?->format('Y-m-d'),
|
|
'is_preferito' => (bool) ($r->is_preferito ?? false),
|
|
'is_attivo' => (bool) ($r->is_attivo ?? true),
|
|
])->all(),
|
|
];
|
|
})
|
|
->action(function (array $data): void {
|
|
/** @var array<int, array<string, mixed>> $rows */
|
|
$rows = $data['ruoli'] ?? [];
|
|
$rows = is_array($rows) ? array_values($rows) : [];
|
|
|
|
DB::transaction(function () use ($rows): void {
|
|
$keepIds = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$id = (int) ($row['id'] ?? 0);
|
|
$ruoloStandard = (string) ($row['ruolo_standard'] ?? 'altro');
|
|
|
|
$attrs = [
|
|
'rubrica_id' => (int) $this->rubrica->id,
|
|
'ruolo_standard' => $ruoloStandard ?: 'altro',
|
|
'ruolo_custom' => ! empty($row['ruolo_custom']) ? (string) $row['ruolo_custom'] : null,
|
|
'stabile_id' => ! empty($row['stabile_id']) ? (int) $row['stabile_id'] : null,
|
|
'unita_immobiliare_id' => ! empty($row['unita_immobiliare_id']) ? (int) $row['unita_immobiliare_id'] : null,
|
|
'data_inizio' => $row['data_inizio'] ?? null,
|
|
'data_fine' => $row['data_fine'] ?? null,
|
|
'is_preferito' => (bool) ($row['is_preferito'] ?? false),
|
|
'is_attivo' => (bool) ($row['is_attivo'] ?? true),
|
|
];
|
|
|
|
if ($id > 0) {
|
|
$existing = RubricaRuolo::query()
|
|
->where('id', $id)
|
|
->where('rubrica_id', (int) $this->rubrica->id)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$existing->fill($attrs);
|
|
$existing->save();
|
|
$keepIds[] = (int) $existing->id;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$created = RubricaRuolo::query()->create($attrs);
|
|
$keepIds[] = (int) $created->id;
|
|
}
|
|
|
|
$deleteQuery = RubricaRuolo::query()->where('rubrica_id', (int) $this->rubrica->id);
|
|
if (! empty($keepIds)) {
|
|
$deleteQuery->whereNotIn('id', $keepIds);
|
|
}
|
|
$deleteQuery->delete();
|
|
});
|
|
|
|
$this->mount((int) $this->rubrica->id);
|
|
|
|
Notification::make()
|
|
->title('Ruoli / legami aggiornati')
|
|
->success()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('torna')
|
|
->label('Torna')
|
|
->icon('heroicon-o-arrow-left')
|
|
->url(fn() => AnagraficaUnica::getUrl(panel: 'admin-filament')),
|
|
];
|
|
}
|
|
|
|
private function fillInlineForm(): void
|
|
{
|
|
$legacyIdentity = $this->extractLegacyIdentityFromNote($this->rubrica->note ?? null);
|
|
$resolvedAddress = $this->parseAddressComponents($this->rubrica->indirizzo_completo ?? null);
|
|
|
|
$this->inlineForm = [
|
|
'nome' => (string) ($this->rubrica->nome ?? ''),
|
|
'cognome' => (string) ($this->rubrica->cognome ?? ''),
|
|
'ragione_sociale' => (string) ($this->rubrica->ragione_sociale ?? ''),
|
|
'tipo_contatto' => (string) ($this->rubrica->tipo_contatto ?? ''),
|
|
'categoria' => (string) ($this->rubrica->categoria ?? ''),
|
|
'codice_fiscale' => (string) ($this->rubrica->codice_fiscale ?? ''),
|
|
'partita_iva' => (string) ($this->rubrica->partita_iva ?? ''),
|
|
'sesso' => (string) ($this->rubrica->sesso ?? $legacyIdentity['sesso'] ?? ''),
|
|
'data_nascita' => (string) (($this->rubrica->data_nascita?->format('Y-m-d')) ?? $legacyIdentity['data_nascita'] ?? ''),
|
|
'luogo_nascita' => (string) ($this->rubrica->luogo_nascita ?? $legacyIdentity['luogo_nascita'] ?? ''),
|
|
'provincia_nascita' => (string) ($this->rubrica->provincia_nascita ?? $legacyIdentity['provincia_nascita'] ?? ''),
|
|
'email' => (string) ($this->rubrica->email ?? ''),
|
|
'pec' => (string) ($this->rubrica->pec ?? ''),
|
|
'telefono_ufficio' => (string) ($this->rubrica->telefono_ufficio ?? ''),
|
|
'telefono_cellulare' => (string) ($this->rubrica->telefono_cellulare ?? ''),
|
|
'telefono_casa' => (string) ($this->rubrica->telefono_casa ?? ''),
|
|
'indirizzo' => (string) ($this->rubrica->indirizzo ?? $resolvedAddress['indirizzo'] ?? ''),
|
|
'civico' => (string) ($this->rubrica->civico ?? $resolvedAddress['civico'] ?? ''),
|
|
'cap' => (string) ($this->rubrica->cap ?? $resolvedAddress['cap'] ?? ''),
|
|
'citta' => (string) ($this->rubrica->citta ?? $resolvedAddress['citta'] ?? ''),
|
|
'provincia' => (string) ($this->rubrica->provincia ?? $resolvedAddress['provincia'] ?? ''),
|
|
'nazione' => (string) ($this->rubrica->nazione ?? 'Italia'),
|
|
'indirizzo_completo' => (string) ($this->rubrica->indirizzo_completo ?? ''),
|
|
'tipo_utenza_call' => (string) ($this->rubrica->tipo_utenza_call ?? ''),
|
|
'riferimento_stabile' => (string) ($this->rubrica->riferimento_stabile ?? ''),
|
|
'riferimento_unita' => (string) ($this->rubrica->riferimento_unita ?? ''),
|
|
'note' => (string) ($legacyIdentity['note_clean'] ?? $this->rubrica->note ?? ''),
|
|
'note_segreteria' => (string) ($this->rubrica->note_segreteria ?? ''),
|
|
];
|
|
}
|
|
|
|
private function cleanNullable(mixed $value): mixed
|
|
{
|
|
if (! is_string($value)) {
|
|
return $value;
|
|
}
|
|
|
|
$trim = trim($value);
|
|
|
|
return $trim === '' ? null : $trim;
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function extractLegacyIdentityFromNote(mixed $note): array
|
|
{
|
|
$raw = trim((string) ($note ?? ''));
|
|
if ($raw === '') {
|
|
return [
|
|
'note_clean' => null,
|
|
'titolo_id' => null,
|
|
'sesso' => null,
|
|
'data_nascita' => null,
|
|
'luogo_nascita' => null,
|
|
'provincia_nascita' => null,
|
|
];
|
|
}
|
|
|
|
$parsed = [
|
|
'titolo_id' => null,
|
|
'sesso' => null,
|
|
'data_nascita' => null,
|
|
'luogo_nascita' => null,
|
|
'provincia_nascita' => null,
|
|
];
|
|
|
|
$cleanLines = [];
|
|
foreach (preg_split('/\r\n|\r|\n/', $raw) ?: [] as $line) {
|
|
$trimmed = trim($line);
|
|
if (! str_starts_with($trimmed, 'Dati anagrafici legacy:')) {
|
|
$cleanLines[] = $line;
|
|
continue;
|
|
}
|
|
|
|
$payload = trim(substr($trimmed, strlen('Dati anagrafici legacy:')));
|
|
$bits = preg_split('/\s*\|\s*/', $payload) ?: [];
|
|
foreach ($bits as $bit) {
|
|
[$key, $value] = array_pad(explode(':', $bit, 2), 2, null);
|
|
$key = mb_strtolower(trim((string) $key));
|
|
$value = trim((string) $value);
|
|
if ($value === '') {
|
|
continue;
|
|
}
|
|
|
|
if ($key === 'titolo') {
|
|
$parsed['titolo_id'] = $this->resolveTitoloId($value);
|
|
continue;
|
|
}
|
|
if ($key === 'sesso') {
|
|
$parsed['sesso'] = $this->normalizeSessoValue($value);
|
|
continue;
|
|
}
|
|
if ($key === 'data nascita') {
|
|
$parsed['data_nascita'] = $this->normalizeLegacyDateValue($value);
|
|
continue;
|
|
}
|
|
if ($key === 'luogo nascita') {
|
|
$parsed['luogo_nascita'] = $value;
|
|
continue;
|
|
}
|
|
if ($key === 'provincia nascita') {
|
|
$parsed['provincia_nascita'] = $this->normalizeProvince($value);
|
|
}
|
|
}
|
|
}
|
|
|
|
$cleanLines = array_values(array_filter($cleanLines, fn($line) => trim((string) $line) !== ''));
|
|
|
|
return $parsed + [
|
|
'note_clean' => $cleanLines === [] ? null : implode("\n", $cleanLines),
|
|
];
|
|
}
|
|
|
|
/** @return array<string, ?string> */
|
|
private function parseAddressComponents(mixed $value): array
|
|
{
|
|
$full = trim((string) ($value ?? ''));
|
|
if ($full === '') {
|
|
return [
|
|
'indirizzo' => null,
|
|
'civico' => null,
|
|
'cap' => null,
|
|
'citta' => null,
|
|
'provincia' => null,
|
|
];
|
|
}
|
|
|
|
$parts = [
|
|
'indirizzo' => $full,
|
|
'civico' => null,
|
|
'cap' => null,
|
|
'citta' => null,
|
|
'provincia' => null,
|
|
];
|
|
|
|
if (preg_match('/\((?<provincia>[A-Za-z]{2})\)\s*$/', $full, $provinceMatch) === 1) {
|
|
$parts['provincia'] = strtoupper($provinceMatch['provincia']);
|
|
$full = trim(substr($full, 0, -strlen($provinceMatch[0])));
|
|
}
|
|
|
|
if (preg_match('/\b(?<cap>\d{5})\b/', $full, $capMatch, PREG_OFFSET_CAPTURE) === 1) {
|
|
$parts['cap'] = $capMatch['cap'][0];
|
|
$capPos = $capMatch['cap'][1];
|
|
$beforeCap = trim(substr($full, 0, $capPos));
|
|
$afterCap = trim(substr($full, $capPos + 5));
|
|
if ($afterCap !== '') {
|
|
$parts['citta'] = $afterCap;
|
|
}
|
|
$full = $beforeCap;
|
|
}
|
|
|
|
if (preg_match('/^(?<indirizzo>.*?)(?:,\s*|\s+)(?<civico>\d+[A-Za-z\/-]*)$/', $full, $streetMatch) === 1) {
|
|
$parts['indirizzo'] = trim($streetMatch['indirizzo']) ?: null;
|
|
$parts['civico'] = trim($streetMatch['civico']) ?: null;
|
|
} else {
|
|
$parts['indirizzo'] = $full;
|
|
}
|
|
|
|
return $parts;
|
|
}
|
|
|
|
private function normalizeLegacyDateValue(mixed $value): ?string
|
|
{
|
|
if ($value instanceof \DateTimeInterface) {
|
|
return $value->format('Y-m-d');
|
|
}
|
|
|
|
$raw = trim((string) ($value ?? ''));
|
|
if ($raw === '') {
|
|
return null;
|
|
}
|
|
|
|
foreach (['Y-m-d', 'd/m/Y', 'd-m-Y', 'd.m.Y', 'd/m/y', 'd-m-y', 'd.m.y', 'm/d/Y', 'm/d/y'] as $format) {
|
|
$dt = \DateTime::createFromFormat($format, $raw);
|
|
if ($dt instanceof \DateTime) {
|
|
return $this->normalizeCenturyDrift($dt)->format('Y-m-d');
|
|
}
|
|
}
|
|
|
|
$ts = strtotime($raw);
|
|
if ($ts === false) {
|
|
return null;
|
|
}
|
|
|
|
return $this->normalizeCenturyDrift((new \DateTime())->setTimestamp($ts))->format('Y-m-d');
|
|
}
|
|
|
|
private function normalizeCenturyDrift(\DateTime $date): \DateTime
|
|
{
|
|
$thresholdYear = (int) date('Y') + 1;
|
|
if ((int) $date->format('Y') > $thresholdYear) {
|
|
$date->modify('-100 years');
|
|
}
|
|
|
|
return $date;
|
|
}
|
|
|
|
private function normalizeSessoValue(mixed $value): ?string
|
|
{
|
|
$raw = strtoupper(trim((string) ($value ?? '')));
|
|
if ($raw === '') {
|
|
return null;
|
|
}
|
|
if ($raw === 'M' || str_starts_with($raw, 'MAS')) {
|
|
return 'M';
|
|
}
|
|
if ($raw === 'F' || str_starts_with($raw, 'FEM')) {
|
|
return 'F';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function normalizeProvince(mixed $value): ?string
|
|
{
|
|
$raw = strtoupper(trim((string) ($value ?? '')));
|
|
if ($raw === '') {
|
|
return null;
|
|
}
|
|
|
|
return substr($raw, 0, 2);
|
|
}
|
|
|
|
private function resolveTitoloId(?string $raw): ?int
|
|
{
|
|
$raw = trim((string) ($raw ?? ''));
|
|
if ($raw === '' || ! Schema::hasTable('titoli')) {
|
|
return null;
|
|
}
|
|
|
|
$key = mb_strtolower($raw);
|
|
$title = Titolo::query()
|
|
->whereRaw('LOWER(sigla) = ?', [$key])
|
|
->orWhereRaw('LOWER(nome) = ?', [$key])
|
|
->first();
|
|
|
|
return $title?->id ? (int) $title->id : null;
|
|
}
|
|
|
|
private function loadEmailMultiple(): void
|
|
{
|
|
$persona = $this->resolvePersonaForRubrica(false);
|
|
if (! $persona instanceof Persona) {
|
|
$this->emailMultiple = [];
|
|
return;
|
|
}
|
|
|
|
$this->emailMultiple = $persona->emailMultiple()
|
|
->orderByDesc('attiva')
|
|
->orderBy('tipo_email')
|
|
->orderBy('email')
|
|
->get()
|
|
->map(fn(PersonaEmailMultipla $row): array=> [
|
|
'id' => (int) $row->id,
|
|
'email' => (string) $row->email,
|
|
'tipo_email' => (string) ($row->tipo_email ?? 'secondaria'),
|
|
'attiva' => (bool) $row->attiva,
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/** @return array<int, array<string, mixed>> */
|
|
private function getAdditionalEmailsFormRows(): array
|
|
{
|
|
return $this->emailMultiple;
|
|
}
|
|
|
|
/** @param array<int, array<string, mixed>> $rows */
|
|
private function saveAdditionalEmails(array $rows): void
|
|
{
|
|
$persona = $this->resolvePersonaForRubrica(true);
|
|
if (! $persona instanceof Persona) {
|
|
throw new \RuntimeException('Impossibile associare la rubrica a una persona.');
|
|
}
|
|
|
|
$this->syncPersonaFromRubrica($persona);
|
|
|
|
DB::transaction(function () use ($rows, $persona): void {
|
|
$keepIds = [];
|
|
|
|
foreach (array_values($rows) as $row) {
|
|
$email = mb_strtolower(trim((string) ($row['email'] ?? '')));
|
|
if ($email === '') {
|
|
continue;
|
|
}
|
|
|
|
$attrs = [
|
|
'email' => $email,
|
|
'tipo_email' => trim((string) ($row['tipo_email'] ?? 'secondaria')) ?: 'secondaria',
|
|
'attiva' => (bool) ($row['attiva'] ?? true),
|
|
];
|
|
|
|
$id = (int) ($row['id'] ?? 0);
|
|
if ($id > 0) {
|
|
$existing = PersonaEmailMultipla::query()
|
|
->where('persona_id', (int) $persona->id)
|
|
->where('id', $id)
|
|
->first();
|
|
|
|
if ($existing instanceof PersonaEmailMultipla) {
|
|
$existing->fill($attrs);
|
|
$existing->save();
|
|
$keepIds[] = (int) $existing->id;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$created = PersonaEmailMultipla::query()->create([
|
|
'persona_id' => (int) $persona->id,
|
|
...$attrs,
|
|
]);
|
|
$keepIds[] = (int) $created->id;
|
|
}
|
|
|
|
$deleteQuery = PersonaEmailMultipla::query()->where('persona_id', (int) $persona->id);
|
|
if ($keepIds !== []) {
|
|
$deleteQuery->whereNotIn('id', $keepIds);
|
|
}
|
|
$deleteQuery->delete();
|
|
});
|
|
|
|
$this->loadEmailMultiple();
|
|
}
|
|
|
|
private function getStudioCollaboratoreEmail(): ?string
|
|
{
|
|
$primary = mb_strtolower(trim((string) ($this->rubrica->email ?? '')));
|
|
if ($primary !== '') {
|
|
return $primary;
|
|
}
|
|
|
|
foreach ($this->emailMultiple as $row) {
|
|
$email = mb_strtolower(trim((string) ($row['email'] ?? '')));
|
|
if ($email !== '' && (bool) ($row['attiva'] ?? true)) {
|
|
return $email;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function canRequestClickToCall(): bool
|
|
{
|
|
$user = Auth::user();
|
|
|
|
return $user instanceof User
|
|
&& (bool) ($user->pbx_click_to_call_enabled ?? false)
|
|
&& trim((string) ($user->pbx_extension ?? '')) !== '';
|
|
}
|
|
|
|
public function richiediClickToCallRubrica(string $field): void
|
|
{
|
|
if (! in_array($field, ['telefono_ufficio', 'telefono_cellulare', 'telefono_casa'], true)) {
|
|
Notification::make()->title('Campo telefono non supportato')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return;
|
|
}
|
|
|
|
$extension = trim((string) ($user->pbx_extension ?? ''));
|
|
if (! (bool) ($user->pbx_click_to_call_enabled ?? false) || $extension === '') {
|
|
Notification::make()->title('Click-to-call non abilitato per il tuo utente')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$phone = $this->normalizePhoneDigits(data_get($this->rubrica, $field));
|
|
if ($phone === '') {
|
|
Notification::make()->title('Numero non valido')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
PbxClickToCallRequest::query()->create([
|
|
'requested_by_user_id' => (int) $user->id,
|
|
'assigned_user_id' => (int) $user->id,
|
|
'stabile_id' => $this->resolveClickToCallStabileId($user),
|
|
'source_extension' => $extension,
|
|
'target_number' => $phone,
|
|
'status' => 'pending',
|
|
'note' => 'Richiesta da scheda rubrica',
|
|
'requested_at' => now(),
|
|
'metadata' => [
|
|
'requested_from' => 'rubrica_universale',
|
|
'rubrica_id' => (int) $this->rubrica->id,
|
|
'field' => $field,
|
|
],
|
|
]);
|
|
|
|
Notification::make()
|
|
->title('Richiesta click-to-call registrata')
|
|
->body('Interno ' . $extension . ' -> ' . $phone)
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
private function resolvePersonaForRubrica(bool $createIfMissing): ?Persona
|
|
{
|
|
if (! Schema::hasTable('persone')) {
|
|
return null;
|
|
}
|
|
|
|
$cf = trim((string) ($this->rubrica->codice_fiscale ?? ''));
|
|
if ($cf !== '') {
|
|
$found = Persona::query()->where('codice_fiscale', $cf)->first();
|
|
if ($found instanceof Persona) {
|
|
return $found;
|
|
}
|
|
}
|
|
|
|
$email = mb_strtolower(trim((string) ($this->rubrica->email ?? '')));
|
|
if ($email !== '') {
|
|
$found = Persona::query()->whereRaw('LOWER(email_principale) = ?', [$email])->first();
|
|
if ($found instanceof Persona) {
|
|
return $found;
|
|
}
|
|
|
|
if (Schema::hasTable('persone_email_multiple')) {
|
|
$match = Persona::query()
|
|
->whereHas('emailMultiple', fn(EloquentBuilderContract $query) => $query->where('email', $email))
|
|
->first();
|
|
if ($match instanceof Persona) {
|
|
return $match;
|
|
}
|
|
}
|
|
}
|
|
|
|
$phone = trim((string) ($this->rubrica->telefono_cellulare ?: $this->rubrica->telefono_ufficio ?: ''));
|
|
if ($phone !== '') {
|
|
$found = Persona::query()->where('telefono_principale', $phone)->first();
|
|
if ($found instanceof Persona) {
|
|
return $found;
|
|
}
|
|
}
|
|
|
|
$nome = trim((string) ($this->rubrica->nome ?? ''));
|
|
$cognome = trim((string) ($this->rubrica->cognome ?? ''));
|
|
if ($nome !== '' && $cognome !== '') {
|
|
$found = Persona::query()->where('nome', $nome)->where('cognome', $cognome)->first();
|
|
if ($found instanceof Persona) {
|
|
return $found;
|
|
}
|
|
}
|
|
|
|
if (! $createIfMissing) {
|
|
return null;
|
|
}
|
|
|
|
if ($nome === '' && $cognome === '' && trim((string) ($this->rubrica->ragione_sociale ?? '')) === '') {
|
|
return null;
|
|
}
|
|
|
|
$persona = Persona::query()->create([
|
|
'tipologia' => $this->rubrica->tipo_contatto === 'persona_giuridica' ? 'giuridica' : 'fisica',
|
|
'nome' => $nome !== '' ? $nome : (trim((string) ($this->rubrica->ragione_sociale ?? '')) !== '' ? 'Impresa' : 'N/D'),
|
|
'cognome' => $cognome !== '' ? $cognome : (trim((string) ($this->rubrica->ragione_sociale ?? '')) !== '' ? trim((string) $this->rubrica->ragione_sociale) : 'N/D'),
|
|
'ragione_sociale' => $this->rubrica->ragione_sociale,
|
|
'codice_fiscale' => $cf !== '' ? $cf : null,
|
|
'partita_iva' => $this->rubrica->partita_iva,
|
|
'data_nascita' => $this->rubrica->data_nascita,
|
|
'residenza_via' => $this->rubrica->indirizzo_completo,
|
|
'telefono_principale' => $phone !== '' ? $phone : null,
|
|
'telefono_secondario' => $this->rubrica->telefono_casa,
|
|
'email_principale' => $email !== '' ? $email : null,
|
|
'email_pec' => $this->rubrica->pec,
|
|
'note' => $this->rubrica->note,
|
|
'attivo' => $this->rubrica->stato !== 'inattivo',
|
|
]);
|
|
|
|
return $persona;
|
|
}
|
|
|
|
private function syncPersonaFromRubrica(Persona $persona): void
|
|
{
|
|
$updates = [];
|
|
|
|
if (! $persona->email_principale && $this->rubrica->email) {
|
|
$updates['email_principale'] = mb_strtolower(trim((string) $this->rubrica->email));
|
|
}
|
|
if (! $persona->email_pec && $this->rubrica->pec) {
|
|
$updates['email_pec'] = $this->rubrica->pec;
|
|
}
|
|
if (! $persona->telefono_principale && ($this->rubrica->telefono_cellulare || $this->rubrica->telefono_ufficio)) {
|
|
$updates['telefono_principale'] = $this->rubrica->telefono_cellulare ?: $this->rubrica->telefono_ufficio;
|
|
}
|
|
if (! $persona->residenza_via && $this->rubrica->indirizzo_completo) {
|
|
$updates['residenza_via'] = $this->rubrica->indirizzo_completo;
|
|
}
|
|
if (! $persona->data_nascita && $this->rubrica->data_nascita) {
|
|
$updates['data_nascita'] = $this->rubrica->data_nascita;
|
|
}
|
|
if (! $persona->codice_fiscale && $this->rubrica->codice_fiscale) {
|
|
$updates['codice_fiscale'] = $this->rubrica->codice_fiscale;
|
|
}
|
|
if (! $persona->partita_iva && $this->rubrica->partita_iva) {
|
|
$updates['partita_iva'] = $this->rubrica->partita_iva;
|
|
}
|
|
|
|
if ($updates !== []) {
|
|
$persona->fill($updates);
|
|
$persona->save();
|
|
}
|
|
}
|
|
|
|
public function getUrlUnita(?int $unitaId): ?string
|
|
{
|
|
if (! $unitaId) {
|
|
return null;
|
|
}
|
|
|
|
if (! class_exists(\App\Filament\Pages\UnitaImmobiliarePage::class)) {
|
|
return null;
|
|
}
|
|
|
|
return \App\Filament\Pages\UnitaImmobiliarePage::getUrl(panel: 'admin-filament')
|
|
. '?unita_id=' . (int) $unitaId
|
|
. '&back=' . urlencode((string) request()->getRequestUri());
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
public static function getRuoliLabels(): array
|
|
{
|
|
return [
|
|
'super_admin' => 'Super admin',
|
|
'amministratore' => 'Amministratore',
|
|
'condomino' => 'Condomino',
|
|
'inquilino' => 'Inquilino',
|
|
'fornitore' => 'Fornitore',
|
|
'manutentore' => 'Manutentore',
|
|
'banca' => 'Banca',
|
|
'assicurazione' => 'Assicurazione',
|
|
'collaboratore' => 'Collaboratore',
|
|
'tester' => 'Tester',
|
|
'ospite' => 'Ospite',
|
|
'altro' => 'Altro',
|
|
];
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
public function getStabiliOptions(): array
|
|
{
|
|
$query = Stabile::query();
|
|
|
|
$user = Auth::user();
|
|
$isSuperAdmin = $user instanceof User && method_exists($user, 'hasRole')
|
|
? (bool) $user->hasRole('super-admin')
|
|
: false;
|
|
|
|
if (! $isSuperAdmin && $this->adminId > 0) {
|
|
$query->where('amministratore_id', $this->adminId);
|
|
}
|
|
|
|
return $query
|
|
->orderBy('codice_stabile')
|
|
->limit(200)
|
|
->get(['id', 'codice_stabile', 'denominazione'])
|
|
->mapWithKeys(fn(Stabile $s) => [
|
|
(int) $s->id => trim((string) ($s->codice_operatore ?? $s->codice_stabile ?? '') . ' - ' . (string) ($s->denominazione ?? 'Stabile')),
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
protected function getTitoliOptions(): array
|
|
{
|
|
if (! Schema::hasTable('titoli')) {
|
|
return [];
|
|
}
|
|
|
|
return Titolo::query()
|
|
->orderBy('ordine')
|
|
->orderBy('nome')
|
|
->get(['id', 'nome', 'sigla'])
|
|
->mapWithKeys(fn(Titolo $t) => [
|
|
(int) $t->id => trim((string) ($t->sigla ?: $t->nome)),
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
public function getUnitaOptions($stabileId): array
|
|
{
|
|
$stabileId = (int) ($stabileId ?? 0);
|
|
if ($stabileId <= 0) {
|
|
return [];
|
|
}
|
|
|
|
$query = UnitaImmobiliare::query()->where('stabile_id', $stabileId);
|
|
|
|
return $query
|
|
->orderBy('palazzina_id')
|
|
->orderBy('scala')
|
|
->orderBy('piano')
|
|
->orderBy('interno')
|
|
->orderBy('id')
|
|
->limit(1000)
|
|
->get(['id', 'codice_unita', 'denominazione', 'scala', 'piano', 'interno'])
|
|
->mapWithKeys(function (UnitaImmobiliare $u) {
|
|
$codice = $u->codice_unita ?: ('ID ' . $u->id);
|
|
$nome = $u->denominazione ?: 'Unità';
|
|
$pos = trim('Scala ' . ($u->scala ?? '-') . ' · Piano ' . ($u->piano ?? '-') . ' · Int. ' . ($u->interno ?? '-'));
|
|
|
|
return [(int) $u->id => trim($codice . ' — ' . $nome . ' (' . $pos . ')')];
|
|
})
|
|
->all();
|
|
}
|
|
|
|
private function hydrateFornitoriWorkspace(): void
|
|
{
|
|
$rubricaId = (int) $this->rubrica->id;
|
|
$cf = trim((string) ($this->rubrica->codice_fiscale ?? ''));
|
|
$piva = trim((string) ($this->rubrica->partita_iva ?? ''));
|
|
$email = mb_strtolower(trim((string) ($this->rubrica->email ?? '')));
|
|
|
|
$query = Fornitore::query();
|
|
if ($this->adminId > 0) {
|
|
$query->where('amministratore_id', $this->adminId);
|
|
}
|
|
|
|
$query->where(function (Builder $q) use ($rubricaId, $cf, $piva, $email): void {
|
|
$q->where('rubrica_id', $rubricaId);
|
|
if ($cf !== '') {
|
|
$q->orWhere('codice_fiscale', $cf);
|
|
}
|
|
if ($piva !== '') {
|
|
$q->orWhere('partita_iva', $piva);
|
|
}
|
|
if ($email !== '') {
|
|
$q->orWhereRaw('LOWER(email) = ?', [$email]);
|
|
}
|
|
});
|
|
|
|
$fornitori = $query
|
|
->orderByRaw("COALESCE(ragione_sociale, '')")
|
|
->orderBy('id')
|
|
->limit(80)
|
|
->get(['id', 'rubrica_id', 'ragione_sociale', 'nome', 'cognome', 'email', 'telefono', 'cellulare', 'codice_fiscale', 'partita_iva']);
|
|
|
|
$this->fornitoriCollegati = $fornitori->map(fn(Fornitore $f) => [
|
|
'id' => (int) $f->id,
|
|
'nome' => (string) ($f->ragione_sociale ?: trim((string) ($f->nome ?? '') . ' ' . (string) ($f->cognome ?? '')) ?: ('Fornitore #' . $f->id)),
|
|
'rubrica_id' => (int) ($f->rubrica_id ?? 0),
|
|
'email' => (string) ($f->email ?? ''),
|
|
'telefono' => (string) ($f->telefono ?: $f->cellulare ?: ''),
|
|
'cf' => (string) ($f->codice_fiscale ?? ''),
|
|
'piva' => (string) ($f->partita_iva ?? ''),
|
|
])->all();
|
|
|
|
$fornitoreIds = $fornitori->pluck('id')->map(fn($v) => (int) $v)->all();
|
|
if ($fornitoreIds === []) {
|
|
$this->dipendentiFornitoreRows = [];
|
|
return;
|
|
}
|
|
|
|
$dipendenti = FornitoreDipendente::query()
|
|
->with(['user.roles', 'fornitore'])
|
|
->whereIn('fornitore_id', $fornitoreIds)
|
|
->orderByDesc('attivo')
|
|
->orderBy('cognome')
|
|
->orderBy('nome')
|
|
->limit(300)
|
|
->get();
|
|
|
|
$this->dipendentiFornitoreRows = $dipendenti->map(function (FornitoreDipendente $d): array {
|
|
$user = $d->user;
|
|
$this->dipendentePbxExtension[(int) $d->id] = (string) ($user?->pbx_extension ?? '');
|
|
|
|
return [
|
|
'id' => (int) $d->id,
|
|
'fornitore_id' => (int) $d->fornitore_id,
|
|
'fornitore_nome' => (string) ($d->fornitore?->ragione_sociale ?: ('Fornitore #' . $d->fornitore_id)),
|
|
'nome' => (string) $d->nome_completo,
|
|
'email' => (string) ($d->email ?? ''),
|
|
'telefono' => (string) ($d->telefono ?? ''),
|
|
'attivo' => (bool) $d->attivo,
|
|
'user_id' => $user ? (int) $user->id : null,
|
|
'ruoli' => $user ? $user->roles->pluck('name')->values()->all() : [],
|
|
'pbx_extension' => (string) ($user?->pbx_extension ?? ''),
|
|
];
|
|
})->all();
|
|
}
|
|
|
|
public function collegaRubricaDipendente(int $fornitoreId): void
|
|
{
|
|
$rubricaId = (int) ($this->dipendenteRubricaSelect[$fornitoreId] ?? 0);
|
|
if ($rubricaId <= 0) {
|
|
Notification::make()->title('Inserisci ID rubrica da collegare')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$fornitore = Fornitore::query()->find($fornitoreId);
|
|
if (! $fornitore || ($this->adminId > 0 && (int) ($fornitore->amministratore_id ?? 0) !== $this->adminId)) {
|
|
Notification::make()->title('Fornitore non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$rubricaDip = RubricaUniversale::query()->find($rubricaId);
|
|
if (! $rubricaDip) {
|
|
Notification::make()->title('Rubrica dipendente non trovata')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$email = mb_strtolower(trim((string) ($rubricaDip->email ?? '')));
|
|
$query = FornitoreDipendente::query()->where('fornitore_id', (int) $fornitore->id);
|
|
if ($email !== '') {
|
|
$query->whereRaw('LOWER(email) = ?', [$email]);
|
|
} else {
|
|
$query->where('nome', (string) ($rubricaDip->nome ?? ''))
|
|
->where('cognome', (string) ($rubricaDip->cognome ?? ''));
|
|
}
|
|
|
|
$dip = $query->first();
|
|
if (! $dip) {
|
|
$dip = new FornitoreDipendente();
|
|
$dip->fornitore_id = (int) $fornitore->id;
|
|
$dip->created_by_user_id = Auth::id();
|
|
}
|
|
|
|
$dip->nome = (string) ($rubricaDip->nome ?: $rubricaDip->ragione_sociale ?: 'Dipendente');
|
|
$dip->cognome = (string) ($rubricaDip->cognome ?? '');
|
|
$dip->email = $email !== '' ? $email : null;
|
|
$dip->telefono = (string) ($rubricaDip->telefono_cellulare ?: $rubricaDip->telefono_ufficio ?: '');
|
|
$dip->attivo = true;
|
|
$dip->updated_by_user_id = Auth::id();
|
|
$dip->save();
|
|
|
|
unset($this->dipendenteRubricaSelect[$fornitoreId]);
|
|
$this->hydrateFornitoriWorkspace();
|
|
|
|
Notification::make()->title('Dipendente collegato da rubrica')->success()->send();
|
|
}
|
|
|
|
public function creaDipendenteManuale(int $fornitoreId): void
|
|
{
|
|
$draft = (array) ($this->dipendenteManualDraft[$fornitoreId] ?? []);
|
|
$nome = trim((string) ($draft['nome'] ?? ''));
|
|
$cognome = trim((string) ($draft['cognome'] ?? ''));
|
|
$email = mb_strtolower(trim((string) ($draft['email'] ?? '')));
|
|
$telefono = trim((string) ($draft['telefono'] ?? ''));
|
|
|
|
if ($nome === '') {
|
|
Notification::make()->title('Nome dipendente obbligatorio')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$fornitore = Fornitore::query()->find($fornitoreId);
|
|
if (! $fornitore || ($this->adminId > 0 && (int) ($fornitore->amministratore_id ?? 0) !== $this->adminId)) {
|
|
Notification::make()->title('Fornitore non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$dip = new FornitoreDipendente();
|
|
$dip->fornitore_id = (int) $fornitore->id;
|
|
$dip->nome = $nome;
|
|
$dip->cognome = $cognome !== '' ? $cognome : null;
|
|
$dip->email = $email !== '' ? $email : null;
|
|
$dip->telefono = $telefono !== '' ? $telefono : null;
|
|
$dip->attivo = true;
|
|
$dip->created_by_user_id = Auth::id();
|
|
$dip->updated_by_user_id = Auth::id();
|
|
$dip->save();
|
|
|
|
unset($this->dipendenteManualDraft[$fornitoreId]);
|
|
$this->hydrateFornitoriWorkspace();
|
|
|
|
Notification::make()->title('Nuovo dipendente creato')->success()->send();
|
|
}
|
|
|
|
public function abilitaAccessoDipendente(int $dipendenteId): void
|
|
{
|
|
$dip = FornitoreDipendente::query()->with('fornitore')->find($dipendenteId);
|
|
if (! $dip || ($this->adminId > 0 && (int) ($dip->fornitore?->amministratore_id ?? 0) !== $this->adminId)) {
|
|
Notification::make()->title('Dipendente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$email = mb_strtolower(trim((string) ($dip->email ?? '')));
|
|
if ($email === '') {
|
|
Notification::make()->title('Email dipendente mancante')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$user = User::query()->whereRaw('LOWER(email) = ?', [$email])->first();
|
|
if (! $user) {
|
|
$tmpPwd = Str::random(12);
|
|
$user = User::query()->create([
|
|
'name' => trim((string) ($dip->nome_completo ?: 'Utente fornitore')),
|
|
'email' => $email,
|
|
'password' => Hash::make($tmpPwd),
|
|
'email_verified_at' => now(),
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
|
|
$user->assignRole('fornitore');
|
|
$dip->user_id = (int) $user->id;
|
|
$dip->updated_by_user_id = Auth::id();
|
|
$dip->save();
|
|
|
|
$this->hydrateFornitoriWorkspace();
|
|
Notification::make()->title('Accesso dipendente abilitato')->success()->send();
|
|
}
|
|
|
|
public function salvaInternoDipendente(int $dipendenteId): void
|
|
{
|
|
$dip = FornitoreDipendente::query()->with('user', 'fornitore')->find($dipendenteId);
|
|
if (! $dip || ! $dip->user || ($this->adminId > 0 && (int) ($dip->fornitore?->amministratore_id ?? 0) !== $this->adminId)) {
|
|
Notification::make()->title('Dipendente/utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
if (! Schema::hasColumn('users', 'pbx_extension')) {
|
|
Notification::make()->title('Campo interno PBX non disponibile')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$ext = trim((string) ($this->dipendentePbxExtension[$dipendenteId] ?? ''));
|
|
$dip->user->pbx_extension = $ext !== '' ? $ext : null;
|
|
$dip->user->save();
|
|
|
|
$this->hydrateFornitoriWorkspace();
|
|
Notification::make()->title('Interno PBX aggiornato')->success()->send();
|
|
}
|
|
|
|
public function abilitaAccessoCollaboratoreStudio(): void
|
|
{
|
|
$email = $this->getStudioCollaboratoreEmail();
|
|
if ($email === null) {
|
|
Notification::make()
|
|
->title('Email rubrica mancante')
|
|
->warning()
|
|
->body('Per creare o collegare il collaboratore di studio serve un indirizzo email principale o aggiuntivo attivo nella rubrica.')
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$name = trim((string) ($this->rubrica->nome_completo ?: $this->rubrica->ragione_sociale ?: 'Collaboratore Studio'));
|
|
|
|
$user = User::query()->whereRaw('LOWER(email) = ?', [$email])->first();
|
|
if (! $user) {
|
|
$tmpPwd = Str::random(12);
|
|
$user = User::query()->create([
|
|
'name' => $name,
|
|
'email' => $email,
|
|
'password' => Hash::make($tmpPwd),
|
|
'email_verified_at' => now(),
|
|
'is_active' => true,
|
|
'registration_status' => 'approved',
|
|
]);
|
|
$this->studioCollaboratoreGeneratedPassword = $tmpPwd;
|
|
}
|
|
|
|
$user->assignRole('collaboratore');
|
|
|
|
$extension = trim($this->studioCollaboratorePbxExtension);
|
|
if (Schema::hasColumn('users', 'pbx_extension')) {
|
|
$user->pbx_extension = $extension !== '' ? $extension : null;
|
|
$user->save();
|
|
}
|
|
|
|
$this->syncStudioCollaboratoreStabili($user);
|
|
$this->hydrateStudioCollaboratoreWorkspace();
|
|
|
|
Notification::make()
|
|
->title('Collaboratore di studio abilitato')
|
|
->success()
|
|
->body('Utente collegato alla rubrica e pronto per la gestione centralino/stabili.')
|
|
->send();
|
|
}
|
|
|
|
public function salvaCollaboratoreStudio(): void
|
|
{
|
|
$userId = (int) ($this->studioCollaboratoreUserId ?? 0);
|
|
if ($userId <= 0) {
|
|
Notification::make()
|
|
->title('Collaboratore non ancora abilitato')
|
|
->warning()
|
|
->body('Crea o collega prima l\'utente collaboratore da questa rubrica.')
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$user = User::query()->find($userId);
|
|
if (! $user) {
|
|
Notification::make()->title('Utente collaboratore non trovato')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
if (Schema::hasColumn('users', 'pbx_extension')) {
|
|
$extension = trim($this->studioCollaboratorePbxExtension);
|
|
$user->pbx_extension = $extension !== '' ? $extension : null;
|
|
$user->save();
|
|
}
|
|
|
|
$this->syncStudioCollaboratoreStabili($user);
|
|
$this->hydrateStudioCollaboratoreWorkspace();
|
|
|
|
Notification::make()
|
|
->title('Collaboratore studio aggiornato')
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
private function hydrateStudioCollaboratoreWorkspace(): void
|
|
{
|
|
$this->studioCollaboratoreUserId = null;
|
|
$this->studioCollaboratoreUserEmail = $this->getStudioCollaboratoreEmail();
|
|
$this->studioCollaboratoreUserName = trim((string) ($this->rubrica->nome_completo ?: $this->rubrica->ragione_sociale ?: '')) ?: null;
|
|
$this->studioCollaboratorePbxExtension = '';
|
|
$this->studioCollaboratoreRoles = [];
|
|
$this->studioCollaboratoreGeneratedPassword = $this->studioCollaboratoreGeneratedPassword ?: null;
|
|
|
|
$defaultStabili = array_values(array_unique(array_filter(array_map(
|
|
fn(array $row): int => (int) ($row['id'] ?? 0),
|
|
$this->stabili
|
|
))));
|
|
|
|
if ($defaultStabili === [] && count($this->adminStabileIds) === 1) {
|
|
$defaultStabili = $this->adminStabileIds;
|
|
}
|
|
|
|
$email = $this->studioCollaboratoreUserEmail;
|
|
if ($email === null) {
|
|
$this->studioCollaboratoreStabileIds = array_map('strval', $defaultStabili);
|
|
return;
|
|
}
|
|
|
|
$user = User::query()->with('roles')->whereRaw('LOWER(email) = ?', [mb_strtolower($email)])->first();
|
|
if (! $user) {
|
|
$this->studioCollaboratoreStabileIds = array_map('strval', $defaultStabili);
|
|
return;
|
|
}
|
|
|
|
$this->studioCollaboratoreUserId = (int) $user->id;
|
|
$this->studioCollaboratoreUserName = (string) $user->name;
|
|
$this->studioCollaboratorePbxExtension = (string) ($user->pbx_extension ?? '');
|
|
$this->studioCollaboratoreRoles = $user->roles->pluck('name')->values()->all();
|
|
|
|
$assignedIds = method_exists($user, 'stabiliAssegnati')
|
|
? $user->stabiliAssegnati()->pluck('stabili.id')->map(fn($v) => (int) $v)->all()
|
|
: [];
|
|
|
|
$assignedForAdmin = ! empty($this->adminStabileIds)
|
|
? array_values(array_intersect($assignedIds, $this->adminStabileIds))
|
|
: $assignedIds;
|
|
|
|
$this->studioCollaboratoreStabileIds = array_map('strval', $assignedForAdmin !== [] ? $assignedForAdmin : $defaultStabili);
|
|
}
|
|
|
|
private function syncStudioCollaboratoreStabili(User $user): void
|
|
{
|
|
if (! Schema::hasTable('collaboratore_stabile') || ! method_exists($user, 'stabiliAssegnati')) {
|
|
return;
|
|
}
|
|
|
|
$requested = [];
|
|
foreach ($this->studioCollaboratoreStabileIds as $value) {
|
|
if (is_numeric($value)) {
|
|
$requested[] = (int) $value;
|
|
}
|
|
}
|
|
|
|
$requested = array_values(array_unique($requested));
|
|
if (! empty($this->adminStabileIds)) {
|
|
$requested = array_values(array_intersect($requested, $this->adminStabileIds));
|
|
}
|
|
|
|
$currentIds = $user->stabiliAssegnati()->pluck('stabili.id')->map(fn($v) => (int) $v)->all();
|
|
$outsideAdminIds = ! empty($this->adminStabileIds)
|
|
? array_values(array_diff($currentIds, $this->adminStabileIds))
|
|
: [];
|
|
|
|
$user->stabiliAssegnati()->sync(array_values(array_unique(array_merge($outsideAdminIds, $requested))));
|
|
}
|
|
|
|
private function normalizePhoneDigits(mixed $value): string
|
|
{
|
|
$digits = preg_replace('/\D+/', '', (string) ($value ?? ''));
|
|
|
|
return is_string($digits) ? $digits : '';
|
|
}
|
|
|
|
private function resolveClickToCallStabileId(User $user): ?int
|
|
{
|
|
$activeStabileId = (int) (StabileContext::resolveActiveStabileId($user) ?? 0);
|
|
if ($activeStabileId > 0) {
|
|
return $activeStabileId;
|
|
}
|
|
|
|
foreach ($this->stabili as $stabile) {
|
|
$candidate = (int) ($stabile['id'] ?? 0);
|
|
if ($candidate > 0) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|