🔬 تشخيص شامل: مشكلة Defer/Recollect في صفحة التجميع
تاريخ التشخيص: ٢٦ مايو ٢٠٢٦
الـ Request المتأثر: LR-2026-00145 (id=87) — Lipid Panel
الـ Environment: Dev backend (moonui.elbaset.com/moon-erp-be/api)
١. الأعراض اللي ظهرت
بعد ما أجلت
TG من Lipid Panel وعملت Recollect:
- الـ Recollected sample (
SMP-2026-05-00103) بيظهر بأكواد LIPID — المفروض يكون TG فقط
- الـ BIO tube الأصلي (
2605266XHLQH-BIO) كمان بيظهر LIPID — وده تقني صحيح لكن مش مفيد للفني
٢. البيانات الفعلية في الـ Database
تركيب الـ Request 87
| Pivot ID | Investigation ID | Code | is_panel | price |
| 1452 | 84 | LIPID | true (parent) | السعر الكامل |
| 1453 | 15 | CHOL | false | 0.000 |
| 1454 | 16 | TG | false | 0.000 |
| 1455 | 39 | HDL | false | 0.000 |
| 1456 | 40 | LDL | false | 0.000 |
| 1457 | 41 | VLDL | false | 0.000 |
السامبلز الـ ٣ الموجودة للـ Request
| ID | Sample # | Barcode | Status | is_deferred | parent_id | deferred_investigation_ids | Notes |
| 269 | SMP-2026-05-00101 | 2605266XHLQH | pending | false | null | null | الـ parent — مخفي لأن عنده child |
| 270 | SMP-2026-05-00101-BIO | 2605266XHLQH-BIO | delivered | false | 269 | null | الـ BIO aliquot |
| 272 | SMP-2026-05-00103 | SMP-2026-05-00103 | collected | false | null | [16] ← TG فقط! | Recollected from deferred samples |
✓ ملاحظة مهمة: الـ database فعلياً مخزّن deferred_investigation_ids = [16] على sample 272 (TG فقط). يعني الـ Backend اشتغل صح، والـ FE PUT workaround بتاعنا فعّال.
٣. ليه الـ UI بيعرض "LIPID" بدل "TG"؟
الـ Logic الحالي في الـ FE
// collection-worklist.component.ts — tubes builder
if (isDeferred) {
// sample مؤجل — display codes من deferred_investigation_ids
} else if (isExt) {
// sample خارجي
} else if (isRecollected) {
// sample مش مؤجل لكن عنده defer_ids → scoped
for (const inv of allInvs) {
const invId = inv.investigation?.id || inv.investigation_id;
if (!scopedIds.has(invId)) continue;
const invIsPanel = !!inv.investigation?.is_panel;
if (invIsPanel) continue;
const code = inv.investigation?.code || '';
if (code) tubeCodes.push(code);
}
} else {
// sample عادي — display billable tests only (panel parent = single test)
const billable = filterBillableTests(allInvs);
...
}
التطبيق على sample 272
scopedIds = new Set([16]) // TG
isRecollected = true
loop over pivot_investigations:
pivot 1452 → inv_id=84 (LIPID) → scopedIds.has(84)? NO → skip ✓
pivot 1453 → inv_id=15 (CHOL) → scopedIds.has(15)? NO → skip ✓
pivot 1454 → inv_id=16 (TG) → scopedIds.has(16)? YES, is_panel=false → ADD "TG" ✓
pivot 1455 → inv_id=39 (HDL) → scopedIds.has(39)? NO → skip ✓
...
النتيجة المتوقعة: tubeCodes = ["TG"]
النتيجة الظاهرة: tubeCodes = ["LIPID"] ← المشكلة!
الـ logic صحيح ١٠٠٪. فلازم يكون فيه واحد من ٣ احتمالات:
- Browser cache: الـ JS chunk الجديد مش بيتحمل فعلياً (lazy chunk بيستجيب من cache)
- الـ chunk اللي يحتوي على الـ logic مش بيتحمل أصلاً (route lazy loading + service worker)
- الـ `isRecollected` branch مش بيتنفذ لسبب خفي (ممكن `scopedIds.size === 0` نتيجة مشكلة JSON parsing)
اختبار سريع لتحديد السبب
الخطوات للمستخدم
- افتح
https://moonui.elbaset.com/app/lab/samples
- اعمل Hard Refresh:
Ctrl+Shift+R (Windows) أو Cmd+Shift+R (Mac)
- افتح Developer Tools (
F12) → tab "Network"
- اعمل refresh تاني، شوف لو الـ chunks بترجع بـ status
200 ولا 304
- افتح tab "Console" واطبع:
localStorage.clear(); sessionStorage.clear(); وriload
٤. المشكلة الجوهرية في الـ Architecture
الـ system بُني على افتراض:
"تحليل واحد = عينة واحدة محددة" — مفيش جدول
sample_investigations صريح. بدلاً منه:
- الـ sample بيتربط بـ
lab_section_id فقط (بنك واسع)
- الـ FE بيـ "يستنتج" أي tests في أي tube عن طريق intersection بين
request_investigations + lab_section_id + specimen_type_id
- الـ panel parent (price > 0) بيمثل الـ panel كله، والـ members (price = 0) مستثنين من العرض
نحن دلوقتي بنوسع
deferred_investigation_ids لتعمل overload: تخدم defer وكمان تخدم "scoped recollect". ده يشتغل لكنه هشّ.
٥. الحل المقترح (Structural)
الخيار أ: حل سطحي (Quick fix)
لو الـ
isRecollected branch صحيح، السبب الحقيقي هو browser cache. الحل:
- هارد refresh من المتصفح
- تأكد إن الـ FE الـ deployed عنده
isRecollected branch مفعّل
- لو لسه المشكلة موجودة، الـ scopedIds مش بتترجم تمام من JSON — نشيك على JSON parsing
الخيار ب: حل ميكانيكي (Workaround)
نضيف عمود
scoped_investigation_ids منفصل عن
deferred_investigation_ids:
| عمود | الاستخدام |
is_deferred + deferred_investigation_ids | المؤجلات فقط (is_deferred=true) |
(جديد) scoped_investigation_ids | أي sample (مؤجل أو لأ) عندها قيود على الـ tests اللي تنفع تطلع منها |
المميزات: سيمنتك أوضح، لا مزيد من overload.
التكلفة: migration جديد + تعديل LabSampleService::recollect.
الخيار ج: حل جذري (Proper structural fix)
نـ introduce جدول
lab_sample_investigations:
lab_sample_investigations
├── id
├── lab_sample_id → fk
├── lab_investigation_id → fk
└── status enum (pending/collected/deferred/rejected/recollected)
كل sample بيبقى متضمن قائمة
صريحة بالـ tests اللي عليها. لا مزيد من الـ "استنتاج" من section/specimen_type.
المميزات:
- تحليل أي tube مباشر — مفيش inference
- الـ panel members ممكن يبقوا individual
- الـ flow بتاع defer/recollect/reject يبقى consistent
التكلفة: migration كبيرة + إعادة كتابة الـ FE display logic + إعادة كتابة الـ aliquot logic + الـ kanban/results يحتاجوا تحديث.
٦. توصيتي
المرحلة الأولى — اليوم:
- تأكد إن الـ browser cache فاضي عند الـ user
- لو الـ
isRecollected branch مش شغّال، نتأكد إنه deployed بـ console.log debug
- اختبر بـ Lipid Panel جديد، أجل TG واحد، شوف الـ display
المرحلة الثانية — بعد الـ kickoff: نتجه للـ
الخيار ج (جدول
lab_sample_investigations). ده الـ fix الجذري اللي يخلي:
- defer واحد من panel = خد ID الـ member
- recollect = خد نفس الـ IDs بدون أي inference
- الـ aliquot logic يقرأ من الجدول مباشرة
- الـ display مفيهوش "panel parent = الكل" — كل tube بيعرض tests الفعلية
بس ده شغل ٢-٣ أيام، ومش هينفع نضربه في feature branch. لازم سبرنت منفصل.
٧. سؤال للمستخدم
- عملت Hard Refresh للصفحة؟ لو لأ، اعمل وقولي لو لسه التحليل بيظهر "LIPID"
- هل تفضل المضي في الخيار ب (سهل، يوم واحد) أو الخيار ج (جذري، أسبوع)؟
- هل في عينات تانية في النظام نفس المشكلة عليها، ولا فقط
SMP-2026-05-00103؟