---
title: الخدمة والإعدادات في الكلينك — التشريح وشبكة الترابط ومخطط الإعدادات
slug: clinic-service-settings
status: active
owner: hazem
updated: 2026-07-02
related:
  - clinic-principles
  - clinic-tightening-plan
  - his
---

> المالك: «جزء أساسي — الخدمة وتكوينها ولوجيكها مع اللوجيك التاني (ربط الدنيا ببعض) + الإعدادات علشان الدنيا ترتب بعضها». مبني على تحليل كود فعلي (opus 2026-07-02).

# الخدمة والإعدادات — تشريح من الكود + ربط الدنيا ببعض

> تحليل read-only من الكود الفعلي. BE: `/home/moonui/moon-erp-be/Modules/Clinic` · FE: `/home/moonui/public_html/moon-erp/src/app/features/clinic`. كل سطر معه دليل file:line.

---

## Part 1A — الخدمة: تشريحها وشبكة ترابطها

### 0) تصنيف الخدمات الثلاثة — توجيه المالك (2026-07-02) وحالة الكود

المالك: **الخدمات 3 أنواع مختلفة في المعاملة والطباعة.**

**النوع 1 — الكشف/الإعادة (أساس الشغل، أو مثيله في أي قسم مستشفى):** يُقدَّم بأحد **3 احتمالات، وكل الحسابات تترتب على الاحتمال**:

| الاحتمال | حالة الكود | الدليل |
|---|---|---|
| (أ) **دكتور بعينه** محدد بساعاته من إنشاء الخدمة | ❌ **غير مدعوم على مستوى الخدمة** — `clinic_services` بلا عمود `doctor_id`. يوجد فقط سعر per-doctor (`ServicePrice scope=doctor`) وجداول مواعيد per-doctor (`doctor_schedule_slots` بساعات) — لكن لا ربط «الخدمة دي بيقدمها د.X بساعاته». | `clinic_services` migration بلا doctor · `doctor_schedule_slots` (start_time/end_time/capacity) `2026_06_22_105001` |
| (ب) **قسم بعينه** والدكتور يُختار **في الحجز** | ✅ **مدعوم** — الموعد `department_id` مطلوب و`doctor_id` **nullable** (department-only booking). | `appointments` migration:19-23 · `BookAppointment.php:48-56` (doctor_id اختياري) |
| (ج) **القسم كله يقدمها واللي يفتح الحالة ياخدها** | ✅ **مدعوم عبر auto-assign عند الوصول** — `DoctorAssignmentService::pickDoctor` يختار أفضل طبيب متاح بالقسم (seatsLeft → أقل طابور → أقل rank)، و`NoScheduleException`→ fallback لطبيب walk-in. الطبيب المسنَد يقود توزيع الإيراد/العمولة. | `DoctorAssignmentService.php` · `MarkArrived.php` · split عبر `RevenueSplitScheme.doctor_id` |

> **الفجوة الجوهرية:** الاحتمال (أ) «خدمة مربوطة بطبيب بعينه بساعاته» **مفقود** — يلزم ربط الخدمة اختياريًا بطبيب/ساعات عند الإنشاء (F9، م1). و«كل الحسابات تترتب على الاحتمال» تعمل جزئيًا: الإسناد يحصل وقت الوصول (ج) ويقود الـ split عبر `doctor_id` على السطر/الـ encounter — لكن attribution «اللي فتح الحالة» صريحًا (مقابل خوارزمية auto) غير مُنمذج كعلم مستقل.

**النوع 2 — التحاليل كخدمة:** إعداد أساسي «المعمل LIS متفعل ولا لأ» = **`clinic.lab_mode` (integrated\|standalone)** — المالك حسمه كـ«إعداد أساسي». متفعل → اختيار الاستقبال يدخلها فلو LIS (`OrderLabFromEncounter` موجود). غير متفعل → خدمة عادية، **والنتايج يدخلها الدكتور بنفسه (قيمة رقمية / صورة / PDF) كخطوة اختيارية**. ✅ **الآلية مشحونة فعلًا:** مسار النتيجة الخارجية L3 (`ClinicalIntent::externalDocument` + `SubmitExternalResult` + external-result dialog) هو **بالضبط** هذه الآلية (value/file لكل نية) — **يُعمَّم على خدمات standalone**.

**النوع 3 — الأشعة كخدمة:** نفس القصة عبر **`clinic.rad_mode`** — integrated (RIS worklist/report) أو standalone (نتيجة يدخلها الطبيب: نص/صورة/PDF عبر نفس مسار L3).

**الطباعة تختلف بالنوع:** كشف/إعادة → إيصال كشف + (اختياري) تشخيص/روشتة · تحاليل → تقرير نتيجة (قيم/مراجع) أو صورة/PDF مرفق · أشعة → تقرير أشعة/صورة. ⇒ كل نوع له قالب طباعة مختلف (يترسّب في م3/م8 الطباعة + مخطط إعدادات الطباعة).

**📜 مرجع الجينا (بنية السرفيس):** الجينا نمذجت كل الكتالوج في جدول مسطّح واحد `detections {title, detectionval(price), del}` — **بلا نوع/قسم/طبيب** (`obgy .../detections`). النقطة الحاسمة: **«الإعادة» = صف كتالوج مستقل أرخص** `(42,'إعادة',170)` مقابل `(50,'كشف أول',300)` — كل شريحة سعر (أول/إعادة/إعادة خفض/حجز إعادة) صف بذاته، مع override يدوي للسعر في الزيارة. والقسم = نفس `detectionid` (يتضاعف كقسم)، والدفعات magic ids: `-99` قسط · `999` دفع باقي · `9999` مرتجع (sentinels في `visits.detectionid`). **نتبنّى:** الكتالوج المسطّح القابل للاختيار + مبدأ «كل شريحة سعر كيان» — **لكن نُرسمِلها**: «الإعادة» = متغيّر أرخص **مربوط** بـ `parent_service_id/revisit_of` (لا صف عارٍ مكرر) + ربط قسم/طبيب صريح (لا id مضمّن) + فصل الكتالوج عن الـ ledger (الدفعات نوع `PaymentTransaction.type` لا sentinel على الخدمة). يعزّز F5 (المتابعة = متغيّر أرخص مربوط) و F9 (ربط الطبيب الصريح).

### 1) تكوين الخدمة اليوم — جدول `clinic_services`

الموديل: `Modules/Clinic/app/Models/ClinicService.php` · الهجرة: `database/migrations/2026_06_22_010001_create_clinic_services_table.php` · implements `FhirServiceItemSource` (unitPrice = base_price).

| العمود | النوع (migration) | المعنى | ملاحظة/دليل |
|---|---|---|---|
| `code` | string(64) | كود الخدمة — يُولَّد تلقائيًا عبر `SequenceService` (entity `clinic/service`) | `UpsertClinicService.php:21-25` · unique(`company_id,code`) `migration:34` |
| `name_ar` / `name_en` | string / nullable | الاسم الثنائي (name_en اختياري) | `migration:16-17` · `displayName()` يفضّل en ثم ar `ClinicService.php:105-108` |
| `service_type` | string(20) → enum | **4 قيم فقط:** `consultation` \| `clinic_service` \| `lab` \| `radiology` | `Enums/ServiceType.php:7-10` · cast `ClinicService.php:47` |
| `department_id` | FK→departments nullable | ربط القسم (يُستخدم في الفلترة والتقارير وتوزيع الإيراد) | `migration:18` (nullOnDelete) · relation `:64-67` |
| `base_price` | decimal(12,3) default 0 | السعر الأساسي — يُزرع كصف `scope='base'` في جدول الأسعار (listener) | `migration:19` · seeding أدناه |
| `requires_doctor` | boolean default true | هل الخدمة تحتاج طبيبًا (يؤثر على الحجز/الجدولة) | `migration:20` |
| `default_duration` | smallint nullable | المدة الافتراضية (دقائق) للحجز | `migration:21` |
| `lis_investigation_id` | bigint nullable — **بلا FK** | جسر ناعم لتحليل LIS مقابل | soft ref (comment `migration:22`) · index `:37` |
| `rad_procedure_id` | bigint nullable — **بلا FK** | جسر ناعم لإجراء أشعة مقابل | `migration:24` |
| `sbscs_code` | string(40) nullable | كود الترميز السعودي (NPHIES) — يُنسخ في المطالبة | `migration:25` · `sbscsCode()` `:95-98` |
| `product_id` | FK→products nullable | ربط اختياري بمنتج مخزون | `migration:26` (nullOnDelete) |
| `is_active` | boolean default true | تفعيل/إيقاف الخدمة | `migration:27` |
| `sort_order` | uint default 0 | الترتيب في القوائم | `migration:28` |

**حارس الجسر (bridge guard):** ليس في الأكشن بل في الـ FormRequests:
- `StoreClinicServiceRequest.php:55-81` و `UpdateClinicServiceRequest.php:48-70`.
- القاعدة: **واحد فقط** من `lis_investigation_id`/`rad_procedure_id` يُسمح، و**فقط** إذا كان `service_type` مطابقًا (lis→lab، rad→radiology). أي غير ذلك = 422.
- FE guard مماثل + يصفّر الـ IDs للأنواع غير lab/rad قبل الإرسال: `clinic-service-list.component.ts:259-266, 276-277`.

**زرع السعر الأساسي (base-price seeding) — موصول:**
- wiring: `EventServiceProvider.php:69-74` → `ClinicServiceCreated → SeedBaseServicePrice`، `ClinicServiceUpdated → SyncBaseServicePrice`.
- `SeedBaseServicePrice.php:15-45`: عند الإنشاء وإن كان `base_price>0` → `updateOrCreate` صف `scope='base'` (dept=null,doctor=null) في `clinic_service_prices` بسعر = base_price. (restore-safe للـ soft-deleted).
- `SyncBaseServicePrice.php:17-56`: عند التعديل يُعيد المزامنة؛ وإن صار `base_price<=0` يحذف صف الـ base. → PricingResolver دائمًا معه floor.

### 2) شبكة الترابط — كل مستهلك للخدمة (الخدمة في المركز)

```
                       عقود الدفع                الحجز/الزيارة
   تسعير 3-محاور   ╲        │                        │        ╱   بند الأوردر
   (doctor→dept→base) ─────  🧩 clinic_services  ───── (service_order_lines)
   PricingResolver   ╱        │                        │        ╲   snapshots
                       التغطية               تقسيم الإيراد
                    (CoverageService)     (RevenueSplitScheme)
                              │                        │
                          المطالبة                  التقارير
                        (ClaimLine)            (by department_id)
```

1. **التسعير الطبيعي — 3 محاور:** `Services/PricingResolver.php` · `resolveNatural(serviceId, dept?, doctor?, company?)` → **doctor (الأخص) → department → base (floor)**؛ يقرأ `clinic_service_prices` (موديل `ServicePrice.php` scope=doctor/department/base). `resolveWithAxis()` يرجّع أي محور حسم السعر.
2. **أسعار العقود (payer):** `Models/ContractServicePrice.php` (`clinic_contract_service_prices`) + `Services/CoverageService.php::resolveContractPrice(contract, serviceId, doctor?)` → **per-doctor-in-contract → service-default-in-contract → طبيعي+applyCoverage**. `assertContractUsable()` يفرض (active + ضمن المدى) وإلا 422. `applyCoverage` نسبة/copay، `applyVisitCap` + `max_coverage_per_year` بإعادة توزيع البواقي.
3. **الحجز/الزيارة:** `BookAppointment.php:56` يخزّن `service_id` على الموعد (بلا تسعير). `CreateVisit.php`: consultations → `AddServiceOrderLine` (تسعير+تغطية داخليًا)، lab → `OrderLabFromEncounter::handleForOrder`، rad → `CreateRadOrder::handleForOrder`، ثم `CollectReceptionReceipt` بعد الـ commit.
4. **بند الأوردر (`service_order_lines`):** `Models/ServiceOrderLine.php` — `clinic_service_id` (**soft ref، بلا FK**) + لقطات write-once at add-time: `unit_price, price_source, contract_price, discount_amount, coverage_amount, patient_amount, doctor_grade_code, revenue_split_scheme_id, description(_ar)`. الأكشن `AddServiceOrderLine.php:63-205`: price عبر `PricingResolver::resolveWithAxis(serviceId=clinic_service_id)`، coverage عبر `CoverageService::resolveContractPrice`، الاسم من `ClinicService::find`, ثم يطلق `ServiceLineAdded → SnapshotLinePricingAndSplit`.
5. **المطالبة (claims):** `Models/ClaimLine.php` (`clinic_claim_lines`) يربط بـ `service_order_line_id` (+ denormalized `clinic_service_id` + `sbscs_code`). `Services/ClaimAssembler.php:38-74`: يمرّ على بنود الأوردر غير الملغاة، ينسخ لقطات السطر المجمّدة (unit_price/coverage/patient) لأعمدة المطالبة. يتطلب encounter + ICD-10 primary.
6. **تقسيم الإيراد (revenue split):** `Models/RevenueSplitScheme.php` (`clinic_revenue_split_schemes`) محاوره: `clinic_service_id + doctor_id? + doctor_grade_code? + payer_contract_id?`. `Services/RevenueSplitService.php::resolveScheme()` → الأخص يفوز (doctor +4 / grade +2 / contract +1، تعادل → أقل id). `snapshot()` يبصم `revenue_split_scheme_id` على السطر ويكتب `EncounterServiceParty`. علاقة مختصرة على الموديل: `ClinicService::splitScheme()` (المخطط الافتراضي بلا محاور).
7. **التقارير:** `Services/ClinicReportService.php:30-31` — «الإيراد حسب القسم» يعمل JOIN من `service_order_lines.clinic_service_id → clinic_services → departments.department_id`. فبُعد الخدمة يصل للتقرير عبر القسم.

**النية (intents) — الجسر `clinic_service`:** ❌ **غير موجود.** `catalog_ref_type` مقيّد في `StoreClinicalIntentRequest.php:50` بـ **`lis_investigation | lis_package | rad_procedure` فقط** (وتعليق الهجرة `2026_07_02_100000_...:37`). لا قيمة `clinic_service` — لا محجوزة ولا مستهلكة. الكلمة `clinic_service` موجودة فقط كقيمة `ServiceType` و`OrderLineType` (غير متعلقة بالنية). مسارا lab/rad للنية **مستهلكان** في `DecideClinicalIntent.php:81-128` (executeHere) لكنهما يستهلكان `catalog_ref_id` كـ investigation/procedure id مباشر.

> ⚠️ **تصحيح لادعاء قائم في الخطة:** قسم «نماذج البيع» في الـ HTML يذكر «النية polymorphic تدعم clinic_service». الدقيق: الأعمدة `catalog_ref_type/id` **عامة (تقدر تحمله فيزيائيًا)** لكن **whitelist التحقق لا يقبله ولا يوجد مستهلك له** — فتفعيله = إضافة القيمة + استهلاكها (fix مربوط بمحطة، أدناه).

### 3) الفجوات ضد مبادئ المالك

- **الجسر LIS/Rad غير مستهلك تمامًا (مؤكد):** القراء الوحيدون لـ `lis_investigation_id`/`rad_procedure_id` = fillable + الحارسان (Store/Update FormRequests) + الـ Resource (إخراج API) + factory. **لا شيء** يقرأهما ليولّد أوردر حقيقي. `OrderLabFromEncounter.php:65-80` و`CreateRadOrder.php:59-62` يأخذان `investigation_id`/`procedure_id` **مباشرة من الطلب/النية**، لا من الخدمة. النتيجة: تعريف خدمة نوعها lab وربطها بتحليل LIS = بيانات ميتة اليوم؛ لا يقود لأي طلب.
- **الوضع المستقل (standalone, نموذج المالك 2):** لا يشتغل end-to-end من كتالوج العيادة بلا LIS. المفقود = (أ) قبول `clinic_service` في whitelist النية + استهلاكه، (ب) مبدّل مصدر الـ picker (كتالوج مقابل LIS/RIS)، (ج) مفاتيح `clinic.lab_mode/rad_mode`. التسعير (3-محاور) والفوترة (ServiceOrderLine) ومسار النتيجة الخارجية (L3) **جاهزة** — الناقص = خطوة إنشاء الطلب من خدمة كتالوج.
- **خدمة المتابعة (م8):** لا يوجد أي مفهوم follow-up/revisit/free-visit في الكود (grep = صفر). لا نافذة متابعة ولا سياسة سعر متابعة.
- **رسوم القراءة الخارجية (قرار §٤ القديم):** لا مفهوم `reading_fee` في الكود. يمكن نمذجته **بلا سكيمة جديدة** = خدمة عادية + `RevenueSplitScheme` توجّه الحصة للطبيب القارئ.
- **مقياس المستشفى:** الخدمات تتضاف فوق نفس الأساس (department_id موجود)، لكن لا بُعد غرفة عمليات/قسم إضافي على الخدمة بعد؛ و doctor pickers تحتاج department-first server-side (كما في قسم النماذج).
- **fail-fast مالي:** `clinic.ar_account_id`/`clinic.revenue_account_id` افتراضهما `null` → القيد يعتمد على وجودهما؛ سياسة المالك = فشل صريح لا صامت (م0/م5).
- **حقول إكلينيكية ثابتة:** vitals أعمدة ثابتة لا تعريفات ديناميكية (السابقة = محرك `history_questions`).

### 4) اللوجيك المستهدف — «أضف خدمة واحدة → تنتشر تلقائيًا»

> **إضافة خدمة واحدة يجب أن:** تظهر في الحجز حسب نوعها/قسمها · تُسعَّر بـ 3-محاور + سعر العقد · تُطلَب من الطبيب حسب الوضع (intent/immediate، ومتكامل/مستقل) · تُفوتَر على بند أوردر بلقطات مجمّدة · تُغطَّى عبر CoverageService · يُقسَّم إيرادها عبر RevenueSplitScheme · تظهر في التقارير حسب القسم — **بلا كود، بمجرد إدخالها + تسعيرها.**

**الإصلاحات المطلوبة (كل واحد بوسم محطة):**

| # | الإصلاح | المحطة |
|---|---|---|
| F1 | استهلاك الجسر أو حذفه: خدمة نوعها lab/rad ترتبط بـ investigation/procedure حقيقي فيولّد اختيار الطبيب الأوردر الصحيح (أو في المستقل تكون الخدمة نفسها هي المطلوب) | م1 (كتالوج) + م4 (تنفيذ) |
| F2 | إضافة `clinic_service` لـ whitelist النية + استهلاكه في `DecideClinicalIntent` (للـ lab/rad المستقل) | م4 / L4 |
| F3 | مفاتيح `clinic.lab_mode` / `clinic.rad_mode` (standalone\|integrated) + مبدّل مصدر الـ picker | م1/م3/م4 / L4 |
| F4 | جعل service_type=lab/radiology قابلًا للتنفيذ end-to-end بلا LIS (إنشاء الطلب من خدمة كتالوج) | م4 |
| F5 | خدمة المتابعة: نافذة متابعة + سياسة سعر (مجاني/مخفّض) + مصدر موعد follow-up | م8 |
| F6 | رسوم القراءة الخارجية = خدمة + RevenueSplitScheme للطبيب القارئ (بلا سكيمة) | م6/م9 |
| F7 | doctor pickers department-first server-side (حجز/جدولة/أسعار/توزيع) للمقياس | م1 |
| F8 | fail-fast على غياب حسابات GL للعيادة | م0/م5 |
| F9 | ربط الخدمة اختياريًا بطبيب بعينه + ساعاته عند الإنشاء (الاحتمال أ للكشف/الإعادة) | م1 |
| F10 | قوالب طباعة مختلفة حسب نوع الخدمة (كشف/تحاليل/أشعة) + إعدادات الطباعة | م3/م8 |

---

## Part 1B — الإعدادات: اللوحة اللي بترتّب الدنيا

### 1) الموجود اليوم

**آلية القراءة:** خدمة واحدة `Modules/Core/app/Services/SettingsService.php` — `get(key, companyId, branch?, user?)` بسلسلة fallback (user+branch → user → branch → company → **default من التعريف** → null)، cache 5د للشركة، cast حسب `value_type`. `getBool()` للأعلام، `set()` يتحقق من `allowed_values`. لا `config('clinic...')` إطلاقًا. الـ API العام: `Modules/Core/app/Http/Controllers/SettingController.php` (index?module= / update). **عميل جديد = صف في `SettingDefinitionSeeder` → يظهر تلقائيًا في الشاشة + يُقرأ بلا كود.**

**التعريفات الموجودة فعلًا (4 فقط) — كتلة «Clinic (HIS)» في `SettingDefinitionSeeder.php:1775`:**

| المفتاح | الافتراضي | النوع | المجموعة | يُقرأ في |
|---|---|---|---|---|
| `clinic.ar_account_id` | null | integer | clinic_accounting | PostReceiptJournalEntry:24 · RefundServiceOrderLine:107 · ApplyAdjudicationToLedger:68 |
| `clinic.revenue_account_id` | null | integer | clinic_accounting | PostReceiptJournalEntry:25 · RefundServiceOrderLine:108 |
| `clinic.allow_overbooking` | true | boolean | clinic_general | BookAppointment:73 · MarkArrived:58 |
| `clinic.ordering_mode` | intent | enum(intent\|immediate) | clinic_general | `Support/OrderingMode.php:21/30` (يفرض 409 على التنفيذ المباشر: LabOrderController:29, RadOrderController:70) |

**مقروء لكن بلا تعريف (fallback صامت، لا يظهر في الشاشة) — فجوة:**
- `his.mode` (single_clinic\|center) — `DoctorScheduleSlotRequest.php:58` (`?? SINGLE_CLINIC`) يفرض غرفة في center. enum `Enums/ClinicMode.php` (const class، ليس PHP enum). **لا SettingDefinition** → يرجّع null → single_clinic دائمًا، ولا UI.
- `clinic.dashboard_top_services_count` — `ClinicDashboardService.php:142` (`?? 5`). غير معرّف.

**FE — شاشة الإعدادات ديناميكية 100%:** `settings/clinic-settings.component.ts` — تحمّل `SettingService.list('clinic')`، تجمّع حسب `display_group` (computed `:114-143`)، ترسم كل حقل حسب النوع (`getFieldType` `:220-227`: boolean→toggle، `*_account_id`→account picker، enum→select، integer/decimal→number، else text)، وتحفظ **المتغيّر فقط** (diff) عبر `PUT /core/settings` في forkJoin. `GROUP_LABELS`/`GROUP_ICONS` فيهما مسبقًا مجموعات مخطط لها (booking/billing/appointments/insurance/notifications) لم تُملأ بعد. → أي تعريفات جديدة لموديول clinic تظهر بلا تعديل FE.

**كبت فاتورة LIS التلقائية (موجود):** `OrderLabFromEncounter.php:70-72` يبني payload بـ `source=clinic` + `auto_generate_invoice=false`؛ و`CreateLabRequest.php:351` بوابة `!_suppress_invoice` → لا فاتورة LIS للطلبات القادمة من العيادة حتى لو `lis.auto_generate_invoice=true`. الأشعة أصلًا بلا مسار فاتورة (CreateRadOrder). العيادة تملك الفوترة عبر ServiceOrderLine.

### 2) المستهدف — مخطط الإعدادات الشامل (6 مجموعات)

الرسالة: «عميل جديد = يظبط الإعدادات فتشتغل الدنيا على وضعه بلا كود». الموجود ✓ / المخطط (محطة):

- **💰 مالية (GL + fail-fast):** `clinic.ar_account_id` ✓ · `clinic.revenue_account_id` ✓ · حساب استرداد/إشعار دائن (م5) · حساب فروقات الكاشير over/short (م5/م9) · **سياسة fail-fast** لغياب أي حساب مطلوب — لا قيد بحساب null (م0).
- **🏗️ أوضاع البيع:** `clinic.ordering_mode` (intent\|immediate) ✓ · **`clinic.lab_mode` (integrated\|standalone) — «إعداد أساسي» بأمر المالك** «المعمل متفعل ولا لأ» (م4/L4) · **`clinic.rad_mode` (integrated\|standalone) — «إعداد أساسي»** (م4/L4) · `clinic.pharmacy_source` (products\|standalone_formulary) (م3/L5) · `his.mode` (single_clinic\|center) — **موجود مقروء لكن يلزم ترقيته لتعريف حقيقي في الشاشة** (م1).
- **🩺 حقول إكلينيكية ديناميكية:** محرك تعريفات vitals (اسم ثنائي + نوع رقمي/تكست + وحدة + مدى طبيعي/حدود خطر بتنبيه + إظهار/إخفاء + ترتيب) + presets تُزرع للعملاء — لا شيء اليوم؛ السابقة = `history_questions` (م3).
- **📅 تشغيل:** `clinic.allow_overbooking` ✓ · فرض السعة per-doctor (م1) · نافذة المتابعة (أيام) + سياسة سعر المتابعة (مجاني/مخفّض) (م8) · إعادة تعيين الطابور (يومي) (م2) · `clinic.dashboard_top_services_count` — **موجود مقروء بلا تعريف** (م9).
- **🖨️ طباعة:** لوجو/هوامش/خطوط + إظهار التشخيص + توقيع لكل طبيب — لا شيء اليوم (م3/م8 طباعة الروشتة والتقرير).
- **💊 روشتة:** مصدر الدواء (products\|standalone) · ديفولتات الجرعة/التكرار/المدة/التعليمات لكل دواء · favorites لكل طبيب — لا شيء اليوم (م3).

**العدّاد:** الموجود = **4 تعريفات** (+2 مقروءان بلا تعريف = 6 نقاط تماس). المخطط ≈ **15 إعدادًا** جديدًا عبر 6 مجموعات. الشاشة جاهزة لعرضها فور إضافة صفوف `SettingDefinition` — لا كود FE.

---

# قرار التصميم: التحاليل والخدمات في الوضعين (سؤال المالك 2026-07-02 — «فكر فيها كويس»)

**السؤال:** لو الكلينك والمعمل شغالين مع بعض — هل نضيف الـ ~500 تحليل كخدمات؟ ولو المعمل مش عندنا — إزاي يضيف/يعدل التحاليل بسهولة؟

## الوضع المتكامل (كلينك + LIS): ❌ لا mirror نهائيًا
- الموظف (دكتور نية / استقبال تنفيذ) يختار **مباشرة من كتالوج LIS** — بدون أي ربط أو نسخ. ده القرار المعتمد §3 من مراجعة الفلو («بلا mirror وبلا مزامنة») **والمبني فعلًا في L1**: النية polymorphic على `lis_investigation`/`lis_package`، والتنفيذ `OrderLabFromEncounter` يسعّر من LIS (`price_source='lis'`, net_amount).
- أسباب رفض الـ mirror: 500 صف مكرر · انحراف أسماء/أسعار حتمي · مصدرا حقيقة · صيانة مضاعفة.
- كتالوج خدمات العيادة في الوضع ده = الخدمات غير المعملية فقط (كشف/إجراءات/أشعة-إن-كانت-standalone).
- ملاحظة تأمين (تنضم لعنقود م7): أسعار تعاقد per-test للتحاليل في الوضع المتكامل مش موجودة في عقود العيادة (contract prices بتشاور clinic_service_id) — التغطية بتتحسب نسبة/copay على سعر LIS (إصلاح F1). لو محتاجين سعر تعاقدي لكل تحليل: يا امتداد عقود العيادة يا استخدام LabInsuranceContract الموجودة في LIS — **قرار م7**.

## الوضع المستقل (كلينك بس): ✅ التحاليل = خدمات نوع lab، والإضافة بضغطة
1. **استيراد الكتالوج الجاهز بنقرة واحدة** — إعادة استخدام مصادر LIS Setup Wizard (الكتالوج المنسق / NAFIS 1,666 / CSV) **أسماء+فئات فقط بلا ماكينة المعمل** → تتزرع كـ clinic_services نوع lab (بسعر افتراضي 0 أو من عمود سعر بالـ CSV). precedent جاهز: منطق الاستيراد موجود في الـ wizard.
2. **كتالوج حر بعدها**: يعدّل الأسماء (ثنائي اللغة)، يضيف، يعطّل — صفوف ملكه بلا أي تزامن. + **إضافة inline سريعة** من شاشة الطلب نفسها (عقد pick-first بند 6: تتكتب للكتالوج وتتحدد للطلب في خطوة واحدة).
3. **CSV import/export** للخدمات نوع lab للتعديل الجماعي.
4. الفئات: خدمات الـ lab تاخد حقل فئة (لتوزيع أعمدة شبكة الاختيار) — يُزرع من فئات الكتالوج المستورد.

## المفتاح الموحد: مكوّن اختيار واحد بمصدرين
شبكة «العرض الأقصى» (عقد الجينا الـ 10 بنود) تُبنى **مرة واحدة** وتقرا حسب `clinic.lab_mode`:
- `lis_integrated` → كتالوج LIS (investigations+packages، بفئاته وfavorites لكل طبيب).
- `standalone` → clinic_services نوع lab (بفئاته وfavorites).
النية تخزن `catalog_ref_type` المناسب (lis_investigation | **clinic_service** — إضافة clinic_service للـ whitelist هي فجوة F-gap مسجلة). نفس الشبكة للدكتور (النية) والاستقبال (التنفيذ). **الأشعة: نفس القرار حرفيًا** (rad_mode: كتالوج rad_procedures المتكامل / خدمات نوع radiology المستقلة).

**الخلاصة للمالك:** متكامل = اختيار مباشر بلا إضافة ولا ربط · مستقل = استيراد بنقرة ثم كتالوج حر — والشاشة واحدة في الحالتين.

---

# تصميم الدفع عند إضافة الزيارة (توجيه المالك 2026-07-02)

**المبدأ:** «في بعض العيادات لا يكون إلا واحد فقط هو اللي بيحجز ويدفع» → خياران عند إضافة الزيارة **وبنفس العملية بالظبط** تحتهم:
1. **تحصيل فوري + إيصال مباشر** من شاشة إضافة الزيارة نفسها (الشخص الواحد).
2. **إرسال للكاشير** (الفصل التقليدي).
الاتنان بينفذوا **نفس عملية التحصيل** (`CollectReceptionReceipt` بجلستها وقيودها) — مفيش مسار مالي تاني، بس نقطة الاستدعاء مختلفة.

**الافتراضيات (يقدر يعدلها):**
- **وسيلة دفع افتراضية** (إعداد جديد: `clinic.default_payment_method` = cash) — تنضم لمجموعة 💰 في مخطط الإعدادات.
- **المبلغ متملي تلقائيًا = كامل الفاتورة كاش** («الطبيعي الدفع كامل») — يصلّح عيب التدقيق «pay-now يدوي بالمليم default 0» في create-visit، ونفس الديفولت في الكاشير (موجود جزئيًا: prefill بالـ outstanding).

**الدفع الجزئي والباقي آجل:**
- كل خدمات الفاتورة بتتدفع من الإيصال ده؛ لو دفع **أقل** → **الباقي يتسجل دينًا على المريض (آجل)** بتأكيد صريح («الباقي X آجل على المريض؟»).
- الميكانيكا المحاسبية (سليمة بالبنية الحالية): قيد الإيراد بيتسجل كامل على البنود المفوترة + قيد التحصيل بالمدفوع فقط → **رصيد AR المتبقي = الدين** تلقائيًا؛ دفتر المريض (PatientLedgerEntry المحسوب) يظهره؛ الإيصال status=`partially_paid` (القيمة موجودة في الـ enum أصلًا).
- **تعديل مطلوب في م5:** حارس TOCTOU الحالي (`Σ billable == paid_total`) وقاعدة exact-match في الكاشير يتوسعوا لوضع «جزئي صريح» (paid ≤ due مع علم الآجل) — مع الحفاظ على منع الكسور غير المقصودة.
- **الظهور:** رصيد المريض حي في الهيدر (إصلاح م1 القائم) + المديونيات في **تقرير اليوم/المديونيات** (م9 — مكافئ تقرير rest في الجينا). سداد الآجل لاحقًا = تحصيل عادي على نفس الأوردر (receipt تاني).
- **precedent الجينا:** «دفع باقي» detectionid=999 + أقساط −99 + تقرير rest — نفس الفكرة مؤسسة صح على ledger حقيقي.

**التوزيع على المحطات:** م1 (زر «حصّل الآن» في إضافة الزيارة بالديفولت الكامل + الوسيلة الافتراضية) · م5 (وضع الجزئي-الصريح + الآجل بالتأكيد + الحارس) · م9 (تقرير المديونيات) · الإعدادات (default_payment_method + سياسة السماح بالآجل on/off لكل عميل؟ — نعم كإعداد `clinic.allow_credit_balance` افتراضي مفعّل).

## قائمة انتظار الكاشير (سؤال المالك 2026-07-02 — «كلامي صح ولا إيه؟» → صح 100%)
**الفجوة المكتشفة:** «إرسال للكاشير» اليوم = router navigate بـ query param على نفس الجهاز — بلا معنى لما الكاشير شخص تاني، **ومفيش عنده worklist أصلًا** (شاشته بتحمّل أوردر واحد من الرابط فقط).
**التصميم:**
- «حصّل الآن» → العملية تتم وتقفل في شاشة الزيارة (الشخص الواحد).
- «إرسال للكاشير» → العملية **لا تتم** — تظهر في **قائمة انتظار الكاشير**: بنود معلقة (مريض · مبلغ مستحق · وقت الإرسال · مين بعته) → الكاشير يفتح البند → يتمم التحصيل (نفس `CollectReceptionReceipt`) → إيصال.
- **مصدر القائمة (توصية):** worklist مشتقة = أوردرات اليوم/الفرع بـ `patient_due>0` وحالة open/partially_billed مرتبة بآخر نشاط (self-healing، مفيش state جديدة تتوه) + طابع `sent_to_cashier_at/by` اختياري للإبراز والترتيب ومعرفة المرسِل. تحديث دوري (30-60s زي الداشبورد). الرابط المباشر بالـ param يفضل كاختصار لوضع الشخص الواحد.
- **المحطة:** م5 (شاشة الكاشير تبدأ بالقائمة قبل التفاصيل) + م1 (زر الإرسال يعلّم الطابع). سيناريو تست: ابعت من جهاز الاستقبال → افتح شاشة كاشير تانية → لاقي البند → تممه.
