Deploy / deploy (push) Successful in 14s
- Migration: shopify_variant_id on products_sizes, shopify_order_id on products_sales - ShopifyService: paginated REST client (2024-10 API) - shopify:map — one-time command to link variants by SKU - shopify:sync — reconciliation command (Shopify is master) - VerifyShopifyWebhook middleware: HMAC-SHA256 verification - ShopifyWebhookController: idempotent orders/paid handler, FIFO inventory deduction - Webhook route: POST /api/webhooks/shopify/orders-paid Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.8 KiB
PHP
79 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\ProductInventory;
|
|
use App\Models\ProductSize;
|
|
use App\Services\ShopifyService;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class ShopifySync extends Command
|
|
{
|
|
protected $signature = 'shopify:sync {--dry-run : Änderungen nur anzeigen, nichts speichern}';
|
|
protected $description = 'Gleicht lokalen Lagerbestand mit Shopify-Inventar ab';
|
|
|
|
public function handle(ShopifyService $shopify): int
|
|
{
|
|
$dryRun = $this->option('dry-run');
|
|
$variants = $shopify->getAllVariants();
|
|
|
|
$added = 0;
|
|
$removed = 0;
|
|
$inSync = 0;
|
|
|
|
DB::transaction(function () use ($variants, $dryRun, &$added, &$removed, &$inSync) {
|
|
foreach ($variants as $variant) {
|
|
$size = ProductSize::where('shopify_variant_id', $variant['id'])->first();
|
|
|
|
if (!$size) {
|
|
continue; // not mapped yet — run shopify:map first
|
|
}
|
|
|
|
$shopifyQty = $variant['inventory_quantity'];
|
|
$localQty = ProductInventory::where('size_id', $size->id)
|
|
->whereNull('out_at')
|
|
->count();
|
|
|
|
if ($localQty === $shopifyQty) {
|
|
$inSync++;
|
|
continue;
|
|
}
|
|
|
|
$diff = $shopifyQty - $localQty;
|
|
|
|
if ($diff > 0) {
|
|
// Shopify hat mehr → Einträge hinzufügen
|
|
$this->line(" +{$diff} {$variant['product_title']} (SKU {$variant['sku']})");
|
|
if (!$dryRun) {
|
|
for ($i = 0; $i < $diff; $i++) {
|
|
ProductInventory::create([
|
|
'product_id' => $size->product_id,
|
|
'size_id' => $size->id,
|
|
]);
|
|
}
|
|
}
|
|
$added += $diff;
|
|
} else {
|
|
// Shopify hat weniger → älteste Einträge als ausgebucht markieren
|
|
$excess = abs($diff);
|
|
$this->line(" -{$excess} {$variant['product_title']} (SKU {$variant['sku']})");
|
|
if (!$dryRun) {
|
|
ProductInventory::where('size_id', $size->id)
|
|
->whereNull('out_at')
|
|
->oldest()
|
|
->limit($excess)
|
|
->update(['out_at' => now()]);
|
|
}
|
|
$removed += $excess;
|
|
}
|
|
}
|
|
});
|
|
|
|
$this->info("✓ Sync abgeschlossen" . ($dryRun ? ' (dry-run)' : ''));
|
|
$this->info(" In Sync: {$inSync} | Hinzugefügt: {$added} | Ausgebucht: {$removed}");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|