Files
2026-09-11 01:17:47 +02:00

123 lines
3.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Console\Commands;
use App\Models\Packing;
use App\Models\Product;
use App\Models\ProductInventory;
use App\Models\ProductSize;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class ImportShopify extends Command
{
protected $signature = 'import:shopify {file : Pfad zur Shopify Inventory-CSV}';
protected $description = 'Importiert Produkte aus einem Shopify Inventory-Export';
public function handle(): int
{
$file = $this->argument('file');
if (! file_exists($file)) {
$this->error("Datei nicht gefunden: $file");
return self::FAILURE;
}
$rows = $this->parseCsv($file);
if (empty($rows)) {
$this->error('CSV ist leer oder konnte nicht gelesen werden.');
return self::FAILURE;
}
$byHandle = collect($rows)->groupBy('Handle');
$this->info("Gefunden: {$byHandle->count()} Produkte, " . count($rows) . ' Varianten');
$stats = ['products' => 0, 'sizes' => 0, 'inventory' => 0, 'skipped' => 0];
DB::transaction(function () use ($byHandle, &$stats) {
foreach ($byHandle as $variants) {
$first = $variants->first();
$product = Product::firstOrCreate(
['title' => $first['Title']],
['recipes_id' => null],
);
if ($product->wasRecentlyCreated) {
$stats['products']++;
}
foreach ($variants as $row) {
$packingRaw = $row['Option1 Value'];
// Format: "Standbodenbeutel 50 g" oder "Metalldose 40 g"
if (! preg_match('/^(.+?)\s+(\d+(?:[.,]\d+)?)\s*g$/i', $packingRaw, $m)) {
$this->warn(" ⚠ Verpackungsformat nicht erkannt: '$packingRaw' übersprungen");
$stats['skipped']++;
continue;
}
$packing = Packing::firstOrCreate([
'name' => trim($m[1]),
'size' => (float) str_replace(',', '.', $m[2]),
]);
$sku = $row['SKU'] ?: null;
$size = ProductSize::firstOrCreate(
['product_id' => $product->id, 'packing_id' => $packing->id],
['sku' => $sku, 'price' => 0],
);
if ($size->wasRecentlyCreated) {
$stats['sizes']++;
}
$onHand = (int) $row['On hand (current)'];
for ($i = 0; $i < $onHand; $i++) {
ProductInventory::create([
'product_id' => $product->id,
'size_id' => $size->id,
]);
$stats['inventory']++;
}
}
}
});
$this->info("✓ {$stats['products']} Produkte erstellt");
$this->info("✓ {$stats['sizes']} Varianten erstellt");
$this->info("✓ {$stats['inventory']} Lagereinträge erstellt");
if ($stats['skipped'] > 0) {
$this->warn("⚠ {$stats['skipped']} Zeilen übersprungen (Verpackungsformat unbekannt)");
}
return self::SUCCESS;
}
private function parseCsv(string $file): array
{
$rows = [];
$headers = null;
$handle = fopen($file, 'r');
if ($handle === false) {
return [];
}
while (($data = fgetcsv($handle, 0, ',')) !== false) {
if ($headers === null) {
$headers = array_map('trim', $data);
continue;
}
if (count($data) === count($headers)) {
$rows[] = array_combine($headers, $data);
}
}
fclose($handle);
return $rows;
}
}