تحليل وخطة تعديل شاشة "إضافة تحليل"

وحدة: المختبر (LIS) — Moon ERP  |  التاريخ: 2026-05-25  |  نطاق: شاشة /lab/investigations — إضافة وتعديل التحاليل
٨
متطلبات جديدة
٤
أعمدة DB جديدة
١
جدول جديد (الوحدات)
٢٠٠+
تحليل موجود
٣٠
وحدة مستخدمة فعلاً

الوضع الحالي

ما هو موجود فعلاً:
ما ينقص (المطلوب من العميل):
  1. اسم بالإنجليزي مطلوب (الفرونت يقبل بدون)
  2. حقل "اسم العرض" منفصل عن الاسم الداخلي للنظام
  3. عدد الخانات العشرية لكل تحليل رقمي
  4. حقل الوحدة كقائمة منسدلة بدل نص حر
  5. وحدة العمر في النطاقات (يوم/أسبوع/شهر/سنة)
  6. قيمة افتراضية للنتائج الكتابية
  7. تعليق ثابت يطبع مع كل نتيجة
  8. طباعة التحليل في صفحة منفصلة

المتطلبات الثمانية بتفصيل

عالٍ فرونت ١) الاسم الإنجليزي مطلوب

الباك اند مضبوط دلوقتي: name_en required و name_ar اختياري. لكن الفرونت يطلب name_ar بدل name_en.

المسار:

src/app/features/lis/investigations/lis-investigations.component.ts:507
// الموجود:
name_ar: ['', Validators.required],
name: [''],

// المطلوب:
name_en: ['', Validators.required],
name_ar: [''],
name: [''],     // داخلي — يتعبأ تلقائياً من name_en

كذلك: في الـ HTML، علامة * تنتقل من NAME_AR إلى NAME_EN.

جديد باك + فرونت ٢) "اسم العرض" منفصل عن الاسم الداخلي

الفرق المطلوب:

الحقلالغرضمثال
name_enالاسم الداخلي للنظام (للبحث، الكود)Complete Blood Count
name_arالاسم الداخلي بالعربيصورة دم كاملة
display_name (جديد)الاسم اللي يظهر في التقارير المطبوعةCBC
display_name_ar (جديد)اسم العرض بالعربيتعداد دم كامل

الحل التقني:

  1. BE: مهاجرة add_display_name_to_lab_investigations تضيف العمودين على الجدول.
  2. BE: تحديث $fillable + الـ Request + الـ Resource.
  3. FE: إضافة الحقلين في الـ form + أسفل قسم "البيانات الأساسية".
  4. تعديل خدمة الـ PDF لتستخدم display_name لو موجود، وإلا name_en.
حرج باك + فرونت ٣) عدد الخانات العشرية للنتائج الرقمية

الفكرة: كل تحليل رقمي له إعداد منفصل لعدد الخانات العشرية (٠، ١، ٢، ٣، ٤). الافتراضي ٠. ولو المستخدم اختار ٢، النتائج تظهر بشكل 2.00 في الإدخال والعرض والطباعة.

الحل التقني:

  1. BE: مهاجرة تضيف decimal_places (smallint, default 0, range 0-4) على lab_investigations.
  2. BE: تحديث $fillable + Request + Resource.
  3. FE: في شاشة التحليل، يظهر الحقل فقط لو result_type === 'numeric' أو 'formula'.
  4. FE: في شاشة إدخال النتيجة (Results page و Kanban): p-inputNumber يستخدم minFractionDigits + maxFractionDigits = decimal_places.
  5. BE: عند حفظ نتيجة عددية، تُقرّب لـ decimal_places خانة قبل التخزين.
  6. محرك الـ formula: ينفذ الحساب ثم يقرّب الناتج بنفس الطريقة.
  7. خدمة الـ PDF: تنسّق الأرقام بـ decimal_places.
أثر التغيير: ٤ مواقع في الكود (إدخال، عرض، طباعة، formula). كل واحدة تستخدم نفس قيمة decimal_places.
عالٍ باك + فرونت ٤) حقل الوحدة (Unit) كقائمة منسدلة

الوضع الحالي: حقل نصي حر. ٢٠٠ تحليل، ٣٠ وحدة فريدة. أكتر الوحدات استخداماً: mg/dL (22), % (15), ng/mL (11), U/L (9), إلخ.

الخيارات:

خيار أ — جدول مرجعي بسيط (مقترح)

  • جدول جديد lab_units (id, code, name, sort_order)
  • سيدر يحط أكتر ٣٠ وحدة الموجودة فعلاً + الشائعة الدولية
  • على lab_investigations: unit_id (foreign key) + الإبقاء على unit string للتوافق
  • الفرونت: p-select filterable + زر "+" لإضافة وحدة جديدة inline
  • الميزة: استمرارية الوحدات بين التحاليل، توحيد الأسماء

خيار ب — قائمة ثابتة في الكود

  • قائمة hardcoded في الفرونت بأكثر ٥٠ وحدة شائعة
  • لا يحتاج مهاجرة أو موديل جديد
  • أسرع تنفيذ بس أقل مرونة
  • المستخدم محتاج يطلب من المطور لإضافة وحدة جديدة

التوصية: خيار أ — لأنك ممكن تضيف وحدات بنفسك من شاشة بسيطة.

عالٍ باك + فرونت ٥) وحدة العمر في النطاقات (يوم/أسبوع/شهر/سنة)

الوضع الحالي: age_from و age_to عبارة عن integer بدون وحدة. النظام يفترضها سنوات.

المطلوب: لكل صف نطاق، حقل وحدة (day/week/month/year) لكل من age_from و age_to.

الحل التقني:

  1. BE: مهاجرة على lab_investigation_normal_ranges: age_from_unit (enum, default 'year') و age_to_unit (enum, default 'year').
  2. FE: في tab الـ Ranges، بجوار كل حقل عمر، select صغير بأربع خيارات (Y/M/W/D).
  3. منطق المطابقة عند جلب النتيجة: حساب عمر المريض بأصغر وحدة (يوم) ومقارنته بالنطاقات بعد تحويلها للأيام.

مثال: نطاق "من ٢٠ إلى ٣٠" يبقى:

متوسط باك + فرونت ٦) قيمة افتراضية للنتائج الكتابية

الفكرة: لو التحليل نتيجته بـ "+" أو "-"، يكون "-" مثلاً هو الافتراضي. عند إدخال نتيجة جديدة، يُملأ تلقائياً وممكن المستخدم يغير.

الحل التقني:

  1. BE: مهاجرة تضيف default_value (string, nullable) على lab_investigations.
  2. FE: في شاشة التحليل، حقل يظهر حسب الـ result_type:
    • لو text أو memo: input نصي حر.
    • لو selection: dropdown يقرأ من selection_options.
    • لو numeric أو formula: input عددي (نادر بس مفيد).
  3. FE: عند إنشاء نتيجة جديدة في صفحة الـ Results، يُملأ القيمة تلقائياً لو موجودة.
متوسط باك + فرونت ٧) تعليق ثابت يُطبع مع كل نتيجة

الفكرة: تعليق بنصين (عربي وإنجليزي) موجود على التحليل نفسه، يظهر دائماً في تقرير النتيجة المطبوع (تحت قيمة النتيجة مباشرة).

الحل التقني:

  1. BE: مهاجرة تضيف static_comment و static_comment_ar (text, nullable) على lab_investigations.
  2. FE: في شاشة التحليل، textarea بسطرين لكل لغة.
  3. خدمة PDF: لو موجود، يضاف سطر تحت النتيجة بالخط الصغير المائل.
عالٍ باك + فرونت ٨) طباعة التحليل في صفحة منفصلة

الفكرة: radio button بسيط في شاشة التحليل: "اطبع هذا التحليل في صفحة منفصلة". لو نعم، التحليل (مع التعليق الثابت تحته) يُطبع في صفحة لوحدها في تقرير النتائج.

الحل التقني:

  1. BE: مهاجرة تضيف print_on_separate_page (boolean, default false) على lab_investigations.
  2. FE شاشة التحليل: toggle switch بجوار "نشط".
  3. خدمة الـ PDF (LisReportPdfService):
    • قبل عرض هذا التحليل، تستدعي doc.addPage().
    • تظهر اسم التحليل + النتيجة + التعليق الثابت في الصفحة لوحدها.
    • الصفحة التالية تعود لباقي التحاليل.

التغييرات في قاعدة البيانات

مهاجرة واحدة جديدة على lab_investigations

Schema::table('lab_investigations', function (Blueprint $table) {
    // REQ 2 — display name
    $table->string('display_name', 255)->nullable()->after('name_en');
    $table->string('display_name_ar', 255)->nullable()->after('display_name');

    // REQ 3 — decimal precision
    $table->unsignedTinyInteger('decimal_places')->default(0)->after('reporting_unit');

    // REQ 4 — unit FK (link to new lab_units table)
    $table->unsignedBigInteger('unit_id')->nullable()->after('unit');
    $table->foreign('unit_id')->references('id')->on('lab_units')->nullOnDelete();

    // REQ 6 — default value
    $table->string('default_value', 500)->nullable()->after('selection_options');

    // REQ 7 — static comments
    $table->text('static_comment')->nullable();
    $table->text('static_comment_ar')->nullable();

    // REQ 8 — print on separate page
    $table->boolean('print_on_separate_page')->default(false);
});

مهاجرة على lab_investigation_normal_ranges

Schema::table('lab_investigation_normal_ranges', function (Blueprint $table) {
    // REQ 5 — age unit per range row
    $table->string('age_from_unit', 10)->default('year')->after('age_from');
    $table->string('age_to_unit', 10)->default('year')->after('age_to');
});

جدول جديد lab_units

Schema::create('lab_units', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('company_id');
    $table->string('code', 30);           // mg/dL, %, etc
    $table->string('name', 100);          // Display name
    $table->string('name_ar', 100)->nullable();
    $table->boolean('is_active')->default(true);
    $table->unsignedInteger('sort_order')->default(0);
    $table->timestamps();
    $table->softDeletes();

    $table->unique(['company_id', 'code']);
});

قائمة التاسكات للتنفيذ

الترتيبالتاسكالجانبالأولويةالتقدير
B-1مهاجرة + موديل + سيدر لجدول lab_unitsباكعالٍ٣٠ د
B-2مهاجرة الأعمدة الجديدة على lab_investigations (display_name، decimal_places، unit_id، default_value، static_comment، print_on_separate_page)باكعالٍ٢٠ د
B-3مهاجرة age_from_unit و age_to_unit على lab_investigation_normal_rangesباكمتوسط١٠ د
B-4تحديث Models، Requests، Resources بالحقول الجديدةباكعالٍ٣٠ د
B-5كنترولر + ريسورس + ريكوست لـ Units (CRUD)باكمتوسط٢٠ د
B-6تقريب النتائج عند الحفظ حسب decimal_placesباكحرج١٥ د
F-1تعديل name_en مطلوب + إضافة display_name fields في الـ formفرونتعالٍ١٥ د
F-2حقل decimal_places في الـ form (يظهر للـ numeric/formula بس)فرونتحرج١٥ د
F-3خدمة + موديل + شاشة بسيطة لإدارة الوحدات (CRUD)فرونتعالٍ٤٥ د
F-4تحويل حقل unit إلى p-select مع زر "+" inlineفرونتعالٍ٢٠ د
F-5في tab الـ Ranges: select وحدة بجوار age_from و age_toفرونتعالٍ١٥ د
F-6حقل القيمة الافتراضية (يتأقلم حسب result_type)فرونتمتوسط١٥ د
F-7حقلَي التعليق الثابت (عربي + إنجليزي) كـ textareaفرونتمتوسط١٠ د
F-8radio/toggle لـ print_on_separate_pageفرونتمتوسط٥ د
F-9شاشة الـ Results: استخدام decimal_places في الإدخال + استخدام default_value كقيمة ابتدائيةفرونتحرج٢٠ د
F-10خدمة الـ PDF: استخدام display_name + decimal_places + static_comment + print_on_separate_pageفرونتحرج٤٠ د
F-11محرك الـ Formula: تقريب الناتج حسب decimal_placesفرونتعالٍ١٠ د
F-12i18n: مفاتيح الترجمة الجديدةفرونتبسيط١٠ د

الإجمالي: ~٥ ساعات شغل (٣٢٠ دقيقة).

ترتيب التنفيذ المقترح

  1. المرحلة الأولى — البنية: B-1, B-2, B-3 (المهاجرات). تشغيل local-deploy.sh للتأكد.
  2. المرحلة الثانية — الباك اند: B-4, B-5, B-6. اختبار curl لكل endpoint.
  3. المرحلة الثالثة — شاشة الوحدات: F-3 (شاشة جديدة).
  4. المرحلة الرابعة — شاشة التحليل: F-1, F-2, F-4, F-5, F-6, F-7, F-8 (تعديلات كثيرة على نفس الشاشة).
  5. المرحلة الخامسة — التأثير على المخرجات: F-9 (إدخال النتائج)، F-10 (PDF)، F-11 (formula).
  6. المرحلة السادسة — الترجمة والاختبار: F-12 + اختبار يدوي شامل.

القرارات النهائية (مؤكدة من العميل 2026-05-25)

  1. التعليق الثابت — التحكم في الظهور: لكل تحليل radio button "اه/لاء" — لو "لاء" التعليق مش موجود أصلاً ولا يظهر/يطبع.
    تنفيذ: عمود إضافي has_static_comment (boolean, default false). لو true يظهر حقل التعليق ويُطبع.
  2. موضع التعليق الثابت في الطباعة:
    • في حالة "طباعة في صفحة منفصلة": التعليق يطبع في آخر الصفحة المنفصلة.
    • في حالة الطباعة المجمعة (الافتراضي): التعليق يظهر تحت التحليل مباشرة وقبل التحليل التالي.
  3. السلوك للتحاليل الموجودة (٢٠٠+): كل التحاليل القديمة الافتراضي عندها decimal_places = 0. المستخدم يقدر يعدل اللي محتاجه.
  4. وحدة العمر للنطاقات (REQ 5): الافتراضي = سنة (year) لكل نطاقات العمر الموجودة والجديدة.
  5. الحد الأقصى للخانات العشرية: ٤ (أكدتها بعد التأكيد).
  6. الوحدات (REQ 4) — لم تذكرها: خيار "أ" — جدول مرجعي + شاشة لإدارته (تقدر تضيف وحدة جديدة من نفسك).
تأثير القرارات على الخطة:

Moon ERP — LIS Module  |  Add Investigation Revamp Plan  |  جاهز للمراجعة