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

528 lines
22 KiB
PHP

<?php
namespace App\Filament\Pages\Fornitore;
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
use App\Models\AssistenzaTecnorepairAllegato;
use App\Models\AssistenzaTecnorepairScheda;
use App\Models\Fornitore;
use App\Models\User;
use App\Services\Catalog\ContabilitaMysqlSerialImportService;
use App\Services\Tecnorepair\TecnoRepairArchiveService;
use BackedEnum;
use Carbon\Carbon;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use UnitEnum;
class PraticheTecnorepair extends Page
{
use ResolvesOperatoreContext;
protected static ?string $navigationLabel = 'Pratiche TecnoRepair';
protected static ?string $title = 'Pratiche & Riparazioni TecnoRepair';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-computer-desktop';
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
protected static ?int $navigationSort = 2;
protected static ?string $slug = 'fornitore/pratiche';
protected string $view = 'filament.pages.fornitore.pratiche-tecnorepair';
public ?int $fornitoreId = null;
public ?string $fornitoreLabel = null;
public bool $missingAdminContext = false;
/** @var array<int, string> */
public array $fornitoriOptions = [];
// Filter properties
public string $searchMatricola = '';
public string $searchNumScheda = '';
public string $searchCliente = '';
public bool $searchClienteParziale = true;
public string $searchNumOrdine = '';
public string $filterStato = '0';
public bool $escludiRiconsegnati = true;
public bool $soloRiconsegnati = false;
public ?int $nonLavorateDaGiorni = null;
public string $dataIngressoDal = '';
public string $dataIngressoAl = '';
// Active Scheda modal state
public ?int $selectedSchedaId = null;
public string $schedaTab = 'entrata';
/** @var array<string, mixed>|null */
public ?array $activeScheda = null;
// Workflow inputs for active scheda
public string $workflowNote = '';
public string $workflowMotivoRottamazione = '';
public bool $workflowComeRicambi = false;
public string $workflowRmaCode = '';
public ?int $workflowFornitoreResoId = null;
/** @var array<int, array<string, mixed>> */
public array $rows = [];
/** @var array<string, int> */
public array $totals = [
'totale' => 0,
'aperte' => 0,
'riparate' => 0,
'rottamate' => 0,
'rma_garanzia' => 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
{
[$fornitore] = $this->resolveOperatoreContext(allowAdminWithoutSupplier: true);
if (! $fornitore instanceof Fornitore) {
$this->missingAdminContext = true;
return;
}
$this->fornitoreId = (int) $fornitore->id;
$this->fornitoreLabel = $this->getFornitoreLabel($fornitore);
$user = Auth::user();
if ($this->isInternalOperator($user)) {
$this->fornitoriOptions = Fornitore::query()
->where(function ($q) {
$q->whereHas('productSerials')
->orWhere('partita_iva', '10055221005')
->orWhere('partita_iva', '14001151001')
->orWhereIn('id', [236, 392]);
})
->orderBy('ragione_sociale')
->get()
->mapWithKeys(fn(Fornitore $f): array => [
(int) $f->id => trim((string) ($f->ragione_sociale ?: ('Fornitore #' . $f->id))) . ($f->partita_iva ? ' (' . $f->partita_iva . ')' : ''),
])
->all();
}
// Optional date range
$this->dataIngressoDal = trim((string) request()->query('dal', ''));
$this->dataIngressoAl = trim((string) request()->query('al', ''));
// Optional query params
if (request()->has('scheda')) {
$this->searchNumScheda = trim((string) request()->query('scheda'));
}
if (request()->has('q')) {
$this->searchMatricola = trim((string) request()->query('q'));
}
$this->refreshData();
if (request()->has('open')) {
$openId = (int) request()->query('open');
if ($openId > 0) {
$this->openScheda($openId);
}
}
}
public function updatedFornitoreId(): void
{
$fornitore = Fornitore::query()->find((int) $this->fornitoreId);
if ($fornitore instanceof Fornitore) {
$this->fornitoreLabel = $this->getFornitoreLabel($fornitore);
$this->selectedSchedaId = null;
$this->activeScheda = null;
$this->refreshData();
}
}
public function updatedSearchMatricola(): void { $this->refreshData(); }
public function updatedSearchNumScheda(): void { $this->refreshData(); }
public function updatedSearchCliente(): void { $this->refreshData(); }
public function updatedFilterStato(): void { $this->refreshData(); }
public function updatedEscludiRiconsegnati(): void { $this->refreshData(); }
public function updatedSoloRiconsegnati(): void { $this->refreshData(); }
public function resetFiltri(): void
{
$this->searchMatricola = '';
$this->searchNumScheda = '';
$this->searchCliente = '';
$this->searchNumOrdine = '';
$this->filterStato = '0';
$this->escludiRiconsegnati = true;
$this->soloRiconsegnati = false;
$this->nonLavorateDaGiorni = null;
$this->dataIngressoDal = now()->subYears(2)->startOfYear()->format('Y-m-d');
$this->dataIngressoAl = now()->addYear()->endOfYear()->format('Y-m-d');
$this->refreshData();
}
public function refreshData(): void
{
if (! $this->fornitoreId) {
$this->rows = [];
return;
}
$query = AssistenzaTecnorepairScheda::query()
->where(function (Builder $builder) {
$builder->where('fornitore_id', (int) $this->fornitoreId);
// Also include merged suppliers (e.g. NCOMSRL if Nethome)
if ((int) $this->fornitoreId === 236) {
$builder->orWhere('fornitore_id', 392);
}
});
// Search filters
if (trim($this->searchMatricola) !== '') {
$val = trim($this->searchMatricola);
$query->where(function (Builder $b) use ($val) {
$b->where('serial_number', 'like', "%{$val}%")
->orWhere('serial_number_2', 'like', "%{$val}%")
->orWhere('product_code', 'like', "%{$val}%");
});
}
if (trim($this->searchNumScheda) !== '') {
$val = trim($this->searchNumScheda);
$query->where(function (Builder $b) use ($val) {
$b->where('legacy_numero_scheda', 'like', "%{$val}%")
->orWhere('legacy_id', $val);
});
}
if (trim($this->searchCliente) !== '') {
$val = trim($this->searchCliente);
$query->where('customer_name', 'like', "%{$val}%");
}
if (trim($this->searchNumOrdine) !== '') {
$val = trim($this->searchNumOrdine);
$query->where('order_number', 'like', "%{$val}%");
}
if ($this->filterStato !== '0' && trim($this->filterStato) !== '') {
$st = trim($this->filterStato);
$query->where(function (Builder $b) use ($st) {
$b->where('status_code', $st)
->orWhere('status_label', 'like', "%{$st}%");
});
}
if ($this->escludiRiconsegnati) {
$query->where(function (Builder $b) {
$b->whereNull('metadata->raw->isRiconsegnato')
->orWhere('metadata->raw->isRiconsegnato', 0)
->orWhere('metadata->raw->isRiconsegnato', '0')
->orWhere('metadata->raw->isRiconsegnato', false);
});
} elseif ($this->soloRiconsegnati) {
$query->where(function (Builder $b) {
$b->where('metadata->raw->isRiconsegnato', 1)
->orWhere('metadata->raw->isRiconsegnato', '1')
->orWhere('metadata->raw->isRiconsegnato', true);
});
}
if ($this->dataIngressoDal !== '') {
$query->where('date_received', '>=', Carbon::parse($this->dataIngressoDal)->startOfDay());
}
if ($this->dataIngressoAl !== '') {
$query->where('date_received', '<=', Carbon::parse($this->dataIngressoAl)->endOfDay());
}
// Count totals across dataset
$baseTotalQuery = AssistenzaTecnorepairScheda::query()
->where(function (Builder $builder) {
$builder->where('fornitore_id', (int) $this->fornitoreId);
if ((int) $this->fornitoreId === 236) {
$builder->orWhere('fornitore_id', 392);
}
});
$this->totals = [
'totale' => (clone $baseTotalQuery)->count(),
'riparate' => (clone $baseTotalQuery)->where('status_code', '4')->count(),
'rottamate' => (clone $baseTotalQuery)->whereIn('status_code', ['10', '23', '37'])->count(),
'rma_garanzia' => (clone $baseTotalQuery)->where('status_code', '33')->count(),
'aperte' => (clone $baseTotalQuery)->whereNotIn('status_code', ['4', '10', '23', '37'])->count(),
];
$records = $query->orderByDesc('legacy_id')->limit(120)->get();
$this->rows = $records->map(function (AssistenzaTecnorepairScheda $s): array {
$raw = (array) data_get($s->metadata, 'raw', []);
$isRiconsegnato = (bool) ($raw['isRiconsegnato'] ?? false);
$statusCode = (string) ($s->status_code ?: '');
$statusLabel = strtoupper((string) ($s->status_label ?: 'IN LAVORAZIONE'));
// Row style logic matching TecnoRepair screenshots:
// Green: RIPARATO (4) / ACQUISTO RICONDIZIONATO (31)
// Red: DISPOSITIVO IRRIPARABILE (10) / PZ-RICAM (23) / SMALTITO (37)
// Blue: INGRESSO ACCETTAZIONE (21)
// Amber: ATTESA RICAMBIO (5) / LABORATORIO ESTERNO (27)
$rowColor = 'white';
if (in_array($statusCode, ['4', '31'], true) || str_contains($statusLabel, 'RIPARATO') || str_contains($statusLabel, 'ACQUISTO RICONDIZIONATO')) {
$rowColor = 'green';
} elseif (in_array($statusCode, ['10', '23', '37'], true) || str_contains($statusLabel, 'IRRIPARABILE') || str_contains($statusLabel, 'RICAMBI') || str_contains($statusLabel, 'SMALTITO')) {
$rowColor = 'red';
} elseif ($statusCode === '21' || str_contains($statusLabel, 'INGRESSO') || str_contains($statusLabel, 'ACCETTAZIONE')) {
$rowColor = 'blue';
} elseif (in_array($statusCode, ['5', '27', '30'], true) || str_contains($statusLabel, 'ATTESA') || str_contains($statusLabel, 'URGENTE')) {
$rowColor = 'amber';
}
return [
'id' => (int) $s->id,
'legacy_id' => (int) ($s->legacy_id ?: $s->id),
'numero_scheda' => (string) ($s->legacy_numero_scheda ?: $s->legacy_id ?: $s->id),
'ingresso' => optional($s->date_received)->format('d/m/Y') ?: '-',
'cliente' => (string) ($s->customer_name ?: '-'),
'telefono' => (string) ($s->customer_phone ?: $s->customer_phone_alt ?: '-'),
'difetto' => (string) ($s->defect_reported ?: '-'),
'marca' => (string) ($raw['Cod_Marca'] ?? $this->extractBrand($s->product_model)),
'modello' => (string) ($s->product_model ?: '-'),
'seriale' => (string) ($s->serial_number ?: '-'),
'cod_prodotto' => (string) ($s->product_code ?: '-'),
'riconsegnato' => $isRiconsegnato,
'data_ricons' => ! empty($raw['DataRiconsegna']) ? Carbon::parse($raw['DataRiconsegna'])->format('d/m/Y') : '-',
'num_ordine' => (string) ($s->order_number ?: '0'),
'stato_rip' => $statusLabel,
'status_code' => $statusCode,
'operatore' => (string) ($s->operator_name ?: $s->technician_name ?: 'MICHELE BARONE'),
'row_color' => $rowColor,
];
})->all();
}
public function openScheda(int $schedaId): void
{
$scheda = AssistenzaTecnorepairScheda::query()
->with(['allegati'])
->find($schedaId);
if (! $scheda instanceof AssistenzaTecnorepairScheda) {
return;
}
$raw = (array) data_get($scheda->metadata, 'raw', []);
$cliente = (array) data_get($scheda->metadata, 'cliente', []);
$ricambi = (array) data_get($scheda->metadata, 'ricambi_scheda', []);
$this->selectedSchedaId = (int) $scheda->id;
$this->schedaTab = 'entrata';
$this->workflowNote = '';
$this->workflowRmaCode = (string) ($scheda->rma_code ?: ('RMA-' . date('Ymd') . '-' . ($scheda->legacy_numero_scheda ?: $scheda->id)));
$this->activeScheda = [
'id' => (int) $scheda->id,
'numero_scheda' => (string) ($scheda->legacy_numero_scheda ?: $scheda->legacy_id ?: $scheda->id),
'data_ingresso' => optional($scheda->date_received)->format('d/m/Y H:i') ?: '-',
'data_riparazione' => ! empty($raw['DataRiparazione']) ? Carbon::parse($raw['DataRiparazione'])->format('d/m/Y H:i') : '-',
'data_riconsegna' => ! empty($raw['DataRiconsegna']) ? Carbon::parse($raw['DataRiconsegna'])->format('d/m/Y') : '-',
'stato_rip' => (string) ($scheda->status_label ?: 'INGRESSO ACCETTAZIONE'),
'status_code' => (string) ($scheda->status_code ?: '21'),
'rma_code' => (string) ($scheda->rma_code ?: ''),
'num_ordine' => (string) ($scheda->order_number ?: '0'),
// Cliente
'cliente_nome' => (string) ($scheda->customer_name ?: '-'),
'cliente_email' => (string) ($scheda->customer_email ?: '-'),
'cliente_telefono' => (string) ($scheda->customer_phone ?: '-'),
'cliente_fisso' => (string) ($scheda->customer_phone_alt ?: '-'),
'cliente_indirizzo' => (string) ($cliente['Indirizzo'] ?? '-'),
'cliente_citta' => (string) ($cliente['Citta'] ?? '-'),
// Apparecchio
'tipo_apparecchio' => (string) ($raw['Cod_TipoApparecchio'] ?? 'AIO / PC / Dispositivo'),
'marca' => (string) ($raw['Cod_Marca'] ?? $this->extractBrand($scheda->product_model)),
'modello' => (string) ($scheda->product_model ?: '-'),
'codice_prodotto' => (string) ($scheda->product_code ?: '-'),
'seriale' => (string) ($scheda->serial_number ?: '-'),
'seriale_2' => (string) ($scheda->serial_number_2 ?: '-'),
'accessori' => (string) ($raw['AccessoriConsegnati'] ?? 'NESSUNO'),
'difetto' => (string) ($scheda->defect_reported ?: '-'),
'richieste_cliente' => (string) ($raw['RiparazioneRichiesta'] ?? '-'),
'stato_generale' => (string) ($raw['StatoGenerale'] ?? '-'),
'codice_pin' => (string) ($scheda->pin_code ?: '-'),
'codice_sblocco' => (string) ($scheda->unlock_code ?: '-'),
'tecnico' => (string) ($scheda->technician_name ?: $scheda->operator_name ?: 'MICHELE BARONE'),
// Riparazione
'descrizione_lavorazione'=> (string) ($scheda->repair_description ?: '-'),
'comunicazioni' => (string) ($scheda->communications ?: '-'),
// Economico
'costo_subito' => (float) str_replace(',', '.', (string) ($raw['CostoSubito'] ?? 0)),
'costo_addebitato' => (float) str_replace(',', '.', (string) ($raw['CostoAddebitato'] ?? 0)),
'acconto' => (float) str_replace(',', '.', (string) ($raw['AccontoRiparazione'] ?? 0)),
'preventivo' => (float) str_replace(',', '.', (string) ($raw['CostoPreventivo'] ?? 0)),
// Ricambi
'ricambi' => $ricambi,
'allegati' => $scheda->allegati->map(fn(AssistenzaTecnorepairAllegato $a) => [
'nome' => $a->file_name,
'path' => $a->file_path,
])->all(),
];
}
public function closeScheda(): void
{
$this->selectedSchedaId = null;
$this->activeScheda = null;
}
public function setSchedaTab(string $tab): void
{
$this->schedaTab = $tab;
}
// Automated Workflow Actions
public function eseguiChiusuraRiparato(): void
{
if (! $this->selectedSchedaId) {
return;
}
$scheda = AssistenzaTecnorepairScheda::query()->find($this->selectedSchedaId);
if (! $scheda instanceof AssistenzaTecnorepairScheda) {
return;
}
$service = app(TecnoRepairArchiveService::class);
$service->chiudiComeRiparato($scheda, $this->workflowNote ?: 'Intervento concluso e collaudato con successo.');
Notification::make()->title('Pratica segnata come RIPARATO con successo')->success()->send();
$this->openScheda($this->selectedSchedaId);
$this->refreshData();
}
public function eseguiChiusuraRottamazione(): void
{
if (! $this->selectedSchedaId) {
return;
}
$scheda = AssistenzaTecnorepairScheda::query()->find($this->selectedSchedaId);
if (! $scheda instanceof AssistenzaTecnorepairScheda) {
return;
}
$service = app(TecnoRepairArchiveService::class);
$service->chiudiComeRottamato(
$scheda,
$this->workflowMotivoRottamazione ?: 'Apparato non riparabile convenientemente',
$this->workflowComeRicambi
);
$label = $this->workflowComeRicambi ? 'destinato a recupero ricambi' : 'contrassegnato per rottamazione';
Notification::make()->title("Apparato {$label}")->warning()->send();
$this->openScheda($this->selectedSchedaId);
$this->refreshData();
}
public function eseguiResoFornitoreRma(): void
{
if (! $this->selectedSchedaId) {
return;
}
$scheda = AssistenzaTecnorepairScheda::query()->find($this->selectedSchedaId);
if (! $scheda instanceof AssistenzaTecnorepairScheda) {
return;
}
$service = app(TecnoRepairArchiveService::class);
$service->rendiAFornitoreRma(
$scheda,
$this->workflowRmaCode,
$this->workflowFornitoreResoId ?: 392,
$this->workflowNote ?: 'Reso in garanzia al produttore / fornitore'
);
Notification::make()->title("Pratica inviata in RMA/Garanzia (RMA: {$scheda->rma_code})")->info()->send();
$this->openScheda($this->selectedSchedaId);
$this->refreshData();
}
public function sincronizzaMdb(): void
{
try {
$service = app(TecnoRepairArchiveService::class);
$stats = $service->importArchive(13, (int) $this->fornitoreId);
$msg = sprintf(
'Archivio MDB TecnoRepair sincronizzato: %d schede (%d create, %d agg.), %d ricambi, %d seriali.',
$stats['schede_lette'],
$stats['schede_create'],
$stats['schede_aggiornate'],
$stats['ricambi_catalogati'],
$stats['seriali_allineati']
);
Notification::make()->title($msg)->success()->send();
$this->refreshData();
} catch (\Throwable $e) {
Notification::make()->title('Errore sincronizzazione TecnoRepair: ' . $e->getMessage())->danger()->send();
}
}
public function sincronizzaContabilita(): void
{
try {
$service = app(ContabilitaMysqlSerialImportService::class);
$stats = $service->importForSupplier('NCOMSRL');
$msg = sprintf(
'Contabilità MySQL sincronizzata: %d fatture lette, %d seriali aggiornati.',
$stats['invoices_count'],
$stats['serials_created'] + $stats['serials_updated']
);
Notification::make()->title($msg)->success()->send();
$this->refreshData();
} catch (\Throwable $e) {
Notification::make()->title('Errore sincronizzazione Contabilità: ' . $e->getMessage())->danger()->send();
}
}
private function extractBrand(?string $model): string
{
$model = trim((string) $model);
if ($model === '') {
return '-';
}
$firstWord = strtoupper(explode(' ', $model)[0] ?? '');
if (in_array($firstWord, ['HP', 'LENOVO', 'DELL', 'ASUS', 'ACER', 'APPLE', 'TOSHIBA', 'MSI', 'SAMSUNG', 'HANSUNG'], true)) {
return $firstWord;
}
return $firstWord ?: '-';
}
}