436 lines
20 KiB
Markdown
436 lines
20 KiB
Markdown
# PRD — ClinicPro Phase 2
|
||
**تاریخ:** ۲۴ خرداد ۱۴۰۵
|
||
**نسخه:** 1.0
|
||
**وضعیت:** Draft
|
||
|
||
---
|
||
|
||
## خلاصه اجرایی
|
||
|
||
این سند نیازمندیهای فاز ۲ پلتفرم **ClinicPro** را پوشش میدهد. هدف: تبدیل پلتفرم از یک سیستم نوبتدهی ساده به یک سیستم مدیریت کلینیک کامل با پنلهای اشتراکی، پرونده بیمار، مدیریت پرسنل و پنل پیامکی.
|
||
|
||
---
|
||
|
||
## وضعیت فعلی پروژه
|
||
|
||
| ماژول | وضعیت |
|
||
|--------|--------|
|
||
| Auth (OTP + JWT) | ✅ کامل |
|
||
| Doctor profile | ✅ کامل |
|
||
| Clinic management | ✅ کامل |
|
||
| Appointment booking | ✅ کامل |
|
||
| Secretary entity (DoctorSecretary) | ✅ موجود — نیاز به UI و permissions بیشتر |
|
||
| Payment gateway (Mellat, SEP) | ✅ کامل — برای نوبت + subscription |
|
||
| SMS (SmsLog, SmsTemplate) | ✅ زیرساخت موجود — نیاز به پنل کلینیک |
|
||
| Wallet / Settlement | ✅ کامل |
|
||
| Site config (SiteConfig key-value) | ✅ موجود |
|
||
| **Staff (پرسنل)** | ❌ موجود نیست |
|
||
| **Panel subscription tiers** | ❌ موجود نیست |
|
||
| **Patient records (پرونده بیمار)** | ❌ موجود نیست |
|
||
| **Clinic services (خدمات)** | ❌ موجود نیست |
|
||
| **SMS panel per clinic** | ❌ UI نیاز دارد |
|
||
|
||
---
|
||
|
||
## اپیکها و قابلیتها
|
||
|
||
---
|
||
|
||
### اپیک ۱ — پرسنل (Staff)
|
||
|
||
#### توضیح
|
||
مدیر کلینیک یا دکتر مستقل میتواند پرسنلی که نیازی به لاگین ندارند را ثبت کند. این پرسنل در سرویسدهی، سشنها و گزارشات استفاده میشوند.
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F1.1** — ایجاد / ویرایش / غیرفعالسازی پرسنل (حذف نرم — soft delete)
|
||
- **F1.2** — فیلدهای پرسنل:
|
||
- نام و نام خانوادگی (اجباری)
|
||
- شماره تماس
|
||
- عنوان شغل (مثال: پرستار، منشی، تکنسین)
|
||
- آدرس
|
||
- کد ملی
|
||
- فعال / غیرفعال
|
||
- **F1.3** — پرسنل به کلینیک یا مطب دکتر وابسته است (FK به clinic_id یا doctor_id)
|
||
- **F1.4** — لیست پرسنل فقط در scope کلینیک/مطب فعال نمایش داده میشود
|
||
- **F1.5** — در ایجاد سرویس، از لیست پرسنل انتخاب میشود
|
||
|
||
#### موجودیتهای جدید مورد نیاز
|
||
```
|
||
ClinicStaff:
|
||
- id, uuid
|
||
- clinic_id (FK → clinics) OR doctor_id (FK → doctors) [یکی از دو]
|
||
- full_name, phone, job_title, address, national_code
|
||
- active (bool, default: true)
|
||
- created_at, updated_at (Unix timestamp)
|
||
```
|
||
|
||
#### API endpoints
|
||
| Method | Path | Permission |
|
||
|--------|------|-----------|
|
||
| `GET` | `/api/v1/staff` | clinic/doctor |
|
||
| `POST` | `/api/v1/staff` | clinic/doctor |
|
||
| `PATCH` | `/api/v1/staff/{uuid}` | clinic/doctor |
|
||
| `PATCH` | `/api/v1/staff/{uuid}/toggle` | clinic/doctor |
|
||
|
||
#### Frontend
|
||
- صفحه: `StaffPage.tsx` — لیست با DataTable + Modal ایجاد/ویرایش
|
||
- در Sidebar زیر بخش «تنظیمات»
|
||
|
||
---
|
||
|
||
### اپیک ۲ — پنل اشتراکی (Subscription Tiers)
|
||
|
||
#### توضیح
|
||
سه سطح پنل وجود دارد. برای هر پنل، ادمین میتواند چند **دوره زمانی** با قیمتهای مختلف تعریف کند (مثلاً ۱ ماهه، ۶ ماهه، ۱۲ ماهه). کلینیک/دکتر دوره و پنل مورد نظر را انتخاب و پرداخت میکند.
|
||
|
||
#### پنلها
|
||
| سطح | نام | قابلیتها |
|
||
|-----|-----|----------|
|
||
| 0 | Free | نوبتدهی + ۱ منشی — رایگان، بدون زمان انقضا |
|
||
| 1 | Basic | ۲ منشی + پرونده بیمار + مدیریت سرویسها |
|
||
| 2 | Professional | ۵ منشی + پرونده بیمار + مدیریت سرویسها |
|
||
|
||
#### دورههای اشتراک (مثال)
|
||
| دوره | مدت (ماه) | قیمت Basic | قیمت Professional |
|
||
|------|-----------|-----------|------------------|
|
||
| ۱ ماهه | 1 | ادمین تعیین میکند | ادمین تعیین میکند |
|
||
| ۳ ماهه | 3 | ادمین تعیین میکند | ادمین تعیین میکند |
|
||
| ۶ ماهه | 6 | ادمین تعیین میکند | ادمین تعیین میکند |
|
||
| ۱۲ ماهه | 12 | ادمین تعیین میکند | ادمین تعیین میکند |
|
||
|
||
> ادمین میتواند دورههای دلخواه اضافه، ویرایش یا غیرفعال کند.
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F2.1** — ادمین برای هر ترکیب (پنل × دوره) یک قیمت تعریف میکند
|
||
- **F2.2** — ادمین میتواند دورههای جدید اضافه کند (مثلاً ۲ ماهه) یا دورهای را غیرفعال کند
|
||
- **F2.3** — کلینیک/دکتر پنل + دوره را انتخاب و از طریق gateway موجود (Mellat/SEP) پرداخت میکند
|
||
- **F2.4** — پس از پرداخت موفق، اشتراک فعال میشود: `starts_at = now`, `expires_at = now + duration_months * 30 * 86400`
|
||
- **F2.5** — تمدید: اگر اشتراک فعال باشد، `starts_at` از تاریخ انقضای قبلی محاسبه میشود (نه از now)
|
||
- **F2.6** — gate: `hasFeature('patient_records')` — بررسی اشتراک فعال و expired نبودن
|
||
- **F2.7** — گزارش فروش پنلها در داشبورد ادمین (تعداد، درآمد، بر اساس دوره و سطح)
|
||
- **F2.8** — نمایش تاریخ انقضا و هشدار ۷ روز قبل به کلینیک/دکتر
|
||
|
||
#### موجودیتهای جدید مورد نیاز
|
||
```
|
||
SubscriptionPlan:
|
||
- id, uuid
|
||
- name: 'free' | 'basic' | 'professional'
|
||
- level: int (0/1/2)
|
||
- max_secretaries: int (1/2/5)
|
||
- features: json { patient_records: bool, services: bool, sms_panel: bool }
|
||
- active: bool
|
||
- created_at, updated_at
|
||
|
||
SubscriptionPeriod:
|
||
- id, uuid
|
||
- plan_id (FK → subscription_plans)
|
||
- label: string (مثال: "۶ ماهه", "سالانه")
|
||
- duration_months: int (1, 3, 6, 12, ...)
|
||
- price_rials: int (قیمت این دوره برای این پنل)
|
||
- active: bool
|
||
- sort_order: int (ترتیب نمایش)
|
||
- created_at, updated_at
|
||
|
||
ClinicSubscription:
|
||
- id, uuid
|
||
- entity_type: 'doctor' | 'clinic'
|
||
- entity_id: int
|
||
- plan_id (FK → subscription_plans)
|
||
- period_id (FK → subscription_periods)
|
||
- payment_id (FK → payments, nullable)
|
||
- starts_at: int (Unix timestamp)
|
||
- expires_at: int (Unix timestamp — null = بینهایت برای Free)
|
||
- created_at
|
||
```
|
||
|
||
#### وابستگی به Payment موجود
|
||
- `Payment.type = 'subscription'` ✅ (از قبل موجود)
|
||
- Callback در `PaymentController::subscriptionCallback()`: ایجاد `ClinicSubscription` پس از تأیید
|
||
|
||
#### API endpoints
|
||
| Method | Path | Permission |
|
||
|--------|------|-----------|
|
||
| `GET` | `/api/v1/subscription/plans` | public — لیست پنلها با دورهها و قیمتها |
|
||
| `GET` | `/api/v1/subscription/my` | clinic/doctor — اشتراک فعال + تاریخ انقضا |
|
||
| `POST` | `/api/v1/subscription-payment` | clinic/doctor — `{ period_uuid }` |
|
||
| `GET` | `/api/v1/admin/subscription/plans` | admin — مدیریت پنلها |
|
||
| `POST` | `/api/v1/admin/subscription/plan` | admin |
|
||
| `PATCH` | `/api/v1/admin/subscription/plan/{uuid}` | admin |
|
||
| `POST` | `/api/v1/admin/subscription/period` | admin — افزودن دوره جدید |
|
||
| `PATCH` | `/api/v1/admin/subscription/period/{uuid}` | admin — ویرایش قیمت/مدت |
|
||
| `DELETE` | `/api/v1/admin/subscription/period/{uuid}` | admin — غیرفعالسازی |
|
||
| `GET` | `/api/v1/admin/subscription/report` | admin — گزارش فروش |
|
||
|
||
#### Frontend
|
||
- صفحه کلینیک/دکتر: `SubscriptionPage.tsx`
|
||
- نمایش پنل فعال + تاریخ انقضا (شمسی)
|
||
- جدول مقایسه پنلها با انتخاب دوره (مثل toggle ماهانه/سالانه)
|
||
- دکمه خرید → redirect به gateway
|
||
- صفحه ادمین: `AdminSubscriptionPage.tsx`
|
||
- تب «پنلها»: ویرایش قیمت دورهها (DataTable با inline edit)
|
||
- تب «گزارش»: فروش بر اساس بازه زمانی، سطح، دوره
|
||
|
||
---
|
||
|
||
### اپیک ۳ — منشی (Secretary) — تکمیل
|
||
|
||
#### توضیح
|
||
جدول `doctor_secretaries` و entity `DoctorSecretary` موجود است. نیاز به:
|
||
- محدودیت تعداد منشی بر اساس پنل
|
||
- صفحه فرانتاند کامل برای مدیریت منشیان و تنظیم دسترسیها
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F3.1** — بررسی سقف تعداد منشی قبل از ایجاد (free→1, basic→2, pro→5)
|
||
- **F3.2** — UI کامل: لیست منشیان + اضافه کردن + ویرایش دسترسی + غیرفعالسازی
|
||
- **F3.3** — پنل دسترسی: checkbox matrix برای هر منشی (permissions JSON موجود)
|
||
- نوبتها: view, create, cancel, update_status
|
||
- آدرسها: view, create, update, delete
|
||
- اطلاعات کلینیک: view, update
|
||
- بیمهها: view, create, update, delete
|
||
- **F3.4** — منشی هرگز حذف نمیشود (soft delete با `active: false`)
|
||
|
||
#### وضعیت فعلی
|
||
```php
|
||
// DoctorSecretary.DEFAULT_PERMISSIONS — موجود
|
||
// API موجود: GET /api/v1/secretaries/{doctorUuid}, POST /api/v1/secretary, PATCH, DELETE
|
||
// صفحه SecretariesPage.tsx موجود — نیاز به بررسی کامل بودن
|
||
```
|
||
|
||
#### تغییرات مورد نیاز
|
||
- Backend: اضافه کردن بررسی سقف پنل در `SecretaryController::create()`
|
||
- Frontend: ویرایشگر گرافیکی permissions (checkbox matrix در Modal)
|
||
|
||
---
|
||
|
||
### اپیک ۴ — سرویسهای کلینیک (Clinic Services)
|
||
|
||
#### توضیح
|
||
دسترسی فقط در پنل Basic و بالاتر. سرویسها در دو سطح تعریف میشوند: بخش و زیر بخش.
|
||
|
||
#### ساختار سرویس
|
||
```
|
||
ServiceSection (بخش — مثال: تزریقات، پانسمان)
|
||
└── ServiceItem (زیر بخش — مثال: سرم)
|
||
- section_id (FK)
|
||
- staff_id (FK → clinic_staff) — پرسنل انجامدهنده
|
||
- price (ریال)
|
||
- active
|
||
```
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F4.1** — CRUD بخشها (ServiceSection)
|
||
- **F4.2** — CRUD زیر بخشها (ServiceItem) با انتخاب بخش و پرسنل
|
||
- **F4.3** — قیمت هر زیر بخش قابل ویرایش
|
||
- **F4.4** — gate: `hasFeature('services')` — بدون Basic+ → 403
|
||
|
||
#### موجودیتهای جدید مورد نیاز
|
||
```
|
||
ServiceSection:
|
||
- id, uuid
|
||
- entity_type: 'doctor' | 'clinic', entity_id
|
||
- name
|
||
- active, created_at, updated_at
|
||
|
||
ServiceItem:
|
||
- id, uuid
|
||
- section_id (FK → service_sections)
|
||
- staff_id (FK → clinic_staff, nullable)
|
||
- name, price_rials
|
||
- active, created_at, updated_at
|
||
```
|
||
|
||
#### API endpoints
|
||
| Method | Path | Permission |
|
||
|--------|------|-----------|
|
||
| `GET` | `/api/v1/service-sections` | clinic/doctor |
|
||
| `POST` | `/api/v1/service-section` | clinic/doctor (basic+) |
|
||
| `PATCH` | `/api/v1/service-section/{uuid}` | clinic/doctor |
|
||
| `DELETE` | `/api/v1/service-section/{uuid}` | clinic/doctor |
|
||
| `GET` | `/api/v1/service-items/{sectionUuid}` | clinic/doctor |
|
||
| `POST` | `/api/v1/service-item` | clinic/doctor (basic+) |
|
||
| `PATCH` | `/api/v1/service-item/{uuid}` | clinic/doctor |
|
||
| `DELETE` | `/api/v1/service-item/{uuid}` | clinic/doctor |
|
||
|
||
#### Frontend
|
||
- صفحه: `ServicesPage.tsx` با دو سطح accordion: بخشها و زیر بخشها
|
||
|
||
---
|
||
|
||
### اپیک ۵ — پنل پیامکی (SMS Panel)
|
||
|
||
#### توضیح
|
||
زیرساخت SMS (SmsLog, SmsTemplate, SmsProvider) موجود است. باید پنل شارژی برای هر کلینیک/دکتر اضافه شود.
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F5.1** — کیف پول پیامک جداگانه از کیف پول مالی (یا همان wallet با type مجزا)
|
||
- **F5.2** — شارژ کردن پنل پیامک از طریق gateway موجود
|
||
- **F5.3** — ادمین تعرفه هر پیامک را در SiteConfig تعیین میکند (`sms_price_rials`)
|
||
- **F5.4** — کسر خودکار هزینه از پنل پس از هر ارسال موفق
|
||
- **F5.5** — تنظیمات ارسال پیامک (در صفحه تنظیمات کلینیک):
|
||
- یادآوری نوبت: فعال/غیرفعال + چند ساعت قبل
|
||
- پیام بعد از ویزیت: فعال/غیرفعال + متن قابل تنظیم
|
||
- **F5.6** — گزارش لاگ پیامکهای هر کلینیک/دکتر
|
||
- **F5.7** — گزارش ادمین: درآمد پیامکی، مصرف هر کلینیک
|
||
|
||
#### موجودیتهای جدید/تغییر یافته
|
||
```
|
||
SmsWallet (یا افزودن فیلد به Doctor/Clinic):
|
||
- entity_type, entity_id
|
||
- balance_rials (موجودی)
|
||
- created_at, updated_at
|
||
|
||
SmsSettings:
|
||
- entity_type, entity_id
|
||
- reminder_enabled (bool)
|
||
- reminder_hours_before (int)
|
||
- post_visit_enabled (bool)
|
||
- post_visit_text (text)
|
||
- updated_at
|
||
```
|
||
|
||
#### API endpoints
|
||
| Method | Path | Permission |
|
||
|--------|------|-----------|
|
||
| `GET` | `/api/v1/sms/wallet/balance` | clinic/doctor |
|
||
| `POST` | `/api/v1/sms/wallet/charge` | clinic/doctor |
|
||
| `GET` | `/api/v1/sms/wallet/logs` | clinic/doctor |
|
||
| `GET` | `/api/v1/sms/settings` | clinic/doctor |
|
||
| `PATCH` | `/api/v1/sms/settings` | clinic/doctor |
|
||
| `GET` | `/api/v1/admin/sms/wallet-report` | admin |
|
||
|
||
#### Frontend
|
||
- تب «پیامک» در صفحه تنظیمات کلینیک/دکتر
|
||
- نمایش موجودی + دکمه شارژ + لاگ
|
||
|
||
---
|
||
|
||
### اپیک ۶ — پرونده بیمار (Patient Records)
|
||
|
||
#### توضیح
|
||
دسترسی در پنل Basic+. پرونده بیمار به صورت خودکار پس از تأیید نوبت ایجاد میشود یا دستی توسط منشی/دکتر.
|
||
|
||
#### ساختار پرونده
|
||
```
|
||
PatientRecord (پرونده بیمار):
|
||
- entity_type, entity_id (کلینیک/مطب که پرونده به آن تعلق دارد)
|
||
- user_id (FK → users — بیمار)
|
||
- created_by (منشی/دکتر که ایجاد کرده)
|
||
- created_at
|
||
|
||
PatientSession (مراجعه/ویزیت):
|
||
- record_id (FK → patient_records)
|
||
- appointment_id (FK → appointments, nullable — اگر از نوبت)
|
||
- insurance_base_id (FK → categories, bundle='insurance_type')
|
||
- insurance_supplementary_id (FK → categories)
|
||
- visit_price_rials (مبلغ کل ویزیت)
|
||
- base_insurance_discount_percent (کسر بیمه پایه)
|
||
- supplementary_discount_percent (کسر بیمه مکمل)
|
||
- final_price_rials (مبلغ نهایی پس از کسورات)
|
||
- payment_method: 'cash' | 'card' | 'insurance' | 'pending'
|
||
- notes (text)
|
||
- created_at, updated_at
|
||
|
||
SessionService (سرویس انجامشده در یک مراجعه):
|
||
- session_id (FK → patient_sessions)
|
||
- service_item_id (FK → service_items)
|
||
- staff_id (FK → clinic_staff — پرسنل انجامدهنده)
|
||
- price_rials (قیمت در زمان ارائه)
|
||
- created_at
|
||
```
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F6.1** — ایجاد خودکار PatientRecord پس از تأیید appointment (اگر وجود ندارد)
|
||
- **F6.2** — ایجاد دستی توسط منشی یا دکتر
|
||
- **F6.3** — ثبت سشن جدید با:
|
||
- انتخاب بیمه پایه و تکمیلی (از categories)
|
||
- تعیین مبلغ ویزیت (از پیش تعیینشده یا قابل ویرایش)
|
||
- محاسبه خودکار مبلغ نهایی
|
||
- ثبت سرویسهای انجام شده (از ServiceItems)
|
||
- تعیین پرسنل انجامدهنده
|
||
- **F6.4** — gate: `hasFeature('patient_records')` — بدون Basic+ → 403
|
||
- **F6.5** — تاریخچه مراجعات یک بیمار قابل نمایش
|
||
|
||
#### API endpoints
|
||
| Method | Path | Permission |
|
||
|--------|------|-----------|
|
||
| `GET` | `/api/v1/patients` | clinic/doctor (basic+) |
|
||
| `POST` | `/api/v1/patient` | clinic/doctor (basic+) |
|
||
| `GET` | `/api/v1/patient/{uuid}` | clinic/doctor |
|
||
| `GET` | `/api/v1/patient/{uuid}/sessions` | clinic/doctor |
|
||
| `POST` | `/api/v1/patient/{uuid}/session` | clinic/doctor |
|
||
| `PATCH` | `/api/v1/session/{uuid}` | clinic/doctor |
|
||
|
||
#### Frontend
|
||
- صفحه `PatientsPage.tsx` — لیست بیماران با جستجو
|
||
- صفحه `PatientDetailPage.tsx` — پرونده کامل + تاریخچه مراجعات
|
||
- Modal ثبت سشن جدید
|
||
|
||
---
|
||
|
||
### اپیک ۷ — داشبورد هوشمند (Smart Dashboard)
|
||
|
||
#### توضیح
|
||
هر داشبورد (ادمین، کلینیک، مطب، منشی) باید چارتهای مناسب با فیلترهای role-based داشته باشد.
|
||
|
||
#### نیازمندیهای کارکردی
|
||
- **F7.1** — داشبورد ادمین:
|
||
- چارت: درآمد پلتفرم (ماهانه، هفتگی)
|
||
- چارت: نوبتهای ثبتشده در بازه زمانی
|
||
- آمار: تعداد کلینیکها، دکترها، کاربران فعال
|
||
- گزارش پنلهای فروختهشده
|
||
- **F7.2** — داشبورد کلینیک/مطب:
|
||
- نوبتهای امروز (موجود)
|
||
- درآمد ماه جاری
|
||
- تعداد بیماران منحصربهفرد
|
||
- موجودی پیامک
|
||
- **F7.3** — فیلتر بازه زمانی در همه چارتها (این هفته / این ماه / ۳ ماه / سفارشی)
|
||
- **F7.4** — فیلتر بر اساس نقش (منشی فقط نوبتها را میبیند)
|
||
|
||
---
|
||
|
||
## اولویتبندی پیادهسازی
|
||
|
||
| اولویت | اپیک | دلیل |
|
||
|--------|------|-------|
|
||
| 1 | اپیک ۱ (Staff) | پیشنیاز اپیک ۴ و ۶ |
|
||
| 2 | اپیک ۲ (Subscription) | پیشنیاز gate checks |
|
||
| 3 | اپیک ۳ (Secretary - تکمیل) | وابسته به پنل |
|
||
| 4 | اپیک ۴ (Services) | پیشنیاز اپیک ۶ |
|
||
| 5 | اپیک ۵ (SMS Panel) | مستقل از بقیه |
|
||
| 6 | اپیک ۶ (Patient Records) | بیشترین وابستگی |
|
||
| 7 | اپیک ۷ (Dashboard) | آخر — نیاز به داده واقعی |
|
||
|
||
---
|
||
|
||
## وابستگیهای فنی
|
||
|
||
### وضعیت زیرساخت موجود و قابل استفاده
|
||
|
||
| زیرساخت | فایل | نحوه استفاده در فاز ۲ |
|
||
|---------|------|----------------------|
|
||
| Payment gateway | `src/Payment/` | پرداخت پنل اشتراکی و شارژ SMS |
|
||
| Wallet | `src/Settlement/Entity/WalletTransaction.php` | بررسی مدل برای SMS wallet |
|
||
| SiteConfig | `src/Config/Entity/SiteConfig.php` | ذخیره قیمت پنلها + تعرفه SMS |
|
||
| Categories (insurance) | `bundle='insurance_type'` | انتخاب بیمه در سشن |
|
||
| DoctorSecretary | `src/Secretary/Entity/DoctorSecretary.php` | افزودن بررسی سقف پنل |
|
||
| SmsLog/SmsTemplate | `src/Sms/Entity/` | زیرساخت ارسال موجود |
|
||
|
||
### نکات معماری
|
||
|
||
- همه entityهای جدید از الگوی Unix timestamp استفاده میکنند
|
||
- همه controllerهای جدید از `BaseController` ارث میبرند
|
||
- gate check برای features با یک helper در `BaseController` یا `SubscriptionService` پیادهسازی شود
|
||
- `entity_type` + `entity_id` pattern (polymorphic) برای Staff، Services، SmsSettings، Patient Records
|
||
|
||
---
|
||
|
||
## معیارهای پذیرش (Definition of Done)
|
||
|
||
برای هر اپیک:
|
||
- [ ] Entity + Migration ایجاد شده
|
||
- [ ] Controller + Repository + Service نوشته شده
|
||
- [ ] API endpoints تستپذیر در Swagger (`/api/doc`)
|
||
- [ ] Frontend page کامل با CRUD
|
||
- [ ] مستندات `docs/api/` بهروزرسانی شده
|
||
- [ ] بررسی gate/permission برای plan-gated features
|