ترجمة لمطلبين وسؤال:
المنهج: قرأت قاعدة المعرفة أولاً، ثم فحصت الكود بثلاثة وكلاء متوازيين على أعلى موديل، ثم تحققت من بياناتك الفعلية على moonui3_dev_be. كل ادعاء تحته مسار ملف ورقم سطر.
// MarkEncounterLabComplete.php:22 if (! $request->encounter_id) { return; } // ← حارس ١ ... ->where('status', OrderLineStatus::Ordered->value) // ← حارس ٢وبياناتك الفعلية بتقول: طلبك LabRequest #1 → encounter_id = NULL (زيارة تحاليل-بس مالهاش encounter — بالتصميم)، وبند العيادة حالته billed (لأنك دفعت). يعني الحارسين الاتنين بيمنعوا التحديث. نفس الحارس حرفياً في DropLabReportOnEncounter — فالتقرير كمان ما بينزلش.
| المحطة | الدليل |
|---|---|
| الاختيار | create-visit.component.ts → GET /lis/investigations?with_prices=1 — كتالوج المعمل مباشرة |
| التسعير | LabPriceResolver (بُني في ISS-2026-0176): B2B ← قائمة الدكتور ← القائمة الافتراضية ← الكتالوج |
| الفوترة | OrderLabFromEncounter::core() → ServiceOrderLine واحد: line_type=lab · price_source='lis' · fulfillment_type='lab_request' |
| طلب المعمل | CreateLabRequest::handleFromArray → LabRequest بـsource=clinic · فاتورة LIS مقموعة (العيادة تملك الفوترة) |
| العيّنة | LabSampleService::generateForRequest — لحظة إنشاء الطلب، قبل أي دفع (CreateLabRequest.php:586). مفيش أي حارس دفع. |
| المحطة | الدليل |
|---|---|
| الإنشاء | شاشة clinic/services بتسمح بنوع lab (clinic-service-list.component.ts:180) — في أي وضع، بدون أي منع. وحقل lis_investigation_id = خانة رقم عارية يكتب فيها المستخدم ID بإيده (.html:482) — بدون بحث ولا تحقق. |
| السعر | بيتزرع تلقائياً في clinic_service_prices (SeedBaseServicePrice.php) — سعر تاني مستقل تماماً عن سعر المعمل. |
| الظهور | ❌ مش في شاشة الحجز (مستبعدة صراحةً: create-visit.component.ts:492) · ❌ مش في شاشة الطبيب في الوضع المتكامل · ✅ في الاستقبال · ✅ في مصفوفة التسعير · ✅ في العقود التأمينية · ✅ في التقارير |
| 🔴 التنفيذ | لا شيء. AddServiceOrderLine بيخزّن السطر ويحاسب — ومفيش LabRequest. التحليل عمره ما يوصل المعمل. |
لو عملت خدمة اسمها CBC نوع تحليل بسعر 200، وعندك في المعمل CBC بسعر 150 من قائمة الأسعار:
| البُعد | النتيجة |
|---|---|
| الكتالوج | 🔴 صفّان مستقلان — clinic_services وlab_investigations. مفيش أي قيد تفرّد ولا كشف تكرار بينهم. |
| السعر | 🔴 سعران حيّان. 200 عبر PricingResolver · 150 عبر LabPriceResolver. المطبَّق يعتمد على أي شاشة استُخدمت — لا على البيانات. |
| الطبيب | في الوضع المتكامل (الافتراضي عندك) بيشوف CBC بتاع المعمل فقط. خدمة الـ200 مالهاش أي مدخل — وده حرفياً «اللي في السيرفس مش بتظهر في الزيارة». |
| الاستقبال | 🔴 يقدر يضيف CBC بطريقتين على نفس الزيارة → احتمال فاتورة مزدوجة على نفس التحليل. |
| المعمل | 🔴 CBC اللي اتضاف كخدمة عيادة عمره ما هيوصل الـworklist. |
| التقارير | 🔴 انقسام مؤكد. سطر المعمل clinic_service_id = null ⇒ خارج تجميع القسم · سطر الخدمة ⇒ داخله. نفس التحليل في bucketين. وزيادة: DecideClinicalIntent.php:110 بيسجّل التحليل المستقل بـline_type='clinic_service' مش 'lab' ⇒ بيختفي من أي فلتر تحاليل. |
| # | ادعاء الـKB | الواقع في الكود |
|---|---|---|
| F2 | clinic_service مش في whitelist النية | ❌ غلط — موجود ومقبول (StoreClinicalIntentRequest.php:55) |
| F3 | مبدّل مصدر الـpicker غير موجود | ❌ غلط — موجود بالكامل في الفرونت (lab-orders.component.ts:101,283) |
| F1 | الجسر lis_investigation_id غير مستهلك | ✅ مؤكد — عمود ميت، صفر قراءات |
الفجوة الحقيقية ليست «المبدّل غير موجود» — بل «المبدّل في الواجهة والباك إند لا تعرف عنه شيئاً».
| الحدث (LIS) | المستمع (العيادة) | بيعمل إيه |
|---|---|---|
| LabResultReleased | DropLabReportOnEncounter | ينشئ ClinicalDocument (تقرير تحليل) + يقلب النية لـresulted |
| LabRequestCompleted | MarkEncounterLabComplete | يقلب بند الخدمة ordered → performed |
| LabResultCorrected | RefreshEncounterLabReport | يحدّث التقرير |
| # | الحلقة | الدليل |
|---|---|---|
| ١ | الحارس المزدوج يقتل سيناريو «تحاليل بس» تماماً | MarkEncounterLabComplete.php:22 بيرجع فوراً لو encounter_id فاضي — وزيارة تحاليل-بس مالهاش encounter (بياناتك: LabRequest #1 → encounter_id = NULL). وحتى مع encounter، بيحدّث ordered بس — وبندك billed. نفس الحارس في DropLabReportOnEncounter ⇒ ولا تقرير. |
| ٢ | صفر سطح عرض في العيادة | قائمة تنقّل العيادة فيها Worklist للأشعة — ولا شيء للمعمل. بحث شامل: الطابور · الداشبورد · الاستقبال · الكاشير · ملف المريض ⇒ صفر عمود/بادج/عدّاد لحالة التحاليل. والـClinicalDocument اللي بيتعمل ⇒ صفر مستهلك في الواجهة. |
| ٣ | حقول ميتة — الواجهة تنتظر بيانات لا تُرسَل | شاشة الطبيب بتحاول تعرض النتيجة (lab-orders.component.html:246 → @if (item.result_status)) والموديل معرّفهم — لكن LabOrderResource.php:26-34 مابيرجّعش result_status ولا result_value. الشارة دي عمرها ما هتظهر. |
| ٤ | قفل صلاحيات على الطباعة | الطباعة محمية بـlis.result-reports.generate، وشاشة أدوار العيادة مقفولة على clinic.% ⇒ مستحيل منح الصلاحية لدور استقبال ⇒ 403. |
| البُعد | (أ) تحاليل فقط | (ب) الدكتور طلب | (ج) خدمة عيادة نوع lab | (د) LIS مباشر |
|---|---|---|---|---|
| مين بيطلب | الاستقبال — شاشة الحجز | الدكتور «نية» → الاستقبال ينفّذ | الاستقبال — من كتالوج الخدمات | المعمل مباشرة (walk-in) |
| الكتالوج | كتالوج LIS | كتالوج LIS | كتالوج العيادة | كتالوج LIS |
| التسعير | قائمة الأسعار | قائمة الأسعار | سعر الخدمة (مستقل) | الكتالوج = 0.000 ← القائمة الافتراضية لا تُطبَّق |
| فاتورة | العيادة | العيادة | العيادة | فاتورة LIS + كاشير LIS |
| يوصل المعمل؟ | ✅ نعم | ✅ نعم | 🔴 لا — أبداً | ✅ نعم |
| encounter_id | NULL | موجود | — | NULL |
| النتيجة ترجع؟ | 🔴 لا (حارس encounter) | للداتا نعم — للشاشة لا | يدخلها الدكتور يدوياً | لا (خارج العيادة) |
| الاستقبال يعرف؟ | 🔴 لا | 🔴 لا | 🔴 لا | — |
لاحظ: الحالة (ج) و(د) لم تذكرهما — لكنهما موجودتان في النظام فعلاً، و(ج) هي مصدر نزيف الفواتير.
| البند | موجود اليوم | المطلوب | لازم يتغيّر |
|---|---|---|---|
| وضع المعمل | مبدّل في الواجهة · الباك عمياء | مصدر حقيقة واحد | الباك يفرض الوضع: يمنع خدمات lab في المتكامل، ويمنع بنود LIS في المستقل |
| سطر تحليل من كتالوج العيادة | 🔴 يتحاسب ولا يصل المعمل | يا يوصل يا يترفض | يُمنع في المتكامل · وفي المستقل يمشي على مسار النتيجة اليدوية |
| الجسر lis_investigation_id | عمود ميت + خانة رقم عارية | يا يُستهلك يا يُحذف | قرار — انظر §11 |
| رجوع النتيجة | مبني — لكن محروس بـencounter_id | يشتغل لكل زيارة | الربط بـservice_order_line بدل الـencounter |
| حالة النتيجة على البند | عمود فوترة (ordered/billed) يُساء استخدامه | حالة نتيجة مستقلة | حقل جديد (result_status/result_ready_at) — لا تُخلط بالفوترة |
| سطح الاستقبال | صفر | «نتائج جاهزة» + طباعة | شاشة/تبويب + بادج على الطابور |
| صلاحية الطباعة | مقفولة على clinic.% | الاستقبال يطبع | صلاحية عيادة تفتح تقرير المعمل (نفس نمط ISS-2026-0177) |
| الباقات (packages) | 🔴 لا تمر على LabPriceResolver ⇒ تُسعَّر من الكتالوج (0.000) | نفس التسعير | ذيل مفتوح من ISS-2026-0176 |
| الحالة | السلوك اليوم |
|---|---|
| المريض مادفعش والعيّنة اتولّدت | 🔴 العيّنة والباركود بيتولّدوا لحظة الطلب — قبل أي دفع، ومفيش أي حارس. المعمل ممكن يشتغل على مريض مادفعش. |
| المريض دفع الكشف ثم الدكتور طلب تحاليل | 🔴 لو أوردر الزيارة اتقفل بالدفع → بيتفتح أوردر جديد → المريض يرجع للكاشير يدفع تاني. لو «دفع لاحقاً» → نفس الأوردر ودفعة واحدة. |
| دفع ومشي والنتيجة اتأخرت | مفيش أي رابط. لو زيارة تحاليل-بس ⇒ صفر أثر في العيادة — المتابعة من المعمل أو بوابة المريض فقط. |
| بوابة المريض | ✅ شغالة — لو lis.auto_publish_on_release مفعّل، المريض بيستلم لينك ويشوف نتيجته بنفسه. الاستقبال برضه مايعرفش. |
الشكل المقترح — للموافقة على هيئة الشاشة قبل أي كود واجهة.
الملفات: شاشة جديدة تحت features/clinic/lab-results/ + عنصر في clinic-layout.component.ts (زي «Radiology Worklist» الموجود).
| WP | النطاق | الريبو | الإثبات | أعلام |
|---|---|---|---|---|
| WP1 | 🔴 سدّ نزيف الفواتير: منع سطر line_type=lab من كتالوج العيادة في الوضع المتكامل (يترفض بـ422 برسالة واضحة). + منع إنشاء خدمة نوع lab في المتكامل. | BE + FE | تست: إضافة سطر lab من كتالوج العيادة في المتكامل → 422 | [FIN] |
| WP2 | الباك يفرض lab_mode: توصيل ClinicCatalogMode بمسارات الخدمات + الاستقبال + النية. مصدر حقيقة واحد. | BE | تستات لكل وضع | — |
| WP3 | 🔴 رجوع النتيجة لكل زيارة: شيل حارس encounter_id — الربط بـfulfillment_id (موجود!). + حقل حالة نتيجة مستقل على البند (result_status / result_ready_at) بدل إساءة استخدام حالة الفوترة. | BE + migration | تست: زيارة تحاليل-بس مدفوعة → النتيجة تُطلق → البند يتعلّم «جاهزة» | migration |
| WP4 | سطح الاستقبال: شاشة «نتائج التحاليل» + بادج على الطابور + زر طباعة. | BE + FE | معاينة §9 | — |
| WP5 | فتح الطباعة للعيادة: صلاحية عيادة تسمح بتقرير المعمل (نفس نمط ISS-2026-0177: صلاحية OR على الكنترولر). | BE | تست: دور استقبال بلا صلاحيات معمل يطبع التقرير | — |
| WP6 | إصلاحات صغيرة: LabOrderResource يرجّع result_status/result_value (حقول ميتة) · DecideClinicalIntent:110 يكتب line_type='lab' للتحاليل. | BE | تستات | — |
| WP7 | الباقات: تمريرها على LabPriceResolver (ذيل مفتوح من ISS-2026-0176 — بتتسعّر 0.000 دلوقتي). | BE | تست تسعير باقة | [FIN] |
الترتيب: WP1 أولاً (نزيف مالي) → WP3 (النتيجة) → WP4+WP5 (السطح) → WP2 → WP6/WP7.
| # | القرار | توصيتي |
|---|---|---|
| ١ | إنت شغال بأي وضع؟ المعمل بتاعنا (متكامل) ولا معمل بره (مستقل)؟ ولا الاتنين حسب العميل؟ | متكامل (وده الافتراضي عندك). ساعتها خدمات نوع «تحاليل» تتمنع من كتالوج العيادة تماماً — والتحاليل تُطلب من كتالوج المعمل بس. ده بيقتل التعارض من جذوره. |
| ٢ | الجسر lis_investigation_id (العمود الميت): نستهلكه ولا نحذفه؟ | نحذفه (أو نخفيه). لو الوضع متكامل، الخدمة نوع lab مالهاش لزوم أصلاً — فالجسر بيربط حاجة لحاجة مالهاش مكان. الإبقاء عليه = فخ للمستقبل. |
| ٣ | حالة النتيجة: حقل جديد مستقل، ولا نعيد استخدام حالة البند (performed)؟ | حقل جديد. حالة البند حالة فوترة — خلطها بحالة النتيجة هو بالظبط سبب الحارس المكسور اليوم (بند مدفوع = billed ⇒ ما بيتحدّثش أبداً). |
| ٤ | سطح الاستقبال: شاشة مستقلة «نتائج التحاليل»، ولا بادج على الطابور بس؟ | الاتنين — الشاشة للبحث والطباعة، والبادج عشان الموظف يشوف من غير ما يدور. المعاينة في §9. |
| ٥ | 🔴 الدفع المزدوج: لو المريض دفع الكشف والدكتور طلب تحاليل بعدها → بيتفتح أوردر جديد ويدفع تاني. مقبول؟ | محتاج قرارك. الحل: نسمح بإعادة فتح الأوردر أو نجمع في فاتورة واحدة. ده سلوك مالي — مش هغيّره من غير موافقتك. |
| ٦ | 🔴 العيّنة قبل الدفع: العيّنة والباركود بيتولّدوا لحظة الطلب — قبل أي تحصيل. المعمل ممكن يشتغل على مريض مادفعش. نحط حارس؟ | محتاج قرارك. فيه عيادات بتشتغل كده بقصد (خدمة أسرع). لو عايز حارس، يبقى إعداد: «التحليل ما يدخلش المعمل إلا بعد الدفع». |
| ٧ | الباقات بتتسعّر 0.000 (ذيل مفتوح من شغل التسعير السابق). نصلحها في نفس الدفعة؟ | نعم — نفس المنطقة، وسهلة، وبتمنع فاتورة بصفر. |