شركات التجزئة والتوزيع التي تدير المخزون عبر مستودعات أو فروع متعددة تصطدم في نهاية المطاف بنفس المشكلة المعمارية: النظام يعمل بشكل صحيح في موقعين ويصبح غير موثوق في عشرة. أنماط الفشل متوقعة وتأتي من الاستهانة بمتطلبات التزامن والاتساق.
إدارة المخزون عبر مستودعات أو فروع متعددة هي واحدة من أكثر المشكلات المعمارية تعقيداً في قطاع التوزيع والتجزئة. المشكلة لا تكمن فقط في تتبع الكميات، بل في ضمان أن كل نظام يقرأ المخزون في اللحظة الصحيحة يحصل على بيانات دقيقة، مع عدم التأثير على الأداء عند الاستفسار المتزامن من عدة فروع في لبنان أو المنطقة.
المشكلة الأساسية في نموذج البيانات
النموذج الواضح للمخزون متعدد المواقع هو جدول بصف واحد لكل منتج في كل موقع:
CREATE TABLE stock (
product_id UUID,
warehouse_id UUID,
quantity INTEGER,
PRIMARY KEY (product_id, warehouse_id)
);
هذا النموذج كافٍ لتقارير حركة المرور المنخفضة، لكنه يفشل للأنظمة التشغيلية حيث يحجز مستخدمون متعددون مخزوناً في وقت واحد. وكيلا مبيعات في فرعين مختلفين في بيروت يمكنهما قراءة الكمية المتاحة = 5، وتقديم أوامر بـ5 كلاهما، ونجاح الاثنين، مما يُنتج حالة بيع مفرط يكتشفها فريق التنفيذ لاحقاً.
النموذج الصحيح: فصل الحالة عن الحركات
نموذج الإنتاج يفصل حالة المخزون الحالية (مستوى مُجسَّد) عن السجل التاريخي للحركات:
CREATE TABLE inventory_levels (
tenant_id UUID NOT NULL,
warehouse_id UUID NOT NULL,
product_id UUID NOT NULL,
quantity INTEGER NOT NULL DEFAULT 0,
reserved INTEGER NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (tenant_id, warehouse_id, product_id)
);
CREATE TABLE inventory_movements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
warehouse_id UUID NOT NULL,
product_id UUID NOT NULL,
quantity_delta INTEGER NOT NULL,
movement_type TEXT NOT NULL,
reference_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
الكمية المتاحة لأي منتج في أي موقع هي دائماً quantity - reserved. عمود reserved يحتفظ بالمخزون المخصص للطلبات المفتوحة لكن غير المُنجزة بعد.
حجز المخزون الذري في Go
عملية الحجز يجب أن تفشل بدلاً من السماح بالبيع المفرط، وأن تكون ذرية، وأن لا تقفل الصف أثناء القراءات غير المترابطة:
const reserveStockQuery = `
UPDATE inventory_levels
SET reserved = reserved + $1,
updated_at = NOW()
WHERE tenant_id = $2
AND warehouse_id = $3
AND product_id = $4
AND (quantity - reserved) >= $1
RETURNING id
`
شرط WHERE يُنجز فحص التوفر والتحديث بشكل ذري. إذا كانت الكمية المتاحة أقل من المطلوبة، تطابق UPDATE صفراً من الصفوف وتُرجع التطبيق خطأ نفاد المخزون دون أي قفل صريح.
نقل المخزون بين المستودعات
نقل المخزون بين مستودعين هو عملية ذات وجهين: خصم من المصدر وإضافة إلى الوجهة. إذا حدث هذان التحديثان في معاملات منفصلة، يترك الفشل بينهما المخزون في حالة غير متسقة.
للنقل داخل نفس قاعدة البيانات، ضع كلا التحديثين في معاملة واحدة. للأنظمة الموزعة حيث المستودعات على مثيلات قاعدة بيانات منفصلة، نمط outbox هو النهج الصحيح.
التعامل مع انقطاع الاتصال في لبنان
أنظمة المخزون المنشورة في لبنان تحتاج تصميماً أولياً للعمل دون اتصال. فرع يفقد الاتصال بالإنترنت يحتاج إلى الاستمرار في استلام المخزون ومعالجة المبيعات وتسجيل التعديلات. المزامنة تحدث عند استعادة الاتصال.
البنية لهذا: كل فرع يشغّل قاعدة بيانات مخزون محلية مع طابور عمليات للمزامنة. عند توفر الاتصال، يرسل الفرع سجل عملياته إلى النظام المركزي الذي يُطبق العمليات ويُعيد الحالة المحدثة.
حل النزاعات يتطلب قواعد محددة قبل التنفيذ: إذا باع فرعان آخر وحدة أثناء الانفصال، يجب عكس إحدى المعاملتين والتواصل مع العميل.
الإبلاغ والرؤية عبر المواقع
مخزون متعدد المستودعات يخلق متطلب تقارير لا تملكه الأنظمة أحادية الموقع. مطالعة تُجمّع هذا دون إعادة الحساب من الحركات:
CREATE VIEW inventory_summary AS
SELECT
tenant_id,
product_id,
SUM(quantity) AS total_quantity,
SUM(reserved) AS total_reserved,
SUM(quantity - reserved) AS total_available,
COUNT(DISTINCT warehouse_id) AS warehouse_count
FROM inventory_levels
GROUP BY tenant_id, product_id;
هذه المطالعة تعمل في ميلي ثوانٍ لأنها تقرأ من جدول المستويات المُجسَّد لا من سجل الحركات.
دروس من الإنتاج
صحة مخزون متعدد المستودعات تأتي من نموذج البيانات لا من منطق التطبيق. النموذج الصحيح: مستويات مُجسَّدة، وسجل حركات منفصل، وحجز ذري بالتحقق من عدد الصفوف، ونقل داخل معاملة واحدة.
الفرق بين العزل بالفهرس في SaaS متعدد المستأجرين هو الفرق بين نظام يتوسع ونظام يتدهور مع نمو عدد المستأجرين.
هل تبني نظام مخزون متعدد المستودعات؟
فريق Voxire يصمم ويبني أنظمة إدارة المخزون والمنصات التشغيلية متعددة المواقع للشركات في لبنان والمنطقة.
https://voxire.com/get-a-quote/



