الدور والنطاق
كانت شركة تجارة إلكترونية تملك نظام ERP خاصًا (NetSuite) تشحن عبر أداة SaaS، بما يرافق ذلك من تكاليف وسيطرة محدودة على عقود شركات الشحن. جاء CarrierAPI بديلًا ذاتي الاستضافة: واجهة برمجية واحدة تنشئ الملصقات وتقارن الأسعار وتتتبع الشحنات وتعالج الدفعات عبر عدة ناقلين، وبالنقاط ذاتها التي كانت تستخدمها الأداة السابقة، حتى يصبح الانتقال من جهة NetSuite استبدالًا مباشرًا لا إعادة بناء.
- بنية محوّلات لكل شركة شحن (PostNL وDHL Express وUPS كمجموعة أساسية؛ وDHL Parcel وFedEx وGLS وSendCloud نفسها كمحوّلات إضافية)، بحيث لا تتطلب إضافة ناقل جديد أي تغيير في بقية النظام.
- استبدال مباشر لأداة SaaS السابقة داخل NetSuite: أسماء الحقول المخصصة نفسها، وتغييرات طفيفة على تكامل SuiteScript القائم.
- ارتباط مباشر بسجلات Item Fulfillment في NetSuite، إضافة إلى المزامنة مع Shopify وتكامل مع Amazon إلى جانب شركات الشحن.
- طريقتان لإدارة بيانات اعتماد الناقلين: عبر الواجهة (مخزّنة في قاعدة البيانات، وهي المفضّلة) أو عبر متغيرات البيئة (مسار قديم للتثبيتات القائمة).
الأثر
- استعادة السيطرة على تكاليف الشحن وعقود الناقلين بدل الارتهان لأسعار وشروط طبقة SaaS وسيطة.
- أُصلح التعرّف على صنف من العناوين كان يُحلَّل بطريقة خاطئة (إذ كان استخراج رقم المنزل يلتقط آخر رقم بدل أول رقم يلي اسم الشارع — وهو خطأ يظهر مثلًا في عناوين المنتجعات التي تحمل رقم منزل ورقم شاليه معًا).
- استمرت مطابقة الفواتير مع UPS في العمل بعد أن غيّرت الشركة صيغة تصدير فواتيرها مرتين، وذلك باعتماد كشف الأعمدة على المحتوى بدل المواقع الثابتة، مع التحقق من النتيجة مقابل الإجمالي الذي تعلنه الفاتورة نفسها.
- سُدّت ثغرة في الجلسات كان المستخدمون المحذوفون يحتفظون معها بالوصول حتى 24 ساعة، لأن النظام كان يتحقق من توقيع رمز الجلسة وتاريخ انتهائه فقط — لا مما إذا كان الحساب لا يزال موجودًا.
البنية التقنية
كل شركة شحن هي محوّل خلف واجهة مشتركة (إنشاء الشحنات وإلغاؤها، وطلب الأسعار، والتتبع، والخدمات المتاحة) — فربط ناقل جديد لا يمس بقية النظام. الواجهة الخلفية مبنية بـ FastAPI (Python، غير متزامن) مع PostgreSQL كقاعدة بيانات رئيسية، وعامل خلفي معتمد على Redis (arq) لمعالجة الملصقات بالدفعات، حتى لا يعطّل إنشاء مئات الملصقات دفعةً واحدة الواجهة البرمجية. أما الملصقات نفسها فتُخزَّن في MinIO (متوافق مع S3)، حتى لا يرتبط التخزين بمزوّد سحابي واحد. الواجهة الأمامية لوحة تحكم بـ Next.js/React لإدارة الشحنات وإعدادات الناقلين وبيانات الاعتماد. ويعمل النظام بالكامل ذاتي الاستضافة عبر Docker Compose خلف Traefik، ويُنشر باستخدام Coolify.
اختيار النموذج
لا ذكاء اصطناعي ولا نماذج لغوية — فهذا نظام حتمي عن قصد: يجب أن تكون الملصقات والأسعار والتتبع قابلة للتنبؤ والتدقيق، لا مولَّدة. القرار الجوهري هنا كان بين الاستضافة الذاتية وSaaS: أداة SaaS أسرع في الإعداد، لكن المنصة ذاتية الاستضافة تمنح السيطرة على عقود الناقلين، وتلغي أي رسوم لكل ملصق أو لكل شحنة تُدفع لطبقة وسيطة، وتتيح بناء الوظائف (مثل التكامل المباشر مع NetSuite) على المقاس تمامًا بدل انتظار ما يدعمه مزوّد SaaS أو لا يدعمه.
التكلفة وزمن الاستجابة
- بنية تحتية ذاتية الاستضافة (Docker Compose وPostgreSQL وRedis وMinIO) بدل رسوم SaaS متكررة عن كل شحنة.
- يجري توليد الملصقات بالدفعات بشكل غير متزامن عبر طابور Redis (arq)، فيبقى النظام مستجيبًا عند إنشاء مئات الملصقات دفعةً واحدة بدل إبقاء مقدّم الطلب في الانتظار.
- جرى التحقق من مطابقة الفواتير على فواتير UPS حقيقية: من بينها فاتورة من 1,275 سطرًا بقيمة 4,855.72 يورو وتصدير من 2,275 سطرًا، وقد عولج كلاهما بشكل صحيح بعد إصلاح كشف الأعمدة.
الدروس المستفادة
كان تحليل العناوين الهولندية يخفق مع صنف محدد من العناوين (منتجعات العطلات التي تحمل رقم منزل ورقم شاليه منفصلًا، مثل "Hessenweg 83 41") لأن المحلّل كان يقرأ آخر رقم في سطر الشارع بوصفه رقم المنزل بدل أول رقم يلي اسم الشارع. حُلّت المسألة بمحلّل موحّد جرى التحقق منه على حالة حقيقية. كذلك تعطّل استيراد فواتير UPS مرتين لأن الشركة غيّرت صيغة تصدير الفواتير (فتبدّل عدد الأعمدة وترتيبها)؛ فاستُبدل نهج المواقع الثابتة بكشف للأعمدة قائم على المحتوى، تُقاطَع نتيجته مع الإجمالي الذي تعلنه الفاتورة نفسها. وكانت تحذيرات التحقق من العنوان تمنع في البداية إنشاء الشحنة كليًا؛ فجرى التراجع عن ذلك ليقتصر المنع على الشراء التلقائي للملصق، بحيث تبقى الشحنة المؤشَّر عليها ظاهرة وقابلة لإعادة المحاولة بدل أن تتعطل. وأخيرًا ثغرة في الجلسات: احتفظ المستخدمون المحذوفون بالوصول حتى 24 ساعة لأن الرموز كانت تُفحص على أساس التوقيع وتاريخ الانتهاء فقط، لا على وجود الحساب — وعولج ذلك بالتحقق مع كل طلب من أن الحساب لا يزال قائمًا.