---
description: حل مشكلة عدم تحديث أرصدة المخزون
alwaysApply: true
---

# حل مشكلة عدم تحديث أرصدة المخزون

## المشكلة

تم اكتشاف مشكلة في نظام المخزون حيث تظهر أرصدة المخزون بقيم صفرية في تقرير أرصدة المخزون على الرغم من وجود فواتير مشتريات ومبيعات تم إنشاؤها في النظام. هذا يشير إلى أن عمليات تحديث المخزون لا تعمل بشكل صحيح.

## تحليل المشكلة

بعد فحص الكود، تبين أن:

1. **آلية تحديث المخزون موجودة** في النظام وتعمل بشكل صحيح في بعض الحالات:
   - عند استلام أوامر الشراء (PurchaseOrderController@receive)
   - عند تعديل المخزون يدوياً (InventoryStockController@adjust)
   - عند نقل المخزون بين المستودعات (InventoryStockController@transfer)

2. **المشكلة الرئيسية**: فواتير المشتريات (PurchaseInvoiceController) لا تقوم بتحديث المخزون تلقائياً عند إنشائها أو اعتمادها.

3. **الحلقة المفقودة**: لا يوجد ربط بين إنشاء فاتورة المشتريات وتحديث أرصدة المخزون.

## الحل المقترح

### 1. إضافة وظيفة تحديث المخزون في PurchaseInvoiceController

يجب إضافة وظيفة لتحديث المخزون عند إنشاء أو اعتماد فاتورة المشتريات:

```php
/**
 * تحديث المخزون بناءً على فاتورة المشتريات
 *
 * @param PurchaseInvoice $invoice
 * @return void
 */
private function updateInventory(PurchaseInvoice $invoice)
{
    $companyId = $invoice->company_id;
    $warehouseId = $invoice->warehouse_id;
    
    foreach ($invoice->items as $invoiceItem) {
        $itemId = $invoiceItem->item_id;
        $quantity = $invoiceItem->quantity;
        
        // الحصول على سجل المخزون أو إنشاء واحد جديد
        $stock = InventoryStock::firstOrNew([
            'company_id' => $companyId,
            'warehouse_id' => $warehouseId,
            'item_id' => $itemId,
        ]);
        
        if (!$stock->exists) {
            $stock->quantity = 0;
            $stock->reserved_quantity = 0;
        }
        
        // زيادة كمية المخزون
        $stock->quantity += $quantity;
        $stock->save();
        
        // إنشاء معاملة مخزون
        $transaction = new InventoryTransaction();
        $transaction->company_id = $companyId;
        $transaction->transaction_type = 'purchase';
        $transaction->item_id = $itemId;
        $transaction->warehouse_id = $warehouseId;
        $transaction->quantity = $quantity;
        $transaction->unit_price = $invoiceItem->unit_price;
        $transaction->reference_number = $invoice->invoice_number;
        $transaction->reference_type = 'purchase_invoice';
        $transaction->reference_id = $invoice->id;
        $transaction->created_by = auth()->id();
        $transaction->notes = 'تم إضافة المخزون من فاتورة المشتريات #' . $invoice->invoice_number;
        $transaction->save();
    }
}
```

### 2. استدعاء وظيفة تحديث المخزون

يجب استدعاء وظيفة تحديث المخزون في الأماكن المناسبة:

1. **عند إنشاء فاتورة جديدة** (في دالة store):

```php
public function store(Request $request)
{
    // الكود الحالي لإنشاء الفاتورة
    
    DB::beginTransaction();
    
    try {
        // إنشاء فاتورة المشتريات
        $invoice = PurchaseInvoice::create([
            // البيانات الحالية
        ]);
        
        // إضافة عناصر الفاتورة
        foreach ($validated['items'] as $item) {
            // الكود الحالي لإضافة العناصر
        }
        
        // تحديث المخزون - إضافة هذا السطر
        $this->updateInventory($invoice);
        
        DB::commit();
        
        return redirect()->route('inventory.purchase-invoices.show', $invoice)
            ->with('success', 'تم إنشاء فاتورة المشتريات وتحديث المخزون بنجاح');
    } catch (\Exception $e) {
        DB::rollBack();
        // الكود الحالي للتعامل مع الأخطاء
    }
}
```

2. **عند تحديث فاتورة** (في دالة update):

```php
public function update(Request $request, PurchaseInvoice $purchaseInvoice)
{
    // الكود الحالي للتحقق والتحديث
    
    DB::beginTransaction();
    
    try {
        // حفظ الكميات القديمة قبل التحديث
        $oldItems = $purchaseInvoice->items->keyBy('id')->toArray();
        
        // تحديث فاتورة المشتريات
        $purchaseInvoice->update([
            // البيانات الحالية
        ]);
        
        // تحديث العناصر
        // الكود الحالي لتحديث العناصر
        
        // إعادة تحميل العناصر بعد التحديث
        $purchaseInvoice->refresh();
        
        // تحديث المخزون بناءً على التغييرات
        $this->updateInventoryOnEdit($purchaseInvoice, $oldItems);
        
        DB::commit();
        
        return redirect()->route('inventory.purchase-invoices.show', $purchaseInvoice)
            ->with('success', 'تم تحديث فاتورة المشتريات وتحديث المخزون بنجاح');
    } catch (\Exception $e) {
        DB::rollBack();
        // الكود الحالي للتعامل مع الأخطاء
    }
}

/**
 * تحديث المخزون عند تعديل فاتورة المشتريات
 *
 * @param PurchaseInvoice $invoice
 * @param array $oldItems
 * @return void
 */
private function updateInventoryOnEdit(PurchaseInvoice $invoice, array $oldItems)
{
    // يجب تنفيذ منطق لحساب الفرق بين الكميات القديمة والجديدة
    // وتحديث المخزون بناءً على هذه الفروق
}
```

### 3. إضافة وظيفة لإعادة حساب أرصدة المخزون

يجب إضافة وظيفة لإعادة حساب أرصدة المخزون بناءً على المعاملات السابقة:

```php
/**
 * إعادة حساب أرصدة المخزون بناءً على المعاملات
 *
 * @return void
 */
public function recalculateInventory()
{
    $companyId = auth()->user()->company_id;
    
    DB::beginTransaction();
    
    try {
        // إعادة تعيين جميع أرصدة المخزون إلى صفر
        InventoryStock::where('company_id', $companyId)->update([
            'quantity' => 0,
            'reserved_quantity' => 0
        ]);
        
        // الحصول على جميع معاملات المخزون مرتبة حسب التاريخ
        $transactions = InventoryTransaction::where('company_id', $companyId)
            ->orderBy('created_at')
            ->get();
        
        // إعادة تطبيق كل معاملة
        foreach ($transactions as $transaction) {
            $itemId = $transaction->item_id;
            $warehouseId = $transaction->warehouse_id;
            $quantity = $transaction->quantity;
            
            $stock = InventoryStock::firstOrNew([
                'company_id' => $companyId,
                'warehouse_id' => $warehouseId,
                'item_id' => $itemId,
            ]);
            
            if (!$stock->exists) {
                $stock->quantity = 0;
                $stock->reserved_quantity = 0;
            }
            
            // تحديث الكمية بناءً على نوع المعاملة
            switch ($transaction->transaction_type) {
                case 'purchase':
                case 'return_from_customer':
                case 'adjustment':
                    $stock->quantity += $quantity;
                    break;
                case 'sale':
                case 'return_to_supplier':
                case 'maintenance_usage':
                case 'write_off':
                    $stock->quantity -= abs($quantity);
                    break;
                case 'transfer':
                    // معالجة خاصة للتحويلات
                    if ($transaction->to_warehouse_id) {
                        $stock->quantity -= abs($quantity);
                        
                        $toStock = InventoryStock::firstOrNew([
                            'company_id' => $companyId,
                            'warehouse_id' => $transaction->to_warehouse_id,
                            'item_id' => $itemId,
                        ]);
                        
                        if (!$toStock->exists) {
                            $toStock->quantity = 0;
                            $toStock->reserved_quantity = 0;
                        }
                        
                        $toStock->quantity += $quantity;
                        $toStock->save();
                    }
                    break;
            }
            
            $stock->save();
        }
        
        DB::commit();
        
        return redirect()->route('inventory.stock.index')
            ->with('success', 'تم إعادة حساب أرصدة المخزون بنجاح');
    } catch (\Exception $e) {
        DB::rollBack();
        
        return redirect()->back()
            ->with('error', 'حدث خطأ أثناء إعادة حساب أرصدة المخزون: ' . $e->getMessage());
    }
}
```

### 4. إضافة زر لإعادة حساب أرصدة المخزون

يجب إضافة زر في واجهة المستخدم لإعادة حساب أرصدة المخزون:

```html
<a href="{{ route('inventory.stock.recalculate') }}" class="btn btn-warning">
    <i class="fas fa-sync"></i> إعادة حساب أرصدة المخزون
</a>
```

### 5. إضافة المسار الجديد

يجب إضافة مسار جديد في ملف `routes/inventory.php`:

```php
Route::get('stock/recalculate', [InventoryStockController::class, 'recalculateInventory'])->name('stock.recalculate');
```

## خطوات التنفيذ

1. تعديل ملف `PurchaseInvoiceController.php` لإضافة وظيفة تحديث المخزون
2. تعديل ملف `InventoryStockController.php` لإضافة وظيفة إعادة حساب أرصدة المخزون
3. تعديل ملف `routes/inventory.php` لإضافة المسار الجديد
4. تعديل ملف `resources/views/inventory/stock/index.blade.php` لإضافة زر إعادة حساب أرصدة المخزون

## ملاحظات هامة

1. يجب التأكد من أن جميع العمليات تتم داخل معاملات قاعدة البيانات (DB transactions) لضمان تكامل البيانات
2. يجب إضافة سجلات في جدول `inventory_transactions` لكل عملية تؤثر على المخزون
3. يجب التعامل مع حالات الخطأ بشكل مناسب وعرض رسائل واضحة للمستخدم
4. يجب اختبار الحل بشكل شامل للتأكد من أنه يعمل بشكل صحيح في جميع السيناريوهات