netgescon-day0/app/Filament/Pages/Fornitore/TicketOperativi.php

364 lines
15 KiB
PHP
Executable File

<?php
namespace App\Filament\Pages\Fornitore;
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
use App\Models\AssistenzaTecnorepairScheda;
use App\Models\Fornitore;
use App\Models\FornitoreDipendente;
use App\Models\TicketAttachment;
use App\Models\TicketIntervento;
use App\Models\User;
use App\Services\ProgramAclService;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use UnitEnum;
class TicketOperativi extends Page
{
use ResolvesOperatoreContext;
protected static ?string $navigationLabel = 'Ticket operativi';
protected static ?string $title = 'Ticket fornitore';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-wrench-screwdriver';
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
protected static ?int $navigationSort = 1;
protected static ?string $slug = 'fornitore/tickets';
protected string $view = 'filament.pages.fornitore.ticket-operativi';
public ?int $fornitoreId = null;
public ?string $fornitoreLabel = null;
public string $status = 'aperti';
public bool $missingAdminContext = false;
/** @var array<int, array<string, mixed>> */
public array $rows = [];
/** @var array<string, int> */
public array $totals = [
'aperti' => 0,
'chiusi' => 0,
'fatturabili' => 0,
];
public ?array $detailModal = null;
public static function canAccess(): bool
{
$user = Auth::user();
return $user instanceof User
&& app(ProgramAclService::class)->canAccessProgram($user, 'fornitore.ticket-operativi');
}
public function mount(): void
{
$this->status = (string) request()->query('stato', 'aperti');
[$fornitore] = $this->resolveOperatoreContext(allowAdminWithoutSupplier: true);
if (! $fornitore instanceof Fornitore) {
$this->missingAdminContext = true;
$this->rows = [];
return;
}
$this->fornitoreId = (int) $fornitore->id;
$this->fornitoreLabel = $this->getFornitoreLabel($fornitore);
$this->refreshData();
}
public function updatedStatus(): void
{
$this->refreshData();
}
public function refreshData(): void
{
if (! $this->fornitoreId) {
$this->rows = [];
$this->totals = ['aperti' => 0, 'chiusi' => 0, 'fatturabili' => 0];
return;
}
[$fornitore, $dipendente] = $this->resolveOperatoreContext($this->fornitoreId);
$query = $this->buildBaseQuery($fornitore, $dipendente);
$this->applyStatusFilter($query, $this->status);
$ticketRows = $query
->limit(100)
->get()
->map(function (TicketIntervento $intervento): array {
$base = $this->buildInterventoRow($intervento);
return array_merge($base, [
'row_type' => 'ticket',
'row_class' => $this->resolveTicketRowClass((string) $intervento->stato),
'badge_class' => $this->resolveTicketBadgeClass((string) $intervento->stato),
'numero' => (string) $intervento->ticket_id,
'cliente' => $base['contatto'],
'difetto' => $base['problema'],
'modello' => $base['apparato'],
'seriale' => '-',
'cod_prodotto' => '-',
'id' => (int) $intervento->id,
'ticket_id' => (int) $intervento->ticket_id,
'titolo' => (string) ($intervento->ticket->titolo ?? '-'),
'stato' => (string) $intervento->stato,
'stabile' => (string) ($intervento->ticket->stabile->denominazione ?? '-'),
'operatore' => (string) $intervento->operatore_assegnato_label,
'tempo_minuti' => (int) ($intervento->tempo_minuti ?? 0),
'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-',
'sort_at' => optional($intervento->created_at)->format('Y-m-d H:i:s') ?: '1970-01-01 00:00:00',
'origine' => 'Ticket amministratore',
'url' => TicketInterventoScheda::getUrl(['record' => $intervento->id], panel: 'admin-filament'),
]);
})
->all();
$tecnorepairRows = $this->buildTecnorepairRows($fornitore);
$rows = array_merge($ticketRows, $tecnorepairRows);
usort($rows, function (array $left, array $right): int {
return strcmp((string) ($right['sort_at'] ?? ''), (string) ($left['sort_at'] ?? ''));
});
$this->rows = array_slice($rows, 0, 150);
$this->totals = [
'aperti' => (clone $this->buildBaseQuery($fornitore, $dipendente))
->whereNotIn('stato', ['chiuso'])
->count() + AssistenzaTecnorepairScheda::query()
->where('fornitore_id', (int) $fornitore->id)
->whereIn('status_bucket', ['open', 'waiting'])
->count(),
'chiusi' => (clone $this->buildBaseQuery($fornitore, $dipendente))
->whereIn('stato', ['chiuso', 'fatturato'])
->count() + AssistenzaTecnorepairScheda::query()
->where('fornitore_id', (int) $fornitore->id)
->where('status_bucket', 'closed')
->count(),
'fatturabili' => (clone $this->buildBaseQuery($fornitore, $dipendente))
->whereIn('stato', ['fatturabile', 'fatturato'])
->count(),
];
}
public function setStatus(string $status): void
{
$this->status = $status;
$this->refreshData();
}
public function getCollaboratoriUrl(): string
{
if ($this->fornitoreId && Auth::user()?->hasAnyRole(['super-admin', 'admin', 'amministratore'])) {
return Collaboratori::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
}
return Collaboratori::getUrl(panel: 'admin-filament');
}
public function getContabilitaUrl(): string
{
if ($this->fornitoreId && Auth::user()?->hasAnyRole(['super-admin', 'admin', 'amministratore'])) {
return Contabilita::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
}
return Contabilita::getUrl(panel: 'admin-filament');
}
public function getImpostazioniUrl(): string
{
if ($this->fornitoreId && Auth::user()?->hasAnyRole(['super-admin', 'admin', 'amministratore'])) {
return ImpostazioniArchivio::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
}
return ImpostazioniArchivio::getUrl(panel: 'admin-filament');
}
public function openInterventoModal(int $interventoId): void
{
if (! $this->fornitoreId) {
Notification::make()->title('Seleziona prima un fornitore')->warning()->send();
return;
}
[$fornitore, $dipendente] = $this->resolveOperatoreContext($this->fornitoreId);
$intervento = $this->buildBaseQuery($fornitore, $dipendente)
->with(['ticket.attachments.user', 'ticket.messages.user'])
->find($interventoId);
if (! $intervento instanceof TicketIntervento) {
Notification::make()->title('Intervento non disponibile per questo fornitore')->danger()->send();
return;
}
$base = $this->buildInterventoRow($intervento);
$attachments = $intervento->ticket->attachments
->map(function (TicketAttachment $attachment): array {
return [
'id' => (int) $attachment->id,
'name' => (string) ($attachment->original_file_name ?? 'allegato'),
'description' => (string) ($attachment->description ?? ''),
'url' => route('filament.tickets.attachments.view', ['attachment' => (int) $attachment->id]),
'is_image' => $attachment->isImage(),
'is_pdf' => $attachment->isPdf(),
'created_at' => optional($attachment->created_at)->format('d/m/Y H:i') ?: '-',
];
})
->all();
$messages = $intervento->ticket->messages
->sortByDesc('created_at')
->take(6)
->map(fn($message) => [
'author' => (string) ($message->user->name ?? 'Sistema'),
'body' => (string) ($message->messaggio ?? ''),
'created_at' => optional($message->created_at)->format('d/m/Y H:i') ?: '-',
])
->values()
->all();
$this->detailModal = [
'id' => (int) $intervento->id,
'ticket_id' => (int) $intervento->ticket_id,
'titolo' => (string) ($intervento->ticket->titolo ?? '-'),
'stato' => (string) $intervento->stato,
'stabile' => (string) ($intervento->ticket->stabile->denominazione ?? '-'),
'contatto' => $base['contatto'],
'telefono' => $base['telefono'],
'problema' => $base['problema'],
'descrizione' => (string) ($intervento->ticket->descrizione ?? ''),
'url' => TicketInterventoScheda::getUrl(['record' => (int) $intervento->id], panel: 'admin-filament'),
'attachments' => $attachments,
'messages' => $messages,
];
}
public function closeInterventoModal(): void
{
$this->detailModal = null;
}
protected function buildBaseQuery(Fornitore $fornitore, ?FornitoreDipendente $dipendente): Builder
{
$query = TicketIntervento::query()
->with(['ticket.stabile', 'ticket.unitaImmobiliare', 'ticket.soggettoRichiedente', 'eseguitoDaDipendente'])
->where('fornitore_id', (int) $fornitore->id)
->orderByDesc('created_at');
if ($dipendente instanceof FornitoreDipendente) {
$query->where(function (Builder $builder) use ($dipendente): void {
$builder->whereNull('eseguito_da_dipendente_id')
->orWhere('eseguito_da_dipendente_id', (int) $dipendente->id);
});
}
return $query;
}
protected function applyStatusFilter(Builder $query, string $status): void
{
if ($status === 'chiusi') {
$query->whereIn('stato', ['chiuso', 'fatturato']);
return;
}
if ($status === 'fatturabili') {
$query->whereIn('stato', ['fatturabile', 'fatturato']);
return;
}
$query->whereNotIn('stato', ['chiuso']);
}
/**
* @return array<int, array<string, mixed>>
*/
protected function buildTecnorepairRows(Fornitore $fornitore): array
{
$query = AssistenzaTecnorepairScheda::query()
->where('fornitore_id', (int) $fornitore->id)
->orderByDesc('date_received')
->orderByDesc('updated_at');
if ($this->status === 'chiusi') {
$query->where('status_bucket', 'closed');
} elseif ($this->status === 'aperti') {
$query->whereIn('status_bucket', ['open', 'waiting']);
} elseif ($this->status === 'fatturabili') {
return [];
}
return $query
->limit(120)
->get()
->map(function (AssistenzaTecnorepairScheda $scheda): array {
return [
'row_type' => 'tecnorepair',
'row_class' => (string) $scheda->row_classes,
'badge_class' => (string) $scheda->status_badge_classes,
'numero' => (string) ($scheda->legacy_numero_scheda ?: $scheda->legacy_id),
'ingresso' => optional($scheda->date_received)->format('d/m/Y') ?: '-',
'cliente' => (string) ($scheda->customer_name ?: 'Cliente TecnoRepair'),
'contatto' => (string) ($scheda->customer_name ?: 'Cliente TecnoRepair'),
'telefono' => (string) ($scheda->customer_phone ?: $scheda->customer_phone_alt ?: ''),
'difetto' => (string) ($scheda->defect_reported ?: '-'),
'problema' => (string) ($scheda->defect_reported ?: '-'),
'apparato' => (string) ($scheda->product_model ?: '-'),
'modello' => (string) ($scheda->product_model ?: '-'),
'seriale' => (string) $scheda->serial_label,
'cod_prodotto' => (string) ($scheda->product_code ?: '-'),
'id' => (int) $scheda->id,
'ticket_id' => null,
'titolo' => (string) $scheda->display_title,
'stato' => (string) ($scheda->status_label ?: $scheda->status_bucket ?: '-'),
'stabile' => 'Archivio TecnoRepair',
'operatore' => (string) ($scheda->technician_name ?: $scheda->operator_name ?: '-'),
'tempo_minuti' => 0,
'updated_at' => optional($scheda->updated_at)->format('d/m/Y H:i') ?: '-',
'sort_at' => optional($scheda->date_received ?? $scheda->updated_at)->format('Y-m-d H:i:s') ?: '1970-01-01 00:00:00',
'origine' => 'TecnoRepair MDB',
'url' => null,
];
})
->all();
}
protected function resolveTicketRowClass(string $status): string
{
$status = strtolower(trim($status));
return match (true) {
in_array($status, ['chiuso', 'fatturato'], true) => 'bg-emerald-50/70 hover:bg-emerald-50',
in_array($status, ['fatturabile', 'verifica'], true) => 'bg-amber-50/70 hover:bg-amber-50',
default => 'bg-sky-50/60 hover:bg-sky-50',
};
}
protected function resolveTicketBadgeClass(string $status): string
{
$status = strtolower(trim($status));
return match (true) {
in_array($status, ['chiuso', 'fatturato'], true) => 'border-emerald-200 bg-emerald-50 text-emerald-800',
in_array($status, ['fatturabile', 'verifica'], true) => 'border-amber-200 bg-amber-50 text-amber-800',
default => 'border-sky-200 bg-sky-50 text-sky-800',
};
}
}