دليل دورة حياة المنتج الشامل
يوفر هذا الدليل جولة شاملة عبر دورة حياة المنتج في OmniCRM، بدءًا من إنشاء تعريف المنتج وحتى توفير الخدمات، وإضافة الإضافات، وإلغاء التوفير. سنغطي استراتيجية التسعير، وتكامل Ansible، وسنقدم أمثلة من العالم الحقيقي طوال الوقت.
نظرة عامة: رحلة المنتج إلى الخدمة
تتبع دورة حياة المنتج في OmniCRM هذه المراحل:
- تعريف المنتج - يقوم المسؤول بإنشاء نموذج المنتج مع قواعد التسعير والتوفير
- إنشاء الخدمة - يقوم العميل بطلب المنتج، ويقوم النظام بتوفير مثيل الخدمة
- دورة حياة الخدمة - يستخدم العميل الخدمة، ويضيف الإضافات/الرصيد، ويعدل الخدمة
- إلغاء التوفير - يتم إنهاء الخدمة، وإطلاق الموارد
فهم التسعير: الجملة مقابل التجزئة
كل منتج وخدمة في OmniCRM له بعدان تسعيريان: الجملة و التجزئة.
تكلفة الجملة
تمثل تكلفة الجملة التكلفة الفعلية لتقديم الخدمة:
- تكاليف البنية التحتية والنطاق الترددي
- رسوم الترخيص
- تكاليف المعدات
- النفقات التشغيلية
تكلفة التجزئة
تكلفة التجزئة هي المبلغ الذي يتم تحصيله من العميل.
تكاليف الإعداد
لكل من الجملة والتجزئة متغيرات تكلفة إعداد لرسوم التوفير لمرة واحدة:
wholesale_setup_cost- تكلفتك للتوفيرretail_setup_cost- المبلغ المحصل من العميل للتفعيل
مثال:
{
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00
}
المرحلة 1: إنشاء تعريف المنتج
المنتجات هي نماذج تحدد ما يتم توفيره وكيف يتم تحصيل الرسوم من العملاء.
إنشاء منتج بطاقة SIM موبايل
دعونا ننشئ منتج بطاقة SIM ��وبايل مسبقة الدفع بسعة 20 جيجابايت من البيانات شهريًا.
الخطوة 1: الانتقال إلى إدارة المنتجات
من واجهة المستخدم الإدارية، انتقل إلى المنتجات → إنشاء منتج.
الخطوة 2: تعريف المعلومات الأساسية
{
"product_name": "Prepaid Mobile 20GB",
"product_slug": "prepaid-mobile-20gb",
"category": "standalone",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-sim-card",
"comment": "Prepaid mobile SIM with 20GB data, unlimited calls & texts"
}
تفسيرات الحقول:
product_name- الاسم الذي يظهر للعميل في الكتالوجproduct_slug- معرف آمن للروابط يُستخدم في استدعاءات API والروابطcategory- "standalone" تعني أن هذا ينشئ خدمة جديدة (مقابل الإضافة/الحزمة)service_type- يجمع المنتجات ذات الصلة، ويستخدم لتصفية الإضافاتenabled- يجب أن يكون صحيحًا ليكون المنتج قابلًا للطلبicon- أيقونة FontAwesome المعروضة في واجهة المستخدمcomment- ملاحظات داخلية للرجوع إليها من قبل الموظفين
الخطوة 3: تعيين التسعير
{
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00,
"contract_days": 30
}
تفصيل التسعير:
- الإيرادات الشهرية لكل عميل: £15.00
- التكلفة الشهرية للتقديم: £5.00
- هامش الربح الشهري: £10.00 (200% زيادة، 67% هامش)
- ربح الإعداد: -£1.00 (مدعوم لجذب العملاء)
- مدة العقد: 30 يومًا (تجديد شهري)
الخطوة 4: تعريف أهلية العميل
{
"residential": true,
"business": false,
"customer_can_purchase": true,
"available_from": "2025-01-01T00:00:00Z",
"available_until": null
}
- يمكن للعملاء السكنيين الطلب
- لا يمكن للعملاء التجاريين (خط إنتاج مختلف)
- تمكين الشراء الذاتي
- متاح من 1 يناير 2025 فصاعدًا
- لا يوجد تاريخ انتهاء (عرض مستمر)
الخطوة 5: تكوين التجديد التلقائي
{
"auto_renew": "prompt",
"allow_auto_renew": true
}
"prompt"- اسأل العميل إذا كان يريد التجديد التلقائي عند الشراء"true"- تجديد تلقائي بدون سؤال"false"- عدم التجديد التلقائي أبدًا (إضافة يدوية فقط)allow_auto_renew: true- يمكن للعميل تمكين/تعطيل التجديد التلقائي لاحقًا
الخطوة 6: تحديد متطلبات المخزون
تحدد متطلبات المخزون الموارد المادية أو الافتراضية التي يجب تخصيصها عند توفير هذا المنتج. هذه خطوة حاسمة تربط كتالوج المنتج الخاص بك بنظام إدارة المخزون.
{
"inventory_items_list": "['SIM Card', 'Mobile Number']"
}
ما هي عناصر المخزون؟
عناصر المخزون هي موارد قابلة للتتبع مخزنة في نظام المخزون الخاص بـ OmniCRM. كل عنصر له:
- النوع - يتم تحديده بواسطة نموذج المخزون (مثل "SIM Card"، "Mobile Number"، "Modem")
- السمات الفريدة - أرقام تسلسلية، عناوين MAC، أرقام هواتف، إلخ.
- الحالة - في المخزون، مخص��، متوقف، إلخ.
- الموقع - موقع مادي أو منطقي
كيف تعمل متطلبات المخزون:
قائمة inventory_items_list هي قائمة بايثون (كـ سلسلة نصية) تحتوي على أسماء أنواع المخزون. يجب أن يتطابق كل اسم تمامًا مع اسم
نموذج المخزون الموجود.
مثال على متطلبات المخزون:
# منتج بطاقة SIM موبايل
inventory_items_list: "['SIM Card', 'Mobile Number']"
# خدمة الإنترنت الثابت
inventory_items_list: "['Modem Router', 'Static IP Address']"
# خدمة رقمية (لا توجد عناصر مادية)
inventory_items_list: "[]"
# لاسلكي ثابت مع CPE
inventory_items_list: "['Fixed Wireless CPE', 'IPv4 Address', 'IPv6 Prefix']"
عملية اختيار المخزون
عندما يقوم المستخدم بتوفير منتج مع متطلبات المخزون، يفرض النظام عملية اختيار إلزامية:
1. تم النقر على زر التوفير
بعد اختيار المنتج، ينقر المستخدم على "توفير". بدلاً من التوفير الفوري، يتحقق النظام من inventory_items_list.
2. يظهر نموذج اختيار المخزون
إذا كانت المخزون مطلوبًا، يظهر مربع حوار مع قائمة منسدلة منفصلة لكل نوع من أنواع المخزون:
3. تصفية المخزون المتاح
تظهر القائمة المنسدلة لكل نوع من أنواع المخزون العناصر التي هي:
- النوع الصحيح - يتطابق مع اسم نموذج المخزون تمامًا
- حالة متاحة -
item_stateهو "جديد" أو "في المخزون" (ليس "مخصص" أو "تالف") - غير مخصص -
service_idوcustomer_idهما NULL - في المخزون في الموقع - يتم تصفيته اختياريًا حسب موقع المستودع/المتجر
مثال على خيارات القائمة المنسدلة:
بالنسبة لنوع المخزون "SIM Card"، قد تظهر القائمة المنسدلة:
كل خيار يعرض:
- معرف المخزون أو رقم المرجع
- المعرف الأساسي (
itemtext1- مثل ICCID لـ SIM، رقم للهاتف) - الموقع الحالي (
item_location)
4. الاختيار مطلوب للمضي قدمًا
قاعدة حاسمة: لا يمكن أن يستمر التوفير بدون اختيار جميع عناصر المخزون المطلوبة.
- يتم تعطيل زر "استمرار" حتى يتم اختيار جميع القوائم المنسدلة
- يجب على المستخدم اختيار عنصر واحد لكل نوع من أنواع المخزون
- يتحقق النظام من الاختيارات قبل المضي قدمًا
5. يتم تمرير المخزون المحدد إلى Ansible
بمجرد أن ينقر المستخدم على "استمرار"، يتم تمرير معرفات المخزون المحددة إلى برنامج Ansible كمتغيرات:
# اختار المستخدم:
# - معرف المخزون لبطاقة SIM: 5001
# - معرف المخزون لرقم الهاتف: 5002
# المتغيرات المرسلة إلى Ansible:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001, # معرف المخزون
"Mobile Number": 5002, # معرف المخزون
"access_token": "eyJ..."
}
ملاحظة: يتطابق اسم المتغير مع نوع المخزون تمامًا. يستخدم البرنامج hostvars[inventory_hostname]['SIM Card'] للوصول إلى معرف المخزون.
6. يسترجع البرنامج تفاصيل المخزون الكاملة
يستخدم بر��امج Ansible معرف المخزون لاسترجاع التفاصيل الكاملة:
- name: Get SIM Card details from inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim
- name: Extract ICCID and IMSI
set_fact:
iccid: "{{ api_response_sim.json.itemtext1 }}"
imsi: "{{ api_response_sim.json.itemtext2 }}"
الآن يحتوي البرنامج على جميع تفاصيل SIM (ICCID، IMSI، إلخ) لتوفير المشترك في HSS.
7. تغير حالة المخزون إلى "مخصص"
بعد إنشاء سجل الخدمة، يقوم البرنامج بتحديث المخزون لربطه بالخدمة:
- name: Assign SIM Card to Service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body:
{
"service_id": "{{ service_creation_response.json.service_id }}",
"customer_id": "{{ customer_id }}",
"item_state": "Assigned"
}
مهم: يحدث تخصيص المخزون أثناء تنفيذ البرنامج كخطوة محددة، وليس عند النقر على زر التوفير. وهذا يعني:
- خطر التخصيص المزدوج: بين النقر على "توفير" وتخصيص المخزون، يمكن لمستخدم آخر نظريًا اختيار نفس عنصر المخزون
- أفضل ممارسة: بالنسبة للعمليات ذات الحجم الكبير، نفذ قفل المخزون أو استخدم معاملات قاعدة البيانات
- الاسترجاع عند الفشل: إذا فشل البرنامج قبل تخصيص المخزون، يبقى المخزون غير مخصص ومتاح لإعادة الاستخدام
لماذا لا يتم التخصيص في وقت سابق؟
لا يتم تخصيص المخزون عند النقر على "توفير" لأن:
- معرف الخدمة مطلوب: لا يوجد
service_idحتى يتم إنشاء الخدمة في البرنامج - بساطة الاسترجاع: إذا فشل التوفير مبكرًا (مثل فشل إنشاء حساب OCS)، لا يحتاج المخزون إلى تنظيف
- المرونة: يمكن للبرنامج أن يقرر عدم تخصيص المخزون بناءً على منطق شرطي
معالجة التوفير الفاشل:
عندما يفشل التوفير بعد تخصيص المخزون، يجب أن يقوم كتلة الإنقاذ بإطلاق المخزون:
rescue:
- name: Release inventory on failure
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body:
{
"service_id": null,
"customer_id": null,
"item_state": "In Stock"
}
when: service_id is defined # فقط إذا تم إنشاء الخدمة
يضمن ذلك عدم ترك المخزون في حالة "مخصص" لخدمة غير موجودة أو فاشلة.
عندما تكون قائمة المخزون فارغة
إذا كانت inventory_items_list: "[]" (قائمة فارغة)، يتم تخطي اختيار المخزون تمامًا ويستمر التوفير على الفور. هذا شائع لـ:
- المنتجات الرقمية - تراخيص البرمجيات، حسابات VPN
- إضافات الخدمة - تعبئة البيانات التي لا تحتاج إلى أجهزة جديدة
- الخدمات الافتراضية - التي لا تستهلك موارد قابلة للتتبع
مثال: تحتوي إضافة "5GB Data Boost" على inventory_items_list: "[]" لأنها تضيف فقط ��صيدًا إلى خدمة موجودة دون الحاجة إلى أجهزة جديدة.
إعداد نموذج المخزون
قبل استخدام نوع المخزون في inventory_items_list، يجب عليك إنشاء نموذج المخزون:
- انتقل إلى الإدارة → المخزون → النماذج
- أنشئ نموذجًا بنفس الاسم بالضبط (مثل "SIM Card")
- عرّف الحقول:
itemtext1_label: "ICCID"itemtext2_label: "IMSI"itemtext3_label: "PUK Code"
- أضف عناصر المخزون من هذا النوع إلى المخزون
للحصول على تفاصيل كاملة حول إنشاء وإدارة نماذج المخزون، راجع إدارة المخزون.
عناصر متعددة من نفس النوع
بينما inventory_items_list هو مصفوفة، فإن وجود أنواع مكررة (مثل "['SIM Card', 'SIM Card']") غير موصى به لأنه قد يسبب ارتباكًا في واجهة المستخدم وتسمية متغيرات البرنامج.
بالنسبة للسيناريوهات التي تتطلب عناصر مشابهة متعددة:
الخيار 1: إنشاء أسماء نماذج مخزون متميزة
# خدمة هاتف ثنائي الشريحة
inventory_items_list: "['Primary SIM Card', 'Secondary SIM Card', 'Mobile Number']"
إنشاء نماذج منفصلة: "Primary SIM Card" و "Secondary SIM Card" بنفس الحقول ولكن بأسماء مختلفة.
الخيار 2: استخدام عنصر مخزون مجمع واحد
# مجموعة ثنائية الشريحة
inventory_items_list: "['Dual SIM Kit', 'Mobile Number']"
حيث يحتوي نموذج المخزون "Dual SIM Kit" على حقول لكلا الشريحتين (itemtext1: ICCID الأساسي، itemtext2: ICCID الثانوي، إلخ).
سيناريوهات المخزون الشائعة
خدمة موبايل:
inventory_items_list: "['SIM Card', 'Mobile Number']"
- بطاقة SIM: مادية أو eSIM مع ICCID/IMSI
- رقم الهاتف: رقم الهاتف (MSISDN)
الإنترنت الثابت:
inventory_items_list: "['Modem Router', 'Static IP Address']"
- جهاز مودم راوتر: جهاز CPE مع عنوان MAC
- عنوان IP ثابت: IPv4 من مجموعة العناوين
لاسلكي ثابت:
inventory_items_list: "['Fixed Wireless CPE', 'IPv4 Address', 'IPv6 Prefix']"
- CPE: معدات في مقر العميل (هوائي، مودم)
- IPv4: عنوان IP عام
- IPv6 Prefix: بادئة /56 أو /64
ملاحظة: المواعيد والجدولة ليست عناصر مخزون. استخدم أنظمة جدولة/تقويم منفصلة لمواعيد التثبيت.
خدمة VoIP:
inventory_items_list: "['DID Number']"
- DID Number: رقم الهاتف المباشر
ملاحظة: أسماء مستخدمي SIP، وكلمات المرور، وتكوينات الحساب تُولد برمجيًا بواسطة برنامج التوفير، وليس من اختيار المخزون.
GPON/الألياف:
inventory_items_list: "['ONT Device', 'GPON Port', 'IPv4 Address', 'Fiber Drop Cable']"
- ONT Device: جهاز الشبكة الضوئية مع رقم تسلسلي
- GPON Port: منفذ محدد على OLT مع اتصال الألياف
- IPv4 Address: IP عام أو خاص
- Fiber Drop Cable: كابل الألياف الفيزيائي من الشارع إلى المبنى (يتتبع لإدارة الأصول)
تأجير المعدات:
inventory_items_list: "['Rental Modem']"
- يتتبع أي مودم مع أي عميل
- مهم لاستعادة المعدات عند الإلغاء
لم��ذا تهم متطلبات المخزون
1. منع التخصيص المزدوج
بدون تتبع المخزون، يمكنك عن غير قصد:
- تخصيص نفس بطاقة SIM لعميلين
- تخصيص نفس عنوان IP لعدة خدمات
- شحن نفس الرقم التسلسلي للمعدات إلى مواقع مختلفة
يضمن اختيار المخزون تخصيص كل عنصر لخدمة واحدة فقط.
2. سجل التدقيق
يخلق تخصيص المخزون سجل تدقيق كامل:
- أي بطاقة SIM مع أي عميل
- متى تم تخصيصها
- أي خدمة تستخدم أي رقم هاتف
- تاريخ المعدات (من كان لديه، ومتى، ولأي خدمة)
3. تخطيط الموارد
تتبع مستويات المخزون:
- تنبيه عند انخفاض بطاقات SIM
- إعادة الطلب قبل نفاد المخزون
- تخطيط جداول الفنيين بناءً على توفر CPE
- إدارة تخصيص مساحة عنوان IP
4. تتبع التكاليف
ربط تكلفة الجملة بعنصر محدد:
- تتبع تكلفة كل بطاقة SIM
- حساب استهلاك المعدات
- تحديد العناصر المفقودة أو المسروقة
- تكلفة السل�� المباعة الدقيقة (COGS)
5. إلغاء التوفير
عند إلغاء الخدمة، يمكن أن يكون المخزون:
- تم إعادته إلى المخزون (بطاقات SIM، مودمات)
- متوقف (معدات تالفة)
- تم إرجاعه إلى البائع (معدات إيجار)
- الاحتفاظ به لفترة السماح (أرقام الهواتف قبل الإفراج)
استكشاف مشكلات اختيار المخزون
المشكلة: تظهر رسالة "لا توجد مخزونات متاحة"
الأسباب:
- لا توجد عناصر مخزون من النوع المطلوب في قاعدة البيانات
- جميع العناصر "مخصصة" بالفعل لخدمات أخرى
- تم وضع علامة على العناصر كـ "تالف" أو "خارج الخدمة"
- اسم نموذج المخزون لا يتطابق تمامًا (حساس لحالة الأحرف)
الحل:
- تحقق من وجود نموذج المخزون: الإدارة → المخزون → النماذج
- تحقق من تطابق اسم النموذج تمامًا (بما في ذلك المسافات، والحالة)
- أضف عناصر المخزون من هذا النوع: الإدارة → المخزون → إضافة عنصر
- تحقق من أن العناصر في حالة "جديد" أو "في المخزون"
- تحقق من أن العناصر ليست مخصصة بالفعل (
service_idيجب أن يكون NULL)
المشكلة: لا يظهر اختيار المخزون
الأسباب:
inventory_items_listفارغ:"[]"inventory_items_listهو NULL أو غير محدد- فئة المنتج هي "إضافة" وترث مخزون الخدمة الأصلية
الحل:
- إذا كانت المخزون مطلوبة، قم بتعيين
inventory_items_list: "['Type1', 'Type2']" - تحقق من أن تعريف المنتج تم حفظه بشكل صحيح
- تحقق من استجابة API للمنتج تشمل inventory_items_list
المشكلة: يفشل البرنامج مع "المخزون غير موجود"
الأسباب:
- يشير البرنامج إلى اسم متغير خاطئ
- لم يتم تمرير معرف المخزون بشكل صحيح
- تم حذف المخزون بين الاختيار والتوفير
الحل:
- تحقق من أن البرنامج يستخدم المتغير الصحيح:
hostvars[inventory_hostname]['SIM Card'] - تحقق من أن المتغير هو عدد صحيح:
{{ hostvars[inventory_hostname]['SIM Card'] | int }} - أضف معالجة الأخطاء في البرنامج للمخزون المفقود
راجع إدارة المخزون للحصول على تفاصيل كاملة حول إنشاء النماذج، وإضافة العناصر، وإدارة مستويات المخزون.
الخطوة 7: تعريف الميزات والشروط
الميزات والشروط هي محتوى تسويقي وقانوني موجه للعميل يساعد العملاء على فهم ما يشترونه والالتزامات المعنية.
{
"features_list": "20GB High-Speed Data. Unlimited Calls & Texts. EU Roaming Included. No Contract. 30-Day Expiry",
"terms": "Credit expires after 30 days. Data, calls, and texts valid only within expiry period. Fair use policy applies. See website for full terms."
}
الغرض والقيمة التجارية
قائمة الميزات - التسويق والمبيعات:
تخدم قائمة الميزات عدة وظائف تجارية حاسمة:
- تمييز المنتج - يساعد العملاء على مقارنة المنتجات بسرعة واختيار المنتج المناسب
- "Prepaid Mobile 20GB" مقابل "Prepaid Mobile 50GB" - الميزات توضح الفرق بوضوح
- بدون الميزات، يرى العملاء السعر فقط، مما يفقدهم قيمة العرض
- الاتصال التسويقي - نقاط البيع الرئيسية معروضة بشكل بارز
- "EU Roaming Included" يجذب المسافرين الدوليين
- "No Contract" يجذب العملاء الذين يتجنبون الالتزام
- الميزات تدفع قرارات الشراء
- توقعات العملاء - تحدد توقعات واضحة حول ما هو مدرج
- تقلل من مكالمات الدعم ("هل يشمل هذا المكالمات؟" → مدرج بوضوح)
- تمنع سوء الفهم وطلبات الاسترداد
- تبني الثقة من خلال الشفافية
- الخدمة الذاتية - تمكن العملاء من اختيار المنتجات المناسبة بأنفسهم
- يقرأ العميل الميزات، ويفهم العرض، ويتخذ قرارًا مستنيرًا
- يقلل من الحاجة إلى شرح موظفي المبيعات
- يسرع من عملية الشراء
- تحسين محركات البحث والاكتشاف - يمكن فهرسة الميزات للبحث
- يبحث العميل عن "خطة موبايل مكالمات غير محدودة" → يظهر المنتج
- يحسن من قابلية البحث في كتالوج المنتجات
الشروط والأحكام - القانونية والامتثال:
تخدم الشروط أغراضًا قانونية وتشغيلية:
- الحماية القانونية - تحمي الأعمال من النزاعات والمسؤولية
- "Credit expires after 30 days" - لا يمكن للعميل المطالبة باسترداد الأموال بعد 31 يومًا
- "Fair use policy applies" - تمنع الإساءة (توصيل المكتب بالكامل على خطة موبايل)
- تخلق اتفاقًا ملزمًا
- إدارة التوقعات - تمنع عدم رضا العملاء
- "Valid only within expiry period" - يعرف العميل موعد انتهاء الاستخدام
- "Cannot be refunded" (للإضافات) - تمنع المشتريات الاحتيالية
- تقلل من الشكاوى والاعتراضات
- ال��متثال التنظيمي - تلبي المتطلبات القانونية
- تتطلب قوانين حماية المستهلك شروطًا واضحة
- تفرض لوائح الاتصالات الإفصاح
- يمكن الإشارة إلى شروط GDPR/الخصوصية
- الحدود التشغيلية - تحدد نطاق الخدمة والقيود
- "Subject to network coverage" - غير مسؤول عن المناطق الميتة
- "Speed may vary" - تدير التوقعات حول السرعات "حتى"
- "Equipment must be returned" - تضمن استعادة معدات الإيجار
- سجل التدقيق - يثبت أن العميل تم إبلاغه
- قبل العميل الشروط عند الشراء
- يسجل النظام توقيت قبول الشروط
- قابل للدفاع في النزاعات أو الإجراءات القانونية
مثال من العالم الحقيقي:
يشتري العميل خطة "مكالمات ونصوص غير محدودة"، ثم يستخدمها للتسويق عبر الهاتف (10,000 مكالمة/يوم). بدون الشروط:
- العميل: "قلت غير محدودة!"
- المزود: "كنا نعني الاستخدام الشخصي..."
- العميل: "هذا لي�� ما أعلنتم عنه!"
- النتيجة: نزاع، احتمال تقديم شكوى للجهات التنظيمية، ضرر للعلامة التجارية
مع الشروط: "تنطبق سياسة الاستخدام العادل. الخدمة للاستخدام الشخصي فقط. الاستخدام التجاري محظور."
- المزود: يشير إلى الشروط التي قبلها العميل
- لا يمكن للعميل الادعاء بالجهل
- أساس قانوني لتعليق الخدمة
- تم حل النزاع لصالح المزود
تنسيق قائمة الميزات:
فهم التنسيق الصحيح أمر حاسم لأن التنسيق غير الصحيح يكسر عرض واجهة المستخدم. قد تظهر الميزات كسلسلة طويلة واحدة بدلاً من نقاط، أو لا تظهر على الإطلاق.
يمكن تنسيق حقل features_list بطريقتين:
الخيار 1: سلسلة مفصولة بالنقاط (موصى به)
تُفصل الميزات بنقطة ومسافة (". "). تقوم واجهة المستخدم بتقسيم هذه الفواصل وتعرض كل ميزة كنقطة.
لماذا هذا التنسيق؟
- سهل التحرير - فقط اكتب الميزات مع نقاط بينها
- لا توجد أحرف خاصة للهروب
- يعمل بشكل موثوق عبر جميع مكونات واجهة المستخدم
- سهل التحديث دون كسر بناء جملة JSON
صحيح مقابل غير صحيح:
الخيار 2: سلسلة مصفوفة JSON
"['20GB High-Speed Data', 'Unlimited Calls & Texts', 'EU Roaming Included']"
يمكن أيضًا لواجهة المستخدم تحليل مصفوفات JSON. لاحظ أن هذه سلسلة تحتوي على JSON، وليست مصفوفة JSON فعلية في قاعدة البيانات.
لماذا يوجد هذا التنسيق؟
- يسمح بالميزات التي تحتوي على نقاط فيها (مثل "حتى 100Mbps. خاضع للتوافر.")
- توليد برمجي من البرامج/API أسهل
- تم استيراده من كتالوجات منتجات خارجية تستخدم المصفوفات
مهم: يجب أن يكون هذا بناء جملة قائمة بايثون صالح كسلسلة. علامات اقتباس مفردة حول كل عنصر، وعلامات اقتباس مزدوجة حول السلسلة الكاملة.
أي تنسيق يجب استخدامه؟
- مفصولة بالنقاط - لإنشاء المنتج يدويًا في واجهة المستخدم (أبسط، أقل عرضة للأخطاء)
- مصفوفة JSON - لإنشاء المنتج بناءً على API/البرامج (أكثر قوة للميزات المعقدة)
يؤدي كلا التنسيقين إلى إنتاج نفس المخرجات في واجهة المستخدم - إنهم فقط يؤثرون على كيفية إدخال البيانات.
أين تظهر الميزات في واجهة المستخدم:
1. كتالوج المنتج (عرض العميل)
عندما يتصفح العملاء المنتجات المتاحة، تُعرض الميزات كنقاط على كل بطاقة منتج:
2. صفحة تفاصيل المنتج
عند النقر على "عرض التفاصيل"، تظهر معلومات المنتج الكاملة بما في ذلك:
- اسم المنتج والأيقونة
- التسعير (التكلفة الشهرية، تكلفة الإعداد)
- قائمة الميزات الكاملة (نقاط)
- الشروط والأحكام (انظر أدناه)
- التوفر والأهلية
3. تأكيد التوفير
أثناء التوفير، تُعرض الميزات للمستخدم للمراجعة قبل التأكيد:
الميزات: • 20GB High-Speed Data • Unlimited Calls & Texts • EU Roaming Included • No Contract • 30-Day Expiry
التكلفة: £15.00/شهر الإعداد: £0.00
[إلغاء] [تأكيد وتوفير]
4. تفاصيل الخدمة (بعد التوفير)
بعد أن تصبح الخدمة نشطة، تُعرض الميزات في صفحة تفاصيل الخدمة للرجوع إليها من قبل العميل.
تنسيق الشروط والأحكام:
حقل terms هو نص عادي يمكن أن يتضمن أسطر جديدة:
أين تظهر الشروط في واجهة المستخدم:
1. صفحة تفاصيل المنتج
تُعرض الشروط في قسم مُنهار يتوسع عند النقر عليه:
2. تأكيد الطلب
أثناء التوفير، يتطلب مربع اختيار من المستخدم قبول الشروط:
زر [توفير] معطل حتى يتم تحديده
3. الفواتير
يمكن تضمين شروط الخدمة في الفواتير كهوامش توضيحية.
أفضل الممارسات:
- الميزات: احتفظ بها مختصرة (أقل من 50 حرفًا لكل منها)، وركز على الفوائد الرئيسية
- الشروط: تضمين المتطلبات القانونية الحيوية، وسياسات انتهاء الصلاحية، وسياسات الاستخد��م العادل
- كلاهما: تحديث عند تغيير المنتج لإبقاء العملاء على اطلاع
الخطوة 8: ربط برنامج توفير Ansible
{
"provisioning_play": "play_local_mobile_sim",
"provisioning_json_vars": "{
\"days\": 30,
\"data_gb\": 20,
\"voice_minutes\": \"unlimited\",
\"sms_count\": \"unlimited\"
}"
}
provisioning_play- اسم برنامج Ansible (بدون امتداد .yaml)provisioning_json_vars- المتغيرات الافتراضية المرسلة إلى البرنامج- يجب أن يوجد البرنامج في:
OmniCRM-API/Provisioners/plays/play_local_mobile_sim.yaml
تعريف المنتج الكامل
{
"product_name": "Prepaid Mobile 20GB",
"product_slug": "prepaid-mobile-20gb",
"category": "standalone",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-sim-card",
"comment": "Prepaid mobile SIM with 20GB data, unlimited calls & texts",
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00,
"contract_days": 30,
"residential": true,
"business": false,
"customer_can_purchase": true,
"available_from": "2025-01-01T00:00:00Z",
"available_until": null,
"auto_renew": "prompt",
"allow_auto_renew": true,
"inventory_items_list": "['SIM Card', 'Mobile Number']",
"features_list": "[
'20GB High-Speed Data',
'Unlimited Calls & Texts',
'EU Roaming Included',
'No Contract',
'30-Day Expiry'
]",
"terms": "Credit expires after 30 days. Data, calls, and texts valid only within expiry period. Fair use policy applies.",
"provisioning_play": "play_local_mobile_sim",
"provisioning_json_vars": "{
\"days\": 30,
\"data_gb\": 20,
\"voice_minutes\": \"unlimited\",
\"sms_count\": \"unlimited\"
}"
}
إنشاء منتج إضافة
تعمل الإضافات على تحسين أو تعديل الخدمات الموجودة. تأتي في نوعين: إضافات افتراضية (لا موارد مادية) و إضافات الأجهزة (تتطلب المخزون).
مثال 1: إضافة افتراضية (5GB Data Boost)
إضافة رقمية تضيف بيانات إلى خدمة موبايل موجودة:
{
"product_name": "5GB Data Boost",
"product_slug": "5gb-data-boost",
"category": "addon",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-plus",
"comment": "Add 5GB extra data to existing mobile service",
"retail_cost": 5.00,
"wholesale_cost": 1.50,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 0.00,
"contract_days": 0,
"residential": true,
"business": true,
"customer_can_purchase": true,
"auto_renew": "false",
"allow_auto_renew": false,
"inventory_items_list": "[]",
"relies_on_list": "",
"features_list": "5GB High-Speed Data. Valid for 7 Days",
"terms": "Data expires after 7 days or when exhausted. Cannot be refunded.",
"provisioning_play": "play_topup_charge_then_action",
"provisioning_json_vars": "{
\"data_gb\": 5,
\"days\": 7
}"
}
مثال 2: إضافة أجهزة (تأجير مودم)
إضافة توفر معدات مادية لخدمة الألياف الموجودة:
{
"product_name": "WiFi 6 Modem Rental",
"product_slug": "wifi6-modem-rental",
"category": "addon",
"service_type": "internet",
"enabled": true,
"icon": "fa-solid fa-router",
"comment": "Add WiFi 6 modem to fiber service - rental",
"retail_cost": 10.00,
"wholesale_cost": 3.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 45.00,
"contract_days": 30,
"residential": true,
"business": true,
"customer_can_purchase": true,
"auto_renew": "true",
"allow_auto_renew": true,
"inventory_items_list": "['Rental Modem']",
"relies_on_list": "",
"features_list": "WiFi 6 (802.11ax). Dual-band 2.4GHz + 5GHz. Up to 40 devices. Parental controls",
"terms": "Equipment rental. Must be returned on service cancellation or £150 replacement fee applies. Equipment remains property of provider.",
"provisioning_play": "play_addon_assign_modem",
"provisioning_json_vars": "{
\"device_type\": \"modem_router\",
\"requires_configuration\": true
}"
}
الاختلافات الرئيسية للإضافات:
category: "addon"- تطبق على الخدمة الموجودة، وليست مستقلةcontract_days: 0(افتراضية) أو30(إيجار متكرر) - تكرار الفوترةinventory_items_list: "[]"(افتراضية) أو"['Rental Modem']"(أجهزة) - موارد ماديةauto_renew: "false"(مرة واحدة) أو"true"(إيجار) - سلوك متكررrelies_on_list: ""- فارغ يعني أنه ينطبق على أي خدمة من نوعservice_typeالمطابق
لماذا تحتاج إضافات الأجهزة إلى ال��خزون:
تتطلب الإضافات الأجهزة inventory_items_list لأنها:
- تتبع المعدات - معرفة أي مودم مع أي عميل
- منع نفاد المخزون - لا يمكن توفير الإضافة إذا لم يكن هناك مودمات في المخزون
- الاسترداد - عند إلغاء العميل، معرفة أي معدات يجب استردادها
- تتبع التكاليف - ربط تكلفة الجملة برقم تسلسلي محدد
- الاستهلاك - تتبع قيمة المعدات على مدى فترة الإيجار
- الضمان - تحديد الوحدات التالفة حسب الرقم التسلسلي
تدفق توفير الإضافات مع المخزون:
عندما يضيف العميل "تأجير مودم WiFi 6" إلى خدمة الألياف الخاصة به:
- تم اختيار الإضافة - ينقر العميل على "إضافة إلى الخدمة"
- يظهر اختيار المخزون - تمامًا مثل الخدمات المستقلة:
- تمت معالجة الدفع - يتم تحصيل £10.00 كإيجار شهري
- تم تخصيص المودم - تم تحديث المخزون:
service_id: مرتبط بخدمة الأليافcustomer_id: مرتبط بالعميلitem_state: "مخصص"
- تم تفعيل الشحن - تم إبلاغ نظام التنفيذ بشحن المودم
- التثبيت - يتلقى العميل المودم، ويقوم بتوصيله إلى ONT
- الفوترة المتكررة - يتم تحصيل £10/شهر حتى يتم إلغاء الإضافة
إلغاء توفير الإضافات الأجهزة:
عندما يلغي العميل تأجير المودم:
- تم بدء الإلغاء - ينقر العميل على "إزالة الإضافة"
- تم بدء عملية الإرجاع:
- يتم إرسال بريد إلكتروني مع تعليمات الإرجاع
- يتم إنشاء ملصق شحن مدفوع مسبقًا
- فترة سماح لمدة 14 يومًا قبل فرض غرامة
- تمت إعادة المعدات:
- تم تحديث المخزون:
item_state= "في المخزون" (بعد التجديد) - أو
item_state= "تالف" (إذا كانت معيبة) - مرتبط بالعميل التالي بمجرد تجديده
- تم تحديث المخزون:
- عدم الإرجاع:
- بعد 14 يومًا، يتم تحصيل غرامة استبدال قدرها £150
- يتم وضع علامة على المخزون:
item_state= "مفقود" - يتم استرداد تكلفة الجملة (£45) + قيمة الاستبدال
التسعير للإضافات:
يمكن تسعير الإضافات بشكل مختلف عن الخدمات المستقلة:
- عادةً ما تكون الإضافات الافتراضية بدون تكاليف إعداد
- قد تحتوي الإضافات الأجهزة على تكاليف إعداد جملة للمعدات
- تستخدم الإضافات الإيجارية
contract_daysلتكرار الفوترة
المرحلة 2: عملية التوفير
عندما يطلب العميل منتج "Prepaid Mobile 20GB"، يقوم OmniCRM بتنظيم التوفير من خلال Ansible.
مخطط تدفق التوفير
طلب العميل → اختيار المخزون → إنشاء وظيفة التوفير ↓ ↓ تفويض الدفع ← تجميع المتغيرات ← تنفيذ برنامج Ansible ↓ ↓ إنشاء سجل الخدمة → إعداد حساب OCS → تخصيص المخزون → الخدمة نشطة
تدفق التوفير خطوة بخطوة
1. يبدأ العميل الطلب
من صفحة العميل:
- ينقر ال��وظف على "إضافة خدمة"
- يختار "Prepaid Mobile 20GB" من دوار المنتج
- تظهر تفاصيل المنتج والتسعير
2. اختيار المخزون
يطلب النظام المخزون المطلوب:
- بطاقة SIM - تظهر القائمة المنسدلة بطاقات SIM المتاحة في المخزون
- مثال: "SIM-00123 - ICCID: 8944..."
- رقم الهاتف - تظهر القائمة المنسدلة أرقام الهواتف المتاحة
- مثال: "+44 7700 900123"
يختار الموظف أو العميل العناصر من المخزون المتاح.
3. تأكيد التسعير
يعرض النظام التسعير النهائي:
- تكلفة الإعداد: £0.00 (تفعيل مجاني)
- التكلفة الشهرية: £15.00
- المستحق اليوم: £15.00 (الشهر الأول)
- تاريخ التجديد: 30 يومًا من اليوم
إذا تم تمكين المطالبة بالتجديد التلقائي، يختار العميل:
- تجديد هذه الخدمة تلقائيًا كل 30 يومًا
4. تم النقر على زر التوفير
عند النقر على "توفير"، يقوم API:
- بإنشاء سجل
Provisionبحالة "قيد التشغي��" (status=1) - يدمج المتغيرات من المنتج + الطلب + اختيارات المخزون
- يولد خيطًا في الخلفية لتنفيذ برنامج Ansible
- يعيد
provision_idإلى واجهة المستخدم لتتبع الحالة
5. تجميع المتغيرات
يدمج النظام المتغيرات من مصادر متعددة:
من المنتج:
{
"days": 30,
"data_gb": 20,
"voice_minutes": "unlimited",
"sms_count": "unlimited"
}
من الطلب:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001,
"Mobile Number": 5002
}
المضاف من النظام:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"initiating_user": 7
}
المتغيرات النهائية المرسلة إلى Ansible:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001,
"Mobile Number": 5002,
"days": 30,
"data_gb": 20,
"voice_minutes": "unlimited",
"sms_count": "unlimited",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"initiating_user": 7
}
6. تنفيذ برنامج Ansible
ينفذ البرنامج play_local_mobile_sim.yaml مع هذه المتغيرات.
فهم برنامج توفير Ansible
دعونا نفحص برنامج توفير حقيقي لفهم ما يحدث خلف الكواليس.
مثال على برنامج توفير بطاقة SIM موبايل
الموقع:
OmniCRM-API/Provisioners/plays/play_local_mobile_sim.yaml
الهيكل العام:
- name: Mobile SIM Provisioning
hosts: localhost
gather_facts: no
become: False
tasks:
- name: Main block
block:
# 1. Load configuration
# 2. Fetch product details from API
# 3. Fetch customer details from API
# 4. Fetch inventory details from API
# 5. Create account in OCS (CGRateS)
# 6. Add balances and allowances to OCS
# 7. Create service record in CRM
# 8. Assign inventory to service
# 9. Record transactions
# 10. Send welcome notifications
rescue:
# Rollback on failure
# - Remove OCS account
# - Release inventory
# - Log error
استعراض تفصيلي للبرنامج:
المهمة 1: تحميل التكوين
- name: Include vars of crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config
يحمّل تكوين النظا�� بما في ذلك:
- عنوان URL وبيانات اعتماد OCS/CGRateS
- عنوان URL الأساسي لـ CRM
- تكوين المستأجر
المهمة 2: استرجاع تفاصيل المنتج
- name: Get Product information from CRM API
uri:
url: "{{ crm_config.crm.base_url }}/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product
ما الذي يفعله هذا:
- يستدعي
GET /crm/product/product_id/42 - يسترجع تعريف المنتج الكامل
- يخزن في متغير
api_response_product
لماذا: على الرغم من أن لدينا provisioning_json_vars من المنتج، نقوم باسترجاع المنتج الكامل للحصول على:
- أحدث التسعير (قد يكون قد تغير منذ بدء الطلب)
- اسم المنتج لتسمية الخدمة
- قائمة الميزات للتوثيق
- تكاليف الجملة لتتبع الهامش
المهمة 3: تعيين حقائق الحزمة
- name: Set package facts
set_fact:
package_name: "{{ api_response_product.json.product_name }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"
setup_cost: "{{ api_response_product.json.retail_setup_cost }}"
يستخرج القيم المستخدمة بشكل شائع إلى متغيرات بسيطة لسهولة القراءة.
المهمة 4: استرجاع تفاصيل المخزون
- name: Get SIM information from CRM API
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim
- name: Set IMSI from Inventory response
set_fact:
imsi: "{{ api_response_sim.json.itemtext2 }}"
iccid: "{{ api_response_sim.json.itemtext1 }}"
ما الذي يفعله هذا:
- يبحث عن معرف مخزون بطاقة SIM 5001
- يسترجع تفاصيل SIM:
itemtext1= ICCID (رقم بطاقة SIM)itemtext2= IMSI (هوية المشترك)
- يفعل نفس الشيء لرقم الهاتف المحمول (يسترجع رقم الهاتف)
لماذا هذا مهم:
- IMSI مطلوب لتوفير المشترك في HSS (خادم المشتركين المنزلي)
- يتم تسجيل ICCID في ملاحظات الخدمة للتعامل مع المشكلات
- يتم عرض رقم الهاتف (MSISDN) للعميل ويستخدم للتوجيه
المهمة 5: توليد UUID للخدمة
- name: Generate UUID Fact
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"
- name: Set Service UUID
set_fact:
service_uuid: "Local_Mobile_SIM_{{ uuid[0:8] }}"
ما الذي يفعله هذا:
- يولد UUID عشوائي
- ينشئ service_uuid مثل
Local_Mobile_SIM_a3f2c1d8
لماذا:
- service UUID هو المعرف الفريد في OCS/CGRateS
- يستخدم لجميع عمليات الشحن
- يجب أن يكون فريدًا عالميًا عبر جميع الخدمات
المهمة 6: إنشاء حساب في OCS
- name: Create account in OCS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
headers:
Content-Type: "application/json"
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": [],
"ExtraOptions": {
"AllowNegative": false,
"Disabled": false
},
"ReloadScheduler": true
}]
}
register: ocs_create_response
ما الذي يفعله هذا:
- يستدعي واجهة برمجة التطبيقات JSON-RPC لـ CGRateS
- ينشئ حسابًا جديدًا مع service_uuid
- يحدد الحساب على أنه نشط (ليس معطلًا)
- يمنع الرصيد السالب (وضع مسبق الدفع)
لماذا:
- حساب OCS هو المكان الذي تحدث فيه جميع عمليات الشحن
- يتم تخزين الأرصدة (البيانات، الصوت، الرسائل القصيرة، المال) هنا
- يتم تتبع الاستخدام وتقييمه في الوقت الحقيقي
المهمة 7: إضافة رصيد البيانات
- name: Add 20GB Data Balance
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "DATA_20GB_Monthly",
"Value": 21474836480,
"ExpiryTime": "+720h",
"Weight": 10,
"DestinationIDs": "*any"
}
}]
}
ما الذي يفعله هذا:
- يضيف رصيد بيانات 20 جيجابايت إلى الحساب
- القيمة: 21474836480 بايت (20 * 1024 * 1024 * 1024)
- تنتهي صلاحيتها في 720 ساعة (30 يومًا)
- الوزن 10 (يتم استهلاك الوزن الأعلى أولاً)
المهمة 8: إضافة صوت ورسائل غير محدودة
- name: Add Unlimited Voice
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*voice",
"Balance": {
"ID": "VOICE_Unlimited",
"Value": 999999999,
"ExpiryTime": "+720h"
}
}]
}
- يضيف 999,999,999 ثانية من الصوت (غير محدود أساسًا)
- تنتهي صلاحيتها في 30 يومًا
المهمة 9: إنشاء سجل الخدمة في CRM
- name: Add Service via API
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"product_id": "{{ product_id }}",
"service_name": "Mobile - {{ phone_number }}",
"service_type": "mobile",
"service_uuid": "{{ service_uuid }}",
"service_status": "Active",
"service_provisioned_date": "{{ provision_datetime }}",
"retail_cost": "{{ monthly_cost }}",
"wholesale_cost": "{{ api_response_product.json.wholesale_cost }}",
"icon": "fa-solid fa-sim-card"
}
register: service_creation_response
ما الذي ينشئه هذا:
- سجل الخدمة المرتبط بالعميل
- يربط OCS عبر
service_uuid - يخزن التكاليف التجزئة والجملة
- يحدد الحالة على أنها "نشطة"
- يعيد
service_idللعمليات اللاحقة
المهمة 10: تخصيص المخزون للخدمة
- name: Assign SIM Card to Service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"service_id": "{{ service_creation_response.json.service_id }}",
"customer_id": "{{ customer_id }}",
"item_state": "Assigned"
}
ما الذي يفعله هذا:
- يقوم بتحديث سجل مخزون بطاقة SIM
- يحدد
service_idلربط SIM بالخدمة - يغير الحالة من "في المخزون" إلى "مخصص"
- يكرر ذلك لرقم الهاتف المحمول
لماذا:
- يتتبع أي بطاقة SIM مخصصة لأي عميل
- يمنع التخصيص المزدوج للمخزون
- يمكّن من تقارير المخزون والتدقيق
المهمة 11: تسجيل معاملة تكلفة الإعداد
- name: Add Setup Cost Transaction
uri:
url: "{{ crm_config.crm.base_url }}/crm/transaction/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_creation_response.json.service_id }}",
"title": "{{ package_name }} - Setup",
"description": "Activation fee",
"retail_cost": "{{ setup_cost }}",
"wholesale_cost": "{{ api_response_product.json.wholesale_setup_cost }}"
}
ما الذي يفعله هذا:
- يسجل تكلفة إعداد قدرها £0.00 للعميل (تجزئة)
- يسجل تكلفة جملة قدرها £1.00
- ينشئ سجل المعاملة للفوترة
المهمة 12: كتلة الإنقاذ (معالجة الأخطاء)
rescue:
- name: Remove account in OCS on failure
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Account": "{{ service_uuid }}"
}]
}
- name: Fail the provisioning
fail:
msg: "Provisioning failed, rolled back OCS account"
ما الذي يفعله هذا:
- إذا فشلت أي مهمة، يتم تنفيذ كتلة الإنقاذ
- يحذف حساب OCS الذي تم إنشاؤه جزئيًا
- يطلق المخزون مرة أخرى إلى "في المخزون"
- يفشل وظيفة التوفير مع رسالة خطأ
لماذا:
- يمنع الحسابات اليتيمة في OCS
- يضمن استرجاع نظيف عند الأخطاء
- يحافظ على اتساق البيانات
التوفير مكتمل: ما تم إنشاؤه
بعد التوفير الناجح، يحتوي النظام على:
1. حساب OCS (CGRateS):
- معرف الحساب:
Local_Mobile_SIM_a3f2c1d8 - الأرصدة:
- 20 جيجابايت من البيانات (تنتهي في 30 يومًا)
- صوت غير محدود (999M ثانية، تنتهي في 30 يومًا)
- رسائل نصية غير محدودة (999M رسالة، تنتهي في 30 يومًا)
2. سجل الخدمة في CRM:
- معرف الخدمة: 1234
- العميل: جون دو (customer_id: 123)
- المنتج: Prepaid Mobile 20GB (product_id: 42)
- اسم الخدمة: "Mobile - +44 7700 900123"
- service UUID:
Local_Mobile_SIM_a3f2c1d8 - الحالة: نشطة
- التكلفة الشهرية: £15.00 (تجزئة)، £5.00 (جملة)
- الربح: £10.00/شهر
3. تخصيصات المخزون:
- بطاقة SIM 5001: مخصصة للخدمة 1234، العميل 123
- رقم الهاتف المحمول 5002: مخصص للخدمة 1234، العميل 123
4. سجلات المعاملات:
- تم إنشاء سجل معاملة تكلفة الإعداد
- تم تسجيل رسوم الشهر الأول
5. يمكن للعميل الآن:
- عرض الخدمة في بوابة الخدمة الذاتية
- رؤية رصيد البيانات 20 جيجابايت
- إجراء المكالمات وإرسال الرسائل النصية
- تعبئة الرصيد أو إضافة الإضافات
- عرض الاستخدام في الوقت الحقيقي
المرحلة 3: إضافة الإضافات والتعبئة
بعد أن تصبح الخدمة نشطة، يمكن للعملاء شراء الإضافات لتعزيز خدمتهم.
تدفق توفير الإضافات
لنقل أن العميل قد استخدم 18 جيجابايت من سعة 20 جيجابايت ويريد شراء إضافة "5GB Data Boost".
1. يتنقل العميل إلى الخدمة
- يفتح صفحة "Mobile - +44 7700 900123"
- يرى الاستخدام الحالي: 18 جيجابايت من 20 جيجابايت مستخدمة (90%)
- ينقر على "إضافة إضافة" أو "تعبئة"
2. يقوم النظام بتصفية الإضافات المتاحة
يظهر فقط الإضافات حيث:
category = "addon"service_type = "mobile"(يتطابق مع نوع الخدمة)residential = true(إذا كان العميل سكنيًا)enabled = true
يرى العميل: "5GB Data Boost - £5.00"
3. يختار العميل الإضافة
- ينقر على "5GB Data Boost"
- يؤكد الشراء مقابل £5.00
- يلتقط النظام تفويض الدفع
4. يتم بدء توفير الإضافة
يستدعي النظام play_topup_charge_then_action.yaml مع المتغيرات:
{
"product_id": 43, # منتج 5GB Data Boost
"customer_id": 123,
"service_id": 1234, # الخدمة الموجودة
"access_token": "eyJ...",
"data_gb": 5, # من provisioning_json_vars
"days": 7 # من provisioning_json_vars
}
الاختلاف الرئيسي عن المستقل:
- يتم تضمين
service_id(الخدمة الموجودة للتعديل) - لا يتطلب المخزون
- لا يتم إنشاء الخدمة (يعدل الموجودة)
استعراض برنامج توفير الإضافات
المهمة 1: استرجاع تفاصيل الخدمة
- name: Get Service information from CRM API
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_service
- name: Set service facts
set_fact:
service_uuid: "{{ api_response_service.json.service_uuid }}"
customer_id: "{{ api_response_service.json.customer_id }}"
لماذا:
- تحتاج إلى
service_uuidلإضافة الرصيد إلى الحساب الصحيح في OCS - يتحقق من وجود الخدمة وأنها نشطة
- يضمن أن الخدمة تعود للعميل
المهمة 2: تحصيل العميل
- name: Get Customer's Default Payment Method
uri:
url: "http://localhost:5000/crm/stripe/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_stripe
- name: Get default card ID
set_fact:
customer_stripe_id: "{{ api_response_stripe.json | json_query(query) }}"
vars:
query: "data[?default_payment_method==`true`].customer_stripe_id | [0]"
- name: Charge card
uri:
url: "http://localhost:5000/crm/stripe/charge_card/{{ customer_stripe_id }}"
method: POST
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"retail_cost": 5.00,
"description": "5GB Data Boost",
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_id }}",
"product_id": "{{ product_id }}",
"wholesale_cost": 1.50,
"invoice": true
}
register: charge_response
- name: Assert payment successful
assert:
that:
- charge_response.status == 200
ما الذي يفعله هذا:
- يجد طريقة الدفع الافتراضية للعميل في Stripe
- يقوم بخصم £5.00 من البطاقة
- يسجل تكلفة الجملة £1.50 لتتبع الهامش
- ينشئ معاملة مرتبطة بالخدمة
- يضيف إلى الفاتورة التالية
- يفشل التوفير إذا فشل الدفع
لماذا يتم التحصيل أولاً:
- لا يتم تسليم الرصيد حتى يتم تأكيد الدفع
- يمنع الاحتيال
- يطابق الدفع مع توفير الإضافة
المهمة 3: إضافة رصيد البيانات إلى OCS
- name: Add 5GB Data Balance
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "DATA_5GB_Boost_{{ uuid }}",
"Value": 5368709120,
"ExpiryTime": "+168h",
"Weight": 20
}
}]
}
ما الذي يفعله هذا:
- يضيف 5 جيجابايت (5368709120 بايت) إلى الحساب
- تنتهي صلاحيتها في 168 ساعة (7 أيام)
- الوزن 20 (يتم استهلاك الوزن الأعلى أولاً - تعزيز قبل الرصيد الشهري)
رصيد العميل بعد الإضافة:
- الشهري الأصلي: 2 جيجابايت متبقية (تنتهي في 25 يومًا)
- التعزيز الجديد: 5 جيجابايت (تنتهي في 7 أيام)
- الإجمالي المتاح: 7 جيجابايت
- ترتيب الاستخدام: يتم استهلاك التعزيز أولاً، ثم الشهري
المهمة 4: تسجيل المعاملة
- name: Add Addon Transaction
uri:
url: "http://localhost:5000/crm/transaction/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_id }}",
"title": "5GB Data Boost",
"description": "Additional 5GB data valid for 7 days",
"retail_cost": 5.00,
"wholesale_cost": 1.50
}
ما الذي يفعله هذا:
- يسجل خصم £5.00 من العميل
- يسجل تكلفة جملة £1.50
- يربط المعاملة بالخدمة للتقارير
ملخص تدفق الإضافات الكامل
- يختار العميل الإضافة من القائمة المصفاة
- يتم تفويض الدفع وتحميله
- يتم إضافة رصيد البيانات إلى حساب OCS
- يتم تسجيل المعاملة في CRM
- يرى العميل على الفور الرصيد المحدث: 7 جيجابايت متاحة
تتبع مالي:
- رسوم الخدمة الشهرية: £15 تجزئة، £5 جملة
- شراء الإضافة: £5 تجزئة، £1.50 جملة
التجديد التلقائي: الإضافات المتكررة
يمكن تعيين بعض الإضافات للتجديد التلقائي (خطط البيانات الشهرية، الاشتراكات، إلخ).
تكوين المنتج:
{
"product_name": "Monthly 10GB Data Plan",
"category": "addon",
"retail_cost": 10.00,
"contract_days": 30,
"auto_renew": "true",
"provisioning_play": "play_topup_charge_then_action"
}
يخلق التوفير ActionPlan:
- name: Create ActionPlan for Auto-Renewal
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV1.SetActionPlan",
"params": [{
"Id": "ServiceID_{{ service_uuid }}__ProductID_{{ product_id }}__MonthlyRenewal",
"ActionPlan": [{
"ActionsId": "Action_{{ product_slug }}",
"Years": "*any",
"Months": "*any",
"MonthDays": "*any",
"WeekDays": "*any",
"Time": "00:00:00",
"Weight": 10
}],
"Overwrite": false,
"ReloadScheduler": true
}]
}
ما الذي يفعله هذا:
- ينشئ مهمة مجدولة في OCS
- ينفذ
Action_{{ product_slug }}كل 30 يومًا - تتقاضى الرسوم من العميل وتعيد تطبيق رصيد البيانات
- تستمر حتى يلغي العميل
إدارة العملاء:
- يرى العميل "التجديد التالي: 1 فبراير 2025 - £10.00" في عرض الخدمة
- يمكنه النقر على "إلغاء التجديد التلقائي" لوقف الرسوم المستقبلية
- يمكنه النقر على "تجديد الآن" لتطبيق رصيد الشهر التالي على الفور
المرحلة 4: إلغاء التوفير للخدمات
عندما يلغي العميل الخدمة، يجب على النظام إزالة جميع الموارد بشكل نظيف.
محفزات إلغاء التوفير
يمكن أن يتم إلغاء التوفير بواسطة:
- إلغاء العميل - ينقر العميل على "إلغاء الخدمة"
- إجراء إداري - يقوم الموظف بتحديد الخدمة للإلغاء
- عدم الدفع - تنتهي الخدمة بسبب عدم التجديد
- نهاية العقد - ينتهي العقد المحدد المدة
تدفق إلغاء التوفير
1. يبدأ العميل الإلغاء
- يتنقل إلى الخدمة
- ينقر على "إلغاء الخدمة"
- يطلب النظام: "هل أنت متأكد؟ سيتم فقدان أي رصيد متبقي."
- يؤكد العميل
2. فترة السماح (اختياري)
تقوم بعض المشغلين بتنفيذ فترة سماح:
- يتم وضع علامة على الخدمة "قيد الإلغاء"
- تبقى نشطة لمدة 7-30 يومًا
- يمكن للعميل عكس الإلغاء خلال فترة السماح
- إلغاء التوفير التلقائي بعد فترة السماح
3. تم إنشاء وظيفة إلغاء التوفير
ينشئ النظام وظيفة التوفير مع:
{
"action": "deprovision",
"service_id": 1234,
"customer_id": 123,
"service_uuid": "Local_Mobile_SIM_a3f2c1d8"
}
يستدعي البرنامج المحدد في service.deprovisioning_play أو كتلة الإنقاذ من البرنامج الأصلي.
4. برنامج إلغاء التوفير Ansible
- name: Deprovision Mobile Service
hosts: localhost
tasks:
- name: Disable OCS Account
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Account": "{{ service_uuid }}",
"ExtraOptions": { "Disabled": true }
}]
}
- name: Remove ActionPlans (stop auto-renewals)
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV1.RemoveActionPlan",
"params": [{
"Id": "ServiceID_{{ service_uuid }}__*"
}]
}
- name: Update Service Status in CRM
uri:
url: "http://localhost:5000/crm/service/{{ service_id }}"
method: PATCH
body:
{
"service_status": "Deactivated",
"service_deactivate_date": "{{ current_datetime }}"
}
- name: Release Inventory to Stock
uri:
url: "http://localhost:5000/crm/inventory/inventory_id/{{ sim_card_id }}"
method: PATCH
body:
{
"service_id": null,
"customer_id": null,
"item_state": "Decommissioned"
}
ما الذي يفعله هذا:
- تعطيل حساب OCS - يتوقف كل الشحن، يتم حظر الاستخدام
- إزالة ActionPlans - تلغي التجديدات التلقائية
- تحديث خدمة CRM - الحالة "معطلة"، يتم تسجيل التاريخ
- إطلاق المخزون - يتم وضع علامة على SIM "متوقف"، متاح لإعادة الاستخدام (بعد التجديد)
5. بعد إلغاء التوفير
يقوم النظام بأداء التنظيف:
- لم يعد العميل يرى الخدمة في بوابة الخدمة الذاتية
- تبقى الخدمة في CRM للتقارير التاريخية
- يتم الحفاظ على المعاملات والفواتير للمحاسبة
- يمكن تجديد المخزون وإعادة استخدامه
- يمكن أرشفة حساب OCS بعد فترة الاحتفاظ
إلغاء التوفير الجزئي مقابل الكامل
إلغاء التوفير الجزئي (التعليق):
- يستخدم لعدم الدفع أو التعليق المؤقت
- يتم تعطيل حساب OCS ولكن لا يتم حذفه
- يتم الحفاظ على الأرصدة
- يمكن إعادة تمكينه عند استلام الدفع
- name: Suspend Service
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Account": "{{ service_uuid }}",
"ExtraOptions": { "Disabled": true }
}]
}
إلغاء التوفير الكامل (الإلغاء الدائم):
- يستخدم للإلغاء الدائم
- يتم حذف حساب OCS بالكامل
- يتم فقدان الأرصدة
- لا يمكن إعادة تمكينه
- name: Remove OCS Account
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Account": "{{ service_uuid }}"
}]
}
أفضل الممارسات لإدارة المنتجات
إدارة دورة حياة المنتج
حالات المنتج:
enabled: true- المنتج متاح للطلبات الجديدةenabled: false- المنتج معطل، تستمر الخدمات الحالية
تعطيل المنتجات:
- ضع علامة على المنتج كـ
enabled: falseلمنع الطلبات الجديدة - تظل الخدمات الحالية نشطة
- يمكن للعملاء تجديد/تعديل الخدمات الحالية
- مفيد لتقليل المنتجات القديمة
إدارة المخزون
حالات المخزون:
New- مخزون جديد، جاهز للتخصيصIn Stock- متاح للتوفيرAssigned- مرتبط بخدمة العميلDecommissioned- يمكن تجديده وإعادة استخدامهDamaged- يحتاج إلى إصلاح أو التخلص
إعادة استخدام المخزون:
بعد إلغاء التوفير:
- بطاقات SIM: تجديد ووضع علامة "في المخزون"
- أرقام الهواتف: إطلاق بعد فترة النقل (30 يومًا)
- المعدات: اختبار، تجديد، وضع علامة "مستخدمة"
مقاييس التوفير
مراقبة:
- معدل نجاح التوفير
- متوسط وقت التوفير
- نقاط الفشل الشائعة
- دوران المخزون