# حزمة UAT — SMARTLAB (نسخة موسّعة)

دليل قبول المستخدم قبل الإطلاق. لكل بند: نفّذ → ✅ نجاح | ❌ فشل (اذكر: الصفحة، المتصفح، الخطوة، رسالة الخطأ أو لقطة).

**محتويات سريعة:** [متطلبات](#متطلبات-وبيانات-اختبار) · [مسار حرج](#مسار-حرج-ـ-15-20-دقيقة) · [1 المختبر](#1-صاحب-المختبر) · [2 الطبيب](#2-الطبيب) · [3 عام](#3-عام--حجز-وواتساب) · [4 API العيادات](#4-api-العيادات) · [5 doctor_api](#5-api-الطبيب-الداخلي) · [6 أمان](#6-أمان-ورgression) · [7 غير وظيفي](#7-فحوص-خفيفة-غير-وظيفية) · [8 تقرير عيب](#8-قالب-تقرير-عيب)

---

## متطلبات وبيانات اختبار

**بيئة**

- نسخة اختبار بنفس **إصدار PHP** و**إعدادات** الإنتاج قدر الإمكان.
- تشغيل **كامل migrations** الرسمية (لا الاعتماد على DDL أثناء التصفح — يُسجّل في السجل عند نقص جدول/عمود).
- تفعيل عرض الأخطاء **معطّل** في الاختبار النهائي (كالإنتاج)؛ راقب `error_log` الخادم أثناء الجلسة.

**حسابات**

| الدور | المصدر | ملاحظة |
|--------|--------|--------|
| مدير/مستخدم مختبر بصلاحيات كاملة | مستخدمو النظام | يغطي Dashboard، Billing، Reports، Inventory، Clinic API |
| مستخدم بصلاحية `integration.manage` فقط (إن وُجد) | الأدوار | للتأكد من 403 على `clinic_api` عند الحرمان |
| طبيب | `index.php?page=admin_doctor_accounts` (مدير المنصة) أو إنشاء من المختبر | ليس نفس كلمة مرور موظف المختبر |

**بيانات تجريبية مقترحة**

- مريض واحد على الأقل مربوط بطلبات للطبيب نفسه.
- طلب **مكتمل** برقم واضح (`order_number`) ونتائج مسجّلة — لـ API العيادات وصفحة نتائج الطبيب.
- مريض له **نتيجة حرجة أو شاذة** (إن أمكن) — لاختبار Triage ولون النتائج.
- دين مفتوح للمريض (إن وُجدت وحدة الفوترة) — لاختبار أعمار الديون وتذكير الدين.
- صنف مخزون بحركة في آخر 30 يوم — لظهور **توقع النفاد** بمعنى؛ وصنف بدون حركة — للتحقق من السلوك الآمن (شرطة أو «—»).

**أدوات**

- متصفح حديث (Chrome/Edge) + جلسة خاصة ثانية للتحقق من عدم تداخل الكوكيز.
- `curl` أو Postman لـ `api/clinic_results.php`.

---

## مسار حرج — 15–20 دقيقة

نفّذ بالترتيب؛ إذا تعطّل بند، سجّل عيباً ثم أكمل إن أمكن.

1. [ ] مختبر: دخول → `index.php?page=dashboard` — بطاقات **مركز العمليات** تظهر بدون شاشة بيضاء.
2. [ ] مختبر: `index.php?page=billing` — فترات التقادم ظاهرة؛ إن وُجد زر تذكير دين، يعمل بدون «انتهت صلاحية الطلب».
3. [ ] طبيب: `doctor.php?page=login` → لوحة → تحقق من تحميل **الصندوق الموحّد** دون خطأ في Network لـ `doctor_api.php?action=get_inbox`.
4. [ ] طبيب: `doctor.php?page=results` — Triage + حفظ ملاحظة قصيرة.
5. [ ] طبيب: `doctor.php?page=alerts` — قائمة غير فارغة إذا كانت الشارة > 0 (تحقق من `ap=` في الترقيم إن وُجد).
6. [ ] حجز: إرسال حجز من `doctor_booking` → زر واتساب + نص الرسالة حسب المواصفات السابقة.
7. [ ] `index.php?page=clinic_api&action=test` — طلب ناجح بمفتاح حقيقي ورقم طلب.

---

## 1) صاحب المختبر

| # | السيناريو | خطوات تفصيلية | متوقع |
|---|-----------|----------------|--------|
| 1.1 | مركز العمليات | Dashboard | أرقام طابور/تنفيذ/عينات/TAT/حرجة؛ لا أخطاء PHP في الصفحة أو السجل لنفس الطلب. |
| 1.2 | أعمار الديون | Billing → ديون المرضى | مجموع الحقوق 0–30 + 31–60 + … منطقي مقارنة بإجمالي «المتبقي» إن عُرض. |
| 1.3 | تذكير دين | نفس الصفحة، نموذج التذكير | نجاح؛ لا CSRF زائف. |
| 1.4 | تقارير مالية ومخزون | `index.php?page=reports` | قسم Aging + ملخص توقع المخزون؛ التمرير والطباعة (إن استخدمت) بدون كسر تخطيط RTL. |
| 1.5 | المخزون | `index.php?page=inventory` | عمود **توقع النفاد**؛ عداد **≤14 يوم**؛ عدم ظهور أخطاء SQL. |
| 1.6 | API العيادات — إدارة | `index.php?page=clinic_api` | فهرس المفاتيح؛ إنشاء مفتاح؛ تعديل **IP whitelist** و**rate_limit**؛ حفظ ينجح. |
| 1.7 | API — سجل ووثائق | `action=logs`، `action=docs` | سجلات تظهر بعد الاختبار؛ الوثائق تعرض URL الصحيح `api/clinic_results.php`. |
| 1.8 | صلاحية | مستخدم **بدون** `integration.manage` يحاول `clinic_api` | 403 واضح، لا تسريب بيانات. |
| 1.9 | طلبات وقائمة عمل (اختياري) | `orders`، `worklist` إن تستعملها يومياً | تدفق «طلب → نتيجة» لا يزال سليماً بعد التحديثات. |

---

## 2) الطبيب

| # | السيناريو | خطوات تفصيلية | متوقع |
|---|-----------|----------------|--------|
| 2.1 | دخول وبوابة موحدة | `doctor.php?page=login`؛ إن كان المستخدم مربوطاً بعدة بوابات | `choose_portal` يعمل؛ الدخول إلى بوابة الطبيب يضبط `Tenant` الصحيح. |
| 2.2 | الصندوق الموحّد | Dashboard | عنصر الويدجت يُحدَّث؛ محتوى يعكس تنبيهات/مهام/حرجة إن وُجدت في النظام. |
| 2.3 | إشعارات وToast | تنقّل 3–4 صفحات بنفس الجلسة | عدم تكرار Toast بدون حدث جديد؛ زر عرض مقروء. |
| 2.4 | التنبيهات | `doctor.php?page=alerts` | ترقيم `&ap=2` لا يكسر `page=alerts`; العدد الإجمالي يتوافق مع القائمة. |
| 2.5 | نتائج حرجة | `doctor.php?page=results&critical=1` | تصفية تعمل؛ لا 500. |
| 2.6 | Triage وملاحظات | `results` → مريض له بيانات | درجة Triage ولون؛ إضافة وتعديل ملاحظة بنجاح. |
| 2.7 | تذكير متابعة | `reminders` أو إضافة من سياق النتائج | إنشاء/إتمام/حذف يعمل مع CSRF. |
| 2.8 | المهام | `doctor.php?page=tasks` | تحميل القائمة؛ أي إجراء POST يحترم `_csrf`. |
| 2.9 | بحث | `doctor.php?page=search` | استعلام بسيط يعيد نتائج متوقعة أو «لا نتائج» دون خطأ. |
| 2.10 | رابط الحجز | `booking_link` | توليد/تجديد؛ نسخ الرابط وفتحه في نافذة خاصة يعرض النموذج. |
| 2.11 | تعارض مواعيد | `appointments` — موعدان متداخلان | رفض مع رسالة واقتراح وقت. |
| 2.12 | موعد غير متعارض | حجز بعد التصحيح | قبول ويظهر في القائمة. |
| 2.13 | خروج | زر الخروج في التخطيط | 302 إلى login؛ لا 405. |

---

## 3) عام — حجز وواتساب

| # | السيناريو | خطوات | متوقع |
|---|-----------|--------|--------|
| 3.1 | حجز POST | `index.php?page=doctor_booking` (+ token إن لزم) | نجاح؛ CSRF موجود في النموذج أو يُحقن تلقائياً من الصفحة الأم. |
| 3.2 | واتساب | بعد إعادة التوجيه `success=1` | زر يفتح `wa.me`؛ النص بدون «عبر موقع المختبر الرئيسي»؛ رقم بصيغة دولية عند الحاجة. |
| 3.3 | مستخدم مختبر مسجّل | وهو مسجّل، يفتح `doctor_booking` | لا 403. |
| 3.4 | انتهاء جلسة (اختياري) | احذف كوكي الجلسة ثم أرسل POST للحجز | سلوك واضح (إعادة عرض/رسالة) وليس خطأ خام. |

---

## 4) API العيادات

**المسار:** `{scheme}://{host}{/path/}api/clinic_results.php`  
**التوثيق:** Header `X-API-Key` أو `Authorization: Bearer <key>`  
**إعداد CORS:** `config/config.php` → `clinic_api.allowed_origins` (مصفوفة أصول كاملة مثل `https://clinic.example.com`).

| # | السيناريو | خطوات | متوقع |
|---|-----------|--------|--------|
| 4.1 | GET + order_number | Header المفتاح + query | `success: true`، هيكل `data.order` و`data.results`. |
| 4.2 | GET + order_id | كما فوق | نفس المنطق. |
| 4.3 | GET + patient_id | آخر نتائج المريض | JSON صالح؛ حدود الخصوصية كما يصممها المختبر (لا بيانات زائدة حساسة في التقرير). |
| 4.4 | POST JSON | `Content-Type: application/json`، المفتاح في Header | مطابقة 4.1. |
| 4.5 | مفتاح في Query (إن اضطررت) | `?api_key=` | يعمل مع **Warning** deprecation في الاستجابة؛ التحضير للترحيل للـ Header فقط. |
| 4.6 | مفتاح خاطئ / معطّل | — | `401` مناسب. |
| 4.7 | بدون مفتاح | — | `400` api_key مطلوب. |
| 4.8 | معاملات ناقصة | بدون order/patient | رسالة توضح الحاجة لأحد المعرفات. |
| 4.9 | IP whitelist | مفتاح بقائمة IP + طلب من خارجها | `403` IP غير مسموح؛ سجل في `clinic_api_logs`. |
| 4.10 | Rate limit | تكرار سريع ضمن الساعة | رسالة تجاوز الحد؛ HTTP مناسب. |
| 4.11 | OPTIONS | preflight من أصل مسموح | `200` ورؤوس CORS متوقعة. |

**أمثلة**

```bash
# رقم طلب
curl -sS "https://YOUR-DOMAIN/api/clinic_results.php?order_number=ORDER123" \
  -H "X-API-Key: YOUR_KEY"

# POST
curl -sS -X POST "https://YOUR-DOMAIN/api/clinic_results.php" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d "{\"order_number\":\"ORDER123\"}"
```

---

## 5) API الطبيب الداخلي

**المسار:** `doctor_api.php` (جلسة كوكي لطبيب مسجّل).

| # | السيناريو | خطوات | متوقع |
|---|-----------|--------|--------|
| 5.1 | get_inbox | GET `?action=get_inbox` من المتصفح وهو داخل بوابة الطبيب | 200، JSON فيه عناصر مرتبة؛ لا 401. |
| 5.2 | POST بدون CSRF | من وحدة تحكم المتصفح احذف `_csrf` من طلب POST للـ API | `419` أو `{error:CSRF}` — رفض صريح. |
| 5.3 | POST من أصل/مرجع أجنبي | إن اختبرت بأداة خارجية بدون نفس الـ Host | `403` طلب غير صالح (حسب التحقق من Origin/Referer في `doctor_api.php`). |

---

## 6) أمان وRegression

**صفحات معفاة من CSRF العام في `index.php` (مرجع للمختبِر):**  
`login`, `logout`, `choose_portal`, `patient_portal`, `doctor_booking`, `home_visit_request`, `signup`, `purchase`, `booking`, `pay`, `subscribe_pay`.

| # | السيناريو | خطوات | متوقع |
|---|-----------|--------|--------|
| 6.1 | POST مختبر بدون `_csrf` | إعدادات أو حفظ كيان من `index.php` | رفض؛ رسالة/توجيه؛ لا تغيير صامت. |
| 6.2 | POST معفى | تسجيل دخول مختبر | لا يُرفض بسبب CSRF على الصفحة نفسها. |
| 6.3 | حقن توكن قديم | أرسل نموذجاً بعد جلسة طويلة أو نسخ HTML قديم | رسالة انتهاء صلاحية الطلب **مرة**؛ إعادة الإرسال بعد تحديث الصفحة تنجح. |

---

## 7) فحوص خفيفة غير وظيفية

| # | السيناريو | ملاحظة |
|---|-----------|--------|
| 7.1 | عرض جوال | اضغط F12 → عرض iPhone: الشريط الجانبي للطبيب والبطاقات لا تتداخل بشكل يمنع الضغط. |
| 7.2 | RTL | أرقام الهاتف والتواريخ في رسالة واتساب ولوحات المختبر تقرأ بشكل مقبول. |
| 7.3 | تحميل متوازي | افتح Dashboard المختبر + تبويب طبيب؛ لا تضارب جلسات إلا إن نفس المتصفح يشارك كوكي (استخدم نافذة خاصة لكل دور إن لزم). |

---

## 8) قالب تقرير عيب

```
العنوان: [مختصر]
الخطورة: حرج / عالٍ / متوسط / منخفض
الدور: مختبر | طبيب | API عيادة | عام
الرابط الدقيق: 
المتصفح والإصدار: 
الخطوات: 1. ... 2. ...
السلوك الحالي: 
المتوقع: 
لقطة/سجل: (اختياري) مسار ملف أو نص من Network/Console
```

---

## 9) توقيع الجولة

| الدور | الاسم | التاريخ | البناء / مرجع Git | نتيجة الحرجة (§ مسار حرج) |
|--------|-------|---------|---------------------|---------------------------|
| مختبر | | | | ☐ ناجح / ☐ فشل |
| طبيب | | | | |
| تكامل عيادة | | | | |

**بعد UAT:** [RELEASE_ROLLBACK_CHECKLIST.md](./RELEASE_ROLLBACK_CHECKLIST.md) — فحص صيغة PHP، migrations، مراقبة السجل أول 24 ساعة، خطة الرجوع.
