Skip to content

Instantly share code, notes, and snippets.

@muath-ye
Created October 29, 2024 17:13
Show Gist options
  • Select an option

  • Save muath-ye/75d2829dbab581763db3c1402f28b3bc to your computer and use it in GitHub Desktop.

Select an option

Save muath-ye/75d2829dbab581763db3c1402f28b3bc to your computer and use it in GitHub Desktop.
الحصول على كمية المنتجات في مخزن معين خلال فترة زمنية محددة
<?php
use Illuminate\Http\Request;
use App\Models\InventoryTransaction;
use Carbon\Carbon;
public function getProductQuantityInWarehouse(Request $request)
{
$request->validate([
'product_id' => 'required|exists:products,id',
'warehouse_id' => 'required|exists:warehouses,id',
'start_date' => 'required|date',
'end_date' => 'required|date|after_or_equal:start_date',
]);
$productId = $request->product_id;
$warehouseId = $request->warehouse_id;
$startDate = Carbon::parse($request->start_date)->startOfDay();
$endDate = Carbon::parse($request->end_date)->endOfDay();
// استعلام عن الحركات المخزنية خلال الفترة المحددة
$transactions = InventoryTransaction::where('product_id', $productId)
->where('warehouse_id', $warehouseId)
->whereBetween('transaction_date', [$startDate, $endDate])
->get();
// حساب الكمية الصافية
$totalQuantity = $transactions->reduce(function ($carry, $transaction) {
return $carry + ($transaction->transaction_type === 'IN' ? $transaction->quantity : -$transaction->quantity);
}, 0);
return response()->json([
'product_id' => $productId,
'warehouse_id' => $warehouseId,
'total_quantity' => $totalQuantity,
'start_date' => $startDate->toDateString(),
'end_date' => $endDate->toDateString(),
]);
}

لإنشاء الاستعلامات الأساسية في MySQL لنظام إدارة المخازن، إليك قائمة بأهم العمليات، بدءًا من إنشاء الجداول وحتى استعلامات الإضافة والاستعلامات الخاصة بالحركات المخزنية.

1. إنشاء الجداول

جدول المنتجات (Products)

CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    sku VARCHAR(255) UNIQUE NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

جدول المستودعات (Warehouses)

CREATE TABLE warehouses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    location VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

جدول الربط بين المنتجات والمستودعات (ProductWarehouse)

CREATE TABLE product_warehouse (
    id INT AUTO_INCREMENT PRIMARY KEY,
    product_id INT NOT NULL,
    warehouse_id INT NOT NULL,
    quantity INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
    FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);

جدول الحركات المخزنية (InventoryTransactions)

CREATE TABLE inventory_transactions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    product_id INT NOT NULL,
    warehouse_id INT NOT NULL,
    transaction_date DATETIME NOT NULL,
    transaction_type ENUM('IN', 'OUT') NOT NULL,
    quantity INT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
    FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);

2. إضافة بيانات

إضافة منتج جديد

INSERT INTO products (name, sku, price) VALUES ('Product Name', 'SKU001', 100.00);

إضافة مستودع جديد

INSERT INTO warehouses (name, location) VALUES ('Warehouse A', 'Location A');

إضافة كمية منتج في مستودع

عند إضافة كمية أولية لمنتج معين في مستودع، يجب تحديث جدول product_warehouse:

INSERT INTO product_warehouse (product_id, warehouse_id, quantity) VALUES (1, 1, 100);

إضافة حركة مخزنية

يمكنك استخدام هذا الاستعلام لتسجيل إدخال أو إخراج منتج من مستودع معين:

INSERT INTO inventory_transactions (product_id, warehouse_id, transaction_date, transaction_type, quantity)
VALUES (1, 1, NOW(), 'IN', 50); -- إدخال كمية 50

لتسجيل إخراج كمية معينة:

INSERT INTO inventory_transactions (product_id, warehouse_id, transaction_date, transaction_type, quantity)
VALUES (1, 1, NOW(), 'OUT', 30); -- إخراج كمية 30

3. استعلامات البحث والحصول على البيانات

الاستعلام عن الكمية المتوفرة لمنتج في مستودع معين

هذا الاستعلام سيحسب الكمية الصافية للمنتج داخل المستودع، من خلال جمع عمليات الإدخال وطرح عمليات الإخراج.

SELECT 
    pw.product_id,
    pw.warehouse_id,
    (COALESCE(SUM(CASE WHEN it.transaction_type = 'IN' THEN it.quantity ELSE 0 END), 0) -
    COALESCE(SUM(CASE WHEN it.transaction_type = 'OUT' THEN it.quantity ELSE 0 END), 0)) AS total_quantity
FROM 
    product_warehouse pw
LEFT JOIN 
    inventory_transactions it ON pw.product_id = it.product_id AND pw.warehouse_id = it.warehouse_id
WHERE 
    pw.product_id = 1 AND pw.warehouse_id = 1
GROUP BY 
    pw.product_id, pw.warehouse_id;

الاستعلام عن الكمية المتوفرة خلال فترة زمنية محددة

يمكنك تخصيص استعلام لحساب الكميات خلال فترة زمنية معينة:

SELECT 
    pw.product_id,
    pw.warehouse_id,
    (COALESCE(SUM(CASE WHEN it.transaction_type = 'IN' THEN it.quantity ELSE 0 END), 0) -
    COALESCE(SUM(CASE WHEN it.transaction_type = 'OUT' THEN it.quantity ELSE 0 END), 0)) AS total_quantity
FROM 
    product_warehouse pw
LEFT JOIN 
    inventory_transactions it ON pw.product_id = it.product_id AND pw.warehouse_id = it.warehouse_id
WHERE 
    pw.product_id = 1 AND pw.warehouse_id = 1
    AND it.transaction_date BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY 
    pw.product_id, pw.warehouse_id;

4. تحديث الكميات بناءً على الحركات المخزنية

تحديث الكمية بعد إدخال أو إخراج منتج

يمكنك كتابة دالة تحديث تلقائية بحيث يتم تعديل الكمية في product_warehouse عند كل عملية إدخال أو إخراج في inventory_transactions.

UPDATE product_warehouse AS pw
JOIN (
    SELECT 
        product_id, 
        warehouse_id, 
        (SUM(CASE WHEN transaction_type = 'IN' THEN quantity ELSE 0 END) -
        SUM(CASE WHEN transaction_type = 'OUT' THEN quantity ELSE 0 END)) AS total_quantity
    FROM 
        inventory_transactions
    GROUP BY 
        product_id, warehouse_id
) AS tq ON pw.product_id = tq.product_id AND pw.warehouse_id = tq.warehouse_id
SET 
    pw.quantity = tq.total_quantity;

ملاحظات إضافية

  • جدولة تحديث الكميات: يمكنك استخدام دوال أو Triggers في MySQL لضبط الكميات تلقائيًا عند إضافة أي حركة جديدة.
  • الفحص قبل العمليات: قبل تسجيل حركة مخزنية من نوع OUT، يفضل فحص الكمية المتوفرة في المستودع لضمان عدم وجود نقص.
  • الربط في Laravel: يمكنك تطبيق الاستعلامات أعلاه باستخدام Eloquent في Laravel لتبسيط العمل مع قاعدة البيانات وجعل الشيفرة قابلة للصيانة.

إليك مقالة شاملة حول كيفية بناء نظام مخزني باستخدام Laravel يتضمن إدارة المنتجات، المستودعات، ربط المنتجات بالمستودعات، تتبع الحركات المخزنية، وإجراء استعلامات للحصول على الكميات المتاحة حسب الفترات الزمنية. سنوضح في هذه المقالة كيفية بناء جداول قاعدة البيانات، إنشاء النماذج، الربط بينها، وكذلك كتابة الشيفرات اللازمة لإدخال واستعلام الكميات المخزنية.


بناء نظام إدارة المخزون باستخدام Laravel

المتطلبات

قبل البدء، تأكد من تثبيت Laravel عبر الوثائق الرسمية.

1. إنشاء قاعدة البيانات والجداول

ابدأ بإنشاء قاعدة بيانات جديدة لنظام المخزون، ثم استخدم أوامر Migrations في Laravel لإنشاء الجداول التالية:

جدول المنتجات (Products)

php artisan make:migration create_products_table

في ملف التهجير (migration)، يمكنك إضافة حقول لجدول المنتجات:

// database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php
Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('sku')->unique();
    $table->decimal('price', 10, 2);
    $table->timestamps();
});

جدول المستودعات (Warehouses)

php artisan make:migration create_warehouses_table

إعداد جدول warehouses:

Schema::create('warehouses', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('location');
    $table->timestamps();
});

جدول الربط بين المنتجات والمستودعات (ProductWarehouse)

هذا الجدول سيسجل الكميات المتاحة لكل منتج في كل مستودع:

php artisan make:migration create_product_warehouse_table
Schema::create('product_warehouse', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->foreignId('warehouse_id')->constrained()->onDelete('cascade');
    $table->integer('quantity')->default(0);
    $table->timestamps();
});

جدول الحركات المخزنية (InventoryTransactions)

لتسجيل عمليات الإدخال والإخراج لكل منتج في كل مستودع:

php artisan make:migration create_inventory_transactions_table
Schema::create('inventory_transactions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->foreignId('warehouse_id')->constrained()->onDelete('cascade');
    $table->dateTime('transaction_date');
    $table->enum('transaction_type', ['IN', 'OUT']);
    $table->integer('quantity');
    $table->timestamps();
});

2. إنشاء النماذج والعلاقات

نموذج Product

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'sku', 'price'];

    public function warehouses()
    {
        return $this->belongsToMany(Warehouse::class, 'product_warehouse')
                    ->withPivot('quantity');
    }
}

نموذج Warehouse

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Warehouse extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'location'];

    public function products()
    {
        return $this->belongsToMany(Product::class, 'product_warehouse')
                    ->withPivot('quantity');
    }
}

نموذج InventoryTransaction

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class InventoryTransaction extends Model
{
    use HasFactory;

    protected $fillable = ['product_id', 'warehouse_id', 'transaction_date', 'transaction_type', 'quantity'];

    public function product()
    {
        return $this->belongsTo(Product::class);
    }

    public function warehouse()
    {
        return $this->belongsTo(Warehouse::class);
    }
}

3. إضافة حركة مخزنية وتحديث الكمية

دالة في Controller لإضافة الحركة المخزنية

public function addInventoryTransaction(Request $request)
{
    $request->validate([
        'product_id' => 'required|exists:products,id',
        'warehouse_id' => 'required|exists:warehouses,id',
        'transaction_type' => 'required|in:IN,OUT',
        'quantity' => 'required|integer|min:1',
    ]);

    DB::transaction(function () use ($request) {
        // إنشاء حركة مخزنية جديدة
        InventoryTransaction::create([
            'product_id' => $request->product_id,
            'warehouse_id' => $request->warehouse_id,
            'transaction_date' => now(),
            'transaction_type' => $request->transaction_type,
            'quantity' => $request->quantity,
        ]);

        // تحديث الكمية في جدول الربط
        $productWarehouse = ProductWarehouse::firstOrCreate(
            ['product_id' => $request->product_id, 'warehouse_id' => $request->warehouse_id],
            ['quantity' => 0]
        );

        if ($request->transaction_type == 'IN') {
            $productWarehouse->quantity += $request->quantity;
        } else {
            if ($productWarehouse->quantity < $request->quantity) {
                throw new \Exception("Insufficient quantity in stock");
            }
            $productWarehouse->quantity -= $request->quantity;
        }

        $productWarehouse->save();
    });

    return response()->json(['message' => 'Transaction added successfully']);
}

4. استعلام عن الكمية المتوفرة لمنتج معين خلال فترة زمنية

دالة في Controller للاستعلام

public function getProductQuantityInWarehouse(Request $request)
{
    $request->validate([
        'product_id' => 'required|exists:products,id',
        'warehouse_id' => 'required|exists:warehouses,id',
        'start_date' => 'required|date',
        'end_date' => 'required|date|after_or_equal:start_date',
    ]);

    $transactions = InventoryTransaction::where('product_id', $request->product_id)
        ->where('warehouse_id', $request->warehouse_id)
        ->whereBetween('transaction_date', [$request->start_date, $request->end_date])
        ->get();

    $totalQuantity = $transactions->reduce(function ($carry, $transaction) {
        return $carry + ($transaction->transaction_type === 'IN' ? $transaction->quantity : -$transaction->quantity);
    }, 0);

    return response()->json([
        'product_id' => $request->product_id,
        'warehouse_id' => $request->warehouse_id,
        'total_quantity' => $totalQuantity,
    ]);
}

5. إعداد RESTful Routes

في ملف routes/api.php، أضف المسارات الخاصة بالعمليات المخزنية:

use App\Http\Controllers\InventoryController;

Route::post('/add-transaction', [InventoryController::class, 'addInventoryTransaction']);
Route::get('/get-quantity', [InventoryController::class, 'getProductQuantityInWarehouse']);

6. اختبار النظام

لإضافة حركة مخزنية

يمكنك اختبار إضافة الحركة بإرسال طلب POST:

POST /api/add-transaction
{
    "product_id": 1,
    "warehouse_id": 1,
    "transaction_type": "IN",
    "quantity": 50
}

لاستعلام الكميات المتاحة

يمكنك استعلام عن الكميات بإرسال طلب GET مع المعلمات التالية:

GET /api/get-quantity?product_id=1&warehouse_id=1&start_date=2024-01-01&end_date=2024-12-31

خاتمة

باتباع هذه الخطوات، يمكنك بناء نظام إدارة مخزون متكامل باستخدام Laravel يشمل إضافة الحركات المخزنية، تتبع الكميات المتاحة لكل منتج في كل مستودع، واستعلام الكميات خلال فترات زمنية معينة.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment