سند تحلیل ماژول — Module 01

ماژول CRM — تحلیل تفصیلی
Contacts · Deals · Pipelines · Automation

تحلیل کامل اولین ماژول تجاری پلتفرم اتوماسیون: دامنه کسب‌وکار، مدل داده، قوانین کسب‌وکار، گره‌های اتوماسیون (Trigger / Action / Condition)، سناریوهای واقعی، API، مجوزها، متریک مصرف و نقشه پیاده‌سازی.

اسلاگ: module-crm وابستگی: automation-core اولویت: فاز ۱ (MVP) گره‌ها: ۲۲ عدد پیش‌نیاز سند: 01-automation-platform-architecture

۱ جایگاه و محدوده ماژول

۱.۱ چرا CRM اولین ماژول است؟

۱) عمومی‌ترین نیاز

تقریباً هر کسب‌وکار B2B و B2C به مخاطب، پیگیری و قیف فروش نیاز دارد. بستر تست ایده برای همه صنایع.

۲) بهترین نقطه شروع اتوماسیون

«وقتی لید جدید ثبت شد → پیامک بفرست → تسک پیگیری بساز → به کارشناس تخصیص بده» روشن‌ترین و پرفروش‌ترین سناریوی اتوماسیون است.

۳) موتور محرک فروش ماژول‌های دیگر

CRM مشتری را نگه می‌دارد و سپس ماژول‌های پیام‌رسان، پرداخت و گزارش روی آن سوار می‌شوند.

۱.۲ محدوده ماژول (Scope) و آنچه خارج از آن است

✅ داخل محدوده (In Scope)

  • مخاطب، شرکت، لید، فرصت فروش (Deal)
  • قیف فروش و مراحل قابل تعریف (Pipeline / Stage)
  • فعالیت‌ها، یادداشت‌ها، تماس‌ها، تسک‌ها
  • فیلدهای سفارشی برای هر tenant
  • برچسب‌ها، سگمنت‌ها، فیلترهای ذخیره‌شده
  • قوانین تخصیص، امتیازدهی (Lead Score)، عدم‌تکرار (Dedupe)
  • گره‌های اتوماسیون مرتبط با موجودیت‌های بالا
  • لیدفرم (Web-to-Lead) با endpoint عمومی

⛔ خارج از محدوده (Out of Scope)

  • ارسال واقعی پیامک/ایمیل ← ماژول module-messaging
  • پرداخت و فاکتورسازی ← ماژول module-payment
  • محصول و موجودی ← ماژول module-ecommerce
  • گزارش‌های تحلیلی پیشرفته ← ماژول module-analytics
  • تماس صوتی / VoIP (فاز ۴)
  • Zapier-like app marketplace خارجی

۱.۳ نقشه همکاری ماژول‌ها

        ┌──────────────────────────────────────────────┐
        │            automation-core (هسته)            │
        │  موتور اجرا · رجیستری گره · بیلینگ · tenant   │
        └───────────────────┬──────────────────────────┘
                            │ (ثبت گره‌ها و متریک‌ها)
       ┌────────────────────┼────────────────────┬───────────────────┐
       ▼                    ▼                    ▼                   ▼
┌─────────────┐   ┌─────────────────┐  ┌─────────────────┐ ┌──────────────┐
│ module-crm  │◄──│ module-messaging│  │ module-payment  │ │ module-       │
│ (این سند)   │   │ پیامک/ایمیل/    │  │ درگاه/فاکتور    │ │ analytics     │
│             │   │ واتس‌اپ/تلگرام  │  │                 │ │ گزارش‌ها      │
└─────────────┘   └─────────────────┘  └─────────────────┘ └──────────────┘
       ▲
       │ گره‌های CRM که ماژول‌های دیگر مصرف می‌کنند:
       │ crm.contact.create · crm.deal.move_stage · crm.activity.log
       └──────────────────────────────────────────────────────────────
اصل طراحی: ماژول CRM هیچ وابستگی‌ای به ماژول‌های دیگر ندارد. تعامل ماژول‌ها فقط از طریق گره‌های ثبت‌شده در هسته انجام می‌شود. یعنی مشتری می‌تواند فقط CRM را بخرد و همان لحظه ارزش بگیرد.

۲ دامنه کسب‌وکار (Domain Model)

۲.۱ موجودیت‌های اصلی

طراحی دامنه CRM بر پایه ۱۱ موجودیت اصلی انجام می‌شود. هر موجودیت یک مفهوم کسب‌وکاری مستقل است و در گره‌های اتوماسیون قابل ارجاع خواهد بود.

موجودیتمفهوم کسب‌وکاریکاربرد در اتوماسیون
Contactشخص: مشتری، لید، سرنخمحرک اصلی (ایجاد/تغییر) و هدف اکثر اقدامات
Companyسازمان/شرکت مرتبط با مخاطبانمبنای سگمنت‌بندی B2B و تخصیص حساب
Dealفرصت فروش با مبلغ و تاریخ بستنمحرک تغییر مرحله، یادآوری، پیش‌بینی درآمد
Pipelineقیف فروش (مثلاً «فروش مستقیم»، «همکاری»)انتخاب مسیر در گره‌های حرکت مرحله
Stageمرحله داخل قیف (لید، مذاکره، برنده، باخت)شرط و اقدام؛ ترتیب و احتمال موفقیت
Activityرویداد تعامل: تماس، ایمیل، جلسه، یادداشتثبت خودکار؛ محرک پیگیری بعدی
Taskکار زمان‌دار برای کارشناسایجاد خودکار با سررسید نسبی (SLAs)
Tagبرچسب چندگانه روی مخاطب/فرصتشرط فیلتر و ورودی سگمنت
Segmentگروه داینامیک بر اساس فیلتر ذخیره‌شدهمخاطب هدف اقدامات گروهی و کمپین
CustomFieldفیلد اختصاصی هر tenant روی موجودیت‌هاشخصی‌سازی برای هر صنعت بدون تغییر کد
LeadFormفرم جذب لید با endpoint عمومیمحرک ورود داده از سایت مشتری

۲.۲ نمودار روابط موجودیت‌ها (ERD)

                    ┌───────────────┐
                    │   companies   │
                    │ name, domain  │
                    └───────┬───────┘
                            │ 1:N
                            ▼
┌───────────────┐   N:1  ┌───────────────┐   N:M   ┌──────────┐
│   segments    │◄──────►│   contacts    │◄───────►│   tags   │
│ (dynamic)     │        │ email, mobile │         │  color   │
└───────────────┘        │ score, status │         └──────────┘
                         └───┬───────┬───┘
                             │       │
                   1:N       │       │     1:N
                             ▼       ▼
              ┌────────────────┐  ┌──────────────────┐
              │    deals       │  │   activities     │
              │ amount, stage  │  │ type, body, ts   │
              │ close_date     │  └──────────────────┘
              └───────┬────────┘
                      │ N:1
                      ▼
              ┌────────────────┐   1:N   ┌──────────────────┐
              │   pipelines    │────────►│     stages       │
              │ name, is_default│        │ order, prob.     │
              └────────────────┘        └──────────────────┘

              ┌────────────────┐        ┌──────────────────┐
              │ custom_fields  │        │   lead_forms     │
              │ entity, type   │        │ public_token     │
              └────────────────┘        └──────────────────┘
              ┌────────────────┐        ┌──────────────────┐
              │  assignment_   │        │   crm_tasks      │
              │  rules         │        │ due_at, assignee │
              └────────────────┘        └──────────────────┘
              ┌────────────────┐
              │  field_values  │  (مقادیر فیلد سفارشی — JSON)
              └────────────────┘

۲.۳ چرخه حیات مخاطب (Contact Lifecycle)

  [ورود از فرم/وب‌هوک/API/ورود دستی]
                 │
                 ▼
          ┌─────────────┐
          │  new (لید)  │  ← امتیاز اولیه = 0
          └──────┬──────┘
                 │ (قوانین امتیازدهی)
       ┌─────────┴─────────┐
       ▼                   ▼
  ┌─────────┐        ┌──────────────┐
  │  cold   │        │  qualified   │  ← MQL (امتیاز ≥ آستانه)
  └────┬────┘        └──────┬───────┘
       │                    │ (تخصیص به کارشناس)
       │                    ▼
       │             ┌──────────────┐
       │             │    lead →    │
       │             │  contacted   │  ← اولین فعالیت ثبت شد
       │             └──────┬───────┘
       │                    ▼
       │             ┌──────────────┐
       │             │  nurturing   │  ← دنبال‌سازی زمان‌بندی‌شده
       │             └──────┬───────┘
       │                    │ (ایجاد Deal)
       └────────►┌──────────▼──────────┐
                 │      customer        │  ← Deal به مرحله «برنده» رسید
                 └──────────┬──────────┘
                            ▼
                     ┌─────────────┐
                     │ churned /   │  ← عدم خرید مجدد پس از N روز
                     │  inactive   │
                     └─────────────┘
نکته طراحی: هر تغییر در این چرخه یک رویداد قابل اتوماسیون تولید می‌کند. به همین دلیل گره‌های Trigger ماژول CRM دقیقاً روی همین گذارها تعریف می‌شوند.

۳ مدل داده و جداول

۳.۱ جدول‌های ماژول (پیشوند crm_)

جدولستون‌های کلیدیتوضیح
crm_contactstenant_id, first_name, last_name, email, mobile, company_id, status, score, owner_user_id, source, last_activity_at, meta JSONموجودیت مرکزی. ایندکس یکتا روی (tenant_id, email) و (tenant_id, mobile)
crm_companiestenant_id, name, domain, industry, size, owner_user_id, meta JSONایندکس یکتا روی (tenant_id, domain)
crm_pipelinestenant_id, name, is_default, sort_orderهر tenant می‌تواند چند قیف داشته باشد
crm_stagespipeline_id, name, sort_order, probability, is_won, is_lost, rotting_daysrotting_days برای هشدار رکود فرصت
crm_dealstenant_id, pipeline_id, stage_id, contact_id, company_id, title, amount, currency, expected_close_date, status, owner_user_id, stage_changed_atایندکس روی (tenant_id, stage_id) و (tenant_id, expected_close_date)
crm_deal_stage_historydeal_id, from_stage_id, to_stage_id, changed_by, changed_at, duration_secondsبرای گزارش قیف و محاسبه مدت هر مرحله
crm_activitiestenant_id, contact_id, deal_id, type (call/email/meeting/note/whatsapp), subject, body, occurred_at, user_id, meta JSONtype قابل توسعه توسط ماژول‌های دیگر
crm_taskstenant_id, contact_id, deal_id, title, due_at, assignee_user_id, status, priority, completed_atمبنای گره «ایجاد تسک پیگیری»
crm_tagstenant_id, name, slug, colorیکتا روی (tenant_id, slug)
crm_taggablestag_id, taggable_type, taggable_idرابطه چندبه‌چند (Polymorphic)
crm_segmentstenant_id, name, entity (contact/deal), filters JSON, is_dynamic, last_countفیلترها در قالب JSON استاندارد تعریف می‌شوند
crm_custom_fieldstenant_id, entity, key, label, type, options JSON, is_required, sort_orderهر tenant فیلدهای خودش را می‌سازد
crm_field_valuestenant_id, custom_field_id, entity_type, entity_id, value_text, value_number, value_date, value_jsonمقادیر با ستون‌های typed برای کوئری و ایندکس‌پذیری
crm_assignment_rulestenant_id, name, strategy (round_robin/load_balanced/fixed/manual), conditions JSON, user_pool JSON, is_active, last_assigned_indexموتور تخصیص خودکار
crm_scoring_rulestenant_id, name, entity, expression JSON, points, is_activeمثلاً «ایمیل شرکتی = +۱۰ امتیاز»
crm_lead_formstenant_id, name, public_token, fields JSON, redirect_url, workflow_id, honeypot, is_activepublic_token برای endpoint عمومی بدون احراز هویت
crm_merge_logstenant_id, primary_id, merged_ids JSON, merged_by, merged_atقابلیت Audit برای ادغام مخاطبان تکراری
قاعده نام‌گذاری: همه جدول‌های ماژول با پیشوند crm_ شروع می‌شوند. این کار هم‌زیستی چند ماژول در یک دیتابیس و مهاجرت‌های مستقل را ممکن می‌کند.

۳.۲ نمونه Migration (جدول مخاطبان)

// app/Modules/Crm/Database/Migrations/..._create_crm_contacts_table.php
Schema::create('crm_contacts', function (Blueprint $t) {
    $t->id();
    $t->foreignId('tenant_id')->constrained()->cascadeOnDelete();
    $t->foreignId('company_id')->nullable()->constrained('crm_companies')->nullOnDelete();
    $t->foreignId('owner_user_id')->nullable()->constrained('users')->nullOnDelete();

    $t->string('first_name', 80)->nullable();
    $t->string('last_name', 80)->nullable();
    $t->string('email', 190)->nullable();
    $t->string('mobile', 32)->nullable();
    $t->string('status', 24)->default('new');   // new|qualified|contacted|nurturing|customer|churned
    $t->integer('score')->default(0);
    $t->string('source', 40)->nullable();      // form|webhook|api|manual|import
    $t->timestamp('last_activity_at')->nullable();
    $t->json('meta')->nullable();
    $t->timestamps();
    $t->softDeletes();                        // حذف منطقی برای احترام به داده مشتری

    $t->unique(['tenant_id', 'email']);
    $t->index(['tenant_id', 'mobile']);
    $t->index(['tenant_id', 'status', 'score']);
    $t->index(['tenant_id', 'owner_user_id']);
});

۳.۳ تصمیم مهم: فیلدهای سفارشی — JSON یا EAV؟

گزینهمزیتعیبحکم
فقط ستون meta JSONسادگی کامل، بدون joinکوئری و ایندکس‌گذاری روی فیلد خاص دشوارکافی نیست
EAV خالص (کلید-مقدار)انعطاف بی‌نهایتکوئری سنگین، پیچیدگی گزارش‌گیریتنها
ترکیبی: تعریف در crm_custom_fields + مقدار در crm_field_values با ستون typedانعطاف + قابلیت فیلتر و ایندکس روی فیلدهای پرکاربردکمی پیچیدگی بیشتر در سرویسانتخاب‌شده

الگوی مقداردهی: بر اساس type فیلد، مقدار در ستون متناظر نوشته می‌شود: text → value_text، number/currency → value_number، date → value_date، multi-select/json → value_json.

هشدار: محدودیت تعداد فیلد سفارشی باید از روز اول در پلن تعریف شود (مثلاً Free = ۳، Business = ۲۵، Enterprise = نامحدود). در غیر این صورت یک مشتری می‌تواند با ۵۰۰ فیلد، کارایی سیستم را برای همه پایین بیاورد.

۴ قوانین کسب‌وکار (Business Rules)

این قوانین درون ماژول پیاده می‌شوند (نه در Workflow) چون رفتار پیش‌فرض و قابل پیش‌بینی مورد انتظار یک CRM هستند. اتوماسیون روی این قوانین سوار می‌شود، جایگزین آن‌ها نمی‌شود.

۴.۱ جلوگیری از تکرار مخاطب (Deduplication)

  ورودی جدید (فرم / وب‌هوک / API / دستی)
              │
              ▼
   ┌─────────────────────────────────────┐
   │ ۱) نرمال‌سازی                      │
   │    email  → lowercase + trim        │
   │    mobile → حذف +98 / 0 / فاصله‌ها  │
   └──────────────┬──────────────────────┘
                  ▼
   ┌─────────────────────────────────────┐
   │ ۲) جست‌وجو در (tenant_id, email)    │
   │    یا (tenant_id, mobile)           │
   └──────────────┬──────────────────────┘
        ┌─────────┴─────────┐
        ▼                   ▼
   ┌─────────────┐   ┌──────────────────────────┐
   │ یافت نشد    │   │ یافت شد                  │
   │ → ایجاد جدید│   │ → سیاست قابل تنظیم tenant │
   └─────────────┘   │   (a) فقط به‌روزرسانی      │
                     │   (b) رد و لاگ conflict   │
                     │   (c) ادغام + ثبت در      │
                     │       crm_merge_logs      │
                     └──────────────────────────┘
کلید تشخیص ۱
email (نرمال‌شده)
کلید تشخیص ۲
mobile (نرمال‌شده به فرمت E.164 بدون +)
کلید تشخیص ۳
external_id (در صورت اتصال به سیستم بیرونی)
سیاست پیش‌فرض
update_only + ثبت رویداد crm.contact.updated
محدوده بررسی
فقط داخل همان tenant_id (هرگز بین مشتریان)

۴.۲ تخصیص خودکار (Assignment Strategies)

استراتژیمنطقمناسب برای
round_robinچرخشی بین اعضای pool با ذخیره ایندکس آخر در last_assigned_indexتیم فروش با حجم مشابه
load_balancedبه کاربری که کمترین مخاطب/فرصت باز داردتیم ناهمگون با ظرفیت متفاوت
fixedهمیشه یک کاربر/تیم مشخصحساب‌های کلیدی سازمانی
rule_basedبر اساس شرط: منبع، استان، مبلغ، صنعتچند تیم موازی با تخصص متفاوت
manualبدون تخصیص؛ انتظار در صف «بدون مالک»کسب‌وکار کوچک
// مقدار اولویت‌بندی: اولین قاعده‌ی منطبق برنده است
{
  "strategy": "rule_based",
  "rules": [
    { "when": { "deal.amount": { "$gte": 100000000 } },
      "assign_to": "user:12", "priority": 1 },
    { "when": { "contact.source": "form", "contact.city": "tehran" },
      "assign_to": "pool:tehran_sales", "strategy": "round_robin" },
    { "when": {}, "assign_to": "pool:general", "priority": 999 }
  ]
}

۴.۳ امتیازدهی سرنخ (Lead Scoring)

فرمول کل
score = Σ (feature_points) − decay(age_days)
ورودی‌های مثبت
ایمیل شرکتی +15 · موبایل معتبر +10 · تکمیل فرم کامل +10 · دانلود محتوا +8 · بازدید صفحه قیمت +12
ورودی‌های منفی
ایمیل رایگان −5 · عدم فعالیت ۳۰ روز −15 · ایمیل نامعتبر −25
آستانه MQL
score ≥ 60 ⇒ status = qualified
بازمحاسبه
رویدادی (در هر فعالیت) + زمان‌بندی‌شده (شبانه برای decay)
نکته پیاده‌سازی: موتور امتیازدهی باید داده‌محور باشد (جدول crm_scoring_rules) تا مشتری بتواند بدون کد، وزن‌ها را تغییر دهد. اجرای محاسبه در یک Job مجزا تا سرعت ثبت مخاطب کاهش نیابد.

۴.۴ رکود فرصت فروش (Deal Rotting)

۴.۵ حذف و حقوق داده (Privacy)

۵ گره‌های اتوماسیون (Nodes)

ماژول CRM مجموعاً ۲۲ گره در هسته ثبت می‌کند: ۸ محرک (Trigger)، ۹ اقدام (Action) و ۵ شرط (Condition). این گره‌ها تنها چیزی‌ست که مشتری در Builder می‌بیند.

۸ Trigger ۹ Action ۵ Condition مجموع: ۲۲ گره

۵.۱ محرک‌ها (Triggers) — ۸ گره

crm.contact.created Trigger

وقتی مخاطب جدیدی در CRM ساخته می‌شود (از فرم، وب‌هوک، API یا ورود دستی). پرکاربردترین محرک کل ماژول.
محدودسازی (Filter)
source · status · tag · score_min · has_email · has_mobile
خروجی
contact.* · company.* · tags[] · custom_fields{}
حالت وقوع
هر رکورد جدید / فقط اگر شرط منطبق باشد

crm.contact.updated Trigger

با هر تغییر روی پروفایل مخاطب فعال می‌شود. مناسب برای همگام‌سازی با سیستم‌های بیرونی.
محدودسازی
fields_changed[] (مثلاً فقط وقتی تلفن تغییر کرد)
خروجی
contact.* · changes{before, after} · changed_by
هشدار
در صورت نبود فیلتر، حجم اجرا بالا می‌رود ⇒ محدودیت هشدار در UI

crm.contact.status_changed Trigger

گذار بین وضعیت‌های چرخه حیات (new → qualified → contacted → nurturing → customer → churned).
محدودسازی
from_status · to_status
خروجی
contact.* · from · to · previous_score · new_score

crm.deal.created Trigger

ایجاد فرصت فروش جدید. نقطه شروع گردش تأیید اعتبار یا ارسال پیش‌فاکتور.
محدودسازی
pipeline_id · stage_id · amount_min · amount_max · owner_user_id
خروجی
deal.* · contact.* · company.* · stage.*

crm.deal.stage_changed Trigger

حرکت فرصت بین مراحل. محرک اصلی اتوماسیون فروش (نوتیفیکیشن، تسک، تغییر وضعیت مخاطب).
محدودسازی
pipeline_id · from_stage · to_stage · include_won · include_lost
خروجی
deal.* · from_stage.* · to_stage.* · duration_in_previous_stage_hours

crm.deal.rotting Trigger

فرصتی بیش از حد مجاز در یک مرحله مانده است. محرک مبتنی بر زمان (شبانه ارزیابی می‌شود).
محدودسازی
pipeline_id · stage_id · min_stuck_days
خروجی
deal.* · stuck_days · stage.* · owner.*
تکرار هشدار
یک‌بار per رکورد (پرچم rotting_notified_at)

crm.task.due Trigger

رسیدن سررسید تسک. برای یادآوری چندمرحله‌ای (X روز قبل، روز موعد، پس از تأخیر).
محدودسازی
offset (قبل/بعد از سررسید) · priority · assignee_user_id · status
خروجی
task.* · contact.* · deal.* · assignee.*

crm.lead_form.submitted Trigger

ارسال لیدفرم عمومی سایت مشتری. شامل داده‌های UTM و صفحه مبدأ برای تحلیل کمپین.
محدودسازی
form_id · contains_field · utm_source · utm_campaign
خروجی
form.* · contact.* (لید ساخته‌شده) · utm{} · referrer · ip_country
ضد‌اسپم
honeypot · rate limit IP · reCAPTCHA اختیاری

۵.۲ اقدام‌ها (Actions) — ۹ گره

crm.contact.create Action

ساخت مخاطب جدید. اگر مخاطب موجود باشد، بر اساس سیاست on_conflict رفتار می‌کند.
ورودی
first_name · last_name · email · mobile · company · tags[] · custom{} · owner · source
on_conflict
skip · update · fail · merge (پیش‌فرض: update)
خروجی
contact.id · contact.* · created (bool) · matched_by
مصرف
۱ واحد از متریک crm.contacts_written

crm.contact.update Action

به‌روزرسانی فیلدهای مشخص روی مخاطب (یا بر اساس فیلتر به‌صورت گروهی).
هدف
بر اساس id / email / mobile / فیلتر سگمنت
ورودی
fields{} (فقط فیلدهای ارسالی تغییر می‌کنند)
خروجی
updated_count · contacts[] · changes{}
حالت انبوه
در صورت هدف قرار دادن سگمنت ⇒ اجرای Chunked Job

crm.contact.assign Action

تخصیص مخاطب به کاربر یا استخر کاربران با استراتژی مشخص.
ورودی
assign_to (user:N یا pool:name) · strategy · notify_assignee (bool)
خروجی
assigned_user.* · assignment_rule.*

crm.contact.change_status Action

تغییر وضعیت چرخه حیات مخاطب؛ محرک زنجیره‌ای برای اتوماسیون‌های بعدی.
ورودی
status · reason · trigger_rescoring (bool)
خروجی
contact.status · previous_status
محافظت از حلقه
وقوع مجدد status_changed با پرچم cascade_depth محدود می‌شود

crm.contact.add_tag / remove_tag Action

افزودن یا حذف برچسب روی مخاطب (افزودن از نوع idempotent است).
ورودی
tags[] (نام یا tag_id) · create_if_missing (bool)
خروجی
contact.tags[] · added[] · removed[]

crm.deal.create Action

ساخت فرصت فروش مرتبط با مخاطب/شرکت در قیف و مرحله مشخص.
ورودی
contact_id · title · pipeline_id · stage_id · amount · currency · expected_close_date · owner
خروجی
deal.* · stage.*
پیش‌فرض هوشمند
در نبود قیف ⇒ قیف پیش‌فرض tenant؛ در نبود مرحله ⇒ اولین مرحله

crm.deal.move_stage Action

انتقال فرصت به مرحله دیگر. برنده/باخت را نیز مدیریت می‌کند.
ورودی
deal_id (یا فیلتر) · to_stage_id · mark_won · mark_lost · lost_reason
خروجی
deal.* · from_stage.* · to_stage.* · history_id
مصرف
۱ واحد از متریک crm.deals_written

crm.activity.log Action

ثبت فعالیت (تماس، ایمیل، جلسه، یادداشت) روی مخاطب/فرصت. معمولاً پس از ارسال پیام استفاده می‌شود.
ورودی
type · subject · body · occurred_at (پیش‌فرض: now) · contact_id · deal_id · user_id
خروجی
activity.* · contact.last_activity_at
اثر جانبی
به‌روزرسانی last_activity_at ⇒ بازنشانی شمارنده امتیازدهی

crm.task.create Action

ساخت تسک پیگیری برای کارشناس با سررسید مطلق یا نسبی.
ورودی
title · due_in (مثلاً 3 days یا 2 hours) · due_at (مطلق) · assignee · priority
خروجی
task.* · due_at محاسبه‌شده · assignee.*
منطق سررسید
در صورت تعطیلی، به اولین روز کاری بعد منتقل می‌شود (تقویم کاری tenant)

۵.۳ شرط‌ها (Conditions) — ۵ گره

crm.contact.exists Condition

بررسی وجود مخاطب بر اساس کلید تشخیص — برای جلوگیری از ایجاد رکورد تکراری پیش از اقدام.
ورودی
match_by (email/mobile/external_id) · value (پشتیبانی از {{ }})
مسیرها
true → مخاطب یافت‌شده · false → مسیر ایجاد

crm.contact.has_tag Condition

بررسی وجود یا عدم وجود یک یا چند برچسب روی مخاطب.
ورودی
tags[] · mode (any / all / none)

crm.contact.score Condition

مقایسه امتیاز سرنخ با آستانه — قلب مسیرهای MQL و تخصیص تیم فروش.
ورودی
operator (gte/gt/lte/lt/eq) · value (پشتیبانی از متغیر)
مثال
crm.contact.score gte 60 ⇒ مسیر تیم فروش

crm.deal.in_stage Condition

بررسی قرار داشتن فرصت در مرحله یا مجموعه‌ای از مراحل مشخص.
ورودی
pipeline_id · stage_ids[] · include_won · include_lost

crm.segment.contains Condition

بررسی عضویت مخاطب در یک سگمنت داینامیک — قدرتمندترین شرط برای مسیرسازی داده‌محور.
ورودی
segment_id · entity (contact / deal)
هشدار کارایی
سگمنت‌های سنگین در اجرای پرحجم کند می‌شوند ⇒ توصیه به استفاده در جریان‌های دسته‌ای
قاعده نام‌گذاری گره‌ها: همیشه module.entity.operation با حروف کوچک و جداکننده نقطه. این قاعده باعث می‌شود پنل بتواند گره‌ها را خودکار دسته‌بندی و فیلتر کند (مثلاً همه گره‌های crm.contact.* در یک گروه).

۶ قرارداد Schema گره‌ها

هر گره باید بتواند خودش را توصیف کند. پنل Nuxt از همین توصیف، فرم پیکربندی، اعتبارسنجی، auto-complete متغیرها و پیش‌نمایش را می‌سازد. بنابراین افزودن گره جدید هیچ تغییری در فرانت لازم ندارد.

۶.۱ ساختار schema — نمونه کامل

{
  "slug": "crm.contact.create",
  "label": "ایجاد مخاطب",
  "category": "crm.contact",
  "kind": "action",                    // trigger | action | condition
  "icon": "user-plus",
  "color": "#5b9dff",
  "docs_url": "https://docs.example.com/modules/crm/contact-create",

  "fields": [
    { "key": "email", "label": "ایمیل", "type": "email",
      "required": false, "supports_variables": true },

    { "key": "mobile", "label": "موبایل", "type": "text",
      "required": false, "supports_variables": true,
      "hint": "فرمت‌های ۰۹۱۲... و +۹۸... خودکار نرمال‌سازی می‌شوند" },

    { "key": "owner_user_id", "label": "کارشناس مسئول",
      "type": "select", "options_source": "crm.users" },

    { "key": "tags", "label": "برچسب‌ها", "type": "multiselect",
      "options_source": "crm.tags", "allow_new": true },

    { "key": "on_conflict", "label": "در صورت تکراری بودن",
      "type": "select", "default": "update",
      "options": [
        { "value": "skip",   "label": "رد کن" },
        { "value": "update", "label": "به‌روزرسانی کن" },
        { "value": "merge",  "label": "ادغام کن" },
        { "value": "fail",   "label": "خطا بده" }
      ] },

    { "key": "custom", "label": "فیلدهای سفارشی",
      "type": "key_value",
      "dynamic_options_source": "crm.custom_fields:contact" }
  ],

  "validation": {
    "at_least_one": ["email", "mobile"],
    "message": "حداقل یکی از ایمیل یا موبایل باید وارد شود."
  },

  "output": {
    "contact": { "id": "string", "email": "string",
                  "mobile": "string", "status": "string" },
    "created": "boolean",
    "matched_by": "string"
  },

  "retry": { "max_attempts": 3, "backoff": "exponential" },
  "timeout_seconds": 10,
  "metering": "crm.contacts_written",
  "required_capability": "module.crm.write"
}

۶.۲ انواع فیلد پشتیبانی‌شده در پنل

typeنمایش در پنلنکته پیاده‌سازی
text / textareaورودی متنپشتیبانی از درج متغیر {{ }}
email / urlورودی با اعتبارسنجیاعتبارسنجی دوطرفه (فرانت + سرور)
number / currencyعدد با فرمت‌بندیواحد پول از تنظیمات tenant خوانده می‌شود
date / datetimeتقویممنطقه زمانی tenant اعمال می‌شود
select / multiselectلیست انتخابیگزینه‌ها از سرور می‌آید (options_source)
key_valueجدول کلید/مقداربرای فیلدهای سفارشی و هدرهای HTTP
expressionادیتور Monacoفقط در گره‌های Condition
jsonادیتور Monaco + اعتبارسنجیبرای مقادیر ساختاریافته
user_pickerجست‌وجوی کاربرکوئری زنده با debounce
contact_pickerجست‌وجوی مخاطبمحدود به tenant جاری

۶.۳ پیاده‌سازی سمت سرور

// app/Modules/Crm/Actions/CreateContact.php
class CreateContact implements NodeContract
{
    public static function slug(): string { return 'crm.contact.create'; }

    public static function schema(): array
    {
        return [
            'kind'   => 'action',
            'label'  => 'ایجاد مخاطب',
            'fields' => [ /* ... مطابق بخش ۶.۱ ... */ ],
            'output' => ['contact' => 'object', 'created' => 'boolean'],
        ];
    }

    public function __construct(
        private readonly ContactService $contacts
    ) {}

    public function execute(NodeContext $ctx): NodeResult
    {
        $config = $ctx->config();      // متغیرها قبلاً resolve شده‌اند

        if (blank($config['email'] ?? null)
            && blank($config['mobile'] ?? null)) {
            throw new NodeValidationException(
                'حداقل یکی از ایمیل یا موبایل الزامی است.'
            );
        }

        [$contact, $created] = $this->contacts->upsert(
            tenant: $ctx->tenantId(),
            data:   $config,
            policy: $config['on_conflict'] ?? 'update',
            source: 'automation',
        );

        if ($created) {
            // تولید رویداد برای اتوماسیون‌های زنجیره‌ای بعدی
            ContactCreated::dispatch($contact, 'automation');
        }

        return NodeResult::make([
            'contact' => $contact->toNodeArray(),
            'created' => $created,
        ]);
    }

    public function retryPolicy(): RetryPolicy
    {
        return RetryPolicy::exponential(attempts: 3);
    }
}

۶.۴ ثبت گره‌ها در هسته (Service Provider)

// app/Modules/Crm/CrmServiceProvider.php
public function register(): void
{
    NodeRegistry::register(module: 'crm', version: '1.0.0', nodes: [
        // ── Triggers ──────────────────────────────────────────
        'crm.contact.created'        => Triggers\ContactCreated::class,
        'crm.contact.updated'        => Triggers\ContactUpdated::class,
        'crm.contact.status_changed' => Triggers\ContactStatusChanged::class,
        'crm.deal.created'           => Triggers\DealCreated::class,
        'crm.deal.stage_changed'     => Triggers\DealStageChanged::class,
        'crm.deal.rotting'           => Triggers\DealRotting::class,
        'crm.task.due'               => Triggers\TaskDue::class,
        'crm.lead_form.submitted'    => Triggers\LeadFormSubmitted::class,

        // ── Actions ───────────────────────────────────────────
        'crm.contact.create'         => Actions\CreateContact::class,
        'crm.contact.update'         => Actions\UpdateContact::class,
        'crm.contact.assign'         => Actions\AssignContact::class,
        'crm.contact.change_status'  => Actions\ChangeContactStatus::class,
        'crm.contact.add_tag'        => Actions\AddTag::class,
        'crm.contact.remove_tag'     => Actions\RemoveTag::class,
        'crm.deal.create'            => Actions\CreateDeal::class,
        'crm.deal.move_stage'        => Actions\MoveDealStage::class,
        'crm.activity.log'           => Actions\LogActivity::class,
        'crm.task.create'            => Actions\CreateTask::class,

        // ── Conditions ────────────────────────────────────────
        'crm.contact.exists'         => Conditions\ContactExists::class,
        'crm.contact.has_tag'        => Conditions\ContactHasTag::class,
        'crm.contact.score'          => Conditions\ContactScore::class,
        'crm.deal.in_stage'          => Conditions\DealInStage::class,
        'crm.segment.contains'       => Conditions\SegmentContains::class,
    ]);

    // متریک‌های مصرف این ماژول (برای بیلینگ پلن‌محور)
    UsageMeter::define('crm.contacts_written');
    UsageMeter::define('crm.deals_written');
    UsageMeter::define('crm.contacts_stored');   // gauge — نه شمارنده

    // منو و مجوزهای پنل
    AdminMenu::add('crm', 'CRM', capability: 'module.crm');
}
نتیجه این طراحی: اگر بعداً ماژول «فروشگاه» گره ecommerce.order.paid را اضافه کند، این گره به‌طور خودکار در پالت Builder کنار گره‌های CRM ظاهر می‌شود — بدون یک خط تغییر در کد Nuxt.

۷ API اختصاصی ماژول

ماژول CRM زیرمجموعه‌ای از قرارداد کلی /api/v1 است و همه قواعد سند ۰۱ (نسخه‌بندی، ساختار ثابت پاسخ، PlanGate، Idempotency) بر آن حاکم است.

۷.۱ نقاط پایانی

متدمسیرتوضیح
GET/api/v1/crm/contactsلیست با فیلتر، مرتب‌سازی و صفحه‌بندی
POST/api/v1/crm/contactsایجاد مخاطب (با اعمال Dedupe)
GET/api/v1/crm/contacts/{id}جزئیات + برچسب‌ها + فیلدهای سفارشی
PUT/api/v1/crm/contacts/{id}ویرایش
DELETE/api/v1/crm/contacts/{id}حذف منطقی
POST/api/v1/crm/contacts/mergeادغام دو یا چند مخاطب تکراری
POST/api/v1/crm/contacts/{id}/tagsافزودن برچسب
DELETE/api/v1/crm/contacts/{id}/tags/{tag}حذف برچسب
GET/api/v1/crm/contacts/{id}/timelineتایم‌لاین فعالیت‌ها، تسک‌ها، فرصت‌ها
GET/api/v1/crm/companiesلیست شرکت‌ها
POST/api/v1/crm/companiesایجاد شرکت
GET/api/v1/crm/pipelinesلیست قیف‌ها به همراه مراحل
POST/api/v1/crm/pipelinesایجاد قیف
POST/api/v1/crm/pipelines/{id}/stagesایجاد مرحله
GET/api/v1/crm/dealsلیست فرصت‌ها (فیلتر قیف/مرحله/مالک)
POST/api/v1/crm/dealsایجاد فرصت
POST/api/v1/crm/deals/{id}/moveانتقال به مرحله دیگر
GET/api/v1/crm/deals/{id}/historyتاریخچه مراحل
GET/api/v1/crm/activitiesلیست فعالیت‌ها
POST/api/v1/crm/activitiesثبت فعالیت
GET/api/v1/crm/tasksلیست تسک‌ها
POST/api/v1/crm/tasksایجاد تسک
PATCH/api/v1/crm/tasks/{id}تکمیل یا تغییر وضعیت
GET/api/v1/crm/segmentsلیست سگمنت‌ها + تعداد تخمینی
POST/api/v1/crm/segmentsساخت سگمنت با فیلتر JSON
GET/api/v1/crm/custom-fieldsلیست فیلدهای سفارشی
POST/api/v1/crm/custom-fieldsساخت فیلد سفارشی
GET/api/v1/crm/lead-formsلیست لیدفرم‌ها
POST/api/v1/crm/lead-formsساخت لیدفرم (تولید public_token)
POST/api/v1/crm/lead-forms/{token}/submitendpoint عمومی دریافت لید
GET/api/v1/crm/statsآمار خلاصه: نرخ تبدیل قیف، میانگین زمان هر مرحله

۷.۲ نمونه فراخوانی — ایجاد مخاطب از سیستم بیرونی

// POST /api/v1/crm/contacts
// Authorization: Bearer ak_live_xxxxxxxxxxxx
// Idempotency-Key: 4f1c9a7e-...   (اختیاری اما توصیه‌شده)

{
  "first_name": "مریم",
  "last_name":  "احمدی",
  "email":      "maryam@acme.ir",
  "mobile":     "09121234567",
  "source":     "api",
  "tags":       ["وبینار-مهر"],
  "custom":     { "city": "تهران", "budget": 50000000 }
}

// 201 Created
{
  "data": {
    "id":         "ctc_01J8X...",
    "status":     "new",
    "score":      25,
    "created":    true,
    "matched_by": null,
    "owner":      { "id": 12, "name": "کارشناس ۳" }
  },
  "meta": { "request_id": "req_...", "tenant": "acme" }
}

۷.۳ قواعد مهم API

فیلتر و صفحه‌بندی

  • صفحه‌بندی مبتنی بر cursor برای دیتاست بزرگ
  • پارامترهای استاندارد: status · tag · owner · score_gte · created_after
  • انتخاب فیلد با ?fields=id,email,score برای کاهش حجم پاسخ

Scopeهای کلید API

  • crm:read — خواندن مخاطب و فرصت
  • crm:write — ایجاد و ویرایش
  • crm:delete — حذف (پیش‌فرض غیرفعال)
  • crm:admin — مدیریت قیف، فیلد سفارشی و قوانین
حالت تست: کلیدهای ak_test_* روی یک tenant سندباکس کار می‌کنند که داده‌هایش هر شب پاک می‌شود و اجراهایش در متریک مصرف پلن حساب نمی‌شود. این برای فرآیند یکپارچه‌سازی مشتری حیاتی است.

۸ سناریوهای واقعی (Recipes)

این سناریوها در پنل به‌صورت قالب آماده (Template) ارائه می‌شوند تا مشتری با یک کلیک آن‌ها را نصب و شخصی‌سازی کند. هر قالب، ارزش فروش ملموس ایجاد می‌کند.

۰۱ — پاسخ فوری به لید جدید (Speed-to-Lead)

هدف: کاهش زمان اولین پاسخ از چند ساعت به زیر ۲ دقیقه · نرخ تبدیل تا ۳ برابر بیشتر.
لیدفرم ثبت شد→ پیامک خوش‌آمد→ ایمیل + فایل راهنما→ تخصیص چرخشی→ تسک تماس تا ۲ ساعت
گره‌های CRM
crm.contact.created · crm.contact.assign · crm.activity.log · crm.task.create
ماژول‌های همراه
messaging (پیامک + ایمیل)
نوع مشتری
هر کسب‌وکاری که فرم سایت دارد — پرطرفدارترین قالب کل پلتفرم

۰۲ — ارتقای وضعیت بر اساس امتیاز (Lead Qualification)

هدف: تفکیک خودکار سرنخ گرم از سرد و تخصیص تیم فروش فقط به موارد ارزشمند.
وضعیت مخاطب تغییر کرد→ امتیاز ≥ ۶۰؟→ بله: تغییر به qualified→ تخصیص به تیم فروش
→→ خیر: برچسب «نیازمند پرورش»→ ورود به کمپین آموزشی
گره‌های CRM
crm.contact.status_changed · crm.contact.score · crm.contact.change_status · crm.contact.add_tag · crm.contact.assign
ماژول‌های همراه
messaging
محافظت
عمق زنجیره (cascade) محدود به ۳ سطح برای جلوگیری از حلقه

۰۳ — پیگیری خودکار پس از تغییر مرحله (Deal Follow-up)

هدف: هیچ فرصت فروشی بدون پیگیری بعدی رها نشود.
مرحله فرصت تغییر کرد→ ثبت فعالیت→ ایمیل خلاصه مذاکره→ تسک پیگیری ۳ روز بعد→ نوتیفیکیشن مدیر اگر مبلغ بالا
گره‌های CRM
crm.deal.stage_changed · crm.activity.log · crm.task.create · crm.deal.in_stage
ماژول‌های همراه
messaging · notification
شخصی‌سازی
آستانه مبلغ از تنظیمات tenant خوانده می‌شود

۰۴ — هشدار فرصت راکد (Deal Rotting Alert)

هدف: بازیابی فرصت‌هایی که در قیف گیر کرده‌اند — معمولاً ۵ تا ۱۵٪ درآمد قابل احیا.
۷ روز بدون تغییر مرحله→ پیام به کارشناس مسئول→ تسک با اولویت بالا→ اگر ۳ روز دیگر بی‌حرکت: هشدار به مدیر
گره‌های CRM
crm.deal.rotting · crm.task.create · crm.activity.log
ماژول‌های همراه
notification
اجرا
Job شبانه؛ هر فرصت حداکثر یک هشدار در دوره

۰۵ — بازگرداندن مشتری غیرفعال (Re-engagement)

هدف: فعال‌سازی مجدد مشتریانی که ۹۰ روز خرید نکرده‌اند — ارزان‌ترین منبع درآمد.
سگمنت «خرید > ۹۰ روز پیش»→ ایمیل پیشنهاد ویژه→ اگر ۷ روز بازخوردی نبود: پیامک→ در صورت خرید: تغییر به customer
گره‌های CRM
crm.segment.contains · crm.contact.change_status · crm.deal.create · crm.activity.log
ماژول‌های همراه
messaging · ecommerce (برای داده خرید)
حجم اجرا
متوسط — اجرای دسته‌ای Chunked برای جلوگیری از فشار روی صف

۰۶ — جشن برد فروش (Deal Won Celebration)

هدف: انگیزه تیمی + شروع فرآیند انبوردینگ مشتری جدید به‌صورت خودکار.
فرصت به مرحله «برنده» رفت→ نوتیفیشن گروه تیم فروش→ تغییر مخاطب به customer→ ایمیل خوش‌آمد + لینک مستندات→ تسک پیگیری موفقیت مشتری در روز ۷
گره‌های CRM
crm.deal.stage_changed · crm.contact.change_status · crm.task.create · crm.activity.log
ماژول‌های همراه
messaging · notification
شخصی‌سازی
متن تبریک و قالب ایمیل قابل ویرایش در پنل

۰۷ — همگام‌سازی دوطرفه با نرم‌افزار بیرونی

هدف: اتصال CRM به ERP یا نرم‌افزار حسابداری مشتری بدون کدنویسی.
مخاطب ایجاد/ویرایش شد→ HTTP POST به API مشتری→ در صورت خطا: مسیر جایگزین→ ثبت فعالیت همگام‌سازی
گره‌های CRM
crm.contact.created · crm.contact.updated · crm.activity.log
گره‌های هسته
action.http_request · condition.if · error branch
امنیت
توکن API در secrets رمزنگاری‌شده ذخیره می‌شود، نه در متن Workflow

۰۸ — یادآوری چندمرحله‌ای تسک (Task SLA Escalation)

هدف: تضمین اجرای تعهدات تیم فروش در زمان مقرر (مبنای SLA داخلی).
۱ روز قبل از سررسید→ یادآوری به کارشناس→ روز سررسید→ نوتیفیشن دوم→ ۲۴ ساعت تأخیر→ اطلاع به مدیر
گره‌های CRM
crm.task.due (با offset منفی و مثبت) · crm.task.create
ماژول‌های همراه
notification · scheduler (تقویم کاری و تعطیلات)
نکته
سه Workflow جدا یا یک Workflow با سه شاخه شرطی — توصیه: یک Workflow با مسیرهای موازی

۰۹ — ادغام خودکار مخاطبان تکراری (Dedupe Pipeline)

هدف: پاک‌سازی پایگاه داده از رکوردهای تکراری که گزارش‌ها را مخدوش می‌کنند.
مخاطب جدید ثبت شد→ مخاطب با همین موبایل وجود دارد؟→ بله: ادغام + انتقال فعالیت‌ها→ ثبت در merge_logs + نوتیفیکیشن
→→ خیر: مسیر عادی ایجاد
گره‌های CRM
crm.contact.created · crm.contact.exists · crm.contact.update · crm.activity.log
امنیت داده
ادغام فقط داخل همان tenant؛ رکورد اصلی در crm_merge_logs قابل بازگشت است

۱۰ — پیش‌بینی و هشدار افت فروش (Pipeline Health)

هدف: شناسایی زودهنگام افت قیف و اطلاع به مدیر پیش از پایان ماه.
هر شنبه ساعت ۹→ بررسی فرصت‌های باز ماه جاری→ اگر پیش‌بینی < ۷۰٪ هدف ماه→ گزارش به مدیر + تسک جلسه بازبینی
گره‌های CRM
crm.segment.contains · crm.deal.in_stage · crm.task.create
گره‌های هسته
trigger.schedule (Cron) · action.data_transform · condition.if
ماژول‌های همراه
analytics (فاز ۳) — نسخه ساده در فاز ۲ با HTTP Action قابل ساخت است
ارزش تجاری این بخش: همین ۱۰ قالب می‌تواند به‌عنوان پیشنهاد اولیه در فرآیند فروش استفاده شود. تجربه نشان می‌دهد مشتری‌ای که یک قالب آماده و کارکرد واقعی می‌بیند، سریع‌تر تصمیم به خرید می‌گیرد تا وقتی فقط با یک کانواس خالی روبه‌رو شود.

۹ رابط کاربری پنل CRM

ماژول CRM علاوه بر گره‌های اتوماسیون، یک پنل عملیاتی مستقل هم دارد؛ چون بدون محیط کار روزمره، مشتری CRM را قبول نمی‌کند. این پنل در Nuxt ساخته می‌شود و از همان REST API تغذیه می‌کند.

۹.۱ نقشه صفحه‌ها

صفحهمسیرامکانات کلیدی
داشبورد CRM/crmKPI خلاصه: مخاطب جدید، فرصت باز، ارزش قیف، تسک‌های عقب‌افتاده، نمودار تبدیل
لیست مخاطبان/crm/contactsجدول با فیلتر پیشرفته، انتخاب گروهی، عملیات دسته‌ای، صادرات CSV
پروفایل مخاطب/crm/contacts/[id]تایم‌لاین یکپارچه، فیلدهای سفارشی، برچسب‌ها، فرصت‌های مرتبط، دکمه اجرای جریان
شرکت‌ها/crm/companiesلیست + مخاطبان و فرصت‌های هر شرکت
برد قیف فروش/crm/pipelineنمای Kanban با Drag&Drop؛ تغییر مرحله ⇒ تولید رویداد اتوماسیون در لحظه
فرصت فروش/crm/deals/[id]جزئیات، تاریخچه مراحل، فعالیت‌ها، تایمر رکود
فعالیت‌ها/crm/activitiesلیست زمان‌محور همه تعاملات تیم
تسک‌ها/crm/tasksنمای «امروز / این هفته / عقب‌افتاده»، تکمیل سریع
سگمنت‌ها/crm/segmentsسگمنت‌ساز بصری (شرط‌های AND/OR)، پیش‌نمایش تعداد، اجرای جریان روی سگمنت
لیدفرم‌ها/crm/formsفرم‌ساز، تولید کد Embed، اتصال به Workflow، آمار ارسال
تنظیمات CRM/crm/settingsقیف‌ها و مراحل، فیلدهای سفارشی، قوانین تخصیص، امتیازدهی، سیاست Dedupe
قالب‌های آماده/crm/templatesگالری ۱۰ سناریوی بخش ۸ با نصب یک‌کلیکی

۹.۲ اسکچ صفحه برد قیف (Kanban)

┌───────────────────────────────────────────────────────────────────────┐
│  CRM › قیف فروش  [قیف اصلی ▾]   [فیلتر: مالک ▾]  [+ فرصت]  [⚙ تنظیمات] │
├───────────────────────────────────────────────────────────────────────┤
│                                                                        │
│ ┌─ لید (۱۲) ─────┐ ┌─ مذاکره (۷) ────┐ ┌─ پیش‌فاکتور (۴) ─┐ ┌─ برده (۳) ┐│
│ ├────────────────┤ ├─────────────────┤ ├──────────────────┤ ├───────────┤│
│ │ ▢ شرکت آلفا    │ │ ▢ شرکت بتا      │ │ ▢ شرکت دلتا      │ │ ▢ شرکت ...││
│ │   ۵۰ م · ۳ روز │ │   ۱۲۰ م·۱۲ روز⚠│ │   ۸۰ م · ۲ روز   │ │   ۲۰۰ م   ││
│ ├────────────────┤ ├─────────────────┤ ├──────────────────┤ ├───────────┤│
│ │ ▢ شرکت گاما    │ │ ▢ شرکت زتا      │ │ ...              │ │ ...       ││
│ │   ۳۰ م · ۱ روز │ │   ۹۰ م · ۱ روز  │ │                  │ │           ││
│ └────────────────┘ └─────────────────┘ └──────────────────┘ └───────────┘│
│                                                                        │
│  ⚠ علامت هشدار = فرصت راکدتر از حد مجاز مرحله                          │
│  Drag & Drop یک کارت ⇒ API فراخوانی می‌شود ⇒ رویداد crm.deal.stage_    │
│  changed منتشر می‌شود ⇒ اتوماسیون‌های فعال در لحظه اجرا می‌شوند.        │
└───────────────────────────────────────────────────────────────────────┘

۹.۳ اجزای رابط کاربری مشترک

ContactCard

کارت خلاصه مخاطب با امتیاز، وضعیت، آخرین فعالیت.

ActivityTimeline

تایم‌لاین یکپارچه با آیکون نوع فعالیت و گروه‌بندی زمانی.

SegmentBuilder

سازنده بصری شرط با گروه‌بندی AND/OR و پیش‌نمایش زنده تعداد.

ScoreBadge

نمایش امتیاز با رنگ‌بندی (سرد/گرم/داغ) و توضیح اجزای امتیاز.

CustomFieldRenderer

رندر داینامیک فیلد سفارشی بر اساس type — مشترک بین فرم و جدول.

RunWorkflowButton

اجرای دستی جریان روی یک رکورد مشخص، با نمایش نتیجه همان لحظه.

نکته UX: در صفحه پروفایل مخاطب، بخش «تایم‌لاین» باید همه رویدادها را یکجا نشان دهد (فعالیت دستی، اجرای اتوماسیون، تغییر مرحله فرصت، ارسال پیام). این قوی‌ترین اهرم برای اثبات ارزش اتوماسیون به مشتری است — مشتری با چشم خودش می‌بیند که سیستم دارد کار می‌کند.

۱۰ مجوزها و نقش‌ها

۱۰.۱ سطوح دسترسی (Capabilities)

هر قابلیت یک اسلاگ دارد. نقش‌ها فقط مجموعه‌ای از این اسلاگ‌ها هستند و مشتری می‌تواند نقش سفارشی بسازد.

Capabilityمعنیسطح ریسک
module.crmدسترسی به ماژول CRM (پیش‌نیاز همه موارد زیر)پایه
crm.contacts.viewمشاهده لیست و پروفایل مخاطبانپایه
crm.contacts.createایجاد مخاطبپایه
crm.contacts.updateویرایش مخاطبمتوسط
crm.contacts.deleteحذف مخاطبحساس
crm.contacts.mergeادغام مخاطبان تکراریحساس
crm.contacts.exportصادرات داده مخاطبانحساس
crm.contacts.view_othersمشاهده مخاطبان سایر کارشناسانمتوسط
crm.deals.view / .manageمشاهده و مدیریت فرصت‌هامتوسط
crm.pipeline.manageساخت و ویرایش قیف و مراحلمتوسط
crm.settings.manageقوانین تخصیص، امتیازدهی، فیلد سفارشیمتوسط
crm.forms.manageساخت و انتشار لیدفرم عمومیحساس
crm.automation.manageساخت و فعال‌سازی جریان‌های اتوماسیونحساس

۱۰.۲ نقش‌های پیش‌فرض

نقشمجموعه دسترسیکاربرد
Owner همه قابلیت‌ها + مدیریت کاربران و صورتحساب مالک کسب‌وکار (صاحب tenant)
Sales Manager مشاهده همه + مدیریت قیف و قوانین + گزارش‌ها + اتوماسیون (بدون صورتحساب) مدیر فروش
Sales Rep مشاهده و ویرایش مخاطبان خودش، فرصت‌ها، تسک‌های خودش کارشناس فروش
Marketing مشاهده همه مخاطبان + ساخت لیدفرم + سگمنت + اتوماسیون بازاریابی (بدون حذف) تیم بازاریابی
Read Only فقط مشاهده، بدون هیچ نوشتن حسابرس یا مدیرعامل
Automation Builder مشاهده داده + ساخت و ویرایش جریان‌ها (بدون حذف داده) تیم فنی مشتری

۱۰.۳ اعمال مجوز در API

// app/Modules/Crm/Http/Controllers/ContactController.php
public function update(UpdateContactRequest $request, Contact $contact)
{
    $this->authorize('crm.contacts.update');

    // محدودیت مالکیت: کارشناس فقط مخاطب خودش را می‌بیند
    if (! $request->user()->can('crm.contacts.view_others')
        && $contact->owner_user_id !== $request->user()->id) {
        throw new AuthorizationException('دسترسی به این مخاطب مجاز نیست.');
    }

    $contact->update($request->validated());
    ContactUpdated::dispatch($contact, 'manual');

    return ContactResource::make($contact);
}
مرز امنیتی مهم: مجوز «مشاهده سایر کارشناسان» و «صادرات داده» باید در سطح پلن هم محدود شوند. در پلن‌های ارزان، صادرات انبوه را غیرفعال کنید تا ریسک از دست دادن داده مشتری توسط کارشناس خروجی‌گرفته کاهش یابد.

۱۱ متریک مصرف و سهم‌بندی با پلن

۱۱.۱ متریک‌های ماژول CRM

متریکنوعواحدکاربرد در بیلینگ
crm.contacts_storedGauge (سطح)تعداد رکورد فعالسقف پلن — کنترل حجم دیتابیس
crm.contacts_writtenCounter (ماهانه)تعداد نوشتنمصرف عملیاتی + overage
crm.deals_writtenCounter (ماهانه)تعداد نوشتنمصرف عملیاتی
crm.custom_fieldsGaugeتعداد فیلدسقف پلن (محافظت از کارایی)
crm.pipelinesGaugeتعداد قیفسقف پلن
crm.lead_formsGaugeتعداد فرمسقف پلن
crm.api_requestsCounter (روزانه)درخواستRate limit و پلن API

۱۱.۲ اختصاص سقف به پلن‌ها

محدودیتFreeStarterBusinessEnterprise
مخاطب فعال (Gauge)۲۰۰۵٬۰۰۰۵۰٬۰۰۰نامحدود*
نوشتن ماهانه مخاطب۱۰۰۲٬۰۰۰۲۰٬۰۰۰نامحدود
قیف فروش۱۲۵نامحدود
فیلد سفارشی۳۱۰۲۵نامحدود
لیدفرم عمومی۱۳۱۰نامحدود
قوانین تخصیص—✔✔✔
امتیازدهی سرنخ——✔✔
صادرات داده—۵٬۰۰۰ ردیف✔✔
فیلد سفارشی فرمول‌دار——✔✔
قالب‌های آماده۳ قالبهمههمههمه + سفارشی

* «نامحدود» در عمل با سیاست منع استفاده غیرمتعارف (Fair Usage) و پایش خودکار محدود می‌شود.

۱۱.۳ رفتار سیستم در رسیدن به سقف

  مصرف ماهانه = ۸۰٪ سقف
        │
        └─► بنر هشدار در پنل + ایمیل به Owner (یک‌بار در دوره)

  مصرف ماهانه = ۱۰۰٪ سقف
        │
        ├─► اجراهای جدید اتوماسیون مرتبط با CRM متوقف می‌شوند  ← 402
        ├─► خواندن داده همچنان آزاد است (مشتری داده‌اش را از دست نمی‌دهد)
        ├─► ورود داده دستی همچنان ممکن است (تجربه کار روزمره قطع نشود)
        └─► اعلان ارتقا + لینک مستقیم به صفحه صورتحساب

  گزینه Overage (اختیاری، فقط پلن Business و Enterprise)
        │
        └─► اجرای بیشتر مجاز است و هزینه مازاد در فاکتور دوره بعد محاسبه می‌شود

۱۱.۴ نکات پیاده‌سازی Metering

اصل طراحی درآمدی: هرگز دسترسی مشتری به داده‌اش را قطع نکنید؛ فقط ظرفیت نوشتن و اجرای جدید را متوقف کنید. این کار هزینه پشتیبانی را به‌شدت کاهش می‌دهد (مشتری عصبانی تماس نمی‌گیرد) و در عین حال فشار ارتقا را حفظ می‌کند.

۱۲ یکپارچه‌سازی با سرویس‌های بیرونی

ماژول CRM خودش پیام ارسال نمی‌کند؛ اما برای اینکه سناریوهای بخش ۸ کار کنند، باید بفهمیم CRM با چه سرویس‌هایی و از چه راهی گفتگو می‌کند.

۱۲.۱ جداسازی مسئولیت (Separation of Concerns)

┌────────────────────┐        ┌────────────────────────┐
│   module-crm       │        │  module-messaging      │
│  رویداد تولید می‌کند│───────►│  پیام ارسال می‌کند      │
│  crm.contact.created│  گره  │  sms.send · email.send  │
└────────────────────┘        └───────────┬────────────┘
                                          │
                              ┌───────────┼───────────┬──────────────┐
                              ▼           ▼           ▼              ▼
                        ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌────────────┐
                        │ کاوه‌نگار│ │ SMTP    │ │ واتس‌اپ  │ │ تلگرام     │
                        │ پیامک   │ │ اختصاصی │ │ Business │ │ Bot API    │
                        └─────────┘ └─────────┘ └──────────┘ └────────────┘
                        (کانکتورها در ماژول messaging پیاده می‌شوند)
چرا این جداسازی مهم است؟ چون مشتری می‌تواند CRM را بخرد و با سرویس پیامکی فعلی خودش (که ما از آن خبر نداریم) از طریق گره http.request کار کند. CRM هرگز گروگان یک سرویس بیرونی نمی‌شود.

۱۲.۲ کانکتورهای درون CRM (محدود)

کانکتورهدفمحل پیاده‌سازی
وب‌هوک خروجی (Outbound Webhook)اعلام رویداد CRM به سیستم‌های دیگرهسته — action.webhook
ایمپورت CSVورود اولیه داده مشتریداخل CRM (لازم است)
Google Calendarهمگام‌سازی تسک‌های زمان‌دارماژول scheduler (فاز ۲)
VoIP / تماسثبت خودکار تماس در تایم‌لاینفاز ۴

۱۲.۳ امضای امنیتی وب‌هوک (HMAC)

هر وب‌هوک خروجی با امضای HMAC-SHA256 ارسال می‌شود تا مشتری بتواند اصالت آن را بررسی کند:

// هدرهای ارسالی
X-Automation-Event:    crm.contact.created
X-Automation-Delivery: dlv_01J8X...
X-Automation-Timestamp: 1758880000
X-Automation-Signature: sha256=<hmac>

// محاسبه امضا (سمت مشتری)
$signature = hash_hmac(
    'sha256',
    $timestamp . '.' . $rawBody,
    $tenantWebhookSecret
);

// بررسی: امضا برابر است و اختلاف زمانی کمتر از ۵ دقیقه (ضد Replay Attack)

۱۲.۴ سیاست تلاش مجدد در وب‌هوک خروجی

تلاشزمان انتظارشرط ادامه
۱فوریپاسخ 2xx ⇒ پایان موفق
۲+۳۰ ثانیهخطای 5xx یا Timeout
۳+۵ دقیقهخطای 5xx یا Timeout
۴+۳۰ دقیقهخطای 5xx یا Timeout
۵+۲ ساعتخطای 5xx یا Timeout
پایان—ثبت در Dead Letter Queue + اعلان به مشتری

نکته: خطاهای 4xx (به‌جز 429) تکرار نمی‌شوند، چون نشانه اشکال در تنظیمات است نه مشکل موقت. این تفکیک از هدر رفتن منابع و سردرگمی مشتری جلوگیری می‌کند.

۱۳ تست و معیار پذیرش

۱۳.۱ ماتریس تست ماژول

دستهنمونه تستنوعاولویت
Dedupeایجاد دو مخاطب با یک ایمیل ⇒ فقط یک رکورد + رویداد updatedFeatureبحرانی
Dedupe موبایل+989121234567 و 09121234567 تکراری تشخیص داده شوندUnitبحرانی
جداسازی tenantکاربر tenant A نتواند مخاطب tenant B را بخواند (۳۲ تست برای همه endpointها)Featureبحرانی
تخصیص چرخشی۱۰ مخاطب متوالی بین ۳ کاربر به‌درستی توزیع شوندFeatureمهم
امتیازدهیترکیب قوانین ⇒ امتیاز مورد انتظار + گذر از آستانه MQLUnitمهم
رکود فرصتفرصت با ۸ روز سکون ⇒ رویداد rotting یک‌بار تولید شود (نه بیشتر)Featureمهم
گره createاجرای گره با ایمیل نامعتبر ⇒ خطای اعتبارسنجی + retry نشودUnitمهم
گره move_stageانتقال به مرحله‌ای از قیف دیگر ⇒ رد شودUnitمهم
حلقه زنجیره‌ایA → B → A با cascade_depth ⇒ پس از ۳ سطح متوقف شودFeatureبحرانی
سقف مصرفرسیدن به سقف نوشتن ⇒ پاسخ 402 + خواندن همچنان کار کندFeatureبحرانی
محدودیت پلنFree با ۲۰۱ مخاطب ⇒ ایجاد مخاطب جدید رد شودFeatureمهم
لیدفرم عمومیارسال ۱۰۰ درخواست در دقیقه ⇒ Rate Limit فعال شودFeatureمهم
Idempotencyدو درخواست با یک Idempotency-Key ⇒ فقط یک رکوردFeatureمهم
ادغامادغام مخاطب با ۱۰ فعالیت ⇒ همه فعالیت‌ها منتقل + قابل بازگشتFeatureمهم
صادرات CSVفایل خروجی فقط داده tenant جاری را داشته باشدFeatureبحرانی
وب‌هوک خروجیمشتری خطای 500 بدهد ⇒ ۵ تلاش با backoff صحیحFeatureمهم
کاراییلیست ۵۰٬۰۰۰ مخاطب با فیلتر ⇒ زیر ۳۰۰ میلی‌ثانیهPerformanceمهم

۱۳.۲ تست نشت داده (Data Leakage Suite) — اجباری

// tests/Feature/Tenancy/CrmIsolationTest.php
class CrmIsolationTest extends TestCase
{
    public function test_tenant_cannot_read_other_tenant_contacts(): void
    {
        [$a, $b] = Tenant::factory()->count(2)->create();

        $contactB = Contact::factory()->for($b)->create();

        $this->actingAsTenant($a)
             ->getJson("/api/v1/crm/contacts/{$contactB->id}")
             ->assertNotFound();          // 404 — نه 403 (عدم افشای وجود)
    }

    public function test_export_never_includes_foreign_rows(): void
    {
        /* بررسی می‌شود که فایل CSV هیچ tenant_id دیگری نداشته باشد */
    }

    // این تست برای «همه» مدل‌های CRM به‌صورت DataProvider اجرا می‌شود
}
الزام: این تست‌ها باید در CI اجرا شوند و شکست در هر یک، مانع merge باشد. در معماری Single-DB، این تنها شبکه امنیتی واقعی شماست.

۱۳.۳ معیار پذیرش ماژول (Definition of Done)

  1. هر ۲۲ گره ثبت‌شده در پالت Builder پنل دیده شوند بدون هیچ تغییر در کد فرانت.
  2. هر گره دارای schema()، outputSchema() و retryPolicy() باشد.
  3. همه endpointها دارای تست Feature با حداقل یک سناریوی موفق و یک سناریوی خطا باشند.
  4. تست نشت داده برای همه مدل‌های ماژول سبز باشد.
  5. ۱۰ قالب سناریو (بخش ۸) به‌صورت Template قابل نصب در پنل موجود باشد.
  6. متریک‌های مصرف در صفحه /usage به‌درستی نمایش داده شوند.
  7. محدودیت‌های پلن (بخش ۱۱.۲) در PlanGate اعمال شده باشد.
  8. مستندات فارسی صفحات ماژول + ویدئوی کوتاه ۳ دقیقه‌ای معرفی قالب‌ها آماده باشد.
  9. لاگ اجرای گره‌ها با ماسک کردن داده حساس ذخیره شود.
  10. کارایی لیست ۵۰٬۰۰۰ رکورد زیر ۳۰۰ms با ایندکس‌های تعریف‌شده.

۱۴ نقشه پیاده‌سازی ماژول

ماژول CRM در ۶ اسپرینت دو‌هفته‌ای (~۳ ماه) با یک توسعه‌دهنده تمام‌وقت به‌همراه هسته قابل تحویل است. ترتیب اسپرینت‌ها بر پایه «اول محصول قابل فروش» است، نه «اول معماری کامل».

Sprint 1 — پایه داده و موجودیت مرکزی هفته ۱–۲
  • Migration‌های crm_contacts، crm_companies، crm_tags، crm_taggables
  • مدل‌ها + BelongsToTenant trait + Global Scope
  • سرویس ContactService::upsert() با نرمال‌سازی email/mobile
  • API: CRUD مخاطب + فیلتر + صفحه‌بندی cursor
  • تست نشت داده پایه (Tenant Isolation)
قابل نمایشپایه همه‌چیز
Sprint 2 — گره‌های اولیه و اتصال به موتور هفته ۳–۴
  • NodeContract + NodeRegistry در هسته (پیاده‌سازی مرجع)
  • گره‌های پایه: contact.created، contact.create، contact.update، contact.exists، activity.log
  • رویدادهای دامنه (Domain Events) و انتشار آن‌ها به Trigger Dispatcher
  • نمایش گره‌ها در GET /nodes/types و تست رندر داینامیک فرم در پنل
اولین اتوماسیون واقعی
Sprint 3 — قیف فروش و فرصت‌ها هفته ۵–۶
  • Migration و مدل pipelines، stages، deals، deal_stage_history
  • سرویس DealService::moveStage() با ثبت تاریخچه
  • گره‌ها: deal.created، deal.stage_changed، deal.create، deal.move_stage، deal.in_stage
  • صفحه برد Kanban در پنل با Drag&Drop
  • Job شبانه رکود + گره deal.rotting
ارزش فروش قویسناریو ۰۳ و ۰۴
Sprint 4 — سگمنت، فیلد سفارشی و تخصیص هفته ۷–۸
  • فیلدهای سفارشی (تعریف + مقدار typed) و رندر داینامیک در پنل
  • سگمنت‌ساز بصری + موتور فیلتر JSON + شمارش تخمینی
  • موتور تخصیص (۵ استراتژی) + صفحه تنظیمات قوانین
  • موتور امتیازدهی سرنخ + گره contact.score + segment.contains
  • گره‌های contact.assign، add_tag، remove_tag، change_status
قدرت واقعی ماژولسناریو ۰۲ و ۰۵
Sprint 5 — لیدفرم، تایم‌لاین و تسک هفته ۹–۱۰
  • لیدفرم + endpoint عمومی + ضد‌اسپم + کد Embed
  • گره lead_form.submitted + contact.updated + contact.has_tag
  • تایم‌لاین یکپارچه مخاطب (فعالیت + اتوماسیون + فرصت)
  • مدیریت تسک + گره task.create و task.due + نمای SLA
  • صفحه‌های /crm/tasks، /crm/activities، /crm/forms
تجربه روزمره مشتریسناریو ۰۱ و ۰۸
Sprint 6 — تجاری‌سازی ماژول هفته ۱۱–۱۲
  • متریک‌های مصرف + اتصال به PlanGate + رفتار سقف
  • ۱۰ قالب آماده سناریو (Template Gallery) + نصب یک‌کلیکی
  • مجوزها و نقش‌های پیش‌فرض + صفحه مدیریت کاربران
  • صادرات CSV/JSON + ایمپورت CSV
  • تست بار (۵۰٬۰۰۰ رکورد) + بهینه‌سازی ایندکس‌ها
  • مستندات فارسی + ویدئوی معرفی + آماده‌سازی برای فروش
آماده فروشپایان فاز ۱
معیار عبور از Sprint 6: مشتری اول می‌تواند وارد شود، پلن Starter بگیرد، ماژول CRM را فعال کند، یکی از قالب‌های آماده را نصب کند، لیدفرم را در سایت خودش بگذارد و شاهد اجرای خودکار جریان باشد — همه بدون دخالت تیم فنی ما.

۱۵ ریسک‌ها و سؤالات باز

۱۵.۱ ریسک‌های ماژول و راهکار کاهش

ریسکاحتمالاثرراهکار کاهش
CRM ما در برابر CRM‌های جاافتاده ضعیف به‌نظر برسد بالابالا تمرکز روی موتور اتوماسیون به‌عنوان تمایز اصلی، نه تکرار CRM‌های موجود. پیام فروش: «CRM + اتوماسیون در یک جا».
انتظار مشتری از «CRM کامل» فراتر از محدوده MVP باشد بالامتوسط محدوده بخش ۱.۲ در قرارداد فروش صریح ذکر شود + نقشه راه ماژول‌های بعدی ارائه شود.
مهاجرت داده مشتری از سیستم قدیمی سخت باشد متوسطبالا ایمپورت CSV با نقشه‌برداری ستون + سرویس مهاجرت در فاز ۱ به‌عنوان خدمت پرداختی.
نشت داده بین tenantها به‌دلیل فراموشی Global Scope متوسطبحرانی تست خودکار نشت داده در CI + بازبینی کد اجباری برای هر مدل جدید.
گره‌های زیاد، رابط Builder را برای مشتری غیرفنی پیچیده کند متوسطمتوسط دسته‌بندی، جست‌وجو، «گره‌های پیشنهادی» بر اساس زمینه و مخفی‌سازی گره‌های پیشرفته.
هزینه ارسال پیامک/ایمیل، حاشیه سود پلن‌های ارزان را بخورد بالامتوسط تفکیک کامل هزینه پیام از اشتراک پلتفرم (اعتبار پیامکی جداگانه) یا سقف پیام در پلن.
مشتری جریان اشتباه بسازد و سیستم را به‌خاطر خرابی سرزنش کند بالامتوسط اعتبارسنجی پیش از فعال‌سازی، اجرای آزمایشی، تشخیص حلقه، هشدار حجم غیرعادی اجرا.
پیچیدگی نگهداری و افزایش هزینه پشتیبانی متوسطمتوسط داشبورد سلامت جریان‌ها (نرخ خطا، جریان‌های شکسته) + هشدار خودکار به مشتری پیش از تماس او.

۱۵.۲ سؤالات باز برای تصمیم‌گیری

#سؤالچرا مهم استپیشنهاد من
۱ صنعت هدف اول برای ماژول CRM چیست؟ (خدماتی / B2B / آموزش / کلینیک / املاک) روی قالب‌های آماده، اصطلاحات فارسی و پیام فروش اثر مستقیم دارد شروع با کسب‌وکارهای خدماتی B2B — بهترین تناسب با قیف فروش و اتوماسیون
۲ پیامک و ایمیل در پلن پایه رایگان باشد یا اعتبار جداگانه فروخته شود؟ مستقیماً بر حاشیه سود و سادگی قیمت‌گذاری اثر دارد اعتبار جداگانه برای پیامک؛ ایمیل در سقف پلن
۳ آیا CRM باید «ایمپورت از سیستم قدیمی» داشته باشد؟ بدون آن، مهاجرت مشتری سخت و هزینه جذب بالا می‌رود بله — CSV با نقشه‌برداری ستون + خدمت مهاجرت پرداختی
۴ آیا فیلد سفارشی فرمول‌دار (امتیاز خودکار، مبلغ محاسباتی) در فاز ۱ لازم است؟ قابلیت قدرتمند ولی پیچیده — تعادل زمان MVP به پلن‌های Business و Enterprise منتقل شود
۵ ویجت جاسازی CRM در سایت مشتری در فاز ۱ باشد؟ ارزش فروش دارد اما زمان‌بر است فقط لیدفرم + کد Embed؛ ویجت کامل در فاز ۳
۶ آیا مشتری می‌تواند داده CRM خود را کامل حذف کند؟ الزام حقوقی در برخی صنایع بله — صفحه «حذف کامل tenant» با تأیید دو مرحله‌ای

۱۵.۳ ماژول در یک نگاه

۲۲ گره

۸ محرک · ۹ اقدام · ۵ شرط

۱۷ جدول

همه با پیشوند crm_

۳۱ endpoint

زیر /api/v1/crm/*

۱۰ قالب آماده

آماده ارائه در فرآیند فروش

۱۲ صفحه پنل

Nuxt 4 + Vue 3

۱۷ تست کلیدی

شامل تست اجباری نشت داده

۶ اسپرینت

~۳ ماه با یک توسعه‌دهنده

۷ متریک مصرف

متصل به PlanGate و بیلینگ

گام بعدی پیشنهادی: سند ۰۳ — اسکیمای کامل دیتابیس و ERD؛ شامل تمام Migration‌های هسته و ماژول، ایندکس‌گذاری دقیق و استراتژی مهاجرت. سپس سند ۰۴ — طراحی UX ورک‌فلو بیلدر که پایه ارزش اصلی محصول است.