تحليل وخطة workflow التأجيل والرفض

وحدة: المختبر (LIS) — Moon ERP  |  التاريخ: 2026-05-25  |  نطاق: التجميع → الاستلام → الطلبات (٣ شاشات) + شاشة جديدة لإدارة الأسباب
٢
workflows جدد
٤
شاشات متأثرة
٦
تاسكات باك اند
٨
تاسكات فرونت
~٦ س
تقدير الشغل

الفكرة في صورة سريعة

التأجيل (Defer) في التجميع: لو المريض مش جاهز أو محتاج رجوع، الموظف يأجل العينة بسبب معين. تتولد عينة "مؤجلة" ببارود منفصل، تظهر في تب جديد، ومن صفحة الطلبات يقدر يرجع لها بضغطة واحدة.
الرفض (Reject) في الاستلام: لو العينة وصلت معطوبة، تترفض بسبب معين. تظهر في تب "العينات المرفوضة"، ومن صفحة الطلبات يقدر يعيد جمعها (كلها أو واحد منها).
إدارة الأسباب: شاشة منفصلة بـ ٢ تابات (Deferral / Rejection) — الأسباب بالإنجليزي فقط — CRUD كامل (إضافة، تعديل، حذف).
الـ Audit Trail: كل تأجيل/رفض/إعادة جمع يتسجل بتاريخ ووقت — لكن دائماً تحت نفس request_id، فالطلب الواحد يقدر يكون فيه عدة محاولات جمع.

الوضع الحالي — ما هو موجود فعلاً

📌 قاعدة التسمية (مؤكدة من العميل):
اكتشافات مهمة (يوفر شغل كبير):
ما يحتاج تنفيذ:

الـ Workflow التفصيلي

سيناريو ١: التأجيل (Defer)

١- المريض موجود لكن محتاج رجوع لاحقاً (مثلاً: مش صائم، أو دواء معين، أو غير قابل للحقن).
٢- الموظف في شاشة التجميع يضغط زر "Defer" بجوار المجموعة (Specimen Group).
٣- Dialog يفتح: قائمة أسباب التأجيل (من جدول الأسباب، category=deferral) + ملاحظات اختيارية.
٤- الموظف يختار السبب ويؤكد.
٥- يتولد sample جديد بـ:
   is_deferred = true
   status = pending (مش collected)
   deferred_reason = <reason_code>
   deferred_investigation_ids = [...] (التحاليل المؤجلة)
   barcode = <serial جديد>
٦- العينة تختفي من المجموعات الجارية، وتظهر في تب جديد "Deferred Samples" فوق.
٧- في صفحة الطلبات، الطلب يكتسب أيقونة 🕐 جديدة "Has deferred samples".
٨- لما المريض يرجع، الموظف يضغط على الأيقونة → ينقله لشاشة التجميع → التب المؤجلة → يضغط "Collect" → العينة تتحول من pending إلى collected (نفس flow التجميع العادي).

سيناريو ٢: الرفض (Reject) — في الاستلام فقط

١- العينة وصلت لشاشة الاستلام بعد التجميع، وبتم اكتشاف إنها معطوبة (هيمولايز / كمية قليلة / حاوية غلط / barcode تالف).
٢- Dialog (محدّث): dropdown أسباب الرفض من جدول الأسباب (category=rejection) — بدل التكست بوكس الحرة.
٣- العينة تتحول إلى status = rejected مع rejected_at + rejected_by + rejection_reason.
٤- في صفحة الطلبات، الطلب يكتسب أيقونة ⚠️ جديدة "Has rejected samples".
٥- لما يضغط على الأيقونة → Dialog يعرض التحاليل المرفوضة كـ checkboxes:
٦- بعد التأكيد، عينة جديدة واحدة تتولّد بالتحاليل المختارة فقط (نفس الطلب، بارود جديد) — تروح لشاشة التجميع.

سيناريو ٣: إعادة الجمع المتعددة في نفس الموعد

المريض ممكن يكون عنده عدة تحاليل مؤجلة ويرجع جميعها في نفس اليوم.
في صفحة الطلبات يضغط 🕐 → Dialog يعرض كل التحاليل المؤجلة (من كل العينات المؤجلة) كـ checkboxes.
يختار "Select All" أو يختار تحاليل معينة فقط (مثلاً يرجع لجزء منها بس).
يضغط "Recollect" → عينة جديدة بالتحاليل المختارة تظهر في شاشة التجميع.
باقي التحاليل (لو مختارش الكل) تفضل في التب "Deferred" لحين رجوعها.

سيناريو ٤: طباعة بارود العينة المؤجلة من صفحة الطلبات

جنب أيقونة 🕐 (المؤجلة) في صفحة الطلبات، أيقونة 🖨️ "Print deferred barcode".
الضغط عليها يطبع labels للعينات المؤجلة فقط (50×25mm زي العادي) لو فيه عينات مؤجلة معاها بارود متولد.
المستخدم يلصقها على الـ tube ويأخذها مع المريض لما يرجع.

تصميم الـ UI الجديد

شاشة التجميع — التابات الجديدة

┌────────────────────────────────────────────────────────────┐
│ Collection Worklist  [Active 8]  [Deferred 3]  [Rejected 1]│  ← tabs
├────────────────────────────────────────────────────────────┤
│                                                            │
│  Active tab:  (السلوك الحالي بدون تغيير)                    │
│  Deferred tab:  جدول بكل المؤجلات                          │
│    - barcode + تحاليل + سبب التأجيل + تاريخ + Recollect btn │
│  Rejected tab:  جدول بكل المرفوضات                         │
│    - barcode + سبب الرفض + تاريخ + Recollect btn           │
└────────────────────────────────────────────────────────────┘

أزرار الـ Specimen Group في التجميع (محدّث)

قبل:
┌─ Blood (Serum) ──────────────────────────────────┐
│  [📋 Collect]  [🚫 Reject]  [🏥 Refer]            │
└───────────────────────────────────────────────────┘

بعد:
┌─ Blood (Serum) ──────────────────────────────────┐
│  [📋 Collect]  [🕐 Defer]  [🏥 Refer]             │
│                  ↑                                 │
│              زر جديد (بديل الـ Reject)             │
└───────────────────────────────────────────────────┘

(الـ Reject الموجود حالياً في التجميع يتشال
 لأنه فعلياً غير منطقي — العينة لسه ما اتجمعتش)

أزرار شاشة الاستلام (تحديث dialog فقط)

قبل (الاستلام):
┌─ Scan & Receive ─────────────────────────────────┐
│  [✓ Accept]    [✗ Reject (textarea للسبب)]       │
└───────────────────────────────────────────────────┘

بعد:
┌─ Scan & Receive ─────────────────────────────────┐
│  [✓ Accept]    [✗ Reject (dropdown من الأسباب)] │
└───────────────────────────────────────────────────┘

(الزر نفسه ما يتغير — بس الـ dialog يستخدم dropdown
 بدل textarea حرة)

صفحة الطلبات — الأيقونات الجديدة

┌─ Lab Requests ─────────────────────────────────────────────────┐
│ #     | Patient    | Status     | Indicators                    │
│ R-001 | Ahmed Ali  | pending    | 🕐(2) 🖨️  ⚠️(1)              │  ← icons
│ R-002 | Sara Mahmd | in_progress|                                │
└────────────────────────────────────────────────────────────────┘

🕐 = has deferred samples (count badge) → opens Recollect Dialog
🖨️ = print deferred barcodes only (visible only when 🕐 > 0)
⚠️ = has rejected samples (count badge) → opens Recollect Dialog

Recollect Dialog (المؤجل أو المرفوض)

┌─ Recollect Deferred Investigations ─────────────────┐
│  Patient: Ahmed Ali  |  Request: R-001               │
│  ───────────────────────────────────────────────────  │
│  ☑ Select All                                        │
│                                                       │
│  ☑ Glucose     (deferred: Patient not fasting)       │
│  ☑ HbA1c       (deferred: Patient not fasting)       │
│  ☐ Cholesterol (deferred: Awaiting medication)       │
│                                                       │
│  Patient appointment: [date+time] (optional)         │
│  ───────────────────────────────────────────────────  │
│              [Cancel]    [Recollect Selected (2)]    │
└──────────────────────────────────────────────────────┘

(الشكل نفسه للمرفوضات مع تغيير العنوان فقط)

شاشة إدارة الأسباب (جديدة)

┌─ Sample Reasons ──────────────────────────────────────────┐
│  [Deferral Reasons]  [Rejection Reasons]                  │  ← tabs
├───────────────────────────────────────────────────────────┤
│  + New Reason                                              │
│                                                            │
│  ┌──────┬─────────────────────┬─────┬────────┐           │
│  │ Code │ Name (English)      │Sort │ Active │           │
│  ├──────┼─────────────────────┼─────┼────────┤           │
│  │ DEF1 │ Patient not fasting │  1  │   ✓   │           │
│  │ DEF2 │ Awaiting medication │  2  │   ✓   │           │
│  │ DEF3 │ Patient ill         │  3  │   ✓   │           │
│  └──────┴─────────────────────┴─────┴────────┘           │
└───────────────────────────────────────────────────────────┘

تغييرات الباك اند

B-1: قاعدة البيانات

لا نحتاج جدول جديد! جدول lab_rejection_reasons موجود فيه category field. هنستخدمه:

مهاجرة بسيطة لو محتاج: نضيف category default value أو نتأكد من إنه nullable.

B-2: Endpoint للتأجيل

POST /api/lis/samples/{id}/defer
Body: {
  reason_code: string,    // من lab_rejection_reasons (category=deferral)
  notes?: string,
  investigation_ids: int[] // التحاليل المراد تأجيلها
}

Behavior:
- لو الـ sample الأصلي عنده تحاليل كثيرة وبس بعضهم اتأجل:
  · ينشئ sample جديد بـ is_deferred=true يحوي التحاليل المؤجلة فقط
  · الـ sample الأصلي يفضل بتحاليله المتبقية (إن وجدت)
- لو كل التحاليل اتأجلت: الـ sample الأصلي يتحوّل لـ is_deferred=true بدلاً من جديد

B-3: Endpoint للـ Recollect

// نسخة per-request (للـ icon في صفحة الطلبات)
POST /api/lis/requests/{requestId}/recollect
Body: {
  source: 'deferred' | 'rejected',
  investigation_ids: int[]  // التحاليل المراد إعادة جمعها (لازم على الأقل واحد)
}

Behavior:
- ينشئ sample جديد واحد للـ request نفسه بكل التحاليل المختارة
- barcode جديد، status=pending، collected_at=null، is_deferred=false
- التحاليل المختارة "تتحرك" من العينات المؤجلة/المرفوضة لعينة جديدة جاهزة للجمع
- باقي التحاليل في العينات المؤجلة/المرفوضة تفضل كما هي (لو ما اخترتش الكل)
- التواريخ القديمة محفوظة (audit) — الـ samples الأصلية مش بتتمسح

B-4: List + Filter Endpoints

GET /api/lis/samples?is_deferred=true&status=pending
GET /api/lis/samples?status=rejected
GET /api/lis/requests?has_deferred=true
GET /api/lis/requests?has_rejected=true

B-5: CRUD لأسباب التأجيل والرفض

الـ LabRejectionReasonController موجود غالباً (لأن الـ rejection شغّال). محتاج نضيف category filter في الـ index:

GET /api/lis/rejection-reasons?category=deferral
GET /api/lis/rejection-reasons?category=rejection
POST /api/lis/rejection-reasons    (body فيه category)
PUT /api/lis/rejection-reasons/{id}
DELETE /api/lis/rejection-reasons/{id}

B-6: تحديث الـ Resource للطلب

// LabRequestResource: إضافة counters
'deferred_samples_count' => $this->samples()->where('is_deferred', true)->where('status', 'pending')->count(),
'rejected_samples_count' => $this->samples()->where('status', 'rejected')->count(),

تغييرات الفرونت اند

F-1: شاشة إدارة الأسباب (جديدة)

المسار: /lab/sample-reasons

F-2: في التجميع — استبدال Reject بـ Defer

src/app/features/lis/samples/lis-samples.component.html + .ts

F-10: في الاستلام — تحديث Reject dialog فقط

src/app/features/lis/specimen-receiving/specimen-receiving.component.html + .ts

F-3: تبات Deferred / Rejected في شاشة التجميع

F-4: أيقونات في صفحة الطلبات + Print Barcode

src/app/features/lis/requests/lis-requests.component.html

F-5: deep-link في شاشة التجميع

F-6: Recollect Dialog مشترك (للمؤجل والمرفوض)

F-9: طباعة بارود العينات المؤجلة من صفحة الطلبات (جديد)

F-7: تحديث خدمة الـ samples

src/app/core/services/lis-sample.service.ts

F-8: i18n keys

قائمة التاسكات بالتفصيل

الترتيبالتاسكالجانبالأولويةالتقدير
B-1تأكيد بنية lab_rejection_reasons + seed أسباب deferral الأساسيةباكمتوسط٢٠ د
B-2POST /samples/{id}/defer endpoint + service logicباكحرج٣٠ د
B-3POST /samples/{id}/recollect endpoint + service logicباكحرج٤٠ د
B-4تحديث LabRequestResource بالعدادات + filter في samples listباكعالٍ٢٠ د
B-5تحديث LabRejectionReasonController بالـ category filter + CRUDباكعالٍ٢٠ د
B-6i18n للـ messages + validation rulesباكمتوسط١٠ د
F-1شاشة إدارة الأسباب الجديدة (component + service + model + routes)فرونتعالٍ٤٥ د
F-2في التجميع: إزالة Reject + إضافة Defer (زر + dialog + dropdown أسباب)فرونتحرج٣٠ د
F-3تبات Deferred/Rejected/Active في شاشة التجميعفرونتحرج٤٠ د
F-4أيقونات الإشعار في صفحة الطلباتفرونتعالٍ٢٥ د
F-5deep-link query params في شاشة التجميعفرونتمتوسط١٥ د
F-6Recollect Dialog مشترك (مؤجل/مرفوض) — checkboxes + select all + countفرونتعالٍ٢٥ د
F-7تحديث LisSampleService بـ defer + recollect + list helpersفرونتعالٍ١٥ د
F-8i18n keys (en + ar) لكل النصوص الجديدةفرونتمتوسط١٥ د
F-9طباعة بارود العينات المؤجلة من صفحة الطلبات (button + print logic)فرونتمتوسط١٥ د
F-10في الاستلام: تأكيد dropdown أسباب الرفض (مع filter category=rejection)فرونتمتوسط١٠ د

الإجمالي: ~٦-٧ ساعات شغل.

الأسئلة المحتاجة قرارك قبل البدء

  1. عند التأجيل، الـ barcode يتعمل تلقائياً ولا الموظف يطبعه بإيده؟
    الاقتراح: تلقائي (يتطبع label تلقائياً مع رسالة "Sample deferred — barcode XX-001-D"). لو المريض رجع نفس اليوم بسرعة، الـ recollect ينشئ barcode جديد جديد.
  2. المؤجل في الـ Dashboard / KPIs: يحسب ضمن "Total Requests" ولا منفصل؟
    الاقتراح: ضمن الإجمالي، لكن مع KPI منفصل "Deferred Today" + "Rejected Today" في الـ Dashboard.
  3. هل التأجيل يحتاج تأكيد إضافي؟ (لأنه يعدّل في الـ workflow)
    الاقتراح: dialog واحد بالـ reason + confirm، مفيش modal تأكيد إضافي.
  4. الفاتورة (Invoice): لما العينة تتأجل أو ترفض، التحاليل تظل في فاتورة الطلب أم تنزل القيمة؟
    الاقتراح: تظل في الفاتورة (المريض دفع للتحليل، إعادة الجمع بدون رسوم إضافية).
  5. أيقونات في صفحة الطلبات: عمود منفصل ولا inline داخل عمود الـ status؟
    الاقتراح: عمود "Indicators" منفصل علشان واضح وقابل للفلترة.
  6. الـ Audit: لو نفس العينة اتأجلت ثم اتجمعت ثم اترفضت ثم اتجمعت تاني، نسجل كل ده؟
    الاقتراح: نعم، لكن من خلال جدول samples (كل عملية = sample row جديد) — مش audit log منفصل. يكفي الدالة الموجودة.

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

  1. الفاز الأول — البنية (~٢ ساعة): B-1, B-2, B-3, B-4, B-5, B-6 + F-7 (service)
  2. الفاز الثاني — شاشة الأسباب (~٤٥ دقيقة): F-1 (شاشة إدارة الـ Sample Reasons)
  3. الفاز الثالث — التجميع (~٧٠ دقيقة): F-2 (زر Defer + dialog) + F-3 (التبات)
  4. الفاز الرابع — صفحة الطلبات (~٤٥ دقيقة): F-4 (الأيقونات) + F-5 (deep-link) + F-6 (recollect dialog)
  5. الفاز الخامس — الترجمة (~١٥ دقيقة): F-8

Moon ERP — LIS Module  |  Defer & Reject Workflow Plan  |  جاهز للمراجعة