263 lines
11 KiB
PHP
263 lines
11 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 Illuminate\Support\Str;
|
|
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);
|
|
}
|
|
|
|
/** @return array<int, array<string, mixed>> */
|
|
public function getAmazonSetupChecklistProperty(): array
|
|
{
|
|
$items = [
|
|
['key' => 'AMAZON_CREATORS_ENABLED', 'required' => true, 'value' => (bool) config('services.amazon.creators_enabled', false) ? 'true' : 'false'],
|
|
['key' => 'AMAZON_CREATORS_API_BASE_URL', 'required' => true, 'value' => (string) config('services.amazon.creators_api_base_url', '')],
|
|
['key' => 'AMAZON_CREATORS_TOKEN_URL', 'required' => true, 'value' => (string) config('services.amazon.creators_token_url', '')],
|
|
['key' => 'AMAZON_CREATORS_CREDENTIAL_ID', 'required' => true, 'value' => (string) config('services.amazon.creators_credential_id', '')],
|
|
['key' => 'AMAZON_CREATORS_CREDENTIAL_SECRET', 'required' => true, 'value' => (string) config('services.amazon.creators_credential_secret', '')],
|
|
['key' => 'AMAZON_CREATORS_SEARCH_PATH', 'required' => true, 'value' => (string) data_get(config('services.amazon.creators_paths', []), 'searchItems', '')],
|
|
['key' => 'AMAZON_CREATORS_GET_ITEMS_PATH', 'required' => true, 'value' => (string) data_get(config('services.amazon.creators_paths', []), 'getItems', '')],
|
|
['key' => 'AMAZON_MARKETPLACE', 'required' => false, 'value' => (string) config('services.amazon.marketplace', 'www.amazon.it')],
|
|
['key' => 'AMAZON_REFERRAL_TAG', 'required' => false, 'value' => (string) config('services.amazon.referral_tag', '')],
|
|
];
|
|
|
|
return array_map(static function (array $item): array {
|
|
$rawValue = trim((string) ($item['value'] ?? ''));
|
|
$isBoolean = ($item['key'] ?? '') === 'AMAZON_CREATORS_ENABLED';
|
|
$configured = $isBoolean ? $rawValue === 'true' : $rawValue !== '';
|
|
|
|
return [
|
|
'key' => (string) ($item['key'] ?? ''),
|
|
'required' => (bool) ($item['required'] ?? false),
|
|
'configured' => $configured,
|
|
'display' => $rawValue !== '' ? Str::limit($rawValue, 54) : 'non valorizzata',
|
|
];
|
|
}, $items);
|
|
}
|
|
}
|