مزامنة المخزون متعدد المواقع ليست نسخ رقم من نظام إلى آخر. قد يباع SKU نفسه من مستودع في الرياض وفرع في جدة ونقطة بيع، بينما يسجل ERP أوWMS الاستلام والتحويل والحجز والتالف والجرد في توقيت مختلف. يحتاج الموصل الموثوق هوية صريحة لكل زوج `product + location`، ومسار تعافٍ عندما تنجح الكتابة في الطرف البعيد لكن تضيع الاستجابة.
توفر Merchant API الحالية في زد اتجاهين مفيدين للدفعات. تحدّث واجهة مخزون المنتج منتجًا واحدًا عبر مواقع متعددة، بينما تحدّث واجهة مخزون الموقع منتجات متعددة داخل موقع واحد. تكتب الواجهتان قيم `available_quantity` مطلقة وتعيدان HTTP 204 من دون Response Body. لذلك يصبح اختيار حدود الدفعة والتحقق بعدها جزءًا من معمارية التكامل.
مثّل المخزون كمصفوفة منتج وموقع
ابدأ بمفتاح ثابت مثل `store_id + product_id + location_id`. لا تستخدم اسم المستودع كهوية؛ فالاسم والعنوان قابلان للتغيير، بينما يجب أن يحافظ التكامل على Mapping ثابت. تستخدم واجهة List Locations صلاحية `inventories.read` وتعيد معرفات المواقع. تنبه صفحتها إلى التعامل مع القائمة كبيانات موجزة، وتوجّه إلى واجهة تفاصيل الموقع للحصول على المعلومات الكاملة. استخدم ID الثابت كمفتاح أجنبي، واجلب التفاصيل عندما يعتمد القرار على خاصية مثل كون الموقع مفعّلًا.
احتفظ بربط صريح بين مستودع وSKU أوVariant في ERP وبين Location وProduct IDs في زد. سجّل حالة الربط ومصدر إنشائه وآخر وقت تحقق. الموقع المجهول أوSKU المرتبط مرتين أوالمستودع المعطل يجب أن يدخل Exception Queue؛ لا ترسله بصمت إلى الموقع الافتراضي. هذا النوع من Fallback ينشئ مخزونًا يبدو صحيحًا لكنه تابع لنقطة Fulfillment خاطئة.
حدد عقد الكمية قبل أول طلب. تستخدم أمثلة زد `available_quantity` وليست Event Delta. قرر هل يرسل ERP الكمية الموجودة فعليًا أمAvailable to Sell بعد الحجوزات أمقيمة مشتقة أخرى. اجعل Safety Stock وحجوزات القنوات المتعددة مسؤولية مالك واحد فقط. إذا خصمت واجهة المتجر وERP الطلب نفسه، سينخفض المخزون مرتين رغم أن كل API Call صحيح تقنيًا.
اختر دفعة حسب المنتج للتغييرات بين المواقع
استخدم `PATCH /v1/products/{product_id}/stocks/` عندما يتغير منتج واحد في عدة مواقع، مثل إعادة توزيع كمية SKU بين المستودعات بعد تحويل مركزي. يحتوي Body على Array من السجلات، في كل منها `location` و`available_quantity` و`is_infinite`، وتحتاج الواجهة إلى `products.read_write`.
[
{ "location": "loc-riyadh", "available_quantity": 18, "is_infinite": false },
{ "location": "loc-jeddah", "available_quantity": 7, "is_infinite": false }
]يناسب حد المنتج عملية Replenishment أوCatalog تملك SKU واحدًا وتريد ضبط صورته الكاملة بين المواقع. كما يسهّل Read-after-write: تعيد `GET /v1/products/{product_id}/stocks/` سجلات مخزون المنتج مع الموقع والكمية باستخدام `products.read`. قارن المصفوفة المعادة بالقيم المطلوبة بدل اعتبار العملية مكتملة لأن PATCH أعادت 204 فقط.
لا ترسل Catalog كاملًا لموقع واحد عبر Loop غير منضبط على هذه الواجهة. سينتج ذلك طلبات مستقلة كثيرة، ويصعّب فهم التقدم الجزئي، وقد يخلط Snapshots قديمة وحديثة للموقع نفسه. اختر Endpoint وفق عملية الأعمال، لا حسب أول URL وجدته.
اختر دفعة حسب الموقع لصور المستودع
استخدم `POST /v1/locations/{location_id}/stock-update/` عندما يعطيك جرد مستودع أوWMS Export أوعملية استلام مجموعة منتجات لموقع واحد. يحتوي Payload على `product_id` و`available_quantity` و`is_infinite`، والصلاحية الموثقة هي `inventories.read_write`. قد يعيد الموقع المعطل HTTP 400، لذلك تحقق من حالته في Onboarding ومجددًا عندما يكشف الخطأ تغير Configuration.
[
{ "product_id": "product-a", "available_quantity": 34, "is_infinite": false },
{ "product_id": "product-b", "available_quantity": 12, "is_infinite": false }
]يتوافق هذا الحد مع Cycle Count أوSnapshot بعد الاستلام: تصبح الوحدة التشغيلية الواحدة إما مرسلة أوموضوعة في Recovery Workflow خاص بالموقع. لا تنص الصفحة العامة لزد على حد أقصى لطول Array؛ فلا تخترع رقمًا. حدد Batch Size محافظًا عبر Sandbox Testing، واضبط حجم Payload والمدة، واحفظ عضوية كل Batch في سجلك. Chunks صغيرة وحتمية تجعل Timeout والمطابقة أرخص من طلب واحد غامض بحجم المستودع.
يجب ألا تعمل الكتابة حسب المنتج والكتابة حسب الموقع باستقلال على خلايا المصفوفة نفسها. عيّن Writer واحدًا وفق المصدر ونوع العملية. يمكن تمثيل التحويل كWorkflow مرتب له خليتان، لكن لا يجوز أن تتجاوز Warehouse Snapshot عملية بيع أوتعديل أحدث. قسّم Queue وفق `store + product + location`، واربط كل Job بإصدار مصدر متزايد، وارفض أي Job أقدم من آخر Version مطبق محليًا.
اعتبر 204 نجاح نقل ثم تحقق من الحالة
توثق صفحتا Bulk استجابة HTTP 204 بلا Body. تؤكد الاستجابة نجاح عملية HTTP، لكنها لا تعطي نتيجة لكل Record لتخزينها. أما Network Timeout فأكثر غموضًا: قد تكون الخدمة البعيدة طبقت القيم المطلقة رغم أن الموصل لم يستلم الاستجابة. تميل إعادة التعيين المطلق نفسه إلى القيمة نفسها، لكن Retry متأخرًا قد يمحو تغييرًا شرعيًا أحدث. لا تساوِ بين Absolute Update وRetry آمن بلا ترتيب.
احفظ كل Snapshot مقصودة قبل الإرسال داخل Durable Outbox. يتضمن السجل المفيد Source Version والمتجر وحد العملية وPayload Hash والخلايا والمحاولات ونتيجة HTTP وحالة التحقق. نفذ Serialization للتغييرات حسب الخلية. قبل إعادة نتيجة `unknown`، قارن إصدارها بأحدث Version موضوع في Queue أوVerified؛ احذف العمل القديم أوSupersede بدل إعادة تشغيله عميانيًا.
بعد كتابة ناجحة أومبهمة، اقرأ مخزون المنتجات المتأثرة وتحقق من الخلايا نفسها. لا تجعل Batch `verified` إلا عندما تطابق القيم، واجعلها `superseded` عندما يملك Version أحدث الخلية، و`drifted` عندما تختلف الحالة البعيدة دون عملية أحدث معروفة. يمنع هذا State Machine أن تتحول 204 إلى وعد غير مفحوص.
تسرّع Webhooks الاكتشاف ولا تستبدل المطابقة
توثق زد Webhook عامة باسم `product.update`، لكن صفحة الأحداث المدعومة لا تسرد Event مستقلة لتغير المخزون. لا تفترض أن الحدث العام سيحمل كل انتقال في المخزون أوسجلًا كاملًا لكل موقع ما لم يثبت Payload والعقد الفعليان ذلك. تستطيع Webhooks تشغيل Refresh محدد، وتبقى القراءة المجدولة آلية التعافي.
شغّل Incremental Reconciliation للخلايا التي تغيرت مؤخرًا، وFull Sweep أبطأ عبر جميع Mappings الفعالة. لكل منتج، قارن استجابة قائمة المخزون بالمصفوفة المتوقعة في الموصل. صنّف الفرق إلى تعديل أحدث من المنصة، أوJob معلق من ERP، أوموقع غير مربوط، أواختلاف في Infinite Stock Mode، أوDrift غير مفسر. لا تنفذ Overwrite تلقائيًا لكل فرق؛ قد يكون التاجر أجرى تصحيحًا طارئًا صحيحًا يجب إدخاله إلى ERP أومراجعته من المشغل.
يحتاج `is_infinite` معالجة صريحة. عندما يكون true، يمكن أن تعرض استجابة المخزون الموثقة `available_quantity` بقيمة null. لا تحولها إلى صفر، ولا تنقل SKU محدودًا إلى Infinite لأن حقل المصدر مفقود. مثّل Stock Mode منفصلًا عن الرقم، واطلب انتقالًا مقصودًا بين النمطين.
ابدأ بملكية واضحة ومقاييس وأقل صلاحيات
ابدأ بمتجر واحد وموقعين ومجموعة منتجات قليلة المخاطر. اختبر إعادة توزيع منتج، وWarehouse Snapshot، وموقعًا معطلًا، وTimeout بعد الإرسال، وStale Retry، وتصحيحًا يجريه المشغل من نظام التاجر. تحقق من API State ومن سلوك البيع الفعلي الذي يتوقعه التاجر.
افصل صلاحيات الموصل: اكتشاف المواقع يستخدم `inventories.read`؛ والكتابة حسب الموقع تستخدم `inventories.read_write`؛ وقراءة مخزون المنتج تستخدم `products.read`؛ والكتابة حسب المنتج تستخدم `products.read_write`. اطلب المسارات التي يحتاجها التطبيق فقط، واعزل Credentials لكل تاجر، ولا تسجل Access Tokens أوبيانات العملاء أوAuthorization Headers كاملة.
راقب عمر Outbox والنتائج المجهولة وزمن Verified Write والـStale Jobs الممنوعة وأخطاء الموقع المعطل وفشل Mapping وفروق Reconciliation وعدد الخلايا في Infinite Mode. اجعل Alert يشير إلى متجر ومنتج وموقع محددين حتى يستطيع المشغل التصرف. الهدف Controlled Convergence، لا Dashboard تعرض HTTP Calls ناجحة فقط.
توفر زد الأدوات الأساسية: اكتشاف المواقع وقراءة سجلات المخزون وكتابة التوافر المطلق في دفعات مرتبة حول منتج أوموقع. ويضيف موصل ERP أوWMS الموثوق هويات ثابتة وWriter واحدًا لكل خلية وإصدارات مرتبة وتسليمًا دائمًا وRead-after-write ومطابقة. اختيار حدود الدفعة الصحيحة يجعل الأعطال صغيرة وقابلة للتفسير، والمخزون دقيقًا بما يكفي للاعتماد عليه.
المراجع الرسمية
تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.
- Zid Merchant API — List Locations, verified 26 September 2026
- Zid Merchant API — List Product Stock Records, verified 26 September 2026
- Zid Merchant API — Bulk Update Product Stock Records, verified 26 September 2026
- Zid Merchant API — Update Product Stock for Location, verified 26 September 2026
- Zid Webhooks — Supported events, verified 26 September 2026
إعداد: Noor Yasser
تعمل على تحدٍ تقني مشابه؟
أساعد الفرق على تحويل القرار المعماري إلى نطاق واضح وتنفيذ يمكن تشغيله ومراجعته بثقة.




