Files
SpiceManagementSystem/app/Console/Commands/ShopifyImport.php
T
Malek TellissiandClaude Sonnet 4.6 32e196719a
Deploy / deploy (push) Successful in 13s
Extend shopify:import to handle Stück, Stangen and Default Title variants
Parses additional packaging formats from option1 and falls back to SKU
parsing for single-variant products with 'Default Title'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-11 15:02:12 +02:00

131 lines
4.9 KiB
PHP
Raw 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 App\Services\ShopifyService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class ShopifyImport extends Command
{
protected $signature = 'shopify:import {--dry-run : Nur anzeigen, nichts speichern}';
protected $description = 'Importiert Produkte und Lagerbestand direkt aus der Shopify API';
public function handle(ShopifyService $shopify): int
{
$dryRun = $this->option('dry-run');
$variants = $shopify->getAllVariants();
if (empty($variants)) {
$this->error('Keine Varianten von Shopify erhalten (Details im Log).');
return self::FAILURE;
}
$this->info('Shopify-Varianten geladen: ' . count($variants));
$stats = ['products' => 0, 'sizes' => 0, 'inventory' => 0, 'skipped' => 0];
DB::transaction(function () use ($variants, $dryRun, &$stats) {
foreach ($variants as $variant) {
$packingRaw = $variant['option1'];
$parsed = $this->parsePacking($packingRaw, $variant['sku']);
if ($parsed === null) {
$this->warn(" ⚠ Verpackungsformat nicht erkannt: '{$packingRaw}' ({$variant['sku']}) – übersprungen");
$stats['skipped']++;
continue;
}
if ($dryRun) {
$this->line(" {$variant['product_title']} | {$parsed['name']} {$parsed['size']} | SKU {$variant['sku']} | Bestand {$variant['inventory_quantity']}");
continue;
}
$packing = Packing::firstOrCreate([
'name' => $parsed['name'],
'size' => $parsed['size'],
]);
$product = Product::firstOrCreate(
['title' => $variant['product_title']],
['recipes_id' => null],
);
if ($product->wasRecentlyCreated) {
$stats['products']++;
}
$size = ProductSize::firstOrCreate(
['product_id' => $product->id, 'packing_id' => $packing->id],
['sku' => $variant['sku'], 'shopify_variant_id' => $variant['id'], 'price' => 0],
);
// Bestehende Size ohne Variant-ID nachträglich verknüpfen
if (! $size->wasRecentlyCreated && ! $size->shopify_variant_id) {
$size->update(['shopify_variant_id' => $variant['id'], 'sku' => $variant['sku']]);
}
if ($size->wasRecentlyCreated) {
$stats['sizes']++;
}
// Lagerbestand: aktuelle Differenz auffüllen
$localQty = ProductInventory::where('size_id', $size->id)->whereNull('out_at')->count();
$diff = $variant['inventory_quantity'] - $localQty;
if ($diff > 0) {
for ($i = 0; $i < $diff; $i++) {
ProductInventory::create([
'product_id' => $product->id,
'size_id' => $size->id,
]);
}
$stats['inventory'] += $diff;
}
}
});
if (! $dryRun) {
$this->info("✓ {$stats['products']} Produkte erstellt");
$this->info("✓ {$stats['sizes']} Varianten erstellt");
$this->info("✓ {$stats['inventory']} Lagereinträge erstellt");
}
if ($stats['skipped']) {
$this->warn("⚠ {$stats['skipped']} Varianten übersprungen (Verpackungsformat unbekannt)");
}
return self::SUCCESS;
}
private function parsePacking(string $option, string $sku): ?array
{
// "Standbodenbeutel 100 g" / "Metalldose 40 g"
if (preg_match('/^(.+?)\s+(\d+(?:[.,]\d+)?)\s*g$/i', $option, $m)) {
return ['name' => trim($m[1]), 'size' => (float) str_replace(',', '.', $m[2])];
}
// "Metalldose 5 Stück" / "Standbodenbeutel 3 Stück"
if (preg_match('/^(.+?)\s+(\d+)\s*Stück$/i', $option, $m)) {
return ['name' => trim($m[1]) . ' (Stück)', 'size' => (float) $m[2]];
}
// "1 Stange" / "3 Stangen" / "5 Stangen"
if (preg_match('/^(\d+)\s*Stangen?$/i', $option, $m)) {
return ['name' => 'Stangen', 'size' => (float) $m[1]];
}
// "Default Title" — Verpackung aus SKU ableiten: z.B. GWM-AAS-100SB → 100g Standbodenbeutel
if ($option === 'Default Title' && preg_match('/(\d+)(SB|MD|GT|BB)$/i', $sku, $m)) {
$names = ['SB' => 'Standbodenbeutel', 'MD' => 'Metalldose', 'GT' => 'Glas', 'BB' => 'Beutel'];
return ['name' => $names[strtoupper($m[2])], 'size' => (float) $m[1]];
}
return null;
}
}