Files
clinicpro/.claude/prompt/create-phase2-tasks.md
T

576 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ساخت تسک‌های فاز ۲ — ClinicPro
## زمینه
PRD فاز ۲ در `docs/PRD/prd.md` تعریف شده و شامل ۷ اپیک است. باید برای هر اپیک یک پوشه تسک در `docs/tasks/` با فایل‌های `task.md`، `architecture.md`، `database.md` و `user_flow.md` ایجاد شود — دقیقاً مطابق ساختار `docs/tasks/task-09-appointment-settings/`.
## وضعیت فعلی
### ساختار نمونه (task-09):
```
docs/tasks/task-09-appointment-settings/
task.md ← endpoints، نمونه request/response، پیش‌نیازها، زمان تخمینی
architecture.md ← ساختار فایل‌های src/، نمونه entity با Doctrine annotations
database.md ← جداول، ستون‌ها با نوع دقیق، ایندکس‌ها، ساختار JSON ها
user_flow.md ← جریان کاربری با pseudocode و diagram ASCII
```
### مسیر نهایی تسک‌های جدید:
```
docs/phase2_taskes/task-10-staff/
docs/phase2_taskes/task-11-subscription/
docs/phase2_taskes/task-12-secretary-completion/
docs/phase2_taskes/task-13-clinic-services/
docs/phase2_taskes/task-14-sms-panel/
docs/phase2_taskes/task-15-patient-records/
docs/phase2_taskes/task-16-smart-dashboard/
```
### API های موجود (نیازی به پیاده‌سازی ندارند):
- `/api/v1/secretary*` — SecretaryController موجود است
- `/api/v1/subscription-payment` و `/api/v1/subscription-payment/callback/{gateway}` — موجود است
- `/api/v1/sms/send`, `/api/v1/sms/template*` — موجود است
- `/api/v1/wallet/*` — WalletTransaction موجود است (برای SMS wallet الگو می‌گیریم)
### Entities موجود مرتبط:
- `src/Secretary/Entity/DoctorSecretary.php` — DEFAULT_PERMISSIONS ساختار permissions
- `src/Settlement/Entity/WalletTransaction.php` — الگوی wallet برای SmsWallet
- `src/Payment/Entity/Payment.php``TYPE_SUBSCRIPTION` const موجود است
- `src/Sms/Entity/SmsLog.php`, `SmsTemplate.php` — زیرساخت SMS موجود
## قوانین کلی ساختار فایل
### قوانین entity در architecture.md:
```php
// همه entity های جدید از این الگو پیروی می‌کنند:
#[ORM\Entity]
#[ORM\Table(name: 'table_name')]
class EntityName
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 36, unique: true)]
private string $uuid;
// polymorphic pattern:
#[ORM\Column(type: 'string', length: 10)]
private string $entityType; // 'doctor' | 'clinic'
#[ORM\Column(type: 'integer')]
private int $entityId;
#[ORM\Column(type: 'integer')]
private int $createdAt;
#[ORM\Column(type: 'integer')]
private int $updatedAt;
}
```
### قوانین controller:
```php
// همه controller ها از BaseController ارث می‌برند
class XxxController extends BaseController
{
// پاسخ موفق: $this->success($data)
// لیست paginated: $this->paginated($items, $total, $page, $limit)
// خطا: $this->error(ErrorCodes::ERR_XXX, 'پیام', Response::HTTP_XXX)
// خطای validation: $this->validationError($violations)
}
```
### قوانین database.md:
- تاریخ‌ها همه `INT NOT NULL` (Unix timestamp) هستند — هیچ‌گاه DATETIME نه
- `uuid` همیشه `CHAR(36) UNIQUE NOT NULL`
- `entity_type` همیشه `VARCHAR(10)` با مقادیر `'doctor'` یا `'clinic'`
- `entity_id` همیشه `INT NOT NULL` (FK به doctors.id یا clinics.id بسته به entity_type)
---
## وظایف
### ۱. ساخت `docs/tasks/task-10-staff/`
**پیش‌نیاز اپیک ۴ و ۶ — باید اول پیاده‌سازی شود.**
#### `task.md`:
- عنوان: «تسک ۱۰: مدیریت پرسنل (Staff)»
- پیش‌نیازها: تسک ۰۲ (Auth)، تسک ۰۵ (Doctor)، تسک ۰۶ (Clinic)
- زمان تخمینی: ۶ تا ۸ ساعت
- Endpoints:
| متد | مسیر | توضیح | نیاز به Auth |
|-----|------|-------|-------------|
| GET | `/api/v1/staff` | لیست پرسنل context جاری | بله (Doctor/Clinic) |
| POST | `/api/v1/staff` | ایجاد پرسنل جدید | بله |
| PATCH | `/api/v1/staff/{uuid}` | ویرایش اطلاعات | بله (مالک) |
| PATCH | `/api/v1/staff/{uuid}/toggle` | فعال/غیرفعال | بله (مالک) |
نمونه Request و Response واقعی با json کامل.
#### `architecture.md`:
```
src/Staff/
├── Controller/StaffController.php
├── Entity/ClinicStaff.php
├── Repository/ClinicStaffRepository.php
└── Service/StaffService.php
```
Entity `ClinicStaff` با تمام فیلدها با Doctrine annotations دقیق.
#### `database.md`:
جدول `clinic_staff`:
- id INT UNSIGNED AUTO_INCREMENT PK
- uuid CHAR(36) UNIQUE NOT NULL
- entity_type VARCHAR(10) NOT NULL (`'doctor'` | `'clinic'`)
- entity_id INT NOT NULL
- full_name VARCHAR(200) NOT NULL
- phone VARCHAR(20) NULL
- job_title VARCHAR(100) NULL
- address TEXT NULL
- national_code CHAR(10) NULL
- active TINYINT(1) NOT NULL DEFAULT 1
- created_at INT NOT NULL
- updated_at INT NOT NULL
ایندکس: `idx_clinic_staff_entity ON clinic_staff(entity_type, entity_id, active)`
#### `user_flow.md`:
جریان ایجاد پرسنل + جریان غیرفعال‌سازی (چرا حذف ممنوع است: تاریخچه سرویس‌ها حفظ می‌شود).
---
### ۲. ساخت `docs/tasks/task-11-subscription/`
**پیش‌نیاز همه gate check ها — باید دوم پیاده‌سازی شود.**
#### `task.md`:
- عنوان: «تسک ۱۱: پنل اشتراکی (Subscription Tiers)»
- پیش‌نیازها: تسک ۰۲، تسک ۱۵ (Payment موجود)
- زمان تخمینی: ۱۴ تا ۱۶ ساعت
- Endpoints (جدید — هیچ‌کدام موجود نیستند به جز `/api/v1/subscription-payment` که payment callback را handle می‌کند):
| متد | مسیر | Permission | توضیح |
|-----|------|-----------|-------|
| GET | `/api/v1/subscription/plans` | public | لیست پنل‌ها + دوره‌ها + قیمت‌ها |
| GET | `/api/v1/subscription/my` | doctor/clinic | اشتراک فعال + `used_trial` flag |
| POST | `/api/v1/subscription/trial` | doctor/clinic | فعال‌سازی تریال Basic (یک‌بار) |
| GET | `/api/v1/admin/subscription/plans` | ROLE_ADMIN | لیست پنل‌ها |
| POST | `/api/v1/admin/subscription/plan` | ROLE_ADMIN | ایجاد پنل |
| PATCH | `/api/v1/admin/subscription/plan/{uuid}` | ROLE_ADMIN | ویرایش پنل |
| POST | `/api/v1/admin/subscription/period` | ROLE_ADMIN | افزودن دوره |
| PATCH | `/api/v1/admin/subscription/period/{uuid}` | ROLE_ADMIN | ویرایش دوره |
| DELETE | `/api/v1/admin/subscription/period/{uuid}` | ROLE_ADMIN | غیرفعال‌سازی |
| GET | `/api/v1/admin/subscription/report` | ROLE_ADMIN | گزارش فروش |
نکته: `/api/v1/subscription-payment` قبلاً در `PaymentController` موجود است — callback آن هم موجود است. فقط باید بعد از callback موفق، `ClinicSubscription` ساخته شود (این وابستگی باید توضیح داده شود).
نمونه Response برای `GET /api/v1/subscription/my`:
```json
{
"success": true,
"data": {
"plan": { "name": "basic", "level": 1, "features": {"patient_records": true, "services": true} },
"period": { "label": "۶ ماهه", "duration_months": 6 },
"is_trial": false,
"starts_at": 1718000000,
"expires_at": 1733360000,
"used_trial": true
}
}
```
#### `architecture.md`:
```
src/Subscription/
├── Controller/SubscriptionController.php
├── Entity/
│ ├── SubscriptionPlan.php
│ ├── SubscriptionPeriod.php
│ └── ClinicSubscription.php
├── Repository/
│ ├── SubscriptionPlanRepository.php
│ ├── SubscriptionPeriodRepository.php
│ └── ClinicSubscriptionRepository.php
└── Service/
└── SubscriptionService.php ← hasFeature() gate method
```
`SubscriptionService::hasFeature(string $feature): bool`:
```php
public function hasFeature(string $feature): bool
{
// 1. entity_type + entity_id از authStore JWT claim
// 2. آخرین ClinicSubscription فعال: expires_at > time() OR expires_at IS NULL
// 3. plan->features[$feature] === true
// برگشتی false → caller باید $this->error(ErrorCodes::ERR_SUBSCRIPTION_REQUIRED, ..., 403) بزند
}
```
Entity‌ها با Doctrine annotations دقیق (نوع `json` برای features در SubscriptionPlan).
#### `database.md`:
سه جدول کامل:
**subscription_plans:**
- id, uuid, name VARCHAR(30) ('free'|'basic'|'professional'), level TINYINT, max_secretaries TINYINT
- features JSON (e.g. `{"patient_records": true, "services": true, "sms_panel": false}`)
- active TINYINT(1), created_at INT, updated_at INT
**subscription_periods:**
- id, uuid, plan_id INT FK→subscription_plans.id
- label VARCHAR(50), duration_months TINYINT, price_rials INT
- is_trial TINYINT(1) DEFAULT 0, active TINYINT(1), sort_order TINYINT
- created_at INT, updated_at INT
**clinic_subscriptions:**
- id, uuid, entity_type VARCHAR(10), entity_id INT
- plan_id INT FK→subscription_plans.id, period_id INT FK→subscription_periods.id
- payment_id INT NULL FK→payments.id (NULL برای تریال)
- is_trial TINYINT(1), starts_at INT, expires_at INT NULL (NULL = Free بی‌نهایت)
- created_at INT
ایندکس‌ها:
```sql
INDEX idx_clinic_subscriptions_entity ON clinic_subscriptions(entity_type, entity_id, expires_at);
UNIQUE idx_subscription_trial_once ON clinic_subscriptions(entity_type, entity_id, is_trial) -- جلوگیری از تریال مکرر
```
**Seed data اولیه** (باید در migration یا fixture باشد):
```sql
INSERT INTO subscription_plans (uuid, name, level, max_secretaries, features, active) VALUES
(UUID(), 'free', 0, 1, '{"patient_records":false,"services":false,"sms_panel":false}', 1),
(UUID(), 'basic', 1, 2, '{"patient_records":true,"services":true,"sms_panel":false}', 1),
(UUID(), 'professional', 2, 5, '{"patient_records":true,"services":true,"sms_panel":true}', 1);
```
#### `user_flow.md`:
- جریان خرید اشتراک (انتخاب پنل + دوره → payment → callback → ClinicSubscription)
- جریان تریال (check used_trial → POST /trial → ClinicSubscription بدون payment_id)
- جریان تمدید (expires_at قبلی + duration_months × 30 × 86400)
- gate check flow: هر endpoint protected → `hasFeature()` → 403 اگر false
---
### ۳. ساخت `docs/tasks/task-12-secretary-completion/`
#### `task.md`:
- عنوان: «تسک ۱۲: تکمیل منشی — محدودیت پنل + UI Permissions»
- پیش‌نیازها: تسک ۱۴ (Secretary — موجود)، **تسک ۱۱** (Subscription)
- زمان تخمینی: ۴ تا ۵ ساعت
- **هیچ endpoint جدیدی نیست** — فقط تغییر در کد موجود:
- Backend: `src/Secretary/Controller/SecretaryController.php::create()` — اضافه کردن gate check
- Frontend: `assets/admin/pages/SecretariesPage.tsx` — Modal ویرایش permissions
نمونه کد Backend تغییر (قبل/بعد):
```php
// قبل:
public function create(Request $request): JsonResponse
{
// validate و ذخیره مستقیم
// بعد — باید اضافه شود:
public function create(Request $request): JsonResponse
{
$limit = $this->subscriptionService->getSecretaryLimit(); // 1 یا 2 یا 5
$current = $this->secretaryRepo->countActive($doctorId);
if ($current >= $limit) {
return $this->error(ErrorCodes::ERR_SECRETARY_LIMIT_REACHED, 'سقف منشی پنل رسیده است', 403);
}
```
نمونه کد Frontend permissions checkbox:
```tsx
// در SecretariesPage.tsx — Modal ویرایش
// DoctorSecretary::DEFAULT_PERMISSIONS ساختار را نشان می‌دهد:
// resources: { appointments: { view, create, cancel, update_status }, addresses: {...}, ... }
// باید یک checkbox matrix رندر شود
```
#### `architecture.md`:
فایل‌هایی که تغییر می‌کنند (نه فایل جدید):
- `src/Secretary/Controller/SecretaryController.php` — inject `SubscriptionService`
- `assets/admin/pages/SecretariesPage.tsx` — Modal با checkbox matrix
#### `database.md`:
- هیچ migration لازم نیست
- توضیح ستون `permissions` در جدول `doctor_secretaries` (JSON)
#### `user_flow.md`:
- جریان تلاش برای افزودن منشی جدید وقتی سقف پنل رسیده
- جریان ویرایش permissions منشی موجود
---
### ۴. ساخت `docs/tasks/task-13-clinic-services/`
#### `task.md`:
- عنوان: «تسک ۱۳: سرویس‌های کلینیک (Clinic Services)»
- پیش‌نیازها: **تسک ۱۰** (Staff)، **تسک ۱۱** (Subscription — gate check)
- زمان تخمینی: ۸ تا ۱۰ ساعت
- gate: همه write endpoints نیاز به `hasFeature('services')` دارند (Basic+ فقط)
- Endpoints:
| متد | مسیر | توضیح |
|-----|------|-------|
| GET | `/api/v1/service-sections` | لیست بخش‌ها |
| POST | `/api/v1/service-section` | ایجاد بخش (Basic+) |
| PATCH | `/api/v1/service-section/{uuid}` | ویرایش |
| DELETE | `/api/v1/service-section/{uuid}` | حذف |
| GET | `/api/v1/service-items/{sectionUuid}` | لیست زیربخش‌های یک بخش |
| POST | `/api/v1/service-item` | ایجاد زیربخش (Basic+) |
| PATCH | `/api/v1/service-item/{uuid}` | ویرایش |
| DELETE | `/api/v1/service-item/{uuid}` | حذف |
#### `architecture.md`:
```
src/ClinicService/
├── Controller/ClinicServiceController.php
├── Entity/
│ ├── ServiceSection.php
│ └── ServiceItem.php
└── Repository/
├── ServiceSectionRepository.php
└── ServiceItemRepository.php
```
#### `database.md`:
**service_sections:**
- id, uuid, entity_type VARCHAR(10), entity_id INT, name VARCHAR(200), active TINYINT(1) DEFAULT 1
- created_at INT, updated_at INT
- INDEX: `idx_service_sections_entity ON service_sections(entity_type, entity_id)`
**service_items:**
- id, uuid, section_id INT FK→service_sections.id ON DELETE CASCADE
- staff_id INT NULL FK→clinic_staff.id ON DELETE SET NULL
- name VARCHAR(200), price_rials INT NOT NULL DEFAULT 0, active TINYINT(1) DEFAULT 1
- created_at INT, updated_at INT
#### `user_flow.md`:
- جریان ایجاد ساختار دو سطحی (بخش → زیربخش)
- جریان انتخاب پرسنل انجام‌دهنده
- gate check — کاربر Free تلاش می‌کند سرویس بسازد → 403
---
### ۵. ساخت `docs/tasks/task-14-sms-panel/`
#### `task.md`:
- عنوان: «تسک ۱۴: پنل پیامکی — کیف پول + تنظیمات»
- پیش‌نیازها: تسک ۱۷ (SMS infrastructure موجود)، تسک ۱۵ (Payment موجود)
- زمان تخمینی: ۱۰ تا ۱۲ ساعت
- نکته: `src/Sms/` و `src/Settlement/` موجودند — کد جدید در `src/Sms/` اضافه می‌شود
- Endpoints جدید:
| متد | مسیر | Permission | توضیح |
|-----|------|-----------|-------|
| GET | `/api/v1/sms/wallet/balance` | doctor/clinic | موجودی |
| POST | `/api/v1/sms/wallet/charge` | doctor/clinic | شارژ — `{ gateway, amount_rials }` |
| GET | `/api/v1/sms/wallet/logs` | doctor/clinic | تاریخچه کسر/شارژ |
| GET | `/api/v1/sms/settings` | doctor/clinic | تنظیمات |
| PATCH | `/api/v1/sms/settings` | doctor/clinic | ذخیره تنظیمات |
| GET | `/api/v1/admin/sms/wallet-report` | ROLE_ADMIN | گزارش مصرف |
#### `architecture.md`:
Entity های جدید در `src/Sms/Entity/`:
```php
// SmsWallet.php — الگو از WalletTransaction.php در src/Settlement/Entity/
// SmsSettings.php — key-value per entity
```
Controller جدید: `src/Sms/Controller/SmsWalletController.php`
شارژ کیف پول: redirect به gateway موجود با `Payment.type = 'sms_wallet'` (const جدید در Payment entity)
#### `database.md`:
**sms_wallets:**
- id, entity_type VARCHAR(10), entity_id INT (UNIQUE PAIR)
- balance_rials INT NOT NULL DEFAULT 0, created_at INT, updated_at INT
- UNIQUE INDEX: `idx_sms_wallets_entity ON sms_wallets(entity_type, entity_id)`
**sms_wallet_transactions:**
- id, uuid, sms_wallet_id INT FK→sms_wallets.id
- type VARCHAR(10) ('credit'|'debit'), amount_rials INT, description VARCHAR(255) NULL
- payment_id INT NULL FK→payments.id, created_at INT
**sms_settings:**
- id, entity_type VARCHAR(10), entity_id INT (UNIQUE PAIR)
- reminder_enabled TINYINT(1) DEFAULT 0, reminder_hours_before TINYINT DEFAULT 2
- post_visit_enabled TINYINT(1) DEFAULT 0, post_visit_text TEXT NULL, updated_at INT
- UNIQUE INDEX: `idx_sms_settings_entity ON sms_settings(entity_type, entity_id)`
SiteConfig key جدید: `sms_price_rials` (ادمین مقدار می‌دهد)
#### `user_flow.md`:
- جریان شارژ کیف پول (مثل payment اما `type='sms_wallet'`)
- جریان کسر خودکار هنگام ارسال پیامک (hook در SmsService)
- جریان تنظیم یادآوری خودکار
---
### ۶. ساخت `docs/tasks/task-15-patient-records/`
#### `task.md`:
- عنوان: «تسک ۱۵: پرونده بیمار (Patient Records)»
- پیش‌نیازها: **تسک ۱۰** (Staff)، **تسک ۱۱** (Subscription)، **تسک ۱۳** (Services)
- زمان تخمینی: ۱۶ تا ۲۰ ساعت
- gate: همه endpoints نیاز به `hasFeature('patient_records')` (Basic+ فقط)
- Endpoints:
| متد | مسیر | توضیح |
|-----|------|-------|
| GET | `/api/v1/patients` | لیست بیماران — query: `?search=` |
| POST | `/api/v1/patient` | ایجاد پرونده دستی |
| GET | `/api/v1/patient/{uuid}` | جزئیات پرونده + خلاصه |
| GET | `/api/v1/patient/{uuid}/sessions` | لیست مراجعات |
| POST | `/api/v1/patient/{uuid}/session` | ثبت مراجعه جدید |
| PATCH | `/api/v1/session/{uuid}` | ویرایش مراجعه |
نمونه Request ثبت سشن:
```json
{
"appointment_uuid": "optional-uuid",
"insurance_base_uuid": "category-uuid",
"insurance_supplementary_uuid": null,
"visit_price_rials": 500000,
"base_insurance_discount_percent": 30,
"supplementary_discount_percent": 0,
"payment_method": "cash",
"notes": "...",
"services": [
{ "service_item_uuid": "...", "staff_uuid": "..." }
]
}
```
نمونه Response (final_price محاسبه‌شده):
```json
{
"success": true,
"data": {
"uuid": "...",
"visit_price_rials": 500000,
"base_insurance_discount_percent": 30,
"supplementary_discount_percent": 0,
"services_total_rials": 150000,
"final_price_rials": 500000,
"payment_method": "cash"
}
}
```
فرمول محاسبه:
```
after_base = visit_price × (1 - base_discount/100)
after_supp = after_base × (1 - supp_discount/100)
final = round(after_supp) + sum(service_item.price_rials)
```
#### `architecture.md`:
```
src/Patient/
├── Controller/PatientController.php
├── Entity/
│ ├── PatientRecord.php
│ ├── PatientSession.php
│ └── SessionService.php
├── Repository/
│ ├── PatientRecordRepository.php
│ └── PatientSessionRepository.php
└── Service/
└── PatientService.php ← calculateFinalPrice(), autoCreateOnAppointmentConfirm()
```
نکته در architecture.md: `autoCreateOnAppointmentConfirm()` باید از `AppointmentController::updateStatus()` صدا شود — وقتی status به `'confirmed'` تغییر می‌کند، PatientRecord چک/ایجاد می‌شود.
#### `database.md`:
**patient_records:**
- id, uuid, entity_type VARCHAR(10), entity_id INT
- user_id INT FK→users.id ON DELETE RESTRICT
- created_by_type VARCHAR(15) ('doctor'|'secretary'|'system'), created_by_id INT
- created_at INT
- UNIQUE: `idx_patient_record_unique ON patient_records(entity_type, entity_id, user_id)`
**patient_sessions:**
- id, uuid, record_id INT FK→patient_records.id ON DELETE CASCADE
- appointment_id INT NULL FK→appointments.id ON DELETE SET NULL
- insurance_base_id INT NULL FK→categories.id, insurance_supplementary_id INT NULL FK→categories.id
- visit_price_rials INT NOT NULL DEFAULT 0
- base_insurance_discount_percent DECIMAL(5,2) DEFAULT 0
- supplementary_discount_percent DECIMAL(5,2) DEFAULT 0
- services_total_rials INT NOT NULL DEFAULT 0
- final_price_rials INT NOT NULL
- payment_method VARCHAR(15) ('cash'|'card'|'insurance'|'pending')
- notes TEXT NULL, created_at INT, updated_at INT
**session_services:**
- id, uuid, session_id INT FK→patient_sessions.id ON DELETE CASCADE
- service_item_id INT FK→service_items.id ON DELETE RESTRICT
- staff_id INT NULL FK→clinic_staff.id ON DELETE SET NULL
- price_rials INT NOT NULL ← کپی از service_item.price_rials در زمان ثبت
- created_at INT
ایندکس‌ها:
```sql
INDEX idx_patient_records_entity ON patient_records(entity_type, entity_id);
INDEX idx_patient_sessions_record ON patient_sessions(record_id, created_at);
```
#### `user_flow.md`:
- جریان خودکار: تأیید نوبت → چک PatientRecord → ایجاد اگر نبود → ایجاد PatientSession
- جریان دستی: منشی/دکتر → جستجو بیمار → ایجاد پرونده → ثبت مراجعه + سرویس‌ها
- محاسبه final_price با step by step مثال عددی
- جریان جستجو بیمار (search در patients API)
---
### ۷. ساخت `docs/tasks/task-16-smart-dashboard/`
#### `task.md`:
- عنوان: «تسک ۱۶: داشبورد هوشمند — چارت + فیلتر زمانی»
- پیش‌نیازها: همه تسک‌های قبل — نیاز به داده واقعی دارد
- زمان تخمینی: ۸ تا ۱۰ ساعت
- **هیچ endpoint جدیدی نیست** — فقط پارامتر `from` و `to` به endpoint های موجود اضافه می‌شود:
- `GET /api/v1/admin/dashboard/charts?from=UNIX&to=UNIX`
- `GET /api/v1/dashboard/clinic?from=UNIX&to=UNIX`
- `GET /api/v1/dashboard/doctor?from=UNIX&to=UNIX`
فیلدهای جدید در response داشبورد کلینیک/دکتر:
```json
{
"sms_wallet_balance": 150000,
"unique_patients_count": 45,
"revenue_current_month": 12500000
}
```
#### `architecture.md`:
فایل‌هایی که تغییر می‌کنند:
- `src/Dashboard/Controller/DashboardController.php` — اضافه کردن `from`/`to` به query
- `assets/admin/pages/DashboardPage.tsx` — date range selector + نمودار (Recharts)
نکته: Recharts پیشنهادی است (سبک، tree-shakeable). نصب با `yarn add recharts`.
#### `database.md`:
- هیچ migration لازم نیست
- query های DQL که باید به `from`/`to` محدود شوند
#### `user_flow.md`:
- کاربر date range انتخاب می‌کند → charts refresh می‌شوند
- preset ها: «این هفته» / «این ماه» / «۳ ماه» / «سفارشی»
- تبدیل تاریخ شمسی به Unix timestamp برای ارسال به API
---
## نکات مهم
- **ترتیب پیاده‌سازی اجباری است**: task-10 → task-11 → task-12 → task-13 → task-14 → task-15 → task-16
- **حذف سخت ممنوع** در ClinicStaff — فقط `active=false`
- **UNIQUE constraint در تریال**: یک entity نمی‌تواند دو بار از تریال استفاده کند — index روی `(entity_type, entity_id, is_trial)` در `clinic_subscriptions`
- **price_rials در session_services کپی می‌شود** — تغییر قیمت سرویس در آینده تأثیری روی سشن‌های قبلی ندارد
- **patient_records UNIQUE per entity+user** — یک بیمار یک پرونده در هر کلینیک/مطب دارد (مراجعات چندگانه = سشن‌های جداگانه در همان پرونده)
- **فایل doc** برای هر اپیک بعد از پیاده‌سازی باید ایجاد شود (`docs/api/staff.md`، `docs/api/subscription.md`، `docs/api/clinic-services.md`، `docs/api/patient.md`)