120 lines
4.4 KiB
PHP
Executable File
120 lines
4.4 KiB
PHP
Executable File
<?php
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Product;
|
|
use App\Services\Catalog\AmazonCreatorsCatalogSyncService;
|
|
use Illuminate\Console\Command;
|
|
use RuntimeException;
|
|
|
|
class SyncAmazonCreatorsCatalogCommand extends Command
|
|
{
|
|
protected $signature = 'catalog:sync-amazon-creators
|
|
{product : ID o internal_code del prodotto}
|
|
{--asin= : ASIN da forzare}
|
|
{--query= : Query da forzare per SearchItems}
|
|
{--json= : File JSON con payload item o risposta SDK/API}
|
|
{--marketplace= : Marketplace, es. www.amazon.it}
|
|
{--dry-run : Risolve il mapping senza scrivere offerte/media/codici}';
|
|
|
|
protected $description = 'Sincronizza un prodotto NetGescon con Amazon Creators API o con un payload JSON esportato dallo SDK.';
|
|
|
|
public function handle(AmazonCreatorsCatalogSyncService $syncService): int
|
|
{
|
|
$product = $this->resolveProduct((string) $this->argument('product'));
|
|
if (! $product instanceof Product) {
|
|
$this->error('Prodotto non trovato.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
try {
|
|
$options = [
|
|
'asin' => trim((string) $this->option('asin')),
|
|
'query' => trim((string) $this->option('query')),
|
|
'marketplace' => trim((string) $this->option('marketplace')),
|
|
'dry_run' => (bool) $this->option('dry-run'),
|
|
'source' => 'api',
|
|
];
|
|
|
|
$jsonPath = trim((string) $this->option('json'));
|
|
if ($jsonPath !== '') {
|
|
$options['response'] = $this->readJsonFile($jsonPath);
|
|
$options['source'] = 'json';
|
|
}
|
|
|
|
$result = $syncService->syncProduct($product, $options);
|
|
} catch (\Throwable $e) {
|
|
$this->error($e->getMessage());
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->table(
|
|
['Campo', 'Valore'],
|
|
[
|
|
['Prodotto', (string) ($product->internal_code ?: ('#' . $product->id)) . ' · ' . (string) ($product->name ?? '')],
|
|
['Fonte', (string) ($result['source'] ?? 'api')],
|
|
['Dry run', ! empty($result['dry_run']) ? 'si' : 'no'],
|
|
['ASIN', (string) ($result['asin'] ?? '')],
|
|
['EAN', (string) ($result['ean'] ?? '')],
|
|
['Titolo Amazon', (string) ($result['title'] ?? '')],
|
|
['Prezzo', $this->formatMoney($result['price_amount'] ?? null, (string) ($result['currency'] ?? 'EUR'))],
|
|
['Prezzo listino', $this->formatMoney($result['price_compare_at'] ?? null, (string) ($result['currency'] ?? 'EUR'))],
|
|
['Disponibilita', (string) ($result['availability'] ?? '')],
|
|
['Detail URL', (string) ($result['detail_url'] ?? '')],
|
|
['Referral URL', (string) ($result['referral_url'] ?? '')],
|
|
['Immagine', (string) ($result['image_url'] ?? '')],
|
|
['Codici creati', (string) ($result['identifiers_created'] ?? 0)],
|
|
['Media creati', (string) ($result['media_created'] ?? 0)],
|
|
['Offer ID', (string) ($result['offer_id'] ?? '')],
|
|
]
|
|
);
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function resolveProduct(string $value): ?Product
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
|
|
$query = Product::query();
|
|
|
|
if (is_numeric($value)) {
|
|
$product = (clone $query)->find((int) $value);
|
|
if ($product instanceof Product) {
|
|
return $product;
|
|
}
|
|
}
|
|
|
|
return (clone $query)
|
|
->where('internal_code', $value)
|
|
->orWhere('canonical_key', $value)
|
|
->first();
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function readJsonFile(string $path): array
|
|
{
|
|
if (! is_file($path)) {
|
|
throw new RuntimeException('File JSON non trovato: ' . $path);
|
|
}
|
|
|
|
$decoded = json_decode((string) file_get_contents($path), true);
|
|
if (! is_array($decoded)) {
|
|
throw new RuntimeException('Il file JSON non contiene un payload valido.');
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
private function formatMoney(mixed $amount, string $currency): string
|
|
{
|
|
return is_numeric($amount)
|
|
? number_format((float) $amount, 2, ',', '.') . ' ' . strtoupper(trim($currency) !== '' ? $currency : 'EUR')
|
|
: '-';
|
|
}
|
|
}
|