تحليل وخطة تعديل شاشة "إضافة طبيب"
وحدة: المختبر (LIS) — Moon ERP |
التاريخ: 2026-05-25 |
المسؤول: حازم |
الفرع: hotfix/features (BE) + fix/doctor-revamp (FE)
الملاحظات الأصلية من العميل
١) كود الطبيب حرج باك + فرونت
يجب أن يكون الكود تسلسلياً تلقائياً، قابلاً للتعديل، ولا يُسمح بتكراره أبداً.
٢) الحقول الإجبارية عالٍ باك + فرونت
الحقول الإجبارية فقط: الكود + رقم الهاتف + القسم. باقي الحقول اختيارية.
٣) منع تكرار رقم الهاتف حرج باك + فرونت
منع تسجيل أكثر من طبيب بنفس رقم الهاتف. عند الإدخال المكرر، تظهر أيقونة/رابط لعرض ملف الطبيب الموجود.
٤) قائمة الأسعار المفضلة متوسط باك + فرونت
إضافة حقل اختيار قائمة الأسعار المفضلة على الطبيب. عند اختياره في شاشة "إضافة زيارة" تُطبق قائمة أسعاره تلقائياً.
٥) إدارة التخصصات (Specialties) عالٍ باك + فرونت
توفير شاشة لإدارة التخصصات (قائمة CRUD)، واستبدال الحقل النصي الحر بقائمة منسدلة تختار منها التخصص.
٦) إصلاح قائمة أنواع العمولة متوسط فرونت فقط
القائمة المنسدلة لأنواع العمولة تفتح لكن الخيارات لا تظهر بشكل صحيح أو تبدو معلقة.
الوضع الحالي في الكود (ما تم اكتشافه)
اكتشافات إيجابية تختصر العمل:
- تسلسل "Doctor Code" موجود فعلاً في
DefaultSequenceSeeder.php:713 — جاهز للاستخدام مع SequenceService::generateNext() بدون أي عمل إضافي على البنية.
- حقل
price_list_id موجود بالفعل في $fillable لموديل LabDoctor — كل المطلوب هو إضافته في النموذج وقواعد التحقق.
- حقل
department_id موجود في $fillable ومُهاجَر منذ المهاجرة الأصلية — كل المطلوب هو جعله مطلوباً في التحقق.
مشاكل في الوضع الحالي تحتاج إصلاح:
code: nullable + unique عام — لا يولَّد تلقائياً، يمكن إدخال طبيب بدون كود.
phone: nullable — لا يوجد قيد فريد، يمكن تكرار نفس الرقم لعدة أطباء.
name: required حالياً — يجب جعله اختيارياً.
specialization: حقل نصي حر — لا توجد قائمة موحدة، يكتب كل مستخدم ما يحلو له.
commission_type: dropdown يفتح ولا يعرض الخيارات (بصرياً) — يحتاج تشخيص في الـ HTML/CSS.
التغييرات المطلوبة على الباك اند
BE-1 — تسلسل تلقائي لكود الطبيب (Auto Code)
| البند | التفاصيل |
| تأثير | كنترولر فقط — لا تعديل قاعدة بيانات |
| ملف | Modules/LIS/app/Http/Controllers/LabDoctorController.php |
| المنطق | في دالة store(): لو code فارغ، استدعِ SequenceService::generateNext($companyId, 'core', 'doctor') وحقن الناتج قبل التحقق. لو المستخدم أدخل كوداً يدوياً، يُحترم ويُتحقق من فرادته. |
| إندبوينت جديد | GET /api/lis/doctors/next-code — يرجع الكود التالي بدون استهلاك العداد (preview فقط)، لعرضه في النموذج عند فتحه. |
BE-2 — منع تكرار رقم الهاتف
| البند | التفاصيل |
| مهاجرة | ملف جديد: 2026_XX_XX_add_unique_phone_to_lab_doctors.php |
| التحقق من التكرار | قبل إضافة الفهرس، نفحص التكرارات الموجودة. لو وُجد، نوقف بـ RuntimeException ونعرض قائمة الـ IDs المكررة (نفس نمط مهاجرة المرضى). |
| الفهرس | UNIQUE(company_id, phone) — multi-tenant scoped |
| تحديث الـ Request | StoreLabDoctorRequest: phone → required + unique scoped + رسالة مخصصة. UpdateLabDoctorRequest: ignore الـ id الحالي. |
| إندبوينت جديد | GET /api/lis/doctors/by-phone/{phone} — يرجع تفاصيل الطبيب المسجل بهذا الرقم (للزر "عرض الطبيب الموجود"). |
BE-3 — تعديل الحقول الإجبارية
| قبل | بعد |
name required | name nullable |
name_en required | name_en nullable |
code nullable | code required (يأتي من التسلسل تلقائياً) |
phone nullable | phone required + unique |
department_id nullable | department_id required |
BE-4 — جدول التخصصات الجديد
| البند | التفاصيل |
| مهاجرة جديدة | 2026_XX_XX_create_lab_doctor_specialties_table.php |
| الأعمدة | id, company_id, name, name_ar, name_en, code, is_active, created_by, updated_by, timestamps, softDeletes |
| الفهارس | UNIQUE(company_id, code), UNIQUE(company_id, name) |
| FK في جدول الأطباء | إضافة عمود specialty_id nullable على lab_doctors + FK |
| الحقل القديم | الإبقاء على specialization (نصي) في الموديل للتوافق العكسي، لكن يصبح للقراءة فقط (مدفوع من اسم التخصص). |
| موديل + ريسورس + ريكويست + كنترولر | LabDoctorSpecialty model, StoreLabDoctorSpecialtyRequest, UpdateLabDoctorSpecialtyRequest, LabDoctorSpecialtyController, LabDoctorSpecialtyResource |
| المسارات | GET /api/lis/doctor-specialties, POST, PUT /:id, DELETE /:id |
| سيدر | إضافة سيدر بأسماء تخصصات افتراضية (طب أطفال، باطنة، نسا وتوليد، أعصاب، جلدية...) باللغتين. |
BE-5 — قائمة الأسعار المفضلة
الحقل price_list_id موجود فعلاً في القاعدة والـ Request. لا يوجد تغيير على الباك اند هنا. التعديل كله في الفرونت اند.
BE-6 — تطبيق قائمة الأسعار في شاشة الزيارة
| البند | التفاصيل |
| المنطق | عند إنشاء طلب مخبري أو زيارة لطبيب، نقرأ doctor.price_list_id ونستخدمه افتراضياً لتسعير الفحوصات إذا لم يتم اختيار قائمة أسعار يدوياً. |
| ملف | LabRequestController أو LabRequestService |
| سلوك الواجهة | الفرونت يقرأ القيمة من بيانات الطبيب ويملأ الحقل تلقائياً (مع إمكانية تغييره). |
BE-7 — جزء التخصصات في الـ Doctor Resource
| البند | التفاصيل |
| ملف | LabDoctorResource |
| التعديل | إضافة specialty ككائن {id, name, name_ar, name_en} بدلاً من نص حر. مع الإبقاء على specialization النصي للتوافق. |
التغييرات المطلوبة على الفرونت اند
FE-1 — إصلاح قائمة "نوع العمولة" المعلقة
| البند | التفاصيل |
| الملف | lis-doctors.component.html (الأسطر 266, 462, 505, 619) |
| التشخيص المتوقع | المشكلة الأكثر احتمالاً: appendTo ناقص على p-select، فيظهر خلف الحوار (p-dialog) — تظهر القائمة بدون خيارات مرئية. |
| الإصلاح | إضافة appendTo="body" على كل p-select داخل الحوار، والتأكد من optionLabel="label" optionValue="value"، وارتفاع كافٍ للقائمة. |
| اختبار يدوي | فتح حوار إضافة/تعديل طبيب → فتح قائمة نوع العمولة → التأكد من ظهور (نسبة مئوية، ثابت لكل إحالة، ثابت لكل تحليل). |
FE-2 — تعديل نموذج إضافة/تعديل طبيب
| التعديل | التفاصيل |
| كود الطبيب | عند فتح الحوار (إضافة جديد فقط): استدعاء GET /lis/doctors/next-code وملء الحقل تلقائياً. الحقل قابل للتعديل اليدوي. تحذير عند التكرار. |
| الحقول المطلوبة | إزالة required من name. إضافة required على code + phone + department_id. |
| منع تكرار الهاتف | عند الكتابة في حقل الهاتف (debounce 500ms): استدعاء GET /lis/doctors/by-phone/{phone}. لو رد بطبيب موجود: عرض شريط تحذير برتقالي تحت الحقل + زر "عرض ملف الطبيب الموجود" يفتح صفحته في تبويب جديد أو modal. |
| التخصص | استبدال حقل النص الحر بـ p-select يقرأ من GET /lis/doctor-specialties. مع زر "+" بجواره لفتح حوار سريع لإضافة تخصص جديد بدون مغادرة النموذج. |
| قائمة الأسعار المفضلة | إضافة p-select جديد بعنوان "قائمة الأسعار المفضلة" يقرأ من GET /lis/price-lists. اختياري. القيمة المختارة تُحفظ في price_list_id. |
FE-3 — شاشة إدارة التخصصات الجديدة
| البند | التفاصيل |
| المسار | /lab/doctor-specialties + /core/lis/doctor-specialties |
| المكون | features/lis/doctor-specialties/lis-doctor-specialties.component.ts — جدول + حوار CRUD بنفس نمط lis-specimen-types. |
| الخدمة | core/services/lis-doctor-specialty.service.ts بنفس نمط الخدمات الأخرى (list/listAll/create/update/delete). |
| الـ Store | (اختياري — يمكن استخدام Signals مباشرة بدون NgRx لأن الشاشة صغيرة). |
| الترجمة | إضافة مفاتيح LIS.DOCTOR_SPECIALTIES.* في ملفات en.json و ar.json. |
| التنقل | إضافة الرابط في الـ Sidebar الجانبي لشاشة المختبر (بعد التخصصات أو ضمن مجموعة "البيانات الأساسية"). |
FE-4 — تطبيق قائمة الأسعار في شاشة الزيارة
| البند | التفاصيل |
| الملف | lis-request-wizard-v2.component.ts + lis-visits.component.ts |
| المنطق | عند اختيار طبيب: قراءة doctor.price_list_id. إذا وُجد، وحقل قائمة الأسعار في النموذج فارغ، يُملأ تلقائياً بقيمة قائمة الطبيب. (لو المستخدم اختار قائمة يدوياً قبل اختيار الطبيب: لا نطغى عليها). |
| إشارة بصرية | عند الملء التلقائي: نص صغير تحت الحقل "🔵 من القائمة المفضلة للطبيب" يوضح للمستخدم سبب التعبئة. |
قائمة التاسكات
| الترتيب | التاسك | الجانب | الأولوية | التقدير | اعتمادية |
| T-1 | BE: إضافة جدول التخصصات + موديل + كنترولر + ريسورس + ريكوست + سيدر | باك اند | عالٍ | 4 س | — |
| T-2 | BE: مهاجرة phone unique + فحص التكرارات + تحديث Requests | باك اند | حرج | 2 س | — |
| T-3 | BE: تعديل الحقول المطلوبة (name اختياري، code/phone/dept مطلوبين) | باك اند | عالٍ | 1 س | T-2 |
| T-4 | BE: توليد تلقائي للكود في store() + إندبوينت preview | باك اند | حرج | 1.5 س | — |
| T-5 | BE: إندبوينت by-phone للبحث عن الطبيب المسجل برقم | باك اند | عالٍ | 1 س | T-2 |
| T-6 | BE: تحديث Resource لإضافة specialty object + ربط FK | باك اند | متوسط | 1 س | T-1 |
| T-7 | BE: تطبيق price_list_id افتراضياً عند إنشاء طلب/زيارة | باك اند | متوسط | 1.5 س | — |
| T-8 | FE: إصلاح dropdown نوع العمولة + appendTo body | فرونت | متوسط | 0.5 س | — |
| T-9 | FE: تعديل نموذج طبيب (كود تلقائي + required جديدة + فحص هاتف + price list + specialty dropdown) | فرونت | حرج | 4 س | T-1 T-2 T-4 T-5 |
| T-10 | FE: شاشة إدارة التخصصات الجديدة + خدمة + ترجمة + تنقل | فرونت | عالٍ | 3 س | T-1 |
| T-11 | FE: تطبيق قائمة الأسعار المفضلة في شاشة الزيارة/الطلب | فرونت | متوسط | 1.5 س | T-7 |
ترتيب التنفيذ المقترح
- اليوم الأول — الباك اند:
- T-1 (التخصصات) → T-2 (الهاتف الفريد) → T-3 (الحقول المطلوبة) → T-4 (التسلسل) → T-5 (بحث الهاتف) → T-6 (Resource) → T-7 (سعر افتراضي).
- تشغيل
local-deploy.sh بعد كل تاسك للتأكد من شغل الكود.
- اختبارات curl سريعة لكل إندبوينت قبل الانتقال للفرونت.
- اليوم الثاني — الفرونت اند:
- T-8 (dropdown bug) → T-9 (نموذج طبيب) → T-10 (شاشة التخصصات) → T-11 (تطبيق السعر).
- بناء ونشر بعد كل تاسك على
moonui.elbaset.com/app/.
- اليوم الثالث — اختبار:
- تكتب كل سيناريو وتختبر بنفسك على الواجهة.
- بعد اعتمادك، نضم تعديلات هذه الخطة لفرع
hotfix/features الذي يضم تعديلات شاشة المرضى.
- عند انتهاء كل الـ hotfixes، نعمل PR واحد لأحمد لدمج
hotfix/features في prod.
المخاطر والملاحظات
مخاطر مهمة:
- الأرقام المكررة في الإنتاج: قبل تطبيق فهرس الهاتف الفريد، يجب فحص قاعدة البيانات الحية لعدد التكرارات. إذا وُجدت، نوقف المهاجرة ونقدم قائمة بالتكرارات للعميل لاتخاذ قرار (دمج/تعديل).
- الأطباء الموجودون بدون كود: بعد جعل الكود مطلوباً، قد يكون لدينا أطباء قدامى بدون كود. نحتاج سكريبت لتعبئة الأكواد لهم من التسلسل قبل تفعيل القاعدة (أو السماح بـ nullable للقدامى وrequired فقط على الجديد).
- التخصصات الموجودة كنص حر: نحتاج خطة هجرة — نقرأ كل القيم الفريدة من
specialization الحالي، ننشئ منها صفوف في الجدول الجديد، ثم نملأ specialty_id للأطباء الحاليين بمطابقة النص.
القرارات النهائية (مؤكدة من العميل 2026-05-25):
-
تكرار رقم الهاتف: منع كامل لإضافة الطبيب — لا يُحفظ السجل أبداً إذا كان الرقم مسجلاً مسبقاً. يُعرض رابط ملف الطبيب الموجود فقط.
-
قائمة الأسعار المفضلة: اختيارية تماماً عند إضافة الطبيب. لكن إذا اختار المستخدم قائمة للطبيب، فعند اختيار هذا الطبيب لاحقاً في شاشة إضافة طلب/تحليل، يجب عرض أسعار الفحوصات من قائمته تلقائياً.
-
الأطباء القدامى: لا نفعل أي شيء معهم. لا سكريبت تعبئة أكواد، لا هجرة تخصصات، لا تعديل أرقام هواتف. القواعد الجديدة تطبق فقط على الإضافة والتعديل من الآن.
-
شاشة التخصصات: داخل وحدة المختبر (LIS) فقط — لا تظهر في إعدادات النظام العامة، لأنها مرتبطة بالمعمل تحديداً.
تأثير القرارات على خطة التنفيذ:
- لا سكريبت هجرة للتخصصات النصية القديمة — حقل
specialization النصي يبقى للأطباء القدامى كما هو، والأطباء الجدد يستخدمون specialty_id فقط.
- لا سكريبت تعبئة أكواد للأطباء القدامى — قاعدة الكود المطلوب تطبق فقط في
StoreLabDoctorRequest (عند الإضافة). الأطباء بدون كود يفضلون كما هم حتى يعدّلهم المستخدم يدوياً.
- قاعدة الهاتف الفريد لن تفحص التكرارات الموجودة في القاعدة الحية — لكن المهاجرة ستفحصها وتعرض قائمة بها فقط لإعلامنا (دون إيقاف). الأطباء القدامى يبقون كما هم، والفهرس الفريد يطبق على الإضافات الجديدة فقط (يمكن تنفيذ ذلك بـ
partial unique index أو فحص مسبق في الـ Request بدلاً من فهرس DB صارم).
- قائمة الأسعار: التطبيق التلقائي عند اختيار الطبيب في شاشة الطلب — مع إشارة بصرية ("🔵 من قائمة الطبيب") وإمكانية تغيير المستخدم لها قبل الحفظ.
- التنقل: شاشة التخصصات تُضاف فقط في Sidebar وحدة المختبر تحت مجموعة "البيانات الأساسية" بجوار "أنواع العينات" و"تصنيفات الفحوصات".
ملخص نهائي:
الخطة تتكون من ٧ تاسكات باك اند + ٤ تاسكات فرونت اند = ١١ تاسك إجمالي بتقدير ~٢١ ساعة عمل (~٣ أيام). كل التعديلات تذهب لفرعَين موجودَين فعلاً: hotfix/features (BE) و fix/doctor-revamp (FE جديد سننشئه عند البدء). لا PR لأحمد قبل اعتمادك الكامل.
Moon ERP — LIS Module | Add Doctor Revamp Plan | جاهز للمراجعة، في انتظار أمرك بالبدء.