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

233 lines
8.4 KiB
PHP

<?php
namespace App\Filament\Pages\Fornitore;
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
use App\Models\Fornitore;
use App\Models\Product;
use App\Models\ProductIdentifier;
use App\Models\ProductOffer;
use App\Models\User;
use App\Services\Catalog\AmazonCreatorsApiService;
use App\Services\Catalog\AmazonCreatorsCatalogSyncService;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Support\Facades\Auth;
use RuntimeException;
use UnitEnum;
class ProdottiCatalogo extends Page
{
use ResolvesOperatoreContext;
protected static ?string $navigationLabel = 'Prodotti';
protected static ?string $title = 'Catalogo prodotti fornitore';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-archive-box';
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
protected static ?int $navigationSort = 4;
protected static ?string $slug = 'fornitore/prodotti';
protected string $view = 'filament.pages.fornitore.prodotti-catalogo';
public ?int $fornitoreId = null;
public ?string $fornitoreLabel = null;
public bool $missingAdminContext = false;
public string $search = '';
public string $amazonQuery = '';
public bool $amazonConfigured = false;
/** @var array<int, array<string, mixed>> */
public array $amazonRows = [];
/** @var array<int, array<string, mixed>> */
public array $rows = [];
public static function canAccess(): bool
{
$user = Auth::user();
return $user instanceof User
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', '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);
$this->amazonConfigured = app(AmazonCreatorsApiService::class)->isConfigured();
$this->refreshRows();
}
public function updatedSearch(): void
{
$this->refreshRows();
}
public function refreshRows(): void
{
if (! $this->fornitoreId) {
$this->rows = [];
return;
}
$term = trim($this->search);
$normalized = preg_replace('/[^A-Za-z0-9]+/', '', strtoupper($term)) ?? '';
$products = Product::query()
->with(['identifiers', 'offers'])
->where('is_active', true)
->where(function ($query): void {
$query->whereNull('default_fornitore_id')
->orWhere('default_fornitore_id', (int) $this->fornitoreId)
->orWhereHas('offers', fn($offerQuery) => $offerQuery->where('fornitore_id', (int) $this->fornitoreId));
})
->when($term !== '', function ($query) use ($term, $normalized): void {
$query->where(function ($builder) use ($term, $normalized): void {
$builder->where('name', 'like', '%' . $term . '%')
->orWhere('brand', 'like', '%' . $term . '%')
->orWhere('model', 'like', '%' . $term . '%')
->orWhere('internal_code', 'like', '%' . $term . '%');
if ($normalized !== '') {
$builder->orWhereHas('identifiers', function ($identifierQuery) use ($normalized): void {
$identifierQuery->where('normalized_code', $normalized)
->orWhere('code_value', 'like', '%' . $normalized . '%');
});
}
});
})
->limit(120)
->get();
$this->rows = $products->map(function (Product $product): array {
$identifier = $product->identifiers
->first(fn(ProductIdentifier $item): bool => (int) ($item->fornitore_id ?? 0) === (int) $this->fornitoreId) ?? $product->identifiers->first();
$offer = $product->offers
->first(fn(ProductOffer $item): bool => (int) ($item->fornitore_id ?? 0) === (int) $this->fornitoreId) ?? $product->offers->first();
return [
'id' => (int) $product->id,
'internal_code' => (string) ($product->internal_code ?? ''),
'label' => trim(implode(' ', array_filter([(string) $product->name, (string) ($product->brand ?? ''), (string) ($product->model ?? '')]))),
'barcode' => (string) ($identifier->code_value ?? ''),
'source' => (string) ($offer?->source_name ?: 'Catalogo interno'),
'price' => $offer?->price_amount !== null ? number_format((float) $offer->price_amount, 2, ',', '.') . ' ' . (string) ($offer->currency ?? 'EUR') : '-',
'external_url' => (string) ($offer?->referral_url ?: $offer?->external_url ?: $this->buildAmazonSearchUrl((string) $product->name)),
];
})->all();
}
public function searchAmazonCatalog(): void
{
if (! $this->amazonConfigured) {
Notification::make()
->title('Amazon Creators API non configurata')
->body('Valorizza prima le variabili AMAZON_CREATORS_* in ambiente.')
->danger()
->send();
return;
}
$query = trim($this->amazonQuery);
if ($query === '') {
Notification::make()->title('Inserisci una query Amazon')->warning()->send();
return;
}
try {
$this->amazonRows = app(AmazonCreatorsCatalogSyncService::class)
->searchCatalog($query, 8);
} catch (RuntimeException $e) {
$this->amazonRows = [];
Notification::make()->title('Ricerca Amazon non riuscita')->body($e->getMessage())->danger()->send();
return;
}
if ($this->amazonRows === []) {
Notification::make()->title('Nessun risultato Amazon')->warning()->send();
}
}
public function saveAmazonCandidate(string $asin): void
{
$asin = trim($asin);
$row = collect($this->amazonRows)->first(fn(array $item): bool => (string) ($item['asin'] ?? '') === $asin);
if (! is_array($row) || ! is_array($row['item_payload'] ?? null)) {
Notification::make()->title('Risultato Amazon non disponibile')->danger()->send();
return;
}
$fornitore = $this->fornitoreId ? Fornitore::query()->find((int) $this->fornitoreId) : null;
if (! $fornitore instanceof Fornitore) {
Notification::make()->title('Fornitore non trovato')->danger()->send();
return;
}
try {
$result = app(AmazonCreatorsCatalogSyncService::class)
->importItemForSupplier($fornitore, $row['item_payload'], ['source' => 'amazon_catalog_search_ui']);
} catch (RuntimeException $e) {
Notification::make()->title('Import Amazon non riuscito')->body($e->getMessage())->danger()->send();
return;
}
$product = $result['product'] ?? null;
Notification::make()
->title(! empty($result['created']) ? 'Prodotto Amazon memorizzato' : 'Prodotto Amazon aggiornato')
->body($product instanceof Product
? 'Articolo salvato nel catalogo come candidato da verificare: ' . ((string) ($product->internal_code ?? '') !== '' ? $product->internal_code . ' · ' : '') . (string) ($product->name ?? '')
: 'Import completato.')
->success()
->send();
$this->refreshRows();
$this->searchAmazonCatalog();
}
public function getLavorazioniUrl(): string
{
return LavorazioniOperative::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament');
}
public function getRubricaUrl(): string
{
return RubricaClienti::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament');
}
protected function buildAmazonSearchUrl(string $term): string
{
$query = ['k' => trim($term)];
$tag = trim((string) config('services.amazon.referral_tag', ''));
if ($tag !== '') {
$query['tag'] = $tag;
}
return 'https://www.amazon.it/s?' . http_build_query($query);
}
}