Implement SMS panel user flow and patient records system; add wallet charging, automatic reminders, and patient session management with detailed database schema and user flows.

This commit is contained in:
hamed
2026-06-14 21:15:40 +03:30
parent a72a6da621
commit b58aacc37f
28 changed files with 3169 additions and 0 deletions
@@ -0,0 +1,71 @@
# معماری — تسک ۱۲: تکمیل منشی
## فایل‌های موجود که تغییر می‌کنند
```
src/Secretary/Controller/SecretaryController.php ← inject SubscriptionService + limit check
src/Shared/Constant/ErrorCodes.php ← ثابت جدید ERR_SECRETARY_LIMIT_REACHED
assets/admin/pages/SecretariesPage.tsx ← Modal permissions checkbox matrix
```
## تغییرات Backend
### SecretaryController — constructor injection
```php
public function __construct(
private readonly EntityManagerInterface $em,
private readonly DoctorSecretaryRepository $secretaryRepo,
private readonly DoctorRepository $doctorRepo,
private readonly SubscriptionService $subscriptionService, // ← جدید
) {}
```
### SecretaryRepository — متد جدید countActive
```php
// src/Secretary/Repository/DoctorSecretaryRepository.php
public function countActive(int $doctorId): int
{
return (int) $this->createQueryBuilder('s')
->select('COUNT(s.id)')
->where('s.doctor = :doctor')
->andWhere('s.active = true')
->setParameter('doctor', $doctorId)
->getQuery()
->getSingleScalarResult();
}
```
## تغییرات Frontend
### SecretariesPage.tsx — ساختار Modal permissions
```tsx
// types جدید
interface PermissionsMatrix {
version: number;
resources: {
appointments?: { view: boolean; create: boolean; cancel: boolean; update_status: boolean };
addresses?: { view: boolean; create: boolean; update: boolean; delete: boolean };
clinic_info?: { view: boolean; update: boolean };
insurances?: { view: boolean; create: boolean; update: boolean; delete: boolean };
};
}
// component
function PermissionsEditor({ value, onChange }: {
value: PermissionsMatrix;
onChange: (v: PermissionsMatrix) => void;
}) {
// رندر جدول checkbox
// هر ردیف: نام بخش + چک‌باکس‌های action
// onChange ساختار را update می‌کند
}
```
### نکته: مدیریت خطای 403 در فرم ایجاد منشی
```tsx
// وقتی POST /api/v1/secretary با 403 برمی‌گردد:
// نمایش toast: "سقف منشی پنل رسیده است. برای افزایش به صفحه اشتراک بروید"
// دکمه "ارتقاء پنل" → navigate('/admin/subscription')
```
@@ -0,0 +1,59 @@
# پایگاه داده — تسک ۱۲: تکمیل منشی
## هیچ migration لازم نیست
جدول `doctor_secretaries` کامل است. فقط باید مطمئن شویم ستون `permissions` درست ذخیره می‌شود.
## ستون موجود: permissions در doctor_secretaries
| ستون | نوع | مقدار پیش‌فرض |
|------|-----|--------------|
| permissions | JSON | `DEFAULT_PERMISSIONS` از DoctorSecretary entity |
### ساختار JSON ذخیره‌شده:
```json
{
"version": 1,
"resources": {
"appointments": {
"view": true,
"create": true,
"cancel": false,
"update_status": true
},
"addresses": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"clinic_info": {
"view": true,
"update": false
},
"insurances": {
"view": true,
"create": false,
"update": false,
"delete": false
}
}
}
```
### نکته `version`:
- `version: 1` — اگر در آینده ساختار permissions تغییر کرد، migration به version بعدی انجام می‌شود
- همیشه هنگام خواندن permissions، `version` بررسی شود و اگر قدیمی بود با DEFAULT_PERMISSIONS merge شود
## جدول doctor_secretaries (مرجع)
| ستون | نوع | توضیح |
|------|-----|-------|
| id | INT PK | |
| uuid | CHAR(36) UNIQUE | |
| doctor_id | INT FK→doctors.id | |
| secretary_id | INT FK→users.id | |
| permissions | JSON | ساختار بالا |
| active | TINYINT(1) DEFAULT 1 | |
| created_at | INT | |
| updated_at | INT | |
@@ -0,0 +1,134 @@
# تسک ۱۲: تکمیل منشی — محدودیت پنل + UI Permissions
## توضیح
Entity `DoctorSecretary` و API کامل موجود است (`src/Secretary/`). این تسک فقط دو چیز اضافه می‌کند:
1. Backend: بررسی سقف تعداد منشی بر اساس پنل اشتراکی
2. Frontend: Modal با checkbox matrix برای ویرایش permissions
**هیچ endpoint جدیدی ایجاد نمی‌شود.**
## فایل‌های موجود که تغییر می‌کنند
| فایل | تغییر |
|------|-------|
| `src/Secretary/Controller/SecretaryController.php` | inject `SubscriptionService`، اضافه کردن limit check در `create()` |
| `assets/admin/pages/SecretariesPage.tsx` | Modal ویرایش با checkbox matrix برای permissions |
## API موجود (بدون تغییر)
| متد | مسیر | توضیح |
|-----|------|-------|
| GET | `/api/v1/secretaries/{doctorUuid}` | لیست منشیان |
| POST | `/api/v1/secretary` | ایجاد منشی ← **اینجا limit check اضافه می‌شود** |
| PATCH | `/api/v1/secretary/{uuid}` | ویرایش ← **permissions هم قابل ویرایش می‌شود** |
| DELETE | `/api/v1/secretary/{uuid}` | غیرفعال‌سازی (soft delete) |
## پیش‌نیازها
- تسک ۱۴ (Secretary — موجود در کد)
- **تسک ۱۱** (Subscription — `SubscriptionService` باید موجود باشد)
## زمان تخمینی
۴ تا ۵ ساعت
## تغییر Backend — SecretaryController::create()
### وضعیت فعلی (قبل):
```php
public function create(Request $request): JsonResponse
{
// validate fields
// create DoctorSecretary
// persist
return $this->success($secretary->toArray());
}
```
### وضعیت جدید (بعد):
```php
public function create(Request $request): JsonResponse
{
// 1. پیدا کردن دکتر از doctorUuid در body
$doctor = $this->doctorRepo->findByUuid($request->get('doctor_uuid'));
// 2. بررسی سقف پنل
$entityType = 'doctor';
$entityId = $doctor->getId();
$limit = $this->subscriptionService->getSecretaryLimit($entityType, $entityId);
$current = $this->secretaryRepo->countActive($doctor->getId());
if ($current >= $limit) {
return $this->error(
ErrorCodes::ERR_SECRETARY_LIMIT_REACHED,
'سقف تعداد منشی پنل اشتراکی شما رسیده است',
Response::HTTP_FORBIDDEN
);
}
// 3. ادامه ایجاد منشی ...
}
```
### خطای جدید که باید در ErrorCodes.php اضافه شود:
```php
// src/Shared/Constant/ErrorCodes.php
const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
// messages: 'سقف تعداد منشی پنل اشتراکی شما رسیده است'
```
## تغییر Frontend — SecretariesPage.tsx
### ساختار DEFAULT_PERMISSIONS (از DoctorSecretary::DEFAULT_PERMISSIONS):
```php
[
'version' => 1,
'resources' => [
'appointments' => ['view' => true, 'create' => true, 'cancel' => false, 'update_status' => true],
'addresses' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
'clinic_info' => ['view' => true, 'update' => false],
'insurances' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
],
]
```
### نمونه UI Permissions checkbox matrix در Modal:
```
┌─────────────────────────────────────────────────────┐
│ دسترسی‌های منشی │
├──────────────┬───────┬────────┬────────┬────────────┤
│ بخش │ مشاهده│ ایجاد │ ویرایش │ حذف/لغو │
├──────────────┼───────┼────────┼────────┼────────────┤
│ نوبت‌ها │ ☑ │ ☑ │ ☑ │ ☐ │
│ آدرس‌ها │ ☑ │ ☐ │ ☐ │ ☐ │
│ اطلاعات کلینیک│ ☑ │ - │ ☐ │ - │
│ بیمه‌ها │ ☑ │ ☐ │ ☐ │ ☐ │
└──────────────┴───────┴────────┴────────┴────────────┘
```
### PATCH /api/v1/secretary/{uuid} با permissions:
```json
{
"permissions": {
"version": 1,
"resources": {
"appointments": { "view": true, "create": true, "cancel": true, "update_status": true },
"addresses": { "view": true, "create": false, "update": false, "delete": false }
}
}
}
```
## کدهای خطای جدید
| کد | پیام فارسی |
|----|-----------|
| `ERR_SECRETARY_LIMIT_REACHED` | سقف تعداد منشی پنل اشتراکی شما رسیده است |
## Response خطا (403)
```json
{
"success": false,
"errors": [{
"code": "ERR_SECRETARY_LIMIT_REACHED",
"message": "سقف تعداد منشی پنل اشتراکی شما رسیده است. برای افزودن منشی بیشتر پنل را ارتقاء دهید."
}]
}
```
@@ -0,0 +1,70 @@
# جریان کاربری — تسک ۱۲: تکمیل منشی
## جریان افزودن منشی جدید با بررسی سقف پنل
```
دکتر روی «افزودن منشی» کلیک می‌کند → Modal باز می‌شود
POST /api/v1/secretary { phone, ... }
├─► 403 ERR_SECRETARY_LIMIT_REACHED:
│ ┌─────────────────────────────────────────┐
│ │ ⚠️ سقف منشی پنل شما تکمیل شده است │
│ │ پنل Free: حداکثر ۱ منشی │
│ │ │
│ │ [ارتقاء به Basic] [بستن] │
│ └─────────────────────────────────────────┘
└─► 201: منشی ایجاد شد → لیست refresh
```
## جریان ویرایش permissions منشی
```
دکتر روی آیکن تنظیمات کنار منشی کلیک می‌کند
GET /api/v1/secretary/{uuid}
→ بارگذاری permissions فعلی در Modal
┌─────────────────────────────────────────────────────────┐
│ دسترسی‌های علی رضایی │
├──────────────────┬──────────┬────────┬────────┬─────────┤
│ بخش │ مشاهده │ ایجاد │ ویرایش │ لغو/حذف│
├──────────────────┼──────────┼────────┼────────┼─────────┤
│ نوبت‌ها │ ✅ │ ✅ │ ✅ │ ☐ │
│ آدرس‌ها │ ✅ │ ☐ │ ☐ │ ☐ │
│ اطلاعات کلینیک │ ✅ │ - │ ☐ │ - │
│ بیمه‌ها │ ✅ │ ☐ │ ☐ │ ☐ │
└──────────────────┴──────────┴────────┴────────┴─────────┘
│ │
│ [لغو] [ذخیره دسترسی‌ها] │
└─────────────────────────────────────────────────────────┘
PATCH /api/v1/secretary/{uuid}
{ permissions: { version: 1, resources: { ... } } }
200: «دسترسی‌های منشی بروزرسانی شد» → Modal بسته می‌شود
```
## ترتیب سطوح سقف منشی
```
اشتراک → سقف → اگر افزودن فراتر رود
─────────────────────────────────────────
Free → 1 → 403
Basic → 2 → 403
Professional → 5 → 403
```
## نکته: منشی غیرفعال از سقف کم نمی‌شود
```
countActive() فقط منشیان active=true را می‌شمارد
یعنی: اگر 2 منشی داری و یکی را غیرفعال کنی → می‌توانی دوباره 1 منشی جدید اضافه کنی
(مادامی که در پنل Basic هستی و سقف 2 داری)
```