1234 lines
50 KiB
Markdown
1234 lines
50 KiB
Markdown
# افزونه اتصال به استریسک و ایزابل — سند اجرایی کامل
|
||
|
||
**کد افزونه:** `asterisk_issabel_connector`
|
||
**نام نمایشی:** اتصال به استریسک و ایزابل
|
||
**دسته مارکتپلیس:** `integration`
|
||
**وضعیت سند:** در حال پیادهسازی فعال
|
||
**نسخه سند:** `1.1.0`
|
||
**تاریخ:** ۱۴۰۵/۰۵/۱۲ (2026-08-03)
|
||
|
||
> بهروزرسانی پیادهسازی: فاز ۰ و ۱ کامل شده؛ فازهای ۲ (ضبط/گزارش/CRM عمیق)، ۳ (داشبورد زنده/کنترل تماس)، ۴ (DLQ/متریک) و اسکفولد فاز ۵ (Softphone) در کد پیاده شدهاند. **هسته Softphone Media Relay (فاز ۵/سند جدا) از ۱۴۰۵/۰۵/۱۵ در حال پیادهسازی است** — جزئیات و چکلیست: `TELEPHONY_SOFTPHONE_MEDIA_RELAY_EXECUTION_SCENARIO.md`.
|
||
|
||
این سند مرجع واحد برای پیادهسازی افزونه تلفنی حسابیکس است. هر فاز باید فقط بر اساس همین سند و چکلیست پذیرش همان فاز انجام شود.
|
||
|
||
---
|
||
|
||
## ۰. خلاصه اجرایی
|
||
|
||
حسابیکس امروز CRM، اشخاص، مانده حساب، فعالیت نوع `call`، اعلان WebSocket، مارکتپلیس افزونه و چندسکویی Flutter (وب / اندروید / iOS / دسکتاپ) دارد، اما **هیچ لایه CTI/Asterisk ندارد**.
|
||
|
||
افزونه باید:
|
||
|
||
1. هر کسبوکار را به یک یا چند PBX (Issabel/Asterisk) وصل کند.
|
||
2. تماس ورودی را با Screen Pop به اپراتور نشان دهد.
|
||
3. Click-to-Call از داخل CRM انجام دهد.
|
||
4. تاریخچه کامل تماس + ضبط را ذخیره کند.
|
||
5. با CRM، اشخاص، مانده حساب، چک، فاکتور و Workflow یکپارچه شود.
|
||
6. داخلی هر کاربر را **بهازای هر کسبوکار** جدا نگه دارد.
|
||
7. روی وب، موبایل و دسکتاپ UI یکپارچه و باکیفیت داشته باشد.
|
||
|
||
**تصمیمهای قفلشده این سند (پیشفرض محصولی):**
|
||
|
||
| موضوع | تصمیم |
|
||
|--------|--------|
|
||
| تماس از CRM در MVP | Click-to-Call با AMI Originate (نه Softphone) |
|
||
| Softphone WebRTC | فقط فاز ۵ (اختیاری) |
|
||
| نصب روی ایزابل | الزامی — Hesabix Telephony Connector |
|
||
| تاریخچه تماس | جدول first-class `telephony_calls` + Activity اختیاری |
|
||
| تیکت از تماس | در MVP = وظیفه/فعالیت CRM؛ ماژول تیکت کسبوکار خارج از scope |
|
||
| ضبط مکالمه | لینک/پروکسی از PBX + آپلود اختیاری به `file_storage` |
|
||
| پلتفرم UI | یک Flutter codebase — Responsive + Adaptive برای Web/Mobile/Desktop |
|
||
|
||
---
|
||
|
||
## ۱. هدف، محدوده و خارج از محدوده
|
||
|
||
### ۱.۱ هدف
|
||
|
||
ارائه یک افزونه مارکتپلیس که کسبوکار بتواند مرکز تلفن Issabel/Asterisk خود را به حسابیکس وصل کند و اپراتورها بتوانند تماس را ببینند، بگیرند، ثبت کنند و با پرونده مشتری ادامه دهند — روی وب، موبایل و دسکتاپ.
|
||
|
||
### ۱.۲ داخل محدوده (In Scope)
|
||
|
||
- لایسنس افزونه، تنظیمات PBX، نگاشت کاربر↔داخلی
|
||
- Connector سمت Issabel (AMI + webhook)
|
||
- Screen Pop، Click-to-Call، تاریخچه، ضبط، اعلانها
|
||
- یکپارچگی با Person / Lead / Deal / CrmActivity / مانده حساب / چک / فاکتور
|
||
- گزارشها و داشبورد لحظهای (فازهای بعدی)
|
||
- کنترلهای پیشرفته Hold/Transfer/Hangup (فاز ۳+)
|
||
- UI کامل چندسکویی
|
||
|
||
### ۱.۳ خارج از محدوده (Out of Scope)
|
||
|
||
- جایگزینی کامل Issabel (IVR builder، مدیریت کامل dialplan)
|
||
- ماژول تیکت پشتیبانی کسبوکار جدید (تا وقتی جداگانه تعریف نشود)
|
||
- تماس ویدیویی / تماس AI voice موجود حسابیکس
|
||
- یکپارچگی با PBXهای غیر Asterisk-family (مثل ۳CX، FreePBX جدا — مگر سازگاری AMI حفظ شود)
|
||
- ضبط مکالمه قانونی/قضاوت حقوقی (مسئولیت پیکربندی با مشتری است)
|
||
|
||
### ۱.۴ فرضیات عملیاتی ایران
|
||
|
||
- Issabel معمولاً پشت NAT/فایروال است → Connector خروجی HTTPS میزند؛ پورت ورودی روی PBX لازم نیست.
|
||
- Caller ID با فرمتهای `09…` / `9…` / `98…` / `+98…` / شهری میآید → نرمالسازی اجباری.
|
||
- اپراتور معمولاً Softphone یا تلفن رومیزی SIP دارد → Originate کافی است.
|
||
- کیفیت شبکه شعب متغیر است → UI باید offline-tolerant برای تاریخچه محلی نباشد، ولی وضعیت اتصال Connector را شفاف نشان دهد.
|
||
|
||
---
|
||
|
||
## ۲. نقشها و سناریوهای کلیدی
|
||
|
||
### ۲.۱ نقشها
|
||
|
||
| نقش | توضیح |
|
||
|------|--------|
|
||
| مالک کسبوکار | خرید افزونه، اتصال PBX، تعریف دسترسی |
|
||
| مدیر تلفن | نگاشت داخلیها، صفها، تنظیمات شناسایی شماره |
|
||
| اپراتور فروش/پشتیبانی | دریافت Pop، تماس خروجی، ثبت نتیجه |
|
||
| ناظر/سرپرست | داشبورد زنده، گزارش عملکرد، گوش دادن به ضبط (با مجوز) |
|
||
|
||
### ۲.۲ سناریوهای پذیرش محصول (Must-pass)
|
||
|
||
1. **ورودی شناختهشده:** زنگ → Pop با نام + مانده → پاسخ → ثبت خودکار → یادداشت پس از تماس.
|
||
2. **ورودی ناشناس:** Pop ناشناس → ایجاد Lead یا Person → ثبت تماس روی همان موجودیت.
|
||
3. **خروجی Click-to-Call:** از پروفایل مشتری → Originate → وضعیت زنده → ثبت خروجی.
|
||
4. **از دسترفته:** Missed → اعلان + ردیف تاریخچه + (اختیاری) وظیفه پیگیری.
|
||
5. **چند کسبوکار:** یک User با دو داخلی در دو Business — با تعویض کسبوکار، context تلفن عوض شود.
|
||
6. **قطع Connector:** UI وضعیت «قطع از مرکز تلفن» نشان دهد؛ تماسها در صف Connector بعداً sync شوند.
|
||
|
||
---
|
||
|
||
## ۳. معماری سیستم
|
||
|
||
### ۳.۱ نمای کلی
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Flutter Client (Web/Mobile/Desktop) │
|
||
│ Phone Bar · Screen Pop · Dialer · History · Live Dashboard │
|
||
└───────────────────────────────▲─────────────────────────────────┘
|
||
│ REST + WebSocket (/ws/notifications
|
||
│ + /ws/telephony اختیاری)
|
||
┌───────────────────────────────┴─────────────────────────────────┐
|
||
│ Hesabix API (FastAPI) │
|
||
│ telephony_* routers · plugin gate · phone normalizer │
|
||
│ CRM / Person / Balance / Check / FileStorage / Workflow hooks │
|
||
└───────────────────────────────▲─────────────────────────────────┘
|
||
│ HTTPS Webhook + Command API
|
||
┌───────────────────────────────┴─────────────────────────────────┐
|
||
│ Hesabix Telephony Connector (on Issabel) │
|
||
│ AMI listener · event mapper · CDR poll · recording index │
|
||
│ command executor (Originate/Hangup/Transfer/Hold) │
|
||
└───────────────────────────────▲─────────────────────────────────┘
|
||
│ AMI / (ARI later)
|
||
┌───────────────────────────────┴─────────────────────────────────┐
|
||
│ Issabel / Asterisk PBX │
|
||
│ Extensions · Queues · CDR · MixMonitor recordings │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### ۳.۲ اصول معماری
|
||
|
||
1. **Tenant boundary = `business_id`** همیشه.
|
||
2. **Connector هویت کسبوکار دارد** (`connector_token` per PBX).
|
||
3. **Idempotency:** هر رویداد Asterisk با `uniqueid`/`linkedid` یکبار پردازش شود.
|
||
4. **Source of truth تماس = `telephony_calls`**؛ CRM Activity مشتق/اختیاری است.
|
||
5. **Realtime فقط به کاربران مجاز همان کسبوکار و داخلی مرتبط** فرستاده شود.
|
||
6. **UI هیچ dialplanی را فرض نکند**؛ فقط وضعیتهای نرمالشده را نشان دهد.
|
||
|
||
### ۳.۳ الگوی همراستا با افزونههای موجود
|
||
|
||
| الگو | مرجع موجود | استفاده در این افزونه |
|
||
|------|------------|------------------------|
|
||
| مارکتپلیس + لایسنس | `payroll`, `basalam_connector` | `asterisk_issabel_connector` |
|
||
| Gate API | `*_plugin_dependency.py` | `telephony_plugin_dependency.py` |
|
||
| Gate Flutter | `payroll_plugin_gate.dart` | `telephony_plugin_gate.dart` |
|
||
| تنظیمات کسبوکار | WooCommerce / Basalam settings | صفحه تنظیمات تلفن |
|
||
| اتصال خارجی | Basalam webhook + bridge | Connector webhook |
|
||
| فایل | `FileStorageService` + Repair attachments | ضبط مکالمه |
|
||
| Realtime | `realtime_manager` | Screen Pop / وضعیت تماس |
|
||
| CRM | `CrmActivity`, Lead convert | پیگیری پس از تماس |
|
||
|
||
---
|
||
|
||
## ۴. مدل داده (ERD اجرایی)
|
||
|
||
همه جداول با prefix `telephony_` و `business_id` (جز جایی که صریحاً گفته شود).
|
||
|
||
### ۴.۱ `telephony_pbx_connections`
|
||
|
||
اتصال یک مرکز تلفن به یک کسبوکار.
|
||
|
||
| فیلد | نوع | توضیح |
|
||
|------|-----|--------|
|
||
| id | PK | |
|
||
| business_id | FK businesses | |
|
||
| name | string | مثلاً «مرکز تهران» |
|
||
| pbx_type | enum | `issabel` \| `asterisk` \| `freepbx_compat` |
|
||
| host_hint | string nullable | فقط نمایشی/دیباگ — اتصال از سمت Connector است |
|
||
| connector_token_hash | string | هش توکن Connector |
|
||
| connector_version | string nullable | |
|
||
| last_seen_at | datetime nullable | heartbeat |
|
||
| status | enum | `pending` \| `online` \| `offline` \| `error` |
|
||
| last_error | text nullable | |
|
||
| settings | JSON | timezone، context پیشفرض Originate، مسیر ضبط، و … |
|
||
| is_active | bool | |
|
||
| created_at / updated_at | datetime | |
|
||
|
||
**Constraint:** چند PBX per business مجاز است.
|
||
|
||
### ۴.۲ `telephony_extensions`
|
||
|
||
کاتالوگ داخلیها (sync یا دستی).
|
||
|
||
| فیلد | نوع | توضیح |
|
||
|------|-----|--------|
|
||
| id | PK | |
|
||
| business_id | FK | |
|
||
| pbx_id | FK telephony_pbx_connections | |
|
||
| extension | string | مثلاً `102` |
|
||
| display_name | string nullable | |
|
||
| queue_codes | JSON nullable | صفهای مرتبط |
|
||
| is_active | bool | |
|
||
| presence_status | enum | `unknown` \| `idle` \| `ringing` \| `busy` \| `unavailable` |
|
||
| presence_updated_at | datetime nullable | |
|
||
|
||
**Unique:** `(pbx_id, extension)`
|
||
|
||
### ۴.۳ `telephony_user_extensions`
|
||
|
||
نگاشت کاربر حسابیکس ↔ داخلی در یک کسبوکار.
|
||
|
||
| فیلد | نوع | توضیح |
|
||
|------|-----|--------|
|
||
| id | PK | |
|
||
| business_id | FK | |
|
||
| user_id | FK users | |
|
||
| pbx_id | FK | |
|
||
| extension_id | FK telephony_extensions | |
|
||
| is_primary | bool | داخلی پیشفرض Click-to-Call |
|
||
| receive_screen_pop | bool | |
|
||
| can_click_to_call | bool | |
|
||
| caller_id_override | string nullable | |
|
||
|
||
**Unique پیشنهادی:** `(business_id, user_id, extension_id)`
|
||
**قانون:** هر کاربر در هر کسبوکار حداقل یک `is_primary` اگر داخلی دارد.
|
||
|
||
### ۴.۴ `telephony_queues`
|
||
|
||
| فیلد | نوع |
|
||
|------|-----|
|
||
| id, business_id, pbx_id | |
|
||
| queue_code, name | |
|
||
| is_active | |
|
||
| live_waiting_count, live_talking_count | cached optional |
|
||
| extra_info | JSON |
|
||
|
||
### ۴.۵ `telephony_calls` (هسته)
|
||
|
||
| فیلد | نوع | توضیح |
|
||
|------|-----|--------|
|
||
| id | PK | |
|
||
| business_id | FK | |
|
||
| pbx_id | FK | |
|
||
| asterisk_uniqueid | string | idempotency |
|
||
| asterisk_linkedid | string nullable | |
|
||
| direction | enum | `inbound` \| `outbound` \| `internal` |
|
||
| status | enum | `ringing` \| `answered` \| `missed` \| `busy` \| `failed` \| `completed` \| `cancelled` |
|
||
| from_number_raw | string | |
|
||
| from_number_normalized | string indexed | |
|
||
| to_number_raw | string | |
|
||
| to_number_normalized | string indexed | |
|
||
| extension | string nullable | داخلی درگیر |
|
||
| queue_code | string nullable | |
|
||
| transferred_from | string nullable | |
|
||
| transferred_to | string nullable | |
|
||
| started_at | datetime | |
|
||
| answered_at | datetime nullable | |
|
||
| ended_at | datetime nullable | |
|
||
| duration_sec | int nullable | کل |
|
||
| talk_sec | int nullable | از answer تا end |
|
||
| hangup_cause | string nullable | |
|
||
| recording_status | enum | `none` \| `pending` \| `available` \| `failed` |
|
||
| recording_file_storage_id | FK nullable | |
|
||
| recording_remote_path | string nullable | مسیر روی PBX |
|
||
| recording_url | string nullable | URL امن پروکسی |
|
||
| person_id | FK nullable | |
|
||
| lead_id | FK nullable | |
|
||
| deal_id | FK nullable | |
|
||
| document_id | FK nullable | فاکتور/سند |
|
||
| crm_activity_id | FK nullable | |
|
||
| assigned_user_id | FK nullable | اپراتور |
|
||
| category | enum/string nullable | `sales` \| `support` \| `finance` \| `other` \| custom |
|
||
| outcome | string nullable | نتیجه انسانی |
|
||
| note | text nullable | |
|
||
| match_method | enum nullable | `auto` \| `manual` \| `created_person` \| `created_lead` |
|
||
| is_anonymous | bool | |
|
||
| extra_info | JSON | |
|
||
| created_at / updated_at | | |
|
||
|
||
**Indexes ضروری:**
|
||
|
||
- `(business_id, started_at DESC)`
|
||
- `(business_id, from_number_normalized)`
|
||
- `(business_id, to_number_normalized)`
|
||
- `(business_id, assigned_user_id, started_at)`
|
||
- `UNIQUE(pbx_id, asterisk_uniqueid)`
|
||
|
||
### ۴.۶ `telephony_call_events`
|
||
|
||
لاگ ریز رویدادها برای دیباگ و state machine.
|
||
|
||
| فیلد | نوع |
|
||
|------|-----|
|
||
| id, call_id, business_id | |
|
||
| event_type | `ringing`, `answer`, `hangup`, `transfer`, `hold`, `unhold`, … |
|
||
| payload | JSON |
|
||
| occurred_at | datetime |
|
||
|
||
### ۴.۷ `telephony_settings`
|
||
|
||
یک ردیف per business (مثل payroll_settings).
|
||
|
||
| فیلد | توضیح |
|
||
|------|--------|
|
||
| business_id | unique |
|
||
| primary_phone_fields | JSON مثلاً `["mobile","mobile_2","mobile_3","phone"]` |
|
||
| number_normalization_rules | JSON |
|
||
| screen_pop_enabled | bool |
|
||
| auto_create_activity | bool |
|
||
| post_call_form_required | bool |
|
||
| default_category | string nullable |
|
||
| missed_call_create_task | bool |
|
||
| missed_call_task_due_minutes | int |
|
||
| recording_access_mode | `owner_and_managers` \| `permission_based` |
|
||
| multi_pbx_enabled | bool |
|
||
| ui_preferences | JSON | رفتار Pop، صدا، و … |
|
||
| extra_settings | JSON |
|
||
|
||
### ۴.۸ جداول کمکی
|
||
|
||
| جدول | نقش |
|
||
|------|-----|
|
||
| `telephony_connector_heartbeats` | اختیاری؛ یا در connection ذخیره شود |
|
||
| `telephony_command_logs` | Originate/Hangup/Transfer برای audit |
|
||
| `telephony_number_aliases` | نگاشت دستی شمارههای خاص به Person |
|
||
|
||
### ۴.۹ تغییرات حداقلی روی موجودیتهای فعلی
|
||
|
||
| موجودیت | تغییر |
|
||
|---------|--------|
|
||
| `CrmActivity` | افزودن `telephony_call_id` nullable + برگرداندن `extra_info` در API در صورت نیاز |
|
||
| `Lead` | اختیاری فاز ۲: فیلد `phone` (ثابت) — در فاز ۱ فقط mobile کافی است |
|
||
| Permissions JSON | بخش جدید `telephony` |
|
||
| Marketplace seed | افزونه جدید |
|
||
| Workflow triggers | `telephony.*` |
|
||
|
||
**Customer 360:** در فاز ۱ تغییر اجباری ندارد؛ Screen Pop از endpoint ترکیبی telephony استفاده میکند که 360 + balance + checks را aggregate میکند.
|
||
|
||
---
|
||
|
||
## ۵. نرمالسازی و تطبیق شماره
|
||
|
||
### ۵.۱ الگوریتم پیشنهادی `normalize_iran_phone(raw) -> candidates[]`
|
||
|
||
1. حذف فاصله، `-`، `()`، حروف فارسی ارقام → لاتین.
|
||
2. اگر با `+` شروع شد نگه دار؛ در غیر این صورت ارقام را استخراج کن.
|
||
3. قواعد:
|
||
- `0098…` → `98…`
|
||
- `+98…` → `98…`
|
||
- `98` + 10 رقم موبایل → همچنین فرم محلی `0` + 10 رقم
|
||
- `0` + 10 رقم موبایل → همچنین `98` + 10 رقم
|
||
- شمارههای شهری: حفظ کد شهر؛ تولید فرمهای با/بدون `0`
|
||
4. خروجی: لیست کاندیدا برای OR-match + فرم canonical ذخیرهشده (`from_number_normalized`).
|
||
|
||
### ۵.۲ ترتیب جستجو
|
||
|
||
1. `telephony_number_aliases`
|
||
2. Person: `mobile`, `mobile_2`, `mobile_3`, `phone` با کاندیداها
|
||
3. Lead: `mobile` (+ `phone` اگر اضافه شد)
|
||
4. اگر چند match → Pop با انتخاب دستی «کدام پرونده؟»
|
||
5. اگر صفر → ناشناس
|
||
|
||
### ۵.۳ تنظیمات کسبوکار
|
||
|
||
- فیلدهای قابل جستجو قابل انتخاب در تنظیمات
|
||
- حداقل طول رقم برای match (پیشفرض ۸)
|
||
|
||
---
|
||
|
||
## ۶. پروتکل Connector ↔ Hesabix
|
||
|
||
### ۶.۱ احراز هویت
|
||
|
||
- هنگام ساخت PBX در UI: توکن یکبارمصرف نمایش داده میشود؛ فقط hash ذخیره میشود.
|
||
- Header: `Authorization: Bearer <connector_token>`
|
||
- همچنین `X-Hesabix-Business-Id` و `X-Hesabix-Pbx-Id` برای دفاع در عمق
|
||
|
||
### ۶.۲ Heartbeat
|
||
|
||
`POST /api/v1/telephony/connector/heartbeat`
|
||
|
||
```json
|
||
{
|
||
"connector_version": "1.2.0",
|
||
"asterisk_version": "18.x",
|
||
"extensions_count": 24,
|
||
"status": "ok"
|
||
}
|
||
```
|
||
|
||
هر ۶۰ ثانیه. اگر > ۳ دقیقه نبض نیاید → `offline`.
|
||
|
||
### ۶.۳ رویداد تماس (Webhook)
|
||
|
||
`POST /api/v1/telephony/connector/events`
|
||
|
||
رویدادهای نرمالشده (نه raw AMI مستقیم به کلاینت):
|
||
|
||
```json
|
||
{
|
||
"event_id": "uuid",
|
||
"pbx_event_at": "2026-08-03T07:15:01Z",
|
||
"type": "call.ringing",
|
||
"uniqueid": "1722666901.42",
|
||
"linkedid": "1722666901.42",
|
||
"direction": "inbound",
|
||
"from": "09121234567",
|
||
"to": "02191000000",
|
||
"extension": "102",
|
||
"queue": "sales",
|
||
"channel": "SIP/102-0000001a",
|
||
"extra": {}
|
||
}
|
||
```
|
||
|
||
انواع الزامی فاز ۱:
|
||
|
||
- `call.ringing`
|
||
- `call.answered`
|
||
- `call.ended`
|
||
- `call.missed`
|
||
- `call.busy`
|
||
- `call.failed`
|
||
- `extension.presence` (فاز ۲/۳)
|
||
- `recording.ready` (فاز ۲)
|
||
|
||
پردازش باید idempotent بر اساس `(pbx_id, event_id)` یا `(pbx_id, uniqueid, type)`.
|
||
|
||
### ۶.۴ دستورات از Hesabix به Connector
|
||
|
||
Connector یا:
|
||
|
||
- **Pull:** `GET /api/v1/telephony/connector/commands/poll`
|
||
یا
|
||
- **Push کانال معکوس:** WebSocket Connector (ترجیحی فاز ۲)
|
||
|
||
دستور Originate فاز ۱:
|
||
|
||
```json
|
||
{
|
||
"command_id": "uuid",
|
||
"type": "originate",
|
||
"extension": "102",
|
||
"destination": "09121234567",
|
||
"caller_id": "02191000000",
|
||
"timeout_ms": 30000,
|
||
"context": "from-internal"
|
||
}
|
||
```
|
||
|
||
پاسخ:
|
||
|
||
```json
|
||
{ "command_id": "uuid", "accepted": true, "asterisk_action_id": "..." }
|
||
```
|
||
|
||
دستورات فاز ۳: `hangup`, `transfer`, `hold`, `resume`.
|
||
|
||
### ۶.۵ امنیت Connector
|
||
|
||
- فقط HTTPS outbound از PBX
|
||
- توکن قابل rotate از UI
|
||
- Rate limit روی endpoints کانکتور
|
||
- عدم پذیرش raw AMI از اینترنت به Hesabix
|
||
|
||
### ۶.۶ پکیج نصب Connector
|
||
|
||
مسیر پیشنهادی ریپو:
|
||
|
||
`extraScripts/HesabixTelephonyConnector/`
|
||
|
||
شامل:
|
||
|
||
- سرویس systemd
|
||
- فایل config (`hesabix-url`, `token`, `ami-host`, `ami-user`, `ami-secret`)
|
||
- اسکریپت نصب Issabel (CentOS/Rocky متداول)
|
||
- README فارسی نصب
|
||
- قابلیت dry-run تست AMI
|
||
|
||
---
|
||
|
||
## ۷. API حسابیکس (قرارداد اجرایی)
|
||
|
||
پایه: `/api/v1/telephony/...`
|
||
همه endpointهای کاربری با:
|
||
|
||
- `require_business_access`
|
||
- `require_telephony_plugin_active`
|
||
- `require_business_permission_dep("telephony", …)`
|
||
|
||
### ۷.۱ تنظیمات و PBX
|
||
|
||
| Method | Path | Perm | توضیح |
|
||
|--------|------|------|--------|
|
||
| GET | `/settings` | view | |
|
||
| PUT | `/settings` | manage | |
|
||
| GET/POST | `/pbx` | view/manage | |
|
||
| POST | `/pbx/{id}/rotate-token` | manage | |
|
||
| GET | `/pbx/{id}/status` | view | |
|
||
| POST | `/pbx/{id}/test` | manage | درخواست تست به Connector |
|
||
|
||
### ۷.۲ داخلی و نگاشت کاربران
|
||
|
||
| Method | Path | Perm |
|
||
|--------|------|------|
|
||
| GET/POST/PATCH | `/extensions` | view/manage |
|
||
| POST | `/extensions/sync` | manage |
|
||
| GET/PUT | `/user-extensions` | manage |
|
||
| GET | `/me/extension-context` | view | داخلیهای کاربر جاری در business جاری |
|
||
|
||
### ۷.۳ تماسها
|
||
|
||
| Method | Path | Perm |
|
||
|--------|------|------|
|
||
| GET | `/calls` | view | فیلتر و صفحهبندی |
|
||
| GET | `/calls/{id}` | view | |
|
||
| PATCH | `/calls/{id}` | view+write-ish | note/category/outcome/links |
|
||
| POST | `/calls/{id}/link-person` | view | |
|
||
| POST | `/calls/{id}/create-person` | view | |
|
||
| POST | `/calls/{id}/create-lead` | view | |
|
||
| POST | `/calls/{id}/create-activity` | view | |
|
||
| POST | `/calls/{id}/create-task` | view | |
|
||
| GET | `/calls/{id}/recording` | listen_recordings | stream/redirect |
|
||
| POST | `/click-to-call` | click_to_call | |
|
||
| GET | `/calls/{id}/screen-pop-context` | view | aggregate Pop |
|
||
|
||
### ۷.۴ زنده و گزارش
|
||
|
||
| Method | Path | Phase | Perm |
|
||
|--------|------|-------|------|
|
||
| GET | `/live/snapshot` | ۳ | live_monitor |
|
||
| GET | `/reports/summary` | ۲ | reports |
|
||
| GET | `/reports/operators` | ۲ | reports |
|
||
| GET | `/reports/by-person` | ۲ | reports |
|
||
|
||
### ۷.۵ Connector (بدون session کاربر — با token)
|
||
|
||
| Method | Path |
|
||
|--------|------|
|
||
| POST | `/connector/heartbeat` |
|
||
| POST | `/connector/events` |
|
||
| GET | `/connector/commands/poll` |
|
||
| POST | `/connector/commands/{id}/ack` |
|
||
| POST | `/connector/recordings/meta` |
|
||
|
||
### ۷.۶ WebSocket
|
||
|
||
گزینه A (فاز ۱): استفاده از `/ws/notifications` با `type: telephony.*`
|
||
گزینه B (فاز ۲): `/ws/telephony` اختصاصی برای ترافیک پرتکرار وضعیت تماس
|
||
|
||
Payload نمونه Screen Pop:
|
||
|
||
```json
|
||
{
|
||
"type": "telephony.incoming_call",
|
||
"business_id": 12,
|
||
"call_id": 987,
|
||
"extension": "102",
|
||
"from_number": "09121234567",
|
||
"person": {"id": 55, "name": "علی احمدی"},
|
||
"balance": {"amount": 2450000, "status": "debtor"},
|
||
"deep_link": "/business/12/telephony/calls/987"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## ۸. مجوزها
|
||
|
||
```json
|
||
{
|
||
"telephony": {
|
||
"view": true,
|
||
"manage": true,
|
||
"click_to_call": true,
|
||
"listen_recordings": true,
|
||
"live_monitor": true,
|
||
"reports": true,
|
||
"control_calls": true
|
||
}
|
||
}
|
||
```
|
||
|
||
| اکشن | معنی |
|
||
|------|------|
|
||
| view | تاریخچه، Pop، جزئیات تماس |
|
||
| manage | PBX، داخلی، تنظیمات، نگاشت کاربران |
|
||
| click_to_call | Originate |
|
||
| listen_recordings | پخش/دانلود ضبط |
|
||
| live_monitor | داشبورد زنده / BLF |
|
||
| reports | گزارشها |
|
||
| control_calls | Hangup/Transfer/Hold |
|
||
|
||
مالک کسبوکار و superadmin طبق الگوی فعلی bypass دارند.
|
||
|
||
---
|
||
|
||
## ۹. یکپارچگی با حسابیکس
|
||
|
||
### ۹.۱ CRM
|
||
|
||
- ساخت/بهروزرسانی `CrmActivity(activity_type=call)` وقتی تنظیم `auto_create_activity` روشن است.
|
||
- اتصال `lead_id` / `deal_id` / `person_id`.
|
||
- Post-call → ایجاد Task با `due_at`.
|
||
- تبدیل ناشناس به Lead با `source_code=telephony` (seed فرایند در صورت نیاز).
|
||
|
||
### ۹.۲ مالی و مشتری
|
||
|
||
Screen Pop Context باید aggregate کند:
|
||
|
||
- خلاصه Person
|
||
- `calculate_person_balance` (+ چند ارزی اگر لازم)
|
||
- آخرین N فاکتور (`Document`)
|
||
- چکهای سررسید/معوق از مدل `check`
|
||
- آخرین فعالیتها / یادداشتها
|
||
- معاملات باز
|
||
|
||
### ۹.۳ Workflow triggers (فاز ۲+)
|
||
|
||
- `telephony.call.ringing`
|
||
- `telephony.call.missed`
|
||
- `telephony.call.completed`
|
||
- `telephony.call.anonymous`
|
||
|
||
Actions پیشنهادی: ایجاد وظیفه، ارسال اعلان، ایجاد Lead.
|
||
|
||
### ۹.۴ اعلانها
|
||
|
||
از `InAppProvider` / `realtime_manager`:
|
||
|
||
- تماس ورودی
|
||
- از دسترفته
|
||
- پایان تماس (درخواست ثبت نتیجه)
|
||
- قطع اتصال Connector (برای مدیران)
|
||
|
||
---
|
||
|
||
## ۱۰. طراحی رابط کاربری (Web / Mobile / Desktop)
|
||
|
||
### ۱۰.۱ اصول طراحی محصول
|
||
|
||
این افزونه UI «مرکز تماس مدرن داخل ERP» است، نه یک داشبورد شلوغ.
|
||
|
||
**اصول سخت:**
|
||
|
||
1. **یک نوار تلفن همیشگی** در shell کسبوکار وقتی افزونه فعال است — هویت بصری ثابت روی هر سه پلتفرم.
|
||
2. **Screen Pop غیرمسدودکننده** — کار فعلی کاربر را کامل قطع نکند؛ روی دسکتاپ/وب پنل کناری، روی موبایل bottom sheet هوشمند.
|
||
3. **Brand/Feature presence:** کلمه «تماس» یا وضعیت داخلی باید در viewport اول صفحات تلفن واضح باشد؛ اما در بقیه CRM فقط بهصورت Phone Bar ظریف ظاهر شود.
|
||
4. **بدون کارتهای تزئینی اضافه** در Pop؛ اطلاعات سلسلهمراتبی و خوانا.
|
||
5. **حرکت هدفمند:** ۲–۳ motion اصلی:
|
||
- ظاهر شدن Pop با slide + fade
|
||
- pulse ملایم وضعیت «زنگ میخورد»
|
||
- انتقال وضعیت تماس در Phone Bar (idle → ringing → in-call)
|
||
6. **تایپوگرافی:** همخوان با سیستم فونت حسابیکس (ایرانیکان/فونت فعلی اپ) — از Inter/Roboto/Arial استفاده نشود اگر اپ فونت برند دارد.
|
||
7. **رنگ:** متغیرهای اختصاصی telephony در تم موجود؛ از تم بنفش پیشفرض AI و پسزمینه کرم کلیشهای پرهیز شود. پیشنهاد:
|
||
- Idle: سبز ملایم وضعیت
|
||
- Ringing: کهربایی/نارنجی هشدار
|
||
- In-call: آبی عملیاتی حسابیکس (یا primary موجود)
|
||
- Missed: قرمز سیستمی موجود
|
||
8. **Dense on desktop, focused on mobile:** دسکتاپ اطلاعات بیشتر؛ موبایل فقط تصمیمهای فوری.
|
||
|
||
### ۱۰.۲ توکنهای UI پیشنهادی
|
||
|
||
```css
|
||
--tel-bar-height-desktop: 48px;
|
||
--tel-bar-height-mobile: 44px;
|
||
--tel-pop-width-desktop: 420px;
|
||
--tel-ring-pulse: 1.2s;
|
||
--tel-status-idle: var(--success);
|
||
--tel-status-ringing: #D97706;
|
||
--tel-status-busy: var(--primary);
|
||
--tel-status-offline: var(--muted);
|
||
--tel-status-missed: var(--danger);
|
||
```
|
||
|
||
### ۱۰.۳ نقاط ورود UI در اپ
|
||
|
||
| نقطه | مسیر پیشنهادی |
|
||
|------|----------------|
|
||
| منوی کسبوکار | «مرکز تماس» (فقط اگر لایسنس فعال) |
|
||
| زیرمسیرها | `/business/:id/telephony/...` |
|
||
| تنظیمات | `/business/:id/settings/telephony` |
|
||
| Phone Bar | داخل `BusinessShell` |
|
||
| آیکون تماس کنار شماره | Person، Lead، 360، لیستها |
|
||
|
||
### ۱۰.۴ Phone Bar (همه پلتفرمها)
|
||
|
||
**محتوا:**
|
||
|
||
- وضعیت داخلی primary: نقطه رنگی + متن کوتاه (`آزاد` / `زنگ` / `مکالمه` / `آفلاین`)
|
||
- شماره داخلی
|
||
- دکمه شمارهگیر
|
||
- Badge از دسترفته امروز
|
||
- دکمه ورود به داشبورد/تاریخچه
|
||
|
||
**Desktop/Web (≥1100px):** نوار افقی زیر هدر یا بالای محتوا.
|
||
**Tablet:** همان نوار فشردهتر.
|
||
**Mobile:** نوار پایینتر از AppBar یا چسبیده بالای bottom navigation؛ در حالت مکالمه به **In-Call Mini Bar** تبدیل میشود.
|
||
|
||
### ۱۰.۵ Screen Pop
|
||
|
||
#### Desktop / Web وسیع
|
||
|
||
پنل end-side (در RTL: چپ) عرض ~420px روی محتوا، با backdrop خیلی ملایم یا بدون قفل کامل.
|
||
|
||
ساختار:
|
||
|
||
1. هدر تماس: جهت + وضعیت + تایمر
|
||
2. هویت: نام / شرکت / شماره (بزرگ و خوانا)
|
||
3. سیگنال مالی یک خط: مانده + وضعیت بدهی
|
||
4. Tabs فشرده: خلاصه | فاکتورها | چکها | سوابق تماس | یادداشت
|
||
5. اکشنهای ثابت پایین: ایجاد Lead / اتصال به شخص / ثبت یادداشت / (در فاز ۳ کنترل تماس)
|
||
|
||
#### Mobile
|
||
|
||
- هنگام زنگ: **high-priority bottom sheet** تا 70% ارتفاع، دستگیره drag
|
||
- یک CTA اصلی واضح نیست اگر پاسخ روی گوشی سختافزاری است؛ بهجای آن «باز کردن پرونده» و «ایجاد سرنخ»
|
||
- اطلاعات مالی فقط ۱ خط؛ جزئیات در تب
|
||
|
||
#### Desktop app (Windows)
|
||
|
||
مثل Web وسیع + پشتیبانی میانبر صفحهکلید:
|
||
|
||
- `Ctrl/Cmd+Shift+D` شمارهگیر
|
||
- `Esc` بستن Pop (اگر در حالت ringing اجباری نباشد)
|
||
|
||
### ۱۰.۶ شمارهگیر (Dialer)
|
||
|
||
- جستجوی شماره هنگام تایپ (Person/Lead)
|
||
- لیست اخیر
|
||
- پد عددی بزرگ روی موبایل؛ روی دسکتاپ پد + فیلد متنی
|
||
- انتخاب داخلی مبدأ اگر کاربر چند داخلی دارد
|
||
|
||
### ۱۰.۷ پنل مکالمه فعال (In-Call)
|
||
|
||
- تایمر بزرگ
|
||
- نام طرف مقابل
|
||
- اکشنها بر اساس فاز: فاز ۱ فقط یادداشت سریع؛ فاز ۳ Hold/Transfer/Hangup
|
||
- پس از پایان: **Post-Call Sheet** اجباری/اختیاری طبق تنظیمات:
|
||
- نتیجه
|
||
- دستهبندی
|
||
- یادداشت
|
||
- ایجاد وظیفه
|
||
- اتصال به Deal/فاکتور
|
||
|
||
### ۱۰.۸ صفحات اصلی
|
||
|
||
#### الف) تاریخچه تماسها
|
||
|
||
- فیلتر چیپی: ورودی/خروجی/از دسترفته/امروز/من
|
||
- جدول در دسکتاپ؛ لیست کارتمانند تعاملمحور در موبایل (کارت فقط چون container تعامل لیست است)
|
||
- ردیف: جهت، طرف، داخلی، مدت، نتیجه، دکمه پخش ضبط
|
||
- جزئیات تماس: timeline رویدادها + پخشکننده صوت
|
||
|
||
#### ب) تنظیمات تلفن
|
||
|
||
بخشها:
|
||
|
||
1. وضعیت اتصال PBX + نصب Connector (لینک راهنما + کپی توکن)
|
||
2. قوانین شماره
|
||
3. نگاشت کاربران به داخلی (جدول دسکتاپ / لیست موبایل)
|
||
4. رفتار Pop و Post-call
|
||
5. دسترسی ضبط
|
||
|
||
#### ج) داشبورد لحظهای (فاز ۳)
|
||
|
||
ترکیب یک ترکیب واحد، نه دیوار کارت:
|
||
|
||
- نوار بالا: فعال / انتظار / از دسترفته امروز / میانگین پاسخ
|
||
- ناحیه اصلی: ماتریس وضعیت داخلیها (BLF)
|
||
- نوار کناری: صفها
|
||
|
||
روی موبایل: فقط KPIهای ضروری + لیست داخلیهای تیم کاربر.
|
||
|
||
#### د) گزارشها (فاز ۲)
|
||
|
||
چارتهای ساده + جدول؛ فیلتر بازه، داخلی، اپراتور، مشتری.
|
||
|
||
### ۱۰.۹ Click-to-Call در سطح سیستم
|
||
|
||
کامپوننت مشترک:
|
||
|
||
`TelephonyPhoneLink(number, personId?, leadId?)`
|
||
|
||
نمایش:
|
||
|
||
- متن شماره قابل کپی
|
||
- آیکون تماس (فقط اگر `click_to_call` + افزونه فعال + داخلی mapped)
|
||
|
||
روی موبایل: تأیید کوتاه قبل از Originate (جلوگیری از لمس تصادفی).
|
||
|
||
### ۱۰.۱۰ حالتهای خالی و خطا (الزامی UI)
|
||
|
||
| حالت | پیام/رفتار |
|
||
|------|-------------|
|
||
| افزونه غیرفعال | `TelephonyPluginGate` → مارکتپلیس |
|
||
| Connector offline | Banner نارنجی در تنظیمات و Phone Bar |
|
||
| کاربر بدون داخلی | Phone Bar: «داخلی تعریف نشده» + لینک به مدیر |
|
||
| شماره نامعتبر | Dialer خطا با راهنمای فرمت |
|
||
| چند match | Pop انتخاب پرونده |
|
||
| بدون مجوز ضبط | پلیر مخفی؛ متن جایگزین |
|
||
|
||
### ۱۰.۱۱ دسترسپذیری و i18n
|
||
|
||
- FA پیشفرض؛ EN برای کلیدها از الگوی i18n موجود
|
||
- کنتراست وضعیتها فقط متکی به رنگ نباشد (آیکون + متن)
|
||
- تایمر و اعداد با ارقام سازگار با locale اپ
|
||
- پخش صوت با کنترل صفحه کلید در وب/دسکتاپ
|
||
|
||
### ۱۰.۱۲ پرفورمنس UI
|
||
|
||
- Pop data از endpoint aggregate؛ نه ۱۰ درخواست جدا در UI
|
||
- debounce جستجوی Dialer 250–300ms
|
||
- WS events فقط برای business فعال در `AuthStore`
|
||
- با تعویض business: unsubscribe ذهنی + clear state تلفن
|
||
|
||
---
|
||
|
||
## ۱۱. ساختار کد پیشنهادی
|
||
|
||
### ۱۱.۱ Backend
|
||
|
||
```
|
||
hesabixAPI/
|
||
adapters/db/models/telephony.py
|
||
adapters/db/repositories/telephony_*.py
|
||
adapters/api/v1/telephony/
|
||
__init__.py
|
||
settings.py
|
||
pbx.py
|
||
extensions.py
|
||
calls.py
|
||
click_to_call.py
|
||
reports.py
|
||
live.py
|
||
connector.py
|
||
app/core/telephony_plugin_dependency.py
|
||
app/services/telephony/
|
||
phone_normalizer.py
|
||
call_state_machine.py
|
||
caller_id_matcher.py
|
||
screen_pop_service.py
|
||
originate_service.py
|
||
recording_service.py
|
||
connector_command_service.py
|
||
reports_service.py
|
||
realtime_publisher.py
|
||
migrations/versions/YYYYMMDD_telephony_*.py
|
||
scripts/add_telephony_plugin.py
|
||
adapters/db/seed_data/marketplace_plugins_seed.py # اضافه کردن افزونه
|
||
```
|
||
|
||
### ۱۱.۲ Frontend
|
||
|
||
```
|
||
hesabixUI/hesabix_ui/lib/
|
||
pages/business/telephony/
|
||
telephony_shell_bar.dart
|
||
telephony_dialer_sheet.dart
|
||
telephony_screen_pop.dart
|
||
telephony_in_call_panel.dart
|
||
telephony_post_call_sheet.dart
|
||
telephony_calls_page.dart
|
||
telephony_call_detail_page.dart
|
||
telephony_settings_page.dart
|
||
telephony_live_dashboard_page.dart
|
||
telephony_reports_page.dart
|
||
widgets/telephony/
|
||
telephony_plugin_gate.dart
|
||
telephony_status_dot.dart
|
||
telephony_phone_link.dart
|
||
telephony_recording_player.dart
|
||
services/telephony/
|
||
telephony_api.dart
|
||
telephony_realtime_controller.dart
|
||
telephony_session_controller.dart
|
||
```
|
||
|
||
### ۱۱.۳ Connector
|
||
|
||
```
|
||
extraScripts/HesabixTelephonyConnector/
|
||
README.md
|
||
install.sh
|
||
config.example.env
|
||
app/
|
||
main.py
|
||
ami_client.py
|
||
event_mapper.py
|
||
hesabix_client.py
|
||
commands.py
|
||
recordings.py
|
||
systemd/hesabix-telephony-connector.service
|
||
```
|
||
|
||
---
|
||
|
||
## ۱۲. فازهای اجرایی
|
||
|
||
هر فاز خروجی قابل دمو و چکلیست پذیرش دارد. تا پذیرش فاز n، فاز n+1 شروع نشود مگر کارهای زیرساختی موازی بیخطر.
|
||
|
||
---
|
||
|
||
### فاز ۰ — آمادهسازی و قراردادها (۳–۵ روز)
|
||
|
||
**هدف:** قفل طراحی، seed افزونه، اسکلت خالی بدون رفتار تلفنی واقعی.
|
||
|
||
**کارها:**
|
||
|
||
1. ثبت افزونه در `marketplace_plugins_seed` با کد `asterisk_issabel_connector`
|
||
2. اسکریپت `add_telephony_plugin.py`
|
||
3. سند API OpenAPI draft داخل همین ریپو یا swagger stubs
|
||
4. تعریف permissionها در UI مجوزها
|
||
5. ایجاد branch/workflow تیمی و برش تیکتها از همین سند
|
||
|
||
**پذیرش:**
|
||
|
||
- افزونه در مارکتپلیس دیده میشود (حتی اگر صفحات «بهزودی» باشند)
|
||
- کد افزونه و قیمتگذاری seed شده
|
||
|
||
---
|
||
|
||
### فاز ۱ — MVP عملیاتی CTI (۶–۸ هفته)
|
||
|
||
**هدف:** اتصال واقعی Issabel، Screen Pop، Click-to-Call، تاریخچه پایه.
|
||
|
||
#### ۱.A Backend
|
||
|
||
- مدلها و migrationهای بخش ۴ (حداقل: settings, pbx, extensions, user_extensions, calls, call_events)
|
||
- plugin dependency gate
|
||
- phone normalizer + matcher
|
||
- connector heartbeat/events/commands poll
|
||
- call state machine
|
||
- click-to-call originate command
|
||
- screen-pop-context aggregate (person/lead + balance پایه)
|
||
- WS notification `telephony.incoming_call` / `telephony.call_ended` / `telephony.call_missed`
|
||
- CRUD تاریخچه و patch نتیجه/یادداشت
|
||
- ایجاد Person/Lead از تماس
|
||
|
||
#### ۱.B Connector
|
||
|
||
- AMI login
|
||
- map رویدادهای Dial/Hangup به webhook
|
||
- originate
|
||
- heartbeat
|
||
- نصبنامه Issabel
|
||
- صف محلی retry اگر Hesabix در دسترس نبود
|
||
|
||
#### ۱.C Flutter UI
|
||
|
||
- Plugin gate + منو + routes
|
||
- Phone Bar در BusinessShell
|
||
- Screen Pop (desktop side panel + mobile sheet)
|
||
- Dialer
|
||
- صفحه تاریخچه و جزئیات تماس
|
||
- تنظیمات: PBX token، نگاشت کاربر↔داخلی، تست اتصال
|
||
- `TelephonyPhoneLink` در Person و Lead
|
||
- Post-call sheet ساده
|
||
|
||
#### ۱.D کیفیت
|
||
|
||
- تست واحد normalizer (ماتریس شمارههای ایرانی)
|
||
- تست idempotency رویدادها
|
||
- تست مجوزها و لایسنس
|
||
- سناریو دستی روی Issabel staging
|
||
|
||
**پذیرش فاز ۱:**
|
||
|
||
- [ ] Connector online در UI
|
||
- [ ] تماس ورودی شناختهشده → Pop صحیح برای همان کاربر/داخلی/کسبوکار
|
||
- [ ] ناشناس → ایجاد Lead/Person
|
||
- [ ] Click-to-Call از پروفایل کار میکند
|
||
- [ ] تاریخچه ورودی/خروجی با مدت و وضعیت
|
||
- [ ] تعویض کسبوکار context داخلی را عوض میکند
|
||
- [ ] UI روی Chrome وب، Android، و Windows desktop قابل استفاده است (layout نشکند)
|
||
|
||
**صریحاً خارج از فاز ۱:** Hold/Transfer، داشبورد زنده، گزارش پیشرفته، Softphone، آپلود انبوه ضبط.
|
||
|
||
---
|
||
|
||
### فاز ۲ — عمق CRM + ضبط + گزارش (۴–۶ هفته)
|
||
|
||
**هدف:** تماس بخشی از فروش و پشتیبانی روزمره شود.
|
||
|
||
**کارها:**
|
||
|
||
- recording.ready از Connector + پروکسی پخش/دانلود با permission
|
||
- آپلود اختیاری به `file_storage` (`module_context=telephony_recording`)
|
||
- اتصال تماس به Deal و Document
|
||
- auto activity + task برای missed
|
||
- بهبود Screen Pop: فاکتورها، چکها، آخرین خریدها، یادداشتها
|
||
- گزارشهای summary / operators / by-person / by-time
|
||
- Workflow triggers اولیه
|
||
- بهبود UI گزارش و پلیر صوت زیبا (waveform ساده یا progress دقیق)
|
||
- number aliases
|
||
- sync بهتر داخلیها از Connector
|
||
|
||
**پذیرش فاز ۲:**
|
||
|
||
- [ ] پخش ضبط برای کاربر مجاز؛ رد برای غیرمجاز
|
||
- [ ] Pop مانده حساب و چک معوق را نشان میدهد
|
||
- [ ] گزارش هفتگی اپراتور قابل استخراج است
|
||
- [ ] missed call میتواند وظیفه بسازد
|
||
|
||
---
|
||
|
||
### فاز ۳ — مرکز تماس زنده و کنترل تماس (۶–۸ هفته)
|
||
|
||
**هدف:** سرپرست و اپراتور دید زنده و کنترل داشته باشند.
|
||
|
||
**کارها:**
|
||
|
||
- live snapshot: تماسهای فعال، صف، حضور داخلی (BLF)
|
||
- UI Live Dashboard تطبیقی
|
||
- دستورات Hangup / Transfer / Hold / Resume (AMI یا ARI)
|
||
- چند PBX / چند شعبه در UX
|
||
- اعلان تماس برگشتی
|
||
- مشاهده تماسهای در حال انجام تیم
|
||
- `control_calls` permission enforcement
|
||
- بهینهسازی WS اختصاصی در صورت نیاز
|
||
|
||
**پذیرش فاز ۳:**
|
||
|
||
- [ ] داشبورد زنده تأخیر محسوس < ۳ ثانیه در شبکه عادی دارد
|
||
- [ ] Transfer از UI روی Issabel واقعی کار میکند
|
||
- [ ] BLF وضعیت آزاد/مشغول/آفلاین را نشان میدهد
|
||
|
||
---
|
||
|
||
### فاز ۴ — سختسازی تولید و عملیات (۳–۴ هفته)
|
||
|
||
**هدف:** آماده فروش گسترده.
|
||
|
||
**کارها:**
|
||
|
||
- مشاهدهپذیری: متریکها، لاگ ساختیافته، DLQ رویدادهای شکستخورده
|
||
- rotate token، audit log دستورات
|
||
- محدودیت نرخ، محافظت replay
|
||
- راهنمای نصب ویدیویی/متنی فارسی کامل
|
||
- تست بار رویداد همزمان
|
||
- بهبود UX حالتهای خطا و بازیابی Connector
|
||
- FA/EN کامل رشتهها
|
||
- چکلیست امنیتی
|
||
|
||
**پذیرش فاز ۴:**
|
||
|
||
- [ ] مستندات نصب توسط یک تکنسین Issabel بدون کمک تیم توسعه انجامپذیر است
|
||
- [ ] قطع و وصل شبکه باعث فساد داده تماس نمیشود
|
||
- [ ] سوپرادمین متریک سلامت کانکتورها را میبیند (حداقلی)
|
||
|
||
---
|
||
|
||
### فاز ۵ — پیشرفته اختیاری (۸+ هفته یا بکلاگ)
|
||
|
||
فقط با تأیید محصول:
|
||
|
||
- Softphone WebRTC داخل Flutter Web/Desktop
|
||
- تحلیل تماس ناشناس
|
||
- صف انتظار پیشرفته و wallboard تمامصفحه
|
||
- اتصال به کانالهای دیگر (در صورت نیاز)
|
||
- ماژول تیکت کسبوکار جدا + اتصال به تماس
|
||
- ضبط با سطح دسترسی بسیار دانهریز per call
|
||
|
||
---
|
||
|
||
## ۱۳. برنامه اسپرینت پیشنهادی فاز ۱
|
||
|
||
| اسپرینت | مدت | خروجی |
|
||
|---------|-----|--------|
|
||
| S1 | ۲ هفته | مدل داده، seed، settings/pbx API، اسکلت Flutter settings + gate |
|
||
| S2 | ۲ هفته | Connector AMI + events + call state + تاریخچه |
|
||
| S3 | ۲ هفته | Screen Pop + WS + matcher + Pop UI همه پلتفرمها |
|
||
| S4 | ۲ هفته | Click-to-Call + Dialer + Phone Bar + PhoneLink + پایدارسازی و QA |
|
||
|
||
---
|
||
|
||
## ۱۴. معیارهای کیفیت و تست
|
||
|
||
### ۱۴.۱ ماتریس تست شماره
|
||
|
||
حداقل ۳۰ نمونه شامل:
|
||
|
||
- `09121234567`, `9121234567`, `989121234567`, `+989121234567`, `00989121234567`
|
||
- شهری `02191001234` با/بدون صفر
|
||
- شماره کوتاه داخلی
|
||
- شماره با خط تیره و فاصله فارسی
|
||
|
||
### ۱۴.۲ تست چندمستأجری
|
||
|
||
- رویداد business A هرگز به کاربر business B نرسد
|
||
- حتی اگر همان user در هر دو عضو است، Pop فقط برای mapping همان business برود
|
||
|
||
### ۱۴.۳ تست پلتفرم UI
|
||
|
||
| پلتفرم | حداقل بررسی |
|
||
|--------|-------------|
|
||
| Web Chrome/Firefox | Pop، Dialer، WS، پخش صوت |
|
||
| Android | bottom sheet، Originate، نوار وضعیت |
|
||
| iOS | همان + محدودیت background (مستند شود) |
|
||
| Windows desktop | میانبرها، پنل کناری، فونت/RTL |
|
||
|
||
### ۱۴.۴ تست کارایی
|
||
|
||
- ۱۰ رویداد/ثانیه per PBX بدون drop (فاز ۱ هدف اولیه)
|
||
- Pop context p95 < ۵۰۰ms روی داده متوسط
|
||
|
||
---
|
||
|
||
## ۱۵. امنیت و حریم خصوصی
|
||
|
||
1. توکن Connector فقط hash در DB
|
||
2. ضبط مکالمه فقط با permission
|
||
3. URL ضبط زماندار یا stream احرازهویتشده — لینک عمومی خام ممنوع
|
||
4. AMI credential فقط روی سرور مشتری (Connector)، نه در کلود حسابیکس
|
||
5. Audit برای listen recording و control commands
|
||
6. حداقل داده در WS (بدون ارسال کامل تاریخچه مالی در خود event اگر سنگین است؛ client میتواند context را fetch کند — یا نسخه خلاصه)
|
||
|
||
**تصمیم فاز ۱ برای Pop:** event WS خلاصه بفرستد؛ UI بلافاصله `screen-pop-context` را GET کند (سریعتر برای امنیت و تازگی داده).
|
||
|
||
---
|
||
|
||
## ۱۶. قیمتگذاری و مارکتپلیس (پیشنهاد اولیه)
|
||
|
||
| دوره | قیمت پیشنهادی seed | قابل تغییر توسط ادمین |
|
||
|------|---------------------|------------------------|
|
||
| ماهانه | ۳۵۰٬۰۰۰ ریال? → **به تومان رایج مارکت:** همتراز integrationها مثلاً ۲۵۰٬۰۰۰–۵۰۰٬۰۰۰ تومان | بله |
|
||
| سالانه | ۱۰× ماهانه با تخفیف | بله |
|
||
| Trial | ۱۴ روز | بله |
|
||
|
||
دسته: `integration`
|
||
توضیح مارکتپلیس باید صریحاً بگوید: «نیاز به نصب Connector روی Issabel/Asterisk دارد».
|
||
|
||
> مبلغ نهایی را مالک محصول قبل از انتشار عمومی در seed قطعی کند.
|
||
|
||
---
|
||
|
||
## ۱۷. ریسکها و کاهش ریسک
|
||
|
||
| ریسک | اثر | کاهش |
|
||
|------|-----|------|
|
||
| تنوع dialplan ایزابل | Originate کار نکند | تنظیم context در settings + راهنمای تشخیص |
|
||
| NAT/قطع اینترنت شعبه | از دست رفتن رویداد | صف retry محلی Connector |
|
||
| Caller ID نامعتبر مخابرات | Pop اشتباه | aliases + UI اصلاح دستی |
|
||
| انتظار Softphone در MVP | نارضایتی | شفافسازی فروش: Click-to-Call نیاز به گوشی SIP دارد |
|
||
| حجم ضبطها | هزینه storage | پیشفرض لینک PBX؛ آپلود اختیاری |
|
||
| پیچیدگی UI مرکز تماس | شلوغی | فازبندی و Phone Bar مینیمال |
|
||
|
||
---
|
||
|
||
## ۱۸. تصمیمهای باز (باید قبل/حین فاز ۱ بسته شوند)
|
||
|
||
| # | موضوع | گزینهها | پیشنهاد سند |
|
||
|---|--------|----------|-------------|
|
||
| 1 | مبلغ نهایی افزونه | — | توسط محصول |
|
||
| 2 | کانال معکوس دستورات | Poll vs WS Connector | فاز ۱ Poll؛ فاز ۲ WS |
|
||
| 3 | آیا Post-call اجباری باشد؟ | بله/خیر/تنظیمی | تنظیمی؛ پیشفرض خیر |
|
||
| 4 | ذخیره ضبط در کلود | همیشه/اختیاری/هرگز | اختیاری |
|
||
| 5 | افزودن `phone` به Lead | بله/خیر | فاز ۲ در صورت نیاز واقعی |
|
||
| 6 | نام منو | مرکز تماس / تلفن / استریسک | «مرکز تماس» |
|
||
|
||
---
|
||
|
||
## ۱۹. چکلیست شروع پیادهسازی (روز ۱)
|
||
|
||
1. تأیید این سند توسط محصول/فنی
|
||
2. بستن جدول تصمیمهای باز بخش ۱۸ (حداقلی: ۱، ۳، ۴، ۶)
|
||
3. ایجاد تیکتهای فاز ۰ و اسپرینت ۱ از بخش ۱۳
|
||
4. آمادهسازی یک Issabel staging با AMI user تست
|
||
5. شروع seed + migration اسکلت طبق فاز ۰/۱.A
|
||
|
||
---
|
||
|
||
## ۲۰. پیوست A — نگاشت لیست نیازمندی کاربر به فاز
|
||
|
||
| نیازمندی | فاز |
|
||
|----------|-----|
|
||
| Screen Pop | ۱ |
|
||
| جستجوی خودکار مخاطب/مشتری | ۱ |
|
||
| سوابق/فاکتور/یادداشت در Pop | ۱ خلاصه؛ ۲ کامل |
|
||
| وضعیت بدهی/اعتبار | ۱ (balance)؛ ۲ غنیتر |
|
||
| ایجاد مخاطب/Lead ناشناس | ۱ |
|
||
| Click-to-Call | ۱ |
|
||
| ثبت خودکار ورودی/خروجی | ۱ |
|
||
| مدت/شروع/پایان/نتیجه/داخلی | ۱ |
|
||
| یادداشت و دستهبندی پس از تماس | ۱ |
|
||
| ضبط لینک/پخش/دانلود/دسترسی | ۲ |
|
||
| نگاشت کاربر↔داخلی + چند داخلی | ۱ |
|
||
| وضعیت داخلی | ۳ (در ۱ فقط هنگام رویداد تماس) |
|
||
| از دسترفته / تعداد امروز | ۱ پایه؛ ۳ زنده |
|
||
| Activity/یادآوری/وظیفه | ۲ |
|
||
| اتصال به Deal/فاکتور | ۲ |
|
||
| گزارشها | ۲ |
|
||
| چند PBX / صفها / گروهها | ۳ (تعریف صف ۲/۳) |
|
||
| اعلانها | ۱ |
|
||
| صف انتظار / BLF / تماس فعال | ۳ |
|
||
| قطع/انتقال/Hold | ۳ |
|
||
| شمارهگیری سریع / جستجو حین تایپ | ۱ |
|
||
| چند شعبه | ۳ |
|
||
| یکپارچگی تیکت/تقویم/اسناد/چک/مکاتبات | ۲ (تیکت کسبوکار خارج مگر تعریف شود) |
|
||
| داشبورد لحظهای | ۳ |
|
||
| Softphone داخل اپ | ۵ |
|
||
|
||
---
|
||
|
||
## ۲۱. پیوست B — نمونه جریان State Machine تماس
|
||
|
||
```
|
||
┌──────────┐
|
||
│ (new) │
|
||
└────┬─────┘
|
||
│ call.ringing
|
||
▼
|
||
┌──────────┐
|
||
┌──────│ ringing │──────┐
|
||
│ └────┬─────┘ │
|
||
call.missed/failed │ answered │ busy
|
||
│ ▼ │
|
||
│ ┌──────────┐ │
|
||
│ │ answered │ │
|
||
│ └────┬─────┘ │
|
||
│ │ ended │
|
||
▼ ▼ ▼
|
||
missed completed busy
|
||
```
|
||
|
||
هر انتقال باید `telephony_call_events` بنویسد و در صورت نیاز WS بفرستد.
|
||
|
||
---
|
||
|
||
## ۲۲. پیوست C — معیار «UI عالی» برای پذیرش بصری
|
||
|
||
قبل از انتشار هر فاز دارای UI، این موارد باید پاس شوند:
|
||
|
||
1. Phone Bar در وب/دسکتاپ/موبایل هممعنا و بدون شکستگی RTL
|
||
2. Pop در ۳ اندازه صفحه (≤400، 768، ≥1280) بازبینی شده
|
||
3. حالت ringing بدون چشمکزن آزاردهنده؛ فقط pulse ملایم
|
||
4. پخش صوت کنترل واضح دارد (play/pause/seek/speed اختیاری)
|
||
5. تنظیمات Connector برای کاربر غیرفنی قابل فهم است (۳ گام: نصب → توکن → تأیید آنلاین)
|
||
6. هیچ صفحه تلفن فقط «جدول خام بدون سلسلهمراتب» نباشد
|
||
7. زمان تعامل اپراتور برای ثبت نتیجه پس از تماس < ۲۰ ثانیه
|
||
|
||
---
|
||
|
||
**پایان سند اجرایی v1.0.0**
|
||
|
||
مرحله بعد پس از تأیید این سند: اجرای فاز ۰ و برش تیکتهای اسپرینت ۱ دقیقاً مطابق بخشهای ۱۲ و ۱۳.
|