411 lines
17 KiB
PHP
Executable File
411 lines
17 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\TicketIntervento;
|
|
use App\Models\User;
|
|
use BackedEnum;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use UnitEnum;
|
|
|
|
class LavorazioniOperative extends Page
|
|
{
|
|
use ResolvesOperatoreContext;
|
|
|
|
protected static ?string $navigationLabel = 'Lavorazioni';
|
|
|
|
protected static ?string $title = 'Lavorazioni operative';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-queue-list';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
|
|
|
|
protected static ?int $navigationSort = 1;
|
|
|
|
protected static ?string $slug = 'fornitore/lavorazioni';
|
|
|
|
protected string $view = 'filament.pages.fornitore.lavorazioni-operative';
|
|
|
|
public ?int $fornitoreId = null;
|
|
|
|
public ?string $fornitoreLabel = null;
|
|
|
|
public bool $missingAdminContext = false;
|
|
|
|
public string $scope = 'tutte';
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $rows = [];
|
|
|
|
public ?array $detailModal = null;
|
|
|
|
/** @var array<string, int> */
|
|
public array $totals = [
|
|
'tutte' => 0,
|
|
'aperte' => 0,
|
|
'chiuse' => 0,
|
|
'ticket_amministratore' => 0,
|
|
'interne' => 0,
|
|
];
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = Auth::user();
|
|
|
|
return $user instanceof User
|
|
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore', 'fornitore']);
|
|
}
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->scope = (string) request()->query('scope', 'tutte');
|
|
[$fornitore] = $this->resolveOperatoreContext(allowAdminWithoutSupplier: true);
|
|
|
|
if (! $fornitore instanceof Fornitore) {
|
|
$this->missingAdminContext = true;
|
|
return;
|
|
}
|
|
|
|
$this->fornitoreId = (int) $fornitore->id;
|
|
$this->fornitoreLabel = $this->getFornitoreLabel($fornitore);
|
|
$this->refreshData();
|
|
}
|
|
|
|
public function updatedScope(): void
|
|
{
|
|
$this->refreshData();
|
|
}
|
|
|
|
public function setScope(string $scope): void
|
|
{
|
|
$this->scope = $scope;
|
|
$this->refreshData();
|
|
}
|
|
|
|
public function refreshData(): void
|
|
{
|
|
if (! $this->fornitoreId) {
|
|
$this->rows = [];
|
|
return;
|
|
}
|
|
|
|
[$fornitore, $dipendente] = $this->resolveOperatoreContext($this->fornitoreId);
|
|
|
|
$baseQuery = $this->buildBaseQuery($fornitore, $dipendente);
|
|
|
|
$ticketCount = (clone $baseQuery)->count();
|
|
$tecnorepairCount = $this->buildTecnorepairQuery($fornitore)->count();
|
|
|
|
$this->totals = [
|
|
'tutte' => $ticketCount + $tecnorepairCount,
|
|
'aperte' => 0,
|
|
'chiuse' => 0,
|
|
'ticket_amministratore' => $ticketCount,
|
|
'interne' => 0,
|
|
];
|
|
|
|
if ($this->scope === 'interne') {
|
|
$this->rows = [];
|
|
return;
|
|
}
|
|
|
|
$ticketRows = $baseQuery
|
|
->limit(120)
|
|
->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),
|
|
'is_closed' => $this->isClosedStatus((string) $intervento->stato),
|
|
'numero' => (string) $intervento->ticket_id,
|
|
'cliente' => $base['contatto'],
|
|
'difetto' => $base['problema'],
|
|
'brand' => $this->extractDeviceBrand($base['apparato']),
|
|
'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' => (int) $intervento->id], panel: 'admin-filament'),
|
|
]);
|
|
})
|
|
->all();
|
|
|
|
$rows = array_merge($ticketRows, $this->buildTecnorepairRows($fornitore));
|
|
|
|
usort($rows, function (array $left, array $right): int {
|
|
return strcmp((string) ($right['sort_at'] ?? ''), (string) ($left['sort_at'] ?? ''));
|
|
});
|
|
|
|
$this->totals['aperte'] = count(array_filter($rows, fn(array $row): bool => ! (bool) ($row['is_closed'] ?? false)));
|
|
$this->totals['chiuse'] = count(array_filter($rows, fn(array $row): bool => (bool) ($row['is_closed'] ?? false)));
|
|
|
|
if ($this->scope === 'aperte') {
|
|
$rows = array_values(array_filter($rows, fn(array $row): bool => ! (bool) ($row['is_closed'] ?? false)));
|
|
} elseif ($this->scope === 'chiuse') {
|
|
$rows = array_values(array_filter($rows, fn(array $row): bool => (bool) ($row['is_closed'] ?? false)));
|
|
}
|
|
|
|
$this->rows = array_slice($rows, 0, 150);
|
|
}
|
|
|
|
public function openRowDetail(string $rowType, int $rowId): void
|
|
{
|
|
if (! $this->fornitoreId) {
|
|
return;
|
|
}
|
|
|
|
if ($rowType === 'tecnorepair') {
|
|
$scheda = AssistenzaTecnorepairScheda::query()
|
|
->where('fornitore_id', (int) $this->fornitoreId)
|
|
->find($rowId);
|
|
|
|
if (! $scheda instanceof AssistenzaTecnorepairScheda) {
|
|
return;
|
|
}
|
|
|
|
$metadata = is_array($scheda->metadata ?? null) ? $scheda->metadata : [];
|
|
$apparecchio = array_filter([
|
|
'Numero scheda' => (string) ($scheda->legacy_numero_scheda ?: $scheda->legacy_id ?: '-'),
|
|
'Codice prodotto' => (string) ($scheda->product_code ?: '-'),
|
|
'Modello' => (string) ($scheda->product_model ?: '-'),
|
|
'Seriale 1' => (string) ($scheda->serial_number ?: '-'),
|
|
'Seriale 2' => (string) ($scheda->serial_number_2 ?: '-'),
|
|
'Numero ordine' => (string) ($scheda->order_number ?: '-'),
|
|
'RMA' => (string) ($scheda->rma_code ?: '-'),
|
|
'PIN' => (string) ($scheda->pin_code ?: '-'),
|
|
'Unlock' => (string) ($scheda->unlock_code ?: '-'),
|
|
], static fn(string $value): bool => trim($value) !== '');
|
|
|
|
$this->detailModal = [
|
|
'type' => 'tecnorepair',
|
|
'title' => (string) $scheda->display_title,
|
|
'status' => (string) ($scheda->status_label ?: $scheda->status_bucket ?: '-'),
|
|
'customer' => (string) ($scheda->customer_name ?: 'Cliente TecnoRepair'),
|
|
'phone' => (string) ($scheda->customer_phone ?: $scheda->customer_phone_alt ?: ''),
|
|
'email' => (string) ($scheda->customer_email ?: ''),
|
|
'difetto_segnalato' => (string) ($scheda->defect_reported ?: '-'),
|
|
'note_interne' => trim((string) (($metadata['NoteInterne'] ?? $metadata['note_interne'] ?? $scheda->communications) ?: '')),
|
|
'note_stampa' => trim((string) (($metadata['NoteStampa'] ?? $metadata['note_stampa'] ?? $scheda->repair_description) ?: '')),
|
|
'communications' => (string) ($scheda->communications ?: ''),
|
|
'repair_description' => (string) ($scheda->repair_description ?: ''),
|
|
'operator' => (string) ($scheda->technician_name ?: $scheda->operator_name ?: '-'),
|
|
'updated_at' => optional($scheda->updated_at)->format('d/m/Y H:i') ?: '-',
|
|
'apparecchio_fields' => $apparecchio,
|
|
'metadata' => $metadata,
|
|
];
|
|
|
|
return;
|
|
}
|
|
|
|
[$fornitore, $dipendente] = $this->resolveOperatoreContext((int) $this->fornitoreId);
|
|
|
|
$intervento = $this->buildBaseQuery($fornitore, $dipendente)->find($rowId);
|
|
if (! $intervento instanceof TicketIntervento) {
|
|
return;
|
|
}
|
|
|
|
$base = $this->buildInterventoRow($intervento);
|
|
$this->detailModal = [
|
|
'type' => 'ticket',
|
|
'title' => 'Ticket #' . (int) $intervento->ticket_id . ' · ' . (string) ($intervento->ticket->titolo ?? '-'),
|
|
'status' => (string) $intervento->stato,
|
|
'customer' => $base['contatto'],
|
|
'phone' => $base['telefono'],
|
|
'email' => '',
|
|
'difetto_segnalato' => $base['problema'],
|
|
'note_interne' => (string) ($intervento->rapporto_fornitore ?? ''),
|
|
'note_stampa' => (string) ($intervento->ticket->descrizione ?? ''),
|
|
'communications' => '',
|
|
'repair_description' => '',
|
|
'operator' => (string) $intervento->operatore_assegnato_label,
|
|
'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-',
|
|
'url' => TicketInterventoScheda::getUrl(['record' => (int) $intervento->id], panel: 'admin-filament'),
|
|
'apparecchio_fields' => [
|
|
'Apparato' => $base['apparato'],
|
|
'Stabile' => (string) ($intervento->ticket->stabile->denominazione ?? '-'),
|
|
],
|
|
'metadata' => [],
|
|
];
|
|
}
|
|
|
|
public function closeDetailModal(): void
|
|
{
|
|
$this->detailModal = null;
|
|
}
|
|
|
|
public function getTicketsUrl(): string
|
|
{
|
|
if ($this->fornitoreId) {
|
|
return TicketOperativi::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
|
|
}
|
|
|
|
return TicketOperativi::getUrl(panel: 'admin-filament');
|
|
}
|
|
|
|
public function getCollaboratoriUrl(): string
|
|
{
|
|
if ($this->fornitoreId) {
|
|
return Collaboratori::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
|
|
}
|
|
|
|
return Collaboratori::getUrl(panel: 'admin-filament');
|
|
}
|
|
|
|
public function getRubricaUrl(): string
|
|
{
|
|
if ($this->fornitoreId) {
|
|
return RubricaClienti::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
|
|
}
|
|
|
|
return RubricaClienti::getUrl(panel: 'admin-filament');
|
|
}
|
|
|
|
public function getProdottiUrl(): string
|
|
{
|
|
if ($this->fornitoreId) {
|
|
return ProdottiCatalogo::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
|
|
}
|
|
|
|
return ProdottiCatalogo::getUrl(panel: 'admin-filament');
|
|
}
|
|
|
|
public function getImpostazioniUrl(): string
|
|
{
|
|
if ($this->fornitoreId) {
|
|
return ImpostazioniArchivio::getUrl(['fornitore' => $this->fornitoreId], panel: 'admin-filament');
|
|
}
|
|
|
|
return ImpostazioniArchivio::getUrl(panel: 'admin-filament');
|
|
}
|
|
|
|
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 buildTecnorepairQuery(Fornitore $fornitore): Builder
|
|
{
|
|
return AssistenzaTecnorepairScheda::query()
|
|
->where('fornitore_id', (int) $fornitore->id)
|
|
->orderByDesc('date_received')
|
|
->orderByDesc('updated_at');
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array<string, mixed>>
|
|
*/
|
|
protected function buildTecnorepairRows(Fornitore $fornitore): array
|
|
{
|
|
return $this->buildTecnorepairQuery($fornitore)
|
|
->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,
|
|
'is_closed' => (bool) ($scheda->is_closed ?? false),
|
|
'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 ?: '-'),
|
|
'brand' => $this->extractDeviceBrand((string) ($scheda->product_model ?: '')),
|
|
'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',
|
|
};
|
|
}
|
|
|
|
protected function isClosedStatus(string $status): bool
|
|
{
|
|
$status = strtolower(trim($status));
|
|
|
|
return str_contains($status, 'chius')
|
|
|| str_contains($status, 'fatturat')
|
|
|| str_contains($status, 'riconsegn')
|
|
|| str_contains($status, 'riparato')
|
|
|| str_contains($status, 'acquisto');
|
|
}
|
|
|
|
protected function extractDeviceBrand(string $label): string
|
|
{
|
|
$label = trim($label);
|
|
if ($label === '' || $label === '-') {
|
|
return '-';
|
|
}
|
|
|
|
$parts = preg_split('/\s+/', $label) ?: [];
|
|
$first = trim((string) ($parts[0] ?? ''));
|
|
|
|
return $first !== '' ? strtoupper($first) : '-';
|
|
}
|
|
}
|