fix: completa contesto ticket per fornitore
This commit is contained in:
parent
0820e06772
commit
8e8f95b7c6
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\FornitoreDipendente;
|
||||
use App\Models\RubricaUniversale;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketIntervento;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
|
@ -140,9 +142,42 @@ protected function authorizeDipendenteIntervento(TicketIntervento $intervento, ?
|
|||
*/
|
||||
protected function buildInterventoRow(TicketIntervento $intervento): array
|
||||
{
|
||||
$descrizione = (string) ($intervento->ticket->descrizione ?? '');
|
||||
$ticket = $intervento->ticket;
|
||||
$descrizione = (string) ($ticket->descrizione ?? '');
|
||||
$caller = $this->extractCallerData($descrizione);
|
||||
|
||||
if (($caller['contatto'] ?? '-') === '-' || trim((string) ($caller['contatto'] ?? '')) === '') {
|
||||
$titleContact = $this->extractCallerNameFromTitle((string) ($ticket->titolo ?? ''));
|
||||
if ($titleContact !== '') {
|
||||
$caller['contatto'] = $titleContact;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ticket?->soggettoRichiedente) {
|
||||
$soggetto = $ticket->soggettoRichiedente;
|
||||
$label = trim((string) ($soggetto->ragione_sociale ?: trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? ''))));
|
||||
|
||||
if ($label !== '') {
|
||||
$caller['contatto'] = $label;
|
||||
}
|
||||
|
||||
$telefono = trim((string) ($soggetto->telefono ?? ''));
|
||||
if ($telefono !== '') {
|
||||
$caller['telefono'] = $telefono;
|
||||
}
|
||||
}
|
||||
|
||||
$rubrica = $this->resolveCallerRubricaFromTicket($ticket, (string) ($caller['contatto'] ?? ''));
|
||||
if ($rubrica instanceof RubricaUniversale) {
|
||||
if (($caller['contatto'] ?? '-') === '-' || trim((string) ($caller['contatto'] ?? '')) === '') {
|
||||
$caller['contatto'] = trim((string) ($rubrica->nome_completo ?: $rubrica->ragione_sociale ?: '-')) ?: '-';
|
||||
}
|
||||
|
||||
if (($caller['telefono'] ?? '') === '') {
|
||||
$caller['telefono'] = trim((string) ($rubrica->telefono_cellulare ?: $rubrica->telefono_ufficio ?: $rubrica->telefono_casa ?: ''));
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'ingresso' => optional($intervento->created_at)->format('d/m/Y H:i') ?: '-',
|
||||
'contatto' => $caller['contatto'],
|
||||
|
|
@ -153,13 +188,15 @@ protected function buildInterventoRow(TicketIntervento $intervento): array
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array{contatto:string,telefono:string,problema:string}
|
||||
* @return array{contatto:string,telefono:string,problema:string,email:string,riferimento:string}
|
||||
*/
|
||||
protected function extractCallerData(string $descrizione): array
|
||||
{
|
||||
$contatto = '-';
|
||||
$telefono = '';
|
||||
$problema = '';
|
||||
$email = '';
|
||||
$riferimento = '';
|
||||
|
||||
$lines = preg_split('/\r\n|\r|\n/', $descrizione) ?: [];
|
||||
foreach ($lines as $line) {
|
||||
|
|
@ -172,15 +209,38 @@ protected function extractCallerData(string $descrizione): array
|
|||
$contatto = trim((string) str_replace('Chiamante selezionato:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Contatto associato:')) {
|
||||
$contatto = trim((string) str_replace('Contatto associato:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Telefono:')) {
|
||||
$telefono = trim((string) str_replace('Telefono:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Telefono richiamabile:')) {
|
||||
$telefono = trim((string) str_replace('Telefono richiamabile:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Email richiedente:')) {
|
||||
$email = trim((string) str_replace('Email richiedente:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Riferimento stabile:')) {
|
||||
$riferimento = trim((string) str_replace('Riferimento stabile:', '', $trim));
|
||||
}
|
||||
|
||||
if (str_starts_with($trim, 'Riferimento unità:')) {
|
||||
$unita = trim((string) str_replace('Riferimento unità:', '', $trim));
|
||||
$riferimento = $riferimento !== '' ? ($riferimento . ' · ' . $unita) : $unita;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'contatto' => $contatto,
|
||||
'telefono' => $telefono,
|
||||
'problema' => $problema,
|
||||
'contatto' => $contatto,
|
||||
'telefono' => $telefono,
|
||||
'problema' => $problema,
|
||||
'email' => $email,
|
||||
'riferimento' => $riferimento,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -196,6 +256,75 @@ protected function extractApparato(string $rapporto): string
|
|||
return '-';
|
||||
}
|
||||
|
||||
protected function extractCallerNameFromTitle(string $title): string
|
||||
{
|
||||
$title = trim($title);
|
||||
if ($title === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (preg_match('/^Segnalazione da\s+(.+)$/iu', $title, $matches)) {
|
||||
return trim((string) ($matches[1] ?? ''));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function resolveCallerRubricaFromTicket(?Ticket $ticket, string $fallbackName = ''): ?RubricaUniversale
|
||||
{
|
||||
if (! $ticket instanceof Ticket) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$adminId = (int) ($ticket->stabile?->amministratore_id ?? 0);
|
||||
$soggetto = $ticket->soggettoRichiedente;
|
||||
$queries = [];
|
||||
|
||||
if ($soggetto && filled($soggetto->codice_fiscale)) {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where('codice_fiscale', strtoupper(trim((string) $soggetto->codice_fiscale)));
|
||||
}
|
||||
|
||||
if ($soggetto && filled($soggetto->email)) {
|
||||
$queries[] = fn() => RubricaUniversale::query()->whereRaw('LOWER(email) = ?', [mb_strtolower(trim((string) $soggetto->email))]);
|
||||
}
|
||||
|
||||
if ($soggetto && filled($soggetto->telefono)) {
|
||||
$phone = preg_replace('/\D+/', '', (string) $soggetto->telefono) ?: '';
|
||||
if ($phone !== '') {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where(function ($builder) use ($phone): void {
|
||||
$builder->whereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_cellulare, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_ufficio, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_casa, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$name = trim($fallbackName);
|
||||
if ($name !== '' && $name !== '-') {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where(function ($builder) use ($name): void {
|
||||
$builder->whereRaw("LOWER(COALESCE(ragione_sociale, '')) = ?", [mb_strtolower($name)])
|
||||
->orWhereRaw("LOWER(TRIM(CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, '')))) = ?", [mb_strtolower($name)]);
|
||||
});
|
||||
}
|
||||
|
||||
foreach ($queries as $factory) {
|
||||
$query = $factory();
|
||||
if ($adminId > 0) {
|
||||
$query->where(function ($builder) use ($adminId): void {
|
||||
$builder->where('amministratore_id', $adminId)
|
||||
->orWhereNull('amministratore_id');
|
||||
});
|
||||
}
|
||||
|
||||
$match = $query->orderByDesc('updated_at')->orderBy('id')->first();
|
||||
if ($match instanceof RubricaUniversale) {
|
||||
return $match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function buildApparatoSummary(string $marca, string $modello, string $seriale): string
|
||||
{
|
||||
$marca = trim($marca);
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ class LavorazioniOperative extends Page
|
|||
|
||||
/** @var array<string, int> */
|
||||
public array $totals = [
|
||||
'tutte' => 0,
|
||||
'tutte' => 0,
|
||||
'ticket_amministratore' => 0,
|
||||
'interne' => 0,
|
||||
'interne' => 0,
|
||||
];
|
||||
|
||||
public static function canAccess(): bool
|
||||
|
|
@ -53,7 +53,7 @@ public static function canAccess(): bool
|
|||
$user = Auth::user();
|
||||
|
||||
return $user instanceof User
|
||||
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'fornitore']);
|
||||
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'fornitore']);
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
|
|
@ -165,7 +165,7 @@ public function getProdottiUrl(): string
|
|||
protected function buildBaseQuery(Fornitore $fornitore, ?FornitoreDipendente $dipendente): Builder
|
||||
{
|
||||
$query = TicketIntervento::query()
|
||||
->with(['ticket.stabile', 'eseguitoDaDipendente'])
|
||||
->with(['ticket.stabile', 'ticket.unitaImmobiliare', 'ticket.soggettoRichiedente', 'eseguitoDaDipendente'])
|
||||
->where('fornitore_id', (int) $fornitore->id)
|
||||
->orderByDesc('created_at');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
<?php
|
||||
namespace App\Filament\Pages\Fornitore;
|
||||
|
||||
use App\Filament\Pages\Contabilita\EstrattoContoSoggetto;
|
||||
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
|
||||
use App\Filament\Pages\Gescon\RubricaUniversaleScheda;
|
||||
use App\Filament\Pages\Gescon\StabileScheda;
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\FornitoreDipendente;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductIdentifier;
|
||||
use App\Models\ProductOffer;
|
||||
use App\Models\RubricaUniversale;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketAttachment;
|
||||
use App\Models\TicketIntervento;
|
||||
|
|
@ -44,7 +48,7 @@ class TicketInterventoScheda extends Page
|
|||
|
||||
public ?FornitoreDipendente $dipendente = null;
|
||||
|
||||
/** @var array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string} */
|
||||
/** @var array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string,rubrica_url:?string,estratto_url:?string,stabile_url:?string,unita_label:string} */
|
||||
public array $caller = [
|
||||
'contatto' => '-',
|
||||
'telefono' => '',
|
||||
|
|
@ -52,6 +56,10 @@ class TicketInterventoScheda extends Page
|
|||
'problema' => '',
|
||||
'sorgente' => '',
|
||||
'riferimento' => '',
|
||||
'rubrica_url' => null,
|
||||
'estratto_url'=> null,
|
||||
'stabile_url' => null,
|
||||
'unita_label' => '',
|
||||
];
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
|
|
@ -382,8 +390,16 @@ public function addSuggestedMaterial(int $productId): void
|
|||
'external_url' => (string) ($offer?->referral_url ?: $offer?->external_url ?: $this->buildAmazonSearchUrl((string) $product->name)),
|
||||
];
|
||||
|
||||
$description = (string) ($this->materialiUtilizzati[array_key_last($this->materialiUtilizzati)]['descrizione'] ?? 'Materiale');
|
||||
|
||||
$this->materialSearch = '';
|
||||
$this->materialSuggestions = [];
|
||||
|
||||
Notification::make()
|
||||
->title('Materiale aggiunto al rapportino')
|
||||
->body($description)
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
public function addManualMaterial(): void
|
||||
|
|
@ -408,6 +424,12 @@ public function addManualMaterial(): void
|
|||
|
||||
$this->materialSearch = '';
|
||||
$this->materialSuggestions = [];
|
||||
|
||||
Notification::make()
|
||||
->title('Materiale manuale aggiunto')
|
||||
->body($term)
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
public function removeMateriale(int $index): void
|
||||
|
|
@ -1065,7 +1087,7 @@ protected function resolveGoogleWorkspaceStatus(): ?array
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string}
|
||||
* @return array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string,rubrica_url:?string,estratto_url:?string,stabile_url:?string,unita_label:string}
|
||||
*/
|
||||
protected function resolveCallerData(?Ticket $ticket): array
|
||||
{
|
||||
|
|
@ -1074,12 +1096,23 @@ protected function resolveCallerData(?Ticket $ticket): array
|
|||
$caller = [
|
||||
'contatto' => $parsed['contatto'],
|
||||
'telefono' => $parsed['telefono'],
|
||||
'email' => '',
|
||||
'email' => $parsed['email'] ?? '',
|
||||
'problema' => $parsed['problema'] !== '' ? $parsed['problema'] : (string) ($ticket?->titolo ?? ''),
|
||||
'sorgente' => 'Descrizione ticket',
|
||||
'riferimento' => $this->buildCallerReference($ticket),
|
||||
'riferimento' => $this->buildCallerReference($ticket, (string) ($parsed['riferimento'] ?? '')),
|
||||
'rubrica_url' => null,
|
||||
'estratto_url'=> null,
|
||||
'stabile_url' => $ticket?->stabile ? StabileScheda::getUrl(['record' => (int) $ticket->stabile->id], panel: 'admin-filament') : null,
|
||||
'unita_label' => $ticket?->unitaImmobiliare ? $this->formatUnitaLabel($ticket->unitaImmobiliare) : '',
|
||||
];
|
||||
|
||||
if (($caller['contatto'] ?? '-') === '-' || trim((string) ($caller['contatto'] ?? '')) === '') {
|
||||
$titleContact = $this->extractCallerNameFromTitle((string) ($ticket?->titolo ?? ''));
|
||||
if ($titleContact !== '') {
|
||||
$caller['contatto'] = $titleContact;
|
||||
}
|
||||
}
|
||||
|
||||
$soggetto = $ticket?->soggettoRichiedente;
|
||||
if ($soggetto) {
|
||||
$label = trim((string) ($soggetto->ragione_sociale ?: trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? ''))));
|
||||
|
|
@ -1088,12 +1121,33 @@ protected function resolveCallerData(?Ticket $ticket): array
|
|||
}
|
||||
|
||||
$caller['telefono'] = trim((string) ($soggetto->telefono ?? '')) ?: $caller['telefono'];
|
||||
$caller['email'] = trim((string) ($soggetto->email ?? ''));
|
||||
$caller['email'] = trim((string) ($soggetto->email ?? '')) ?: $caller['email'];
|
||||
$caller['sorgente'] = 'Richiedente collegato al ticket';
|
||||
$caller['estratto_url'] = EstrattoContoSoggetto::getUrl(['record' => (int) $soggetto->id], panel: 'admin-filament');
|
||||
|
||||
$rubrica = $this->resolveCallerRubrica($ticket, $caller);
|
||||
if ($rubrica instanceof RubricaUniversale) {
|
||||
$caller['rubrica_url'] = RubricaUniversaleScheda::getUrl(['record' => (int) $rubrica->id], panel: 'admin-filament');
|
||||
$caller['telefono'] = trim((string) ($rubrica->telefono_cellulare ?: $rubrica->telefono_ufficio ?: $rubrica->telefono_casa ?: '')) ?: $caller['telefono'];
|
||||
$caller['email'] = trim((string) ($rubrica->email ?? '')) ?: $caller['email'];
|
||||
}
|
||||
|
||||
return $caller;
|
||||
}
|
||||
|
||||
$rubrica = $this->resolveCallerRubrica($ticket, $caller);
|
||||
if ($rubrica instanceof RubricaUniversale) {
|
||||
$label = trim((string) ($rubrica->nome_completo ?: $rubrica->ragione_sociale ?: ''));
|
||||
if ($label !== '') {
|
||||
$caller['contatto'] = $label;
|
||||
}
|
||||
|
||||
$caller['telefono'] = trim((string) ($rubrica->telefono_cellulare ?: $rubrica->telefono_ufficio ?: $rubrica->telefono_casa ?: '')) ?: $caller['telefono'];
|
||||
$caller['email'] = trim((string) ($rubrica->email ?? '')) ?: $caller['email'];
|
||||
$caller['rubrica_url'] = RubricaUniversaleScheda::getUrl(['record' => (int) $rubrica->id], panel: 'admin-filament');
|
||||
$caller['sorgente'] = 'Rubrica dedotta dal ticket';
|
||||
}
|
||||
|
||||
$openedBy = $ticket?->apertoDaUser;
|
||||
if ($openedBy instanceof User) {
|
||||
$caller['contatto'] = trim((string) ($openedBy->name ?? '')) ?: $caller['contatto'];
|
||||
|
|
@ -1104,10 +1158,10 @@ protected function resolveCallerData(?Ticket $ticket): array
|
|||
return $caller;
|
||||
}
|
||||
|
||||
protected function buildCallerReference(?Ticket $ticket): string
|
||||
protected function buildCallerReference(?Ticket $ticket, string $parsedReference = ''): string
|
||||
{
|
||||
if (! $ticket instanceof Ticket) {
|
||||
return '';
|
||||
return $parsedReference;
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
|
|
@ -1135,6 +1189,85 @@ protected function buildCallerReference(?Ticket $ticket): string
|
|||
$parts[] = 'Luogo: ' . $luogoIntervento;
|
||||
}
|
||||
|
||||
if ($ticket->unitaImmobiliare) {
|
||||
$parts[] = 'Unità: ' . $this->formatUnitaLabel($ticket->unitaImmobiliare);
|
||||
}
|
||||
|
||||
if ($parsedReference !== '') {
|
||||
$parts[] = $parsedReference;
|
||||
}
|
||||
|
||||
return implode(' · ', array_unique($parts));
|
||||
}
|
||||
|
||||
private function resolveCallerRubrica(?Ticket $ticket, array $caller): ?RubricaUniversale
|
||||
{
|
||||
$adminId = (int) ($ticket?->stabile?->amministratore_id ?? 0);
|
||||
|
||||
$queries = [];
|
||||
$soggetto = $ticket?->soggettoRichiedente;
|
||||
if ($soggetto && filled($soggetto->codice_fiscale)) {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where('codice_fiscale', strtoupper(trim((string) $soggetto->codice_fiscale)));
|
||||
}
|
||||
|
||||
$email = mb_strtolower(trim((string) ($caller['email'] ?? '')));
|
||||
if ($email !== '') {
|
||||
$queries[] = fn() => RubricaUniversale::query()->whereRaw('LOWER(email) = ?', [$email]);
|
||||
}
|
||||
|
||||
$phone = $this->normalizePhoneDigits((string) ($caller['telefono'] ?? ''));
|
||||
if ($phone !== '') {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where(function ($builder) use ($phone): void {
|
||||
$builder->whereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_cellulare, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_ufficio, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_casa, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone]);
|
||||
});
|
||||
}
|
||||
|
||||
$name = trim((string) ($caller['contatto'] ?? ''));
|
||||
if ($name !== '' && $name !== '-') {
|
||||
$queries[] = fn() => RubricaUniversale::query()->where(function ($builder) use ($name): void {
|
||||
$builder->whereRaw("LOWER(COALESCE(ragione_sociale, '')) = ?", [mb_strtolower($name)])
|
||||
->orWhereRaw("LOWER(TRIM(CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, '')))) = ?", [mb_strtolower($name)]);
|
||||
});
|
||||
}
|
||||
|
||||
foreach ($queries as $factory) {
|
||||
$query = $factory();
|
||||
if ($adminId > 0) {
|
||||
$query->where(function ($builder) use ($adminId): void {
|
||||
$builder->where('amministratore_id', $adminId)
|
||||
->orWhereNull('amministratore_id');
|
||||
});
|
||||
}
|
||||
|
||||
$match = $query->orderByDesc('updated_at')->orderBy('id')->first();
|
||||
if ($match instanceof RubricaUniversale) {
|
||||
return $match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function formatUnitaLabel(mixed $unita): string
|
||||
{
|
||||
if (! $unita) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parts = array_filter([
|
||||
(string) ($unita->denominazione ?? ''),
|
||||
! empty($unita->scala) ? 'Scala ' . $unita->scala : null,
|
||||
! empty($unita->interno) ? 'Int. ' . $unita->interno : null,
|
||||
! empty($unita->piano) ? 'Piano ' . $unita->piano : null,
|
||||
]);
|
||||
|
||||
return implode(' · ', $parts) ?: ('Unità #' . (int) ($unita->id ?? 0));
|
||||
}
|
||||
|
||||
private function normalizePhoneDigits(string $value): string
|
||||
{
|
||||
return preg_replace('/\D+/', '', $value) ?: '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ public function closeInterventoModal(): void
|
|||
protected function buildBaseQuery(Fornitore $fornitore, ?FornitoreDipendente $dipendente): Builder
|
||||
{
|
||||
$query = TicketIntervento::query()
|
||||
->with(['ticket.stabile', 'eseguitoDaDipendente'])
|
||||
->with(['ticket.stabile', 'ticket.unitaImmobiliare', 'ticket.soggettoRichiedente', 'eseguitoDaDipendente'])
|
||||
->where('fornitore_id', (int) $fornitore->id)
|
||||
->orderByDesc('created_at');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@
|
|||
use App\Models\ChiamataPostIt;
|
||||
use App\Models\PbxClickToCallRequest;
|
||||
use App\Models\RubricaUniversale;
|
||||
use App\Models\Soggetto;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketAttachment;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use App\Models\User;
|
||||
use App\Services\Cti\LiveIncomingCallService;
|
||||
use App\Services\Support\TicketAttachmentUploadService;
|
||||
|
|
@ -564,20 +566,23 @@ public function creaTicketRapido(): void
|
|||
return;
|
||||
}
|
||||
|
||||
$stabileId = StabileContext::resolveActiveStabileId($user);
|
||||
if (! $stabileId) {
|
||||
Notification::make()
|
||||
->title('Seleziona prima uno stabile attivo')
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$caller = null;
|
||||
if ($this->selectedCallerId) {
|
||||
$caller = RubricaUniversale::query()->find($this->selectedCallerId);
|
||||
}
|
||||
|
||||
$activeStabileId = StabileContext::resolveActiveStabileId($user);
|
||||
$callerContext = $this->resolveCallerTicketContext($caller, $activeStabileId);
|
||||
$stabileId = $callerContext['stabile_id'] ?? $activeStabileId;
|
||||
|
||||
if (! $stabileId) {
|
||||
Notification::make()
|
||||
->title('Seleziona prima uno stabile attivo oppure un contatto legato a uno stabile')
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$descrizione = trim((string) $this->newTicketDescrizione);
|
||||
if ($caller) {
|
||||
$telefono = $caller->telefono_cellulare ?: ($caller->telefono_ufficio ?: $caller->telefono_casa);
|
||||
|
|
@ -585,6 +590,15 @@ public function creaTicketRapido(): void
|
|||
if ($telefono) {
|
||||
$descrizione .= "\nTelefono richiamabile: {$telefono}";
|
||||
}
|
||||
if (filled($caller->email)) {
|
||||
$descrizione .= "\nEmail richiedente: " . trim((string) $caller->email);
|
||||
}
|
||||
if (($callerContext['stabile_label'] ?? '') !== '') {
|
||||
$descrizione .= "\nRiferimento stabile: " . $callerContext['stabile_label'];
|
||||
}
|
||||
if (($callerContext['unita_label'] ?? '') !== '') {
|
||||
$descrizione .= "\nRiferimento unità: " . $callerContext['unita_label'];
|
||||
}
|
||||
}
|
||||
|
||||
if (filled($this->newTicketFotoNote)) {
|
||||
|
|
@ -598,6 +612,8 @@ public function creaTicketRapido(): void
|
|||
|
||||
$ticket = Ticket::query()->create([
|
||||
'stabile_id' => $stabileId,
|
||||
'unita_immobiliare_id'=> $callerContext['unita_id'] ?? null,
|
||||
'soggetto_richiedente_id' => $callerContext['soggetto_id'] ?? null,
|
||||
'aperto_da_user_id' => $user->id,
|
||||
'titolo' => (string) $this->newTicketTitolo,
|
||||
'descrizione' => $descrizione,
|
||||
|
|
@ -1162,7 +1178,11 @@ public function excerpt(?string $text, int $limit = 160): string
|
|||
|
||||
public function getCallerStabileLabel(int $rubricaId): ?string
|
||||
{
|
||||
$stabile = Stabile::query()->where('rubrica_id', $rubricaId)->first();
|
||||
$caller = RubricaUniversale::query()->find($rubricaId);
|
||||
$stabile = $caller instanceof RubricaUniversale
|
||||
? $this->resolveCallerStabile($caller)
|
||||
: null;
|
||||
|
||||
if (! $stabile) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1170,6 +1190,185 @@ public function getCallerStabileLabel(int $rubricaId): ?string
|
|||
return $stabile->denominazione ?: ('Stabile #' . $stabile->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{stabile_id:?int,stabile_label:string,soggetto_id:?int,unita_id:?int,unita_label:string}
|
||||
*/
|
||||
private function resolveCallerTicketContext(?RubricaUniversale $caller, ?int $fallbackStabileId = null): array
|
||||
{
|
||||
$stabile = $caller instanceof RubricaUniversale
|
||||
? $this->resolveCallerStabile($caller, $fallbackStabileId)
|
||||
: ($fallbackStabileId ? Stabile::query()->find($fallbackStabileId) : null);
|
||||
|
||||
$soggetto = $caller instanceof RubricaUniversale
|
||||
? $this->resolveCallerSoggetto($caller, $stabile?->id)
|
||||
: null;
|
||||
|
||||
$unita = $soggetto instanceof Soggetto
|
||||
? $this->resolveCallerUnita($soggetto, $stabile?->id, (string) ($caller?->riferimento_unita ?? ''))
|
||||
: null;
|
||||
|
||||
if (! $stabile && $unita instanceof UnitaImmobiliare) {
|
||||
$stabile = $unita->stabile;
|
||||
}
|
||||
|
||||
return [
|
||||
'stabile_id' => $stabile?->id,
|
||||
'stabile_label' => $stabile ? (string) ($stabile->denominazione ?: ('Stabile #' . $stabile->id)) : '',
|
||||
'soggetto_id' => $soggetto?->id,
|
||||
'unita_id' => $unita?->id,
|
||||
'unita_label' => $unita ? $this->formatUnitaLabel($unita) : '',
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveCallerStabile(RubricaUniversale $caller, ?int $fallbackStabileId = null): ?Stabile
|
||||
{
|
||||
$stabile = Stabile::query()->where('rubrica_id', (int) $caller->id)->first();
|
||||
if ($stabile instanceof Stabile) {
|
||||
return $stabile;
|
||||
}
|
||||
|
||||
$reference = trim((string) ($caller->riferimento_stabile ?? ''));
|
||||
if ($reference !== '') {
|
||||
$query = Stabile::query();
|
||||
if ((int) ($caller->amministratore_id ?? 0) > 0) {
|
||||
$query->where('amministratore_id', (int) $caller->amministratore_id);
|
||||
}
|
||||
|
||||
$exact = (clone $query)
|
||||
->where(function ($builder) use ($reference): void {
|
||||
$builder->where('denominazione', $reference)
|
||||
->orWhere('codice_stabile', $reference);
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($exact instanceof Stabile) {
|
||||
return $exact;
|
||||
}
|
||||
|
||||
$loose = (clone $query)
|
||||
->where(function ($builder) use ($reference): void {
|
||||
$builder->where('denominazione', 'like', '%' . $reference . '%')
|
||||
->orWhere('indirizzo', 'like', '%' . $reference . '%');
|
||||
})
|
||||
->orderBy('id')
|
||||
->first();
|
||||
|
||||
if ($loose instanceof Stabile) {
|
||||
return $loose;
|
||||
}
|
||||
}
|
||||
|
||||
return $fallbackStabileId ? Stabile::query()->find($fallbackStabileId) : null;
|
||||
}
|
||||
|
||||
private function resolveCallerSoggetto(RubricaUniversale $caller, ?int $stabileId = null): ?Soggetto
|
||||
{
|
||||
$queries = [];
|
||||
|
||||
$fiscalCode = strtoupper(trim((string) ($caller->codice_fiscale ?? '')));
|
||||
if ($fiscalCode !== '') {
|
||||
$queries[] = fn() => Soggetto::query()->where('codice_fiscale', $fiscalCode);
|
||||
}
|
||||
|
||||
$email = mb_strtolower(trim((string) ($caller->email ?? '')));
|
||||
if ($email !== '') {
|
||||
$queries[] = fn() => Soggetto::query()->whereRaw('LOWER(email) = ?', [$email]);
|
||||
}
|
||||
|
||||
$phone = $this->normalizePhoneDigits((string) ($caller->telefono_cellulare ?: $caller->telefono_ufficio ?: $caller->telefono_casa ?: ''));
|
||||
if ($phone !== '') {
|
||||
$queries[] = fn() => Soggetto::query()->whereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') = ?", [$phone]);
|
||||
}
|
||||
|
||||
$name = trim((string) ($caller->ragione_sociale ?: $caller->nome_completo ?: ''));
|
||||
if ($name !== '') {
|
||||
$queries[] = fn() => Soggetto::query()->where(function ($builder) use ($caller, $name): void {
|
||||
$builder->whereRaw("LOWER(COALESCE(ragione_sociale, '')) = ?", [mb_strtolower($name)]);
|
||||
|
||||
if (filled($caller->nome) || filled($caller->cognome)) {
|
||||
$builder->orWhere(function ($inner) use ($caller): void {
|
||||
if (filled($caller->nome)) {
|
||||
$inner->whereRaw("LOWER(COALESCE(nome, '')) = ?", [mb_strtolower(trim((string) $caller->nome))]);
|
||||
}
|
||||
if (filled($caller->cognome)) {
|
||||
$inner->whereRaw("LOWER(COALESCE(cognome, '')) = ?", [mb_strtolower(trim((string) $caller->cognome))]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
foreach ($queries as $factory) {
|
||||
$query = $factory();
|
||||
if ($stabileId) {
|
||||
$match = (clone $query)
|
||||
->whereHas('unitaImmobiliari', fn($builder) => $builder->where('stabile_id', (int) $stabileId))
|
||||
->first();
|
||||
|
||||
if ($match instanceof Soggetto) {
|
||||
return $match;
|
||||
}
|
||||
}
|
||||
|
||||
$match = $query->first();
|
||||
if ($match instanceof Soggetto) {
|
||||
return $match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolveCallerUnita(Soggetto $soggetto, ?int $stabileId = null, string $reference = ''): ?UnitaImmobiliare
|
||||
{
|
||||
$query = $soggetto->unitaImmobiliari();
|
||||
if ($stabileId) {
|
||||
$query->where('unita_immobiliari.stabile_id', (int) $stabileId);
|
||||
}
|
||||
|
||||
$unita = $query->with('stabile')->get();
|
||||
if ($unita->count() <= 1) {
|
||||
return $unita->first();
|
||||
}
|
||||
|
||||
$normalizedReference = $this->normalizeReferenceText($reference);
|
||||
if ($normalizedReference === '') {
|
||||
return $unita->first();
|
||||
}
|
||||
|
||||
return $unita->first(function (UnitaImmobiliare $item) use ($normalizedReference): bool {
|
||||
foreach ([$item->codice_unita, $item->denominazione, $item->scala, $item->interno, $item->numero_interno, $item->descrizione] as $candidate) {
|
||||
if ($this->normalizeReferenceText((string) $candidate) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_contains($this->normalizeReferenceText((string) $candidate), $normalizedReference)
|
||||
|| str_contains($normalizedReference, $this->normalizeReferenceText((string) $candidate))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}) ?? $unita->first();
|
||||
}
|
||||
|
||||
private function formatUnitaLabel(UnitaImmobiliare $unita): string
|
||||
{
|
||||
$parts = array_filter([
|
||||
$unita->denominazione ?: null,
|
||||
$unita->scala ? 'Scala ' . $unita->scala : null,
|
||||
$unita->interno ? 'Int. ' . $unita->interno : null,
|
||||
$unita->piano ? 'Piano ' . $unita->piano : null,
|
||||
]);
|
||||
|
||||
return implode(' · ', $parts) ?: ('Unità #' . $unita->id);
|
||||
}
|
||||
|
||||
private function normalizeReferenceText(string $value): string
|
||||
{
|
||||
return preg_replace('/[^A-Z0-9]+/', '', strtoupper(trim($value))) ?: '';
|
||||
}
|
||||
|
||||
private function aggiornaStatoTicket(int $ticketId, string $nuovoStato, bool $assegnaAUtente = false): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
|
|
|||
|
|
@ -84,7 +84,13 @@
|
|||
<div class="mt-3 grid gap-3 md:grid-cols-2">
|
||||
<div>
|
||||
<div class="text-xs uppercase tracking-wide text-gray-500">Richiedente</div>
|
||||
<div class="mt-1 text-sm">{{ $caller['contatto'] ?: '-' }}</div>
|
||||
<div class="mt-1 text-sm">
|
||||
@if(!empty($caller['rubrica_url']))
|
||||
<a href="{{ $caller['rubrica_url'] }}" class="text-primary-600 hover:underline">{{ $caller['contatto'] ?: '-' }}</a>
|
||||
@else
|
||||
{{ $caller['contatto'] ?: '-' }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-xs uppercase tracking-wide text-gray-500">Telefono</div>
|
||||
|
|
@ -120,7 +126,31 @@
|
|||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<div class="text-xs uppercase tracking-wide text-gray-500">Riferimento stabile</div>
|
||||
<div class="mt-1 text-sm">{{ $caller['riferimento'] !== '' ? $caller['riferimento'] : '-' }}</div>
|
||||
<div class="mt-1 text-sm">
|
||||
@if(!empty($caller['stabile_url']) && $caller['riferimento'] !== '')
|
||||
<a href="{{ $caller['stabile_url'] }}" class="text-primary-600 hover:underline">{{ $caller['riferimento'] }}</a>
|
||||
@else
|
||||
{{ $caller['riferimento'] !== '' ? $caller['riferimento'] : '-' }}
|
||||
@endif
|
||||
</div>
|
||||
@if($caller['unita_label'] !== '')
|
||||
<div class="mt-1 text-xs text-gray-500">Unità collegata: {{ $caller['unita_label'] }}</div>
|
||||
@endif
|
||||
</div>
|
||||
@if(!empty($caller['rubrica_url']) || !empty($caller['estratto_url']) || !empty($caller['stabile_url']))
|
||||
<div class="md:col-span-2">
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
@if(!empty($caller['rubrica_url']))
|
||||
<a href="{{ $caller['rubrica_url'] }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Apri scheda contatto</a>
|
||||
@endif
|
||||
@if(!empty($caller['estratto_url']))
|
||||
<a href="{{ $caller['estratto_url'] }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Apri estratto conto</a>
|
||||
@endif
|
||||
@if(!empty($caller['stabile_url']))
|
||||
<a href="{{ $caller['stabile_url'] }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Apri stabile</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 whitespace-pre-wrap rounded-lg bg-gray-50 p-3 text-sm text-gray-700">{{ $ticket->descrizione }}</div>
|
||||
|
|
|
|||
|
|
@ -411,33 +411,17 @@
|
|||
@if(count($this->selectedUploads) > 0)
|
||||
<div class="md:col-span-2">
|
||||
<div class="mb-2 text-xs font-semibold text-gray-700">Coda allegati pronta per il ticket</div>
|
||||
<div class="mb-2 text-[11px] text-gray-500">Le immagini locali restano visibili fino all'invio del ticket. La rinomina definitiva viene applicata solo al momento del salvataggio.</div>
|
||||
<div class="mb-2 text-[11px] text-gray-500">Qui sotto restano solo i dati operativi del file. L'anteprima immagine resta nel riquadro sopra fino all'invio del ticket.</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
@foreach($this->selectedUploads as $upload)
|
||||
<div class="rounded-xl border bg-white p-3 text-xs shadow-sm">
|
||||
<div class="aspect-[4/3] rounded-lg border bg-gray-50 p-2">
|
||||
@if($upload['is_image'] && !empty($upload['preview_url']))
|
||||
<img src="{{ $upload['preview_url'] }}" alt="{{ $upload['name'] }}" class="h-full w-full rounded object-cover" />
|
||||
@elseif($upload['is_image'])
|
||||
<template x-if="previewFor(@js($upload['name']), @js($upload['original_name']))">
|
||||
<img :src="previewFor(@js($upload['name']), @js($upload['original_name']))" alt="{{ $upload['name'] }}" class="h-full w-full rounded object-cover" />
|
||||
</template>
|
||||
<template x-if="!previewFor(@js($upload['name']), @js($upload['original_name']))">
|
||||
<div class="flex h-full items-center justify-center text-center text-gray-500">
|
||||
<div>
|
||||
<div class="text-sm font-semibold">IMG</div>
|
||||
<div class="mt-1 text-[11px]">Anteprima locale in attesa</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@else
|
||||
<div class="flex h-full items-center justify-center text-center text-gray-500">
|
||||
<div>
|
||||
<div class="text-sm font-semibold">{{ strtoupper(pathinfo($upload['name'], PATHINFO_EXTENSION) ?: 'FILE') }}</div>
|
||||
<div class="mt-1 text-[11px]">{{ $upload['name'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-wrap items-center gap-2 rounded-lg border bg-gray-50 px-3 py-2">
|
||||
<span class="inline-flex items-center rounded-full px-2 py-1 text-[10px] font-semibold {{ $upload['is_image'] ? 'bg-sky-100 text-sky-700' : 'bg-slate-200 text-slate-700' }}">
|
||||
{{ $upload['is_image'] ? 'Immagine' : (strtoupper(pathinfo($upload['name'], PATHINFO_EXTENSION) ?: 'FILE')) }}
|
||||
</span>
|
||||
<div class="text-[11px] text-gray-600">
|
||||
{{ $upload['is_image'] ? 'Anteprima visibile sopra' : 'Allegato pronto per l\'invio' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 font-medium">{{ $upload['name'] }}</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user