نظرة عامة
> ملاحظة: الشركة الموصوفة أدناه هي سيناريو تمثيلي مبني على أنماط تشغيلية شائعة في قطاع التوزيع بالتجزئة السوري. الأسماء والأرقام والنتائج توضيحية، ولا تستند إلى عميل معيّن أو بيانات موثّقة.
موزّع متوسط الحجم لبضائع منزلية يمتلك خمسة فروع تجزئة موزّعة في حلب ومحيطها، نما عضوياً على مدى ثماني سنوات حتى بلغ عدد المنتجات التي يتعامل بها أكثر من 4,000 صنف، وبلغت معاملاته اليومية نحو 600 معاملة عبر جميع الفروع. كان المستودع المركزي يزوّد كل فرع بجولة أسبوعية، لكن الأدوات المستخدمة لم تواكب هذا النمو.
كان كل فرع يعتمد على جدول بيانات مستقل لتسجيل المبيعات، وعلى مجموعة واتساب لطلب تجديد المخزون. أما فريق المستودع فكان يجري جرداً يدوياً مرة في الأسبوع للتحقق من الكميات. بحلول عام 2025، باتت هذه الثغرات تُكلّف الشركة خسائر لا يمكن تجاهلها.
---
المشكلة التشغيلية
مخزون لا يمكن الوثوق به
بدون رؤية فورية موحّدة، كان فريق المستودع يبالغ في طلب المنتجات سريعة البيع خشية النفاد، فيما تتراكم المنتجات بطيئة البيع دون أن يلاحظها أحد. كان مديرو الفروع يرسلون طلبات مكرّرة للمخزون ذاته، ولم يكن لدى الإدارة المركزية رؤية موحّدة لما يتوفر عبر الفروع الخمسة في أي وقت.
نتج عن ذلك دورة متكرّرة من:
- نفاد المخزون من المنتجات المطلوبة في منتصف الأسبوع قبل دورة التجديد.
- مخزون راكد في فرع بينما فرع آخر يعاني نقصاً في المنتج نفسه.
- شطب بضائع في نهاية الشهر حين تختلف نتائج الجرد الفعلي عن أرقام الجداول.
التسوية الأسبوعية: طقس كل جمعة
في كل مساء جمعة، كان فريق المحاسبة يجمع تقارير المبيعات من الفروع عبر واتساب ثم يدمجها يدوياً في ملف Excel رئيسي. كانت العملية تستغرق ثلاث إلى أربع ساعات وتُنتج أخطاء — رموز فروع خاطئة، سطور مفقودة، أخطاء في التنسيق — تستلزم متابعة يوم السبت.
تباين الأسعار
لأن كل مدير فرع يحدّث جدوله الخاص، كانت أسعار العروض الترويجية الصادرة من المركز تُطبَّق بشكل غير متسق: فرع يخفّض السعر، وآخر يبيع بالسعر الأصلي، وثالث لم يتلقَّ التعميم أصلاً. تزايدت شكاوى العملاء حول تفاوت الأسعار.
---
تصميم الحل
اقترحت دراجون سوفت (Dragonfly Soft) منصة ويب مخصصة تتضمن ثلاثة وحدات متكاملة:
| الوحدة | الغرض | |--------|--------| | سجل المخزون المركزي | مرجع موحّد لجميع الأصناف والكميات والمواقع | | نقطة بيع الفروع | واجهة كاشير خفيفة على المتصفح لكل فرع | | محرك إعادة التخزين | تنبيهات إعادة الطلب وطلبات نقل المخزون وسير الموافقة |
جاء قرار البناء المخصص بدلاً من نظام ERP جاهز بسبب قيدين محليين:
- انقطاع الإنترنت المتقطع في بعض الفروع يستلزم قدرة نقطة البيع على العمل دون اتصال والمزامنة عند عودة الشبكة.
- واجهة عربية أولاً لصرّافي الفروع، كثيرٌ منهم لا يتعامل بارتياح مع البرامج الإنجليزية.
المكدّس التقني
الواجهة الأمامية (POS) React 18 + PWA (service worker للعمل أوفلاين)
الـ API الخلفية Node.js (NestJS) + REST
قاعدة البيانات PostgreSQL 16 — نظام متعدد المخططات (multi-schema)
المزامنة الفورية WebSocket (Socket.io) لعدادات المخزون الحية
الاستضافة VPS واحد (مزوّد إقليمي) + Cloudflare CDN
تطبيق المدير React Native
> ملاحظة: مقاطع الكود في هذا المقال توضيحية لأنماط التصميم المستخدمة، وليست كوداً جاهزاً للإنتاج.
---
تفاصيل التنفيذ
1. نموذج البيانات: مخزون واحد، رؤى متعددة
الفكرة المحورية كانت الاحتفاظ بـسجل مخزون واحد لكل صنف على مستوى المستودع واشتقاق كميات الفروع من خلال حركات الصنف بدلاً من تخزينها في جداول منفصلة، مما يتفادى مشكلة المزامنة كلياً.
-- جداول أساسية (مبسّطة)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
sku TEXT UNIQUE NOT NULL,
name_ar TEXT NOT NULL,
name_en TEXT,
unit TEXT DEFAULT 'piece',
reorder_qty INTEGER DEFAULT 10
);
CREATE TABLE stock_locations (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL -- 'warehouse', 'branch_a', ...
);
CREATE TABLE stock_movements (
id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES products(id),
from_loc_id INTEGER REFERENCES stock_locations(id),
to_loc_id INTEGER REFERENCES stock_locations(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
movement_type TEXT NOT NULL, -- 'sale', 'transfer', 'adjustment', 'return'
created_at TIMESTAMPTZ DEFAULT now(),
created_by INTEGER
);
استخدام نموذج حوادث الحركة (event-sourced) يعني أن كل تغيير في المخزون يُسجَّل بشكل دائم، وإصدار تقرير مخزون تاريخي لأي تاريخ لا يتجاوز إعادة حساب الحركات حتى ذلك التاريخ.
2. نقطة بيع تعمل بدون إنترنت
بُنيت نقطة البيع كـتطبيق ويب تقدّمي (PWA) يستخدم Service Worker لتخزين نموذج التطبيق محلياً وقائمة انتظار المعاملات في IndexedDB عند انقطاع الشبكة.
// service-worker.js (توضيحي)
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/transactions')) {
event.respondWith(
fetch(event.request).catch(() => {
// لا يوجد إنترنت — احفظ الطلب في قائمة الانتظار المحلية
return cacheTransaction(event.request.clone()).then(() =>
new Response(JSON.stringify({ queued: true }), {
headers: { 'Content-Type': 'application/json' },
})
);
})
);
}
});
عند عودة الاتصال، يُرسل نظام المزامنة في الخلفية المعاملات المتراكمة بالترتيب إلى الخادم، ضامناً اتساق السجل المركزي. يرى الصرّافون مؤشراً صغيراً للحالة (أخضر / أصفر / أحمر) ويمكنهم الاستمرار في العمل دون انقطاع أثناء أعطال الشبكة.
3. تسعير مركزي مع إمكانية تجاوز الفروع
جدول pricing_rules يحتفظ بالسعر الأساسي وأي عروض ترويجية بفترات زمنية محددة. تجلب نقطة البيع السعر الفعّال من الـ API لحظة المعاملة، مما يلغي توزيع قوائم الأسعار يدوياً.
// pricing.service.ts (NestJS — توضيحي)
async getEffectivePrice(productId: number, branchId: number, date: Date): Promise<number> {
const promotion = await this.db.query(
`SELECT promo_price FROM pricing_rules
WHERE product_id = $1
AND (branch_id = $2 OR branch_id IS NULL)
AND valid_from <= $3 AND valid_to >= $3
ORDER BY branch_id NULLS LAST
LIMIT 1`,
[productId, branchId, date]
);
if (promotion.rows.length) return Number(promotion.rows[0].promo_price);
// إذا لم يوجد عرض، أعِد السعر الأساسي
const base = await this.db.query(
'SELECT base_price FROM products WHERE id = $1',
[productId]
);
return Number(base.rows[0].base_price);
}
بإمكان مديري الفروع تطبيق خصومات محلية قصيرة المدى ريثما تُوافق عليها الإدارة المركزية، وهي عملية تُدار عبر إشعار في تطبيق المدير.
4. تنبيهات إعادة التخزين
يعمل محرك إعادة التخزين كـمهمة مجدولة كل ساعتين: يقارن المخزون الحالي لكل صنف في كل فرع بحد الطلب reorder_qty المضبوط مسبقاً، ويُنشئ طلب نقل فور انخفاض الرصيد عن ذلك الحد.
يتلقى مشرف المستودع لوحة موحّدة بالنقلات المعلّقة كل صباح. بنقرة موافقة واحدة، يُولّد النظام قائمة اختيار ويُحدّث السجل فور تأكيد إرسال البضاعة.
5. لوحة التقارير
تصل إدارة المركز إلى لوحة تحكم بواجهة عربية تُظهر:
- مستويات المخزون الحية لكل فرع (مُحدَّثة عبر WebSocket).
- إجماليات المبيعات اليومية لكل فرع ولكل فئة منتج.
- خط سير النقلات: قيد الانتظار، في الطريق، تم التسليم.
- امتثال الأسعار: تنبيه حين يختلف سعر معاملة ما عن السعر المسجّل في النظام.
---
التحديات وكيفية معالجتها
التحدي الأول: مقاومة الصرّافين للتغيير
كان صرّافو الفروع معتادين على الإيصالات اليدوية. صُمِّمت واجهة نقطة البيع بأزرار لمس عربية كبيرة، وزر مسح الباركود بارزاً، وأقل عدد ممكن من الشاشات بين فتح المعاملة وطباعة الإيصال. اكتفت كل فرع بجلسة تدريب ليوم واحد.
التحدي الثاني: بيانات المنتجات القديمة
تراكم لدى الشركة سجلات منتجات موزّعة على خمسة جداول بيانات متفاوتة التسمية وبدون رمز SKU موحّد. بنى الفريق سكريبت ETL لمرة واحدة لدمج البيانات وإلغاء التكرار، وتعيين رمز SKU أساسي لكل منتج وطباعة ملصقات باركود محدّثة لرفوف المستودع.
التحدي الثالث: ضعف الاتصال في الفروع النائية
فرع واحد كان يفقد الإنترنت بانتظام لساعتين إلى ثلاث ساعات بعد الظهر. تعاملت قائمة الانتظار الأوفلاين مع معظم هذه الحالات، لكن الفريق ضبط أيضاً Service Worker لتحديث كتالوج المنتجات ليلاً، لضمان توفّر بيانات المنتجات المحدّثة للصرّافين حتى خلال انقطاع الشبكة.
---
النتائج
بعد ثلاثة أشهر من التشغيل عبر جميع الفروع، رصد الفريق التحسينات التالية:
| المجال | قبل | بعد | |--------|-----|-----| | تسوية الجمعة | 3–4 ساعات يدوية | تقرير آلي ليلي | | حوادث نفاد المخزون | متكررة في منتصف الأسبوع | نادرة (تنبيهات إعادة التخزين فعّالة) | | تباين الأسعار | متعدد أسبوعياً | شبه منعدم | | طلبات النقل المكرّرة | شائعة | انعدمت (طلب واحد لكل صنف/فرع) | | شطب المخزون | يظهر شهرياً | تراجع ملحوظ |
> تنبيه: الأرقام أعلاه تقديرية توضيحية لهذا السيناريو. النتائج الفعلية تعتمد على الانضباط التشغيلي وجودة البيانات وظروف الاتصال في كل موقع.
---
مبادئ التصميم الرئيسية
- الأوفلاين أولاً لا الأمل في الاتصال — البيئة التشغيلية السورية لا تضمن انقطاعاً صفرياً؛ يجب أن يتدهور النظام بانسيابية.
- واجهة عربية أصيلة — التعريب ليس فكرة لاحقة؛ كل حقل وتنبيه وتقرير يُصاغ بالعربية منذ البداية.
- سجل الحركات بدلاً من الأرصدة القابلة للتعديل — الحركات حقائق ثابتة والأرصدة مشتقة، مما يمنح قابلية التدقيق دون تعقيد.
- الإطلاق التدريجي — بدأ المستودع وفرع واحد بالتشغيل أولاً لتحديد مشكلات جودة البيانات قبل التوسع.
- بصمة خادم بسيطة — VPS واحد مضبوط جيداً مع PostgreSQL يكفي لمئات المستخدمين المتزامنين؛ التوسع السحابي المبكر يُضيف تكلفة بلا مقابل.
---
الخاتمة
منصة موحّدة لنقاط البيع والمخزون لا تحتاج أن تكون نظام ERP ضخماً ومكلفاً لتُحدث أثراً تشغيلياً حقيقياً لشركة تجزئة سورية متعددة الفروع. بالتركيز على نقاط الألم المحددة — التسوية، ونفاد المخزون، وتباين الأسعار — والتصميم بما يتلاءم مع واقع الاتصال المحلي واللغة العربية، يصبح النظام المخصص العمود الفقري للعمليات في وقت قصير.
تجمع دراجون سوفت (Dragonfly Soft) في منهجها بين تقنيات مفتوحة المصدر مثبتة الكفاءة، وفلسفة تصميم عربية أولاً، ونموذج تسليم تدريجي يتيح للعملاء رؤية القيمة مبكراً بدلاً من انتظار إطلاق شامل بعيد.
---
مستعد لتوحيد فروعك؟ تواصل معنا لبناء منصة مخزون ونقاط بيع مخصصة لأعمالك.