chengwl 3 päivää sitten
vanhempi
commit
fda4f57f77
1 muutettua tiedostoa jossa 109 lisäystä ja 0 poistoa
  1. 109 0
      packages/Longyi/Core/src/Listeners/VariantInventoryListener.php

+ 109 - 0
packages/Longyi/Core/src/Listeners/VariantInventoryListener.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace Longyi\Core\Listeners;
+
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Longyi\Core\Models\ProductVariant;
+
+/**
+ * Keeps `product_variants.quantity` in sync with order lifecycle events.
+ *
+ * Bagisto's OrderItemRepository::manageInventory only knows about the standard
+ * `product_inventories` / `product_ordered_inventories` tables; flexible_variant
+ * carries inventory on its own table, so we adjust it here.
+ *
+ * Strategy: decrement at order save, restore on cancel. Refunds are not handled
+ * yet — extend with `sales.refund.save.after` if needed.
+ */
+class VariantInventoryListener
+{
+    /**
+     * Decrement variant stock for every order item that carries a `variant_id`.
+     */
+    public function decrementOnOrderSave($order): void
+    {
+        if (! $order || ! method_exists($order, 'getRelationValue')) {
+            return;
+        }
+
+        try {
+            DB::transaction(function () use ($order) {
+                foreach ($order->items as $item) {
+                    $variantId = $this->extractVariantId($item);
+                    $qty = (int) ($item->qty_ordered ?? 0);
+
+                    if (! $variantId || $qty <= 0) {
+                        continue;
+                    }
+
+                    ProductVariant::whereKey($variantId)->decrement('quantity', $qty);
+                }
+            });
+        } catch (\Throwable $e) {
+            Log::error('VariantInventoryListener::decrementOnOrderSave failed', [
+                'order_id' => $order->id ?? null,
+                'error'    => $e->getMessage(),
+            ]);
+        }
+    }
+
+    /**
+     * Restore variant stock when an order (or a portion of it) is cancelled.
+     */
+    public function restoreOnOrderCancel($order): void
+    {
+        if (! $order) {
+            return;
+        }
+
+        try {
+            DB::transaction(function () use ($order) {
+                foreach ($order->items as $item) {
+                    $variantId = $this->extractVariantId($item);
+
+                    if (! $variantId) {
+                        continue;
+                    }
+
+                    $remaining = (int) ($item->qty_ordered ?? 0)
+                        - (int) ($item->qty_canceled ?? 0)
+                        - (int) ($item->qty_refunded ?? 0);
+
+                    if ($remaining > 0) {
+                        ProductVariant::whereKey($variantId)->increment('quantity', $remaining);
+                    }
+                }
+            });
+        } catch (\Throwable $e) {
+            Log::error('VariantInventoryListener::restoreOnOrderCancel failed', [
+                'order_id' => $order->id ?? null,
+                'error'    => $e->getMessage(),
+            ]);
+        }
+    }
+
+    /**
+     * Pull `variant_id` out of an order item's `additional` payload.
+     *
+     * `additional` is normally a JSON column cast to array, but be defensive in case
+     * it arrives as a JSON string.
+     */
+    protected function extractVariantId($item): ?int
+    {
+        $additional = $item->additional ?? null;
+
+        if (is_string($additional)) {
+            $decoded = json_decode($additional, true);
+            $additional = is_array($decoded) ? $decoded : null;
+        }
+
+        if (! is_array($additional)) {
+            return null;
+        }
+
+        $variantId = $additional['variant_id'] ?? null;
+
+        return $variantId ? (int) $variantId : null;
+    }
+}