⬢ دراسة + خطة حل — بانتظار الاعتماد قبل التنفيذ

رفض عينات B2B:
العميل لا يعلم أبداً أن تحليله رُفض

معمل العميل يرسل طلباً عبر البورتال، عينته تُرفض داخلياً (عينة منحلّة، كمية غير كافية…) — واليوم لا يصل للعميل أي أثر لذلك: التحليل يظل «قيد الانتظار» للأبد، والعينة البديلة تُوجَّه لقائمة سحب معملنا الداخلي رغم أن المريض موجود عند العميل. هذه الدراسة توثّق الوضع بالدليل السطري وتقترح الحل.

📅 التاريخ: 10 يونيو 2026 🔍 المنهجية: 3 مسارات تحليل متوازية، قراءة فقط 🧪 النطاق: 3 مسارات رفض × 4 أسطح بورتال
0
مرة تظهر كلمة "rejected" في كود بورتال العميل كله
3
مسارات رفض داخلية (عينة / كانبان / فقدان المندوب)
0
إشعار أو حقل سبب يعبر حدود البورتال
+2
خلل إضافي اكتُشف أثناء الدراسة (أخطرها is_complete)
2
مرحلتا حل مقترحتان (~4 أيام عمل)
1

الخلاصة التنفيذية

Executive Summary

الرفض الداخلي موثّق بالكامل عندنا (حالة العينة، السبب، التوقيت، المنفّذ، تسلسل كل تحليل) — لكن ولا معلومة واحدة منه تُسلسَل لأي endpoint من endpoints البورتال. خريطة حالة التحليل في البورتال تعرف ثلاث قيم فقط: released / partial / pending — فالتحليل المرفوض يُبلَّغ كـ«pending» إلى الأبد، والتقرير المطبوع يُسقطه بصمت بلا هامش.

الأخطر من الإظهار — التوجيه: كل مسارات إعادة السحب تُنشئ العينة البديلة بحالة Pending الداخلية، فتهبط على قائمة سحب معملنا — بينما المريض جالس عند معمل العميل. والبورتال لا يستطيع جمعها حتى لو عَلِم (نداءات الجمع تقبل PendingCollection فقط)، ومنظومة المناديب لا تراها (تغذيتها at_external_lab فقط). أي: بعد أي رفض لعينة B2B، الطلب يدخل طريقاً مسدوداً لا يخرج منه إلا بتدخل يدوي خارج النظام.
خللان إضافيان اكتُشفا أثناء التتبع: (1) is_complete يُحسب من صفوف النتائج فقط — تحليل رُفض قبل أن تُنشأ له نتيجة يسقط من المقام، فقد يظهر الطلب للعميل «مكتمل ✓» وفيه تحليل مرفوض ينتظر إعادة سحب. (2) ربط «الأصلية ↔ البديلة» مختلف في كل مسار: الكانبان يربط بعمود خاص، فقدان المندوب بـ parent_id، وإعادة السحب اليدوية بلا أي ربط.
2

الوضع الحالي — ماذا يُسجَّل داخلياً مقابل ما يعبر للبورتال

Internal Signals vs Portal Boundary
الإشارةتُسجَّل داخلياً؟تعبر للبورتال؟الدليل
حالة العينة rejected + السبب + التوقيت + المنفّذنعم — كاملةالحالة الخام فقط بلا سبب — وتُعرض بلا ترجمة ولا تنسيق (chip شفاف)LabSampleService.php:207-242show() :730client-shared.scss:126-157
رفض تحليل بعينه (pivot) + السببنعم (السبب في اللوج فقط)لا — يُبلَّغ «pending» للأبدExternalLabPortalRequestController.php:606-619
رفض الكانبان (مسار أقدم منفصل) + عينة بديلة مربوطةنعم — بعمود ربط خاصلا شيءKanbanRejectService.php:20-148
فقدان المندوب للعينة (مرفوضة + بديلة تلقائية)نعم — بربط parent_idلا شيء — العميل لا يعلم أن شحنته فُقدت!CourierPickupService.php:365-513
العينة البديلة قابلة للتنفيذ من طرف العميللا — تُنشأ Pending داخلية: البورتال لا يجمعها والمندوب لا يراها وready-for-pickup لا يُعاد فتحهrecollect :305collectAll :991CourierPickupService.php:114
عدّاد «مرفوض» للطلبموجود لشاشات الموظفينغير موجود في index()LabRequestResource.php:80
نتيجة منشورة أُبطلت بسبب الرفضInvalidated + سببتختفي من التقرير بصمتLabSampleService.php:182-193
كتالوج أسباب الرفض ثنائي اللغةموجود لكن «عرض فقط» — النداءات تقبل نصاً حراً فتضيع الترجمةlab_rejection_reasonsLabSampleController.php:382-388
أي آلية إشعار لمعامل البورتاللا توجد إطلاقاً — لا Notifiable ولا أحداث رفض ولا polling مخصصgrep: 0 hits
3

تجربة العميل اليوم ← وبعد الحل

Client Experience: Today vs Proposed

اليوم — القائمة

LR-2026-00321 · أحمد محمد
In Progress وصلت للمعمل
عدد العينات كبر فجأة (ظهرت البديلة) بلا تفسير، والشارة الخضراء تطمئنه بينما عينته مرفوضة

اليوم — التفاصيل

CBC  pending … للأبد
عينة SMP-118  rejected (شفافة بلا ستايل ولا سبب)
عينة SMP-121  pending (مين دي؟)

اليوم — التقرير

التحليل المرفوض غير موجود أصلاً في التقرير — لا قيمة ولا هامش ولا اعتذار

بعد الحل — القائمة

LR-2026-00321 · أحمد محمد
In Progress 1 مرفوض — يلزم إعادة سحب

بعد الحل — التفاصيل

CBC  مرفوض — عينة منحلّة 10:42ص
↳ بديلة SMP-121  بانتظار سحبكم  
وللمعامل المفعّل عندها المناديب: بعد الجمع تدخل سلسلة المندوب تلقائياً

بعد الحل — التقرير

CBC — رُفضت العينة (عينة منحلّة) وأُعيد طلب السحب بتاريخ …
هامش صريح بدل الاختفاء الصامت
4

خطة الحل — مرحلتان

Two-Phase Solution
المرحلة 1 — الخادم

الحقيقة تعبر الحدود + توجيه البديلة صح

⏱ ~يومان · Pest جديدة + تغطية الرجعية بالسويتات القائمة
  • show(): حالة تحليل rejected + سبب الرفض (ثنائي اللغة عبر كود الكتالوج عند توفره، وإلا النص الحر) + التوقيت؛ وحالة recollect_pending للتحاليل المعاد سحبها؛ والعينات تحمل rejection_reason / rejected_at / parent_sample_id
  • توحيد الربط على parent_id في المسارات الثلاثة (الكانبان واليدوي يلحقان بمسار المندوب)
  • index():rejected_count + recollect_pending_count + إصلاح خلل is_complete (العدّ من تحاليل الطلب لا من صفوف النتائج فقط)
  • توجيه البديلة لطلبات B2B (helper واحد لكل المسارات): تبدأ PendingCollection ليجمعها العميل بنداءات الجمع الموجودة فعلاً؛ وبعد جمع البورتال، لو المعمل مفعّل عنده المناديب تتحول at_external_lab وتظهر للمندوب فوراً (بلا ready ثانية — هي مجموعة بالفعل). الطلبات الداخلية تبقى كما هي (Pending)
  • يبقى recollect قرار staff صريحاً كما هو اليوم (التاريخ أثبت أن الإنشاء التلقائي يولّد أنابيب مكررة) — باستثناء مساري الكانبان والمندوب المؤتمتين أصلاً
المرحلة 2 — واجهة البورتال

العميل يرى ويتصرف

⏱ ~يومان · يشمل بوابة المريض حيث ينطبق
  • القائمة: شارة حمراء «N مرفوض — يلزم إعادة سحب» في عمود الحالة + إصلاح شارة البيك أب المضللة («وصلت للمعمل» رغم الرفض)
  • التفاصيل: chip «مرفوض» مترجم بستايل + السبب والتوقيت + صف البديلة متداخلاً تحت الأصلية بحالة «بانتظار سحبكم» وزر «جُمعت — اطبع الباركود» (ولمعامل المناديب: تتبُّع دخولها السلسلة)
  • التقرير المطبوع: هامش «رُفضت العينة (السبب) وأُعيد طلب السحب» بدل الإسقاط الصامت — في القوالب الستة عبر العقد القائم
  • رئيسية البورتال: عدّاد «بانتظار إعادة سحب» ضمن المؤشرات
  • الإشعار v1 (موصى به): سلبي بالكامل — شارات وعدّادات بلا بنية تحتية جديدة؛ ‏v2 اختياري لاحقاً: endpoint إشعارات بورتال بالـ polling
لماذا هذا التقسيم آمن؟ المرحلة 1 كلها إضافات حقول وعدّادات + توجيه حالة البديلة (سلوك جديد لطلبات B2B فقط — الداخلي لا يتغير). المستهلك الوحيد الحساس هو is_complete وإصلاحه يصحّح كذبة قائمة أصلاً. اختبارات Pest بورتال جديدة + السويتات القائمة (B2B/Courier/PdfParity) تغطي الرجعية، وهامش التقرير يدخل تحت حماية عقد القوالب التشغيلي.
5

قرارات مطلوب اعتمادها قبل التنفيذ

Decisions Needed

القرار 1 — مسار البديلة لمعامل المناديب

بعد أن يجمع العميل العينة البديلة من البورتال: تدخل سلسلة المندوب تلقائياً (at_external_lab) أم تحتاج «جاهزة للاستلام» جديدة؟

التوصية: تلقائياً — العميل أكد الجمع للتو، وطلب «ready» ثانية خطوة بيروقراطية ستُنسى وتعلّق البديلة. (مع تسجيل custody كالمعتاد.)

القرار 2 — فوترة التحليل المرفوض نهائياً

فاتورة B2B تُنشأ مقدماً بكامل التحاليل. تحليل رُفض ولم يُعَد سحبه أبداً (أو ألغاه العميل بعد الرفض): يبقى على الفاتورة أم يُخصم؟ خارج نطاق هذه الخطة تقنياً لكنه سيُسأل عنه فور ظهور شارة «مرفوض».

التوصية: v1 يبقى على الفاتورة مع ظهور الحالة بشفافية، ويُعالج الخصم ضمن دورة المطالبات الشهرية كقرار مالي منفصل.

القرار 3 — إشعارات v2

هل نكتفي بالشارات (v1) أم نضيف endpoint إشعارات بورتال (polling) يعرض «رُفضت عينة في LR-321 — السبب…» كقائمة؟

التوصية: v1 الآن؛ وv2 يُقيَّم بعد أسبوعين من الاستخدام الفعلي — البيانات ستقول إن كانت الشارات كافية.
6

منهجية الدراسة

Methodology
  • 3 مسارات تحليل متوازية (قراءة فقط): مسارات الرفض في الخادم وتتبّع كل ما يُكتب، أسطح البورتال الأربعة (قائمة/تفاصيل/تقرير/رئيسية)، وتركيب الفجوة وتصميم الحل بتحقق مستقل.
  • كل ادعاء في هذا المستند موثّق بملف وسطر — شاملاً الخللين الإضافيين (is_complete والربط المتضارب) اللذين لم يكونا في سؤال الدراسة الأصلي.
  • بوابة المريض فُحصت أيضاً: تعاني العمى نفسه — الحل يُصمَّم بحيث تتشاركه حيث ينطبق.