مرحباً بالجميع،
أنا أقوم بإنشاء سير عمل مزامنة في n8n (إصدار Cloud 2.22.6) لتحديث بيانات المبيعات من نظام ERP الخاص بنا إلى ملفات تعريف أعمال GoHighLevel (GHL) (الشركات).
هدفي:
نحن نعالج ملف CSV يحتوي على إجمالي الفواتير مقسمة حسب العلامة التجارية. نحتاج إلى البحث عن النشاط التجاري في GHL باستخدام معرف ERP خارجي ثابت (codice_cliente) مخزن داخل حقل مخصص في شركة GHL (business.id_gestionale)، ثم:
-
إذا كان النشاط التجاري موجوداً: تحديث حقوله المخصصة المالية عبر PUT.
-
إذا كان النشاط التجاري جديداً: إنشاء ملف تعريف نشاط تجاري جديد عبر POST.
إعداد سير العمل الحالي:
-
المشغل / قراءة الملف: يستورد ملف CSV مع أعمدة مثل codice_cliente و ragione_sociale و totale_ecopots و totale_lechuza وغيرها.
-
طلب HTTP (GET): يستدعي واجهة برمجة تطبيقات GHL V2 باستخدام مصادقة الرأس (Authorization: Bearer pit-... و Version: 2021-04-15).
-
عقدة If: يتحقق مما إذا تم العثور على النشاط التجاري.
-
طلب HTTP (POST / PUT): عقد HTTP قياسية لتوجيه البيانات.
العقبات التي نواجهها:
المشكلة 1: فشل البحث GET (قيود واجهة برمجة التطبيقات V2)
لا يمكننا تصفية طلب GET مباشرة حسب حقلنا المخصص.
-
إذا استخدمنا https://services.leadconnectorhq.com/businesses/search?locationId=XXXX&query={{ $json.codice_cliente }}، فستُرجع مصفوفة فارغة [] لأن استعلام GHL العام لا يفهرس الحقول المخصصة الرقمية.
-
إذا حاولنا إضافة مرشح الحقل المخصص في سلسلة الاستعلام (&customFields=[...])، تُرجع GHL خطأ 422: "property customFields should not exist".
-
إذا انتقلنا إلى جلب عام (/businesses/?limit=100)، يسحب n8n حمولة ضخمة بحجم 1.2 ميجابايت تحتوي على جميع الشركات، مما يؤدي إلى الكتابة فوق السياق الثنائي / JSON لصفوف CSV الواردة الأصلية، مما يجعل المحاذاة اللاحقة معقدة جداً.
المشكلة 2: حلقة مصادقة POST (خطأ 401)
عندما تنتقل سجل إلى مسار POST لإنشاء شركة جديدة (https://services.leadconnectorhq.com/businesses/)، ترجع GHL:
401 - {"message":"LocationId is not specified","error":"Unauthorized","statusCode":401}
على الرغم من أن locationId تم تمريره بوضوح داخل نص JSON، وتعمل نفس رمز Bearer pit-... بشكل مثالي لطلبات GET وطلبات PUT على معرفات موجودة. يبدو أن مفاتيح API على مستوى الموقع في GHL مقيدة تماماً من استخدام POST على نقطة نهاية /businesses/ ما لم تستخدم تطبيق Marketplace OAuth كاملاً.
البديل المقترح (الانتقال إلى جهات الاتصال + اسم الشركة):
بما أن الكتابة إلى نقطة نهاية /businesses/ يبدو أنها مقيدة بشدة باستخدام مفاتيح API قياسية، فإننا نقيّم الانتقال إلى نقطة نهاية جهات الاتصال.
نفكر في إنشاء / تحديث جهات الاتصال بدلاً من ذلك، وتمرير معرف ERP وحقول الفواتير داخل حقول مخصصة للاتصال، مع تعيين حقل companyName داخل كائن الاتصال لربطه بالنشاط التجاري.
أسئلتي للمجتمع:
-
ما هي أفضل ممارسة في n8n للاستعلام عن نشاط تجاري في GHL باستخدام قيمة حقل مخصص دون جلب قاعدة البيانات بأكملها بحجم 1.2 ميجابايت أو مواجهة أخطاء 422؟
-
كيف يمكننا تجاوز سلوك الكتابة فوق البيانات بعد عقدة طلب HTTP بحيث تتمتع عقد PUT / POST النهائية بالوصول إلى بيانات صف CSV الأصلية (إجماليات الفواتير)؟ هل يجب أن نستخدم العقدة الجديدة Compare Datasets / Merge (Enrich Existing Data) مباشرة بعد استيراد الملف؟
-
هل نجح أحد في تنفيذ POST إلى /businesses/ باستخدام مفتاح API Location قياسي، أم أن التكامل الكامل لـ OAuth إلزامي لإنشاء شركات؟
-
بخصوص بديلنا: هل تعتقد أن التبديل إلى نقطة نهاية جهات الاتصال (مع ملء حقل companyName داخل الاتصال) هو حل بديل قوي وموثوق به لتجاوز قيود واجهة برمجة تطبيقات Business، أم أن هناك مسارات معمارية أفضل تود أن تقترحها؟
أي نصيحة أو حل بديل أو مقتطف JSON لسير العمل سيكون موضع تقدير عميق!
ديدي، يبدو أن هناك عائقين منفصلين وليس خطأ واحد في GHL: البحث عن الشركة الصحيحة باستخدام codice_cliente، ثم إنشاؤها/تحديثها دون رفض POST body. إذا كان معرف ERP هذا موجوداً فقط في حقل مخصص للشركة لا يستطيع بحث GHL تصفيته، فقد لا يكون GHL مصدر بحث موثوقاً؛ احتفظ بخريطة صغيرة codice_cliente -> businessId في بيانات خاصة بك وحدّث حسب businessId.
قبل إعادة البناء حول Contacts، قم بفحص آمن واحد: أرسل طلب إنشاء شركة بحد أدنى بنفس الرمز وlocationId، لكن بدون حمل الفاتورة/الحقول المخصصة. هل يعود LocationId is not specified، أم فقط عند إرسال الـ body المعين من CSV كاملاً؟
هذه مشكلة موثقة جيدًا جدًا وقد قمت بمعظم العمل التشخيصي بنفسك بالفعل. دعني أمر على كل مشكلة.
بخصوص قيود البحث GET الحل الأنظف هو جلب جميع الشركات مرة واحدة في بداية سير العمل باستخدام التقسيم، ثم استخدام عقدة Code لتصفية البيانات في الذاكرة حسب قيمة الحقل المخصص. نعم إنها حمولة كبيرة، لكنك تستدعيها مرة واحدة فقط لكل تشغيل سير عمل، وليس مرة واحدة لكل صف في CSV. شيء من هذا القبيل:
javascript
const allBusinesses = $input.all();
const target = allBusinesses.find(b =>
b.json.customFields?.find(f => f.key === 'id_gestionale' && f.value === $('CSV Node').item.json.codice_cliente)
)
return target ?
بخصوص الحفاظ على بيانات CSV بعد عقدة HTTP Request — استخدم عقدة Merge مضبوطة على وضع “Combine” بعد استدعاء GET الخاص بك. أدخل عنصر CSV الأصلي واستجابة HTTP معًا فيها. بهذه الطريقة ستظل لعقد PUT/POST الموجودة في أسفل السير لديها إمكانية الوصول إلى جميع حقول الفاتورة الأصلية إلى جانب بيانات استجابة GHL. هذا هو النمط القياسي لهذا الموقف بالذات.
بخصوص خطأ POST 401: أنت محق وهذا حد معروف من حدود GHL. مفاتيح Location API القياسية (pit-...) لا تملك الإذن لإنشاء سجلات Business جديدة عبر POST. هذا المسار يتطلب إما رمز OAuth App كامل أو رمز Private Integration بنطاق مرتفع. إذا كان OAuth الكامل ليس خيارًا الآن، فالتحول إلى Contacts الخاص بك هو في الواقع المسار الأكثر عملية.
بخصوص تحول Contacts: إنه حل قوي ويُستخدم على نطاق واسع. املأ companyName على المتصل و GHL سيربطه تلقائيًا بـ Business المطابق. احفظ codice_cliente وإجمالي الفواتير في حقول مخصصة للمتصل وبهذا تتجاوز قيود نقطة نهاية Business كليًا. المقابل الرئيسي هو أن بيانات المالية الخاصة بك تعيش على مستوى Contact بدلاً من مستوى Business، مما قد يؤثر على التقارير اعتمادًا على كيفية إعداد حسابك في GHL.
إذا كان الحفاظ على البيانات على مستوى Business مهمًا على المدى الطويل، فالإصلاح الصحيح هو إعداد تطبيق GHL Marketplace مع OAuth لكن هذا استثمار أكبر والنهج الذي يعتمد على Contacts سيوفر لك الفرصة اليوم.
أنت تواجه خطأين منفصلين هنا، وهناك فائدة في إصلاحهما واحداً تلو الآخر، لأن الخطأ 401 والخطأ 422 يعنيان شيئين مختلفين تماماً.
الخطأ 401 يتعلق بالمصادقة أو النطاق. رموز GHL API V2 مقسمة حسب المورد، وكتابة حقول مخصصة للشركة/الأعمال تتطلب نطاق كتابة الأعمال على وجه التحديد، وهو سهل الإغفال إذا تم إعداد الرمز الخاص بك للجهات الاتصال. تأكد من أن الرمز (أو تطبيق OAuth الموجود خلفه) يحتوي فعلاً على نطاق الكتابة للأعمال (الشركات)، وأنك تستخدم عنوان URL الأساسي V2 مع رأس الإصدار الصحيح الذي يتطلبه GHL، فقط رأس إصدار API مفقود أو خاطئ يمكن أن يرمي خطأ 401 على V2.
الخطأ 422 يتعلق بالحمولة. يجب أن تُشار إلى الحقول المخصصة في GHL من خلال معرّف الحقل الخاص بها، وليس باسم الحقل، وشكل الجسم للحقول المخصصة محدد (مصفوفة من الكائنات تحتوي على معرّف الحقل والقيمة، وليس مفتاحاً مستوى العليا). إذا كنت تُرسل codice_cliente باسم أو كخاصية على المستوى الأعلى، فهذا هو الخطأ 422 الخاص بك. اسحب تعريفات حقول الشركة المخصصة أولاً للحصول على معرّف الحقل الدقيق، ثم اكتب باستخدام هذا المعرّف. تأكد أيضاً من أن نوع القيمة يتطابق مع نوع الحقل، فإرسال سلسلة نصية في حقل رقمي سيعطي 422 أيضاً.
إذاً: أصلح النطاق ورأس إصدار الـ API للخطأ 401، ثم طابق معرّف الحقل المخصص الدقيق وشكل الجسم للخطأ 422. أي واحد ينطلق أولاً عند تشغيله، الخطأ 401؟ يجب أن يتم مسح هذا أولاً قبل أن يكون الخطأ 422 قابلاً للوصول حتى.
[تم الحل] مزامنة المبيعات الإضافية للجهات من ملف CSV إلى GoHighLevel عبر جهات متعددة/مكررة مع n8n
مرحبا بالجميع! أردت أن أشارك حلاً لمشكلة واجهناها بخصوص تحديث بيانات المبيعات السنوية الخاصة بالعلامة التجارية بشكل إضافي من ملف CSV إلى GoHighLevel (GHL). هذا مفيد بشكل خاص عندما يحتوي نظام إدارة علاقات العملاء (CRM) على جهات اتصال متعددة أو مكررة (على سبيل المثال، موظفون مختلفون أو مديرو فروع) تابعة لحساب شركة واحد.
بعد عدة اختبارات، قمنا بنجاح ببناء سير عمل خطي وقوي يجمع البيانات بنظافة في البداية ويفكها لاحقاً لتحديث كل جهة اتصال مكررة متطابقة في GoHighLevel في نفس الوقت.
ما يفعله سير العمل وكيفية عمله
تم تصميم سير العمل لأتمتة استيراد البيانات من أنظمة المحاسبة أو إدارة المستندات. يحسب مقاييس المبيعات التقدمية من سنة إلى أخرى مقسمة حسب علامات المنتجات الفردية ويحدث ديناميكياً “تاريخ آخر عملية شراء”.
فيما يلي العمارة المنطقية الدقيقة خطوة بخطوة:
1. استخراج البيانات والتجميع المسبق (CSV)
-
التحليل القوي: يستقبل سير العمل ملف CSV (عبر Google Drive أو تنبيه يدوي). غالباً ما تلتف تصدير بيانات المحاسبة على حقول البيانات بأقواس مزدوجة صارمة تكسر محللات CSV الأصلية. تقوم عقدة JavaScript الأولى بتنظيف تنسيق الفاصلة العشرية (التعامل مع الفواصل) واستخدام نص تحليل الصفوف المخصص لإعادة بناء كائن بيانات نظيف من 8 أعمدة.
-
التوحيد المسبق: إذا كان ملف CSV يحتوي على فواتير أو إيصالات تسليم متعددة لنفس العميل في نفس الملف، فإن الكود يجمع الإجماليات برمجياً، مما ينتج عنه سجل إجمالي واحد فقط لكل معرّف متجر فريد. يمنع هذا n8n من إرسال طلبات متوازية غير متزامنة لنفس العميل إلى GHL، مما يتجنب قفل معدل API أو ظروف السباق (حيث تستبدل العمليات بعضها البعض).
2. البحث عن جهة اتصال في نظام إدارة علاقات العملاء (GET)
-
يرسل سير العمل طلب GET إلى نقطة نهاية GoHighLevel /contacts/. لتجاوز الاختلافات أو التناقضات في عناوين البريد الإلكتروني للشركات عبر الإدخالات المكررة، يبحث الاستعلام باستخدام اسم الشركة (أو معرّف متجر خارجي فريد).
-
يستجيب GoHighLevel برافتة JSON تحتوي على مصفوفة contacts تسرد كل جهة اتصال تطابق استعلام الشركة المحدد.
3. التقسيم المنطقي (الدمج و IF)
-
تقوم عقدة Merge بربط بيانات CSV المجمعة الجديدة مع مخرجات البحث من GHL.
-
تقيّم عقدة IF ما إذا كانت الشركة موجودة بالفعل في نظام إدارة علاقات العملاء (meta.total > 0). إذا كان العميل جديداً، يتفرع سير العمل إلى مسار FALSE لإنشاء سجل جديد (POST). إذا كان موجوداً، فإنه ينتقل على طول مسار TRUE للتحديثات.
4. فك تعبئة المكررات والخطية (Scompatta Duplicati)
-
هذا هو السر لمعالجة السجلات المكررة. تقع عقدة JavaScript المخصصة فوراً بعد مخرجات TRUE من عقدة IF، معزولة من مصفوفة جهات الاتصال المرجعة من GHL و “تفرقع” إلى سطور تنفيذ مستقلة لـ n8n.
-
على سبيل المثال، إذا كانت هناك 3 ملفات تعريف جهات اتصال مكررة موجودة تحت نفس اسم الشركة في GoHighLevel، فإن هذه العقدة تحول فوراً خيط التنفيذ الواحد إلى 3 عناصر منفصلة تتدفق لاحقاً بالتوازي.
5. الحساب الإضافي (رياضيات العلامة التجارية)
- تعالج عقدة JavaScript اللاحقة هذه الجهات المفكوكة واحدة تلو الأخرى. تنظر داخل الحقول المخصصة لـ معرّف جهة الاتصال المحددة (على سبيل المثال،
totale_ecopots_2026، totale_lechuza_2026)، وتستخرج القيمة الرقمية التاريخية الحالية، و تضيف رياضياً حجم المبيعات الجديد المحسوب من ملف CSV الحالي. ينتج عنها حمل نظيف وجاهز للحفظ.
6. تحديثات الحقول المخصصة الشامل (PUT)
-
تنفذ عقدة HTTP Request الأخيرة أمر PUT موجه إلى سلسلة عنوان URL ديناميكي: https://services.leadconnectorhq.com/contacts/``{{ $json.id_contatto }}.
-
بإيقاف تبديل Execute Once داخل إعدادات العقدة المتقدمة، ينفذ n8n حلقة متسلسلة مثالية. يطلق عدداً من طلبات PUT الفردية مثل هناك ملفات تعريف مكررة تم إنشاؤها بواسطة كود فك التعبئة، مما يحدث بسلاسة مقاييس تاريخية عبر كل ملف تعريف ممثل مرتبط بهذا الحساب في نظام إدارة علاقات العملاء.
شكراً لكم جميعاً على المؤشرات السابقة. آمل أن يساعد هذا التخطيط الهيكلي أي شخص يتعامل مع حسابات إضافية معقدة على ملفات تعريف قاعدة بيانات مكررة!