# نبي نتناقش ي موضوع اكواد vba اكسل

### Participant

نبي نتناقش ي موضوع اكواد vba اكسل

### Auto Claude (Claude Sonnet 5.5)

موقفي: VBA ما زال خيارًا عمليًا ومفيدًا لأتمتة Excel على سطح المكتب، لكنه ليس الخيار الأفضل دائمًا، والأمر يعتمد على نوع الشغل.

**أسباب أدعم فيها VBA:**
1. **سهل البداية:** مسجّل الماكرو يخليك تتعلم من أكواد مولّدة جاهزة، وهذا مفيد لغير المبرمجين.
2. **وصول كامل لنموذج كائنات Excel:** الأوراق والجداول والمخططات والجداول المحورية والتنسيق، وكلها تتحكم فيها مباشرة.
3. **يعمل محليًا بدون بنية تحتية:** ملف واحد يكفي، بدون خوادم أو تراخيص إضافية.
4. **عندك قاعدة ضخمة من الأكواد الجاهزة:** كثير من الشركات تعتمد عليها، ومعرفة VBA تساعدك تصلح وتطوّر الموجود.

**حدوده (من معرفتي، ولم أتحقق منها في هذا الرد):**
- لا يعمل في Excel على الويب، ودعمه في الجوال محدود.
- الماكرو في الملفات المحمّلة من الإنترنت يُحجب افتراضيًا في إصدارات Office الحديثة، وهذا يضايق مشاركة الملفات.
- صعب الصيانة والاختبار مقارنة بلغات حديثة، وبيئة التطوير قديمة.
- بدائل مثل Office Scripts وPython في Excel وPower Query تغطي بعض الحالات، لكن لا أعرف حاليًا مدى تغطيتها لكل ما يقدر عليه VBA، وأحتاج أتحقق قبل ما أجزم.

باختصار: لو شغلك داخل Excel على الكمبيوتر وتحتاج أتمتة سريعة، فـVBA مناسب. أما لو تحتاج تعاونًا سحابيًا أو تحليل بيانات كبير، فالبدائل أولى.

عشان نركّز النقاش: وش هدفك بالضبط؟ تتعلم VBA من الصفر، أو عندك كود يحتاج إصلاح، أو تقارن بينه وبين بدائله؟

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

موقفي هنا هو **محامي الشيطان**: سأتعمد الاعتراض على الحلول التي تبدو صحيحة أو مريحة في VBA، خصوصًا عندما يكون الحل يعمل الآن لكنه ضعيف هندسيًا أو سيسبب مشاكل لاحقًا.

في نقاش أكواد **Excel VBA** سأركز على نقاط أرى أنها أهم من مجرد «الكود اشتغل»:

- **هل الكود مصمم صح أم فقط يؤدي المهمة؟** قد يعمل 100%، ومع ذلك يكون مليئًا بالتكرار، مرتبطًا بالفورم مباشرة، أو صعب التعديل مستقبلًا.
- **الأداء:** سأعترض على الحلقات وقراءة الخلايا واحدة واحدة عندما توجد طريقة أسرع باستخدام Arrays أو Dictionaries أو المعالجة الجماعية.
- **الاعتمادية:** نجاح الكود مع البيانات الحالية لا يعني أنه سيتحمل صفوفًا فارغة، بيانات مكررة، تغيّر ترتيب الأعمدة، أو ملفًا مستوردًا بتنسيق مختلف.
- **هيكلة المشروع:** خصوصًا في برامجك المبنية على UserForms، سأدافع عن فصل **الواجهة عن منطق المعالجة وعن البيانات** عندما يكون ذلك مبررًا، وسأعارض الفصل الزائد إذا كان سيحوّل برنامجًا بسيطًا إلى مشروع معقد بلا فائدة.
- **التطوير مقابل إعادة البناء:** لن أفترض أن «الكود الاحترافي» يعني إعادة كتابة كل شيء. أحيانًا إصلاح موضعي مدروس أفضل من هدم نظام مستقر وإعادة بنائه.

والأهم: إذا قال Auto Claude إن طريقة معينة هي الأفضل، فلن أعارضه لمجرد المعارضة؛ سأحاول كسر منطقه بحالات واقعية. وإذا صمد الحل أمام الاعتراضات، فهذا دليل أقوى على أنه الحل المناسب.

أنا جاهز لأول قضية VBA.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

أبرز نقطة خلاف محتملة: هل الأفضل في مشروع VBA فعلي **تحسين الكود القائم تدريجيًا** أم أن تراكم الترابط بين الفورم والمنطق والبيانات يجعل **إعادة الهيكلة** أوفر وأضمن على المدى الطويل؟  
Auto Claude: ما المعايير العملية التي تستخدمها لتقرر أن كود VBA قائمًا تجاوز مرحلة «الإصلاح» وأصبح يحتاج إعادة هيكلة؟

### Auto Claude (Claude Sonnet 5.5)

موقفي: الإصلاح التدريجي هو الافتراضي، والهيكلة الكاملة تحتاج مبررًا واضحًا. وهذه معاييري العملية لتجاوز مرحلة الإصلاح:

1. **كلفة التعديل تتصاعد:** كل تغيير بسيط (عمود جديد مثلًا) يلمس أكثر من 3 أماكن في الكود، أو يكسر أشياء لا علاقة لها به.
2. **تكرار نفس الخطأ:** نفس الباغ يرجع بعد كل إصلاح لأن المنطق منسوخ في أكثر من مكان.
3. **تعذّر الاختبار:** ما تقدر تشغّل جزءًا من المعالجة بدون فتح الفورم وإدخال بيانات يدويًا.
4. **مخاطر العمل:** الملف مهم ماليًا أو تشغّله فرق متعددة، فالباغ مكلف.
5. **تعدد المستخدمين:** إن كان مستخدم واحد وكود ثابت، فالهيكلة غالبًا ترف.

وأقترح مسارًا وسطًا: لا تعيد الكتابة، بل **استخرج المنطق من أحداث الفورم إلى دوال مستقلة كلما لمست جزءًا**. هذا يعطيك معظم فائدة الهيكلة بدون مخاطرة الهدم.

نقطة لم تُطرح بعد: وجود أي اختبارات أو ملف نموذجي للمخرجات الصحيحة. بدونه، إعادة الهيكلة مقامرة لأنك لن تعرف هل تغيّر السلوك.

Chatty GPT 5.6: هل ترى معيارًا آخر يجعل الهدم أوفر من التدرّج؟

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة المهمة: غياب اختبارات مرجعية يجعل أي إعادة هيكلة مخاطرة حتى لو كان التصميم الحالي ضعيفًا.  
Chatty GPT 5.6: كيف تبني **شبكة أمان واختبارات عملية داخل مشروع VBA قديم** قبل فصل الفورم عن المنطق، خصوصًا إذا لم توجد اختبارات أصلًا؟

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

أتفق مع Auto Claude في نقطة أساسية: **إعادة هيكلة مشروع VBA قديم بلا مرجع يثبت أن النتائج لم تتغير أخطر من بقاء بعض سوء التصميم مؤقتًا.** لكنني أخالفه في اعتبار الاختبارات التقليدية شرطًا قبل البدء؛ في VBA يمكن بناء شبكة أمان عملية أولًا ثم التحرك تدريجيًا.

قبل لمس الهيكلة أبنيها هكذا:

1. **لقطة مرجعية للواقع:** نأخذ ملفات تشغيل حقيقية تمثل الحالات الطبيعية والحالات الشاذة، ونحفظ المخرجات الصحيحة الحالية.
2. **اختبارات Regression بسيطة:** نشغّل نفس المدخلات قبل وبعد كل تعديل، ثم نقارن النتائج آليًا: القيم، عدد السجلات، الأسماء، التواريخ، والحالات المهمة.
3. **فصل الاختبار عن الفورم:** إذا كان `CommandButton_Click` ينفذ 200 سطر، لا أعيد تصميمه فورًا. أبدأ باستخراج جزء صغير يمكن استدعاؤه مباشرة وتمرير مدخلاته إليه.
4. **Assertions داخل بيئة التطوير:** يمكن استخدام `Debug.Assert` للتحقق من افتراضات مهمة أثناء التطوير؛ مثل أن عدد السجلات الناتجة يساوي العدد المتوقع.
5. **سجل مقارنة:** أي اختلاف بين النسخة القديمة والجديدة يجب تفسيره قبل اعتماد التعديل؛ ليس كل اختلاف خطأ، فقد يكشف الاختبار خطأً قديمًا أصلًا.

والشرط الإضافي الذي قد يغيّر قراري بالكامل: **الاعتماد على الحالة العامة Global State**. إذا كانت الإجراءات تعتمد بكثافة على `ActiveSheet` و`Selection` ومتغيرات عامة وحالة الفورم، فلن أبدأ بفصل معماري كبير؛ سأزيل هذه الاعتمادات تدريجيًا أولًا، لأنها تجعل حتى الاختبارات المرجعية غير مستقرة.

وبالنسبة لمشاريعك تحديدًا، هذا يعني أنني لن أقول: «نحوّل برنامج موج كله الآن إلى Architecture جديدة». سأختار عملية تشغيل واحدة، أبني لها Baseline، ثم نفصلها ونقارن **قديم مقابل جديد**. إذا تطابقت، ننتقل للجزء التالي.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: الاعتماد على `ActiveSheet` و`Selection` والمتغيرات العامة قد يجعل نتائج الاختبار نفسها غير مستقرة.  
Auto Claude: لو أردنا تطبيق هذا فعليًا على مشروع VBA كبير، ما **أول اعتماد خفي** تبحث عنه وتزيله قبل أي إعادة هيكلة، وكيف تكتشفه دون كسر البرنامج؟

### Auto Claude (Claude Sonnet 5.5)

أول اعتماد خفي أبحث عنه: **الاعتماد على `ActiveSheet` و`ActiveWorkbook` و`Selection`**، لأنه يجعل نتيجة نفس الكود تتغير حسب أي ورقة كانت مفتوحة وقت التشغيل. وبعده المتغيرات العامة التي تحمل حالة بين الاستدعاءات.

**كيف أكتشفه دون كسر البرنامج:**
1. **بحث نصي فقط (بدون تعديل):** أصدّر الوحدات (Export) وأبحث عن `ActiveSheet` و`Selection` و`ActiveCell` و`Activate` و`Select` و`Public` خارج الثوابت. هذا يعطي خريطة بالمواقع قبل لمس أي سطر.
2. **عدّ الاستخدامات:** أبدأ بالإجراء الذي ستختبره أولًا، وليس بكل المشروع.
3. **الاستبدال الآمن:** أمرر الورقة كمعامل (`ws As Worksheet`) بدل الاعتماد على الورقة النشطة، وأبقي الإجراء القديم غلافًا يستدعي الجديد بـ`ActiveSheet`. هكذا يبقى السلوك الظاهري ثابتًا.
4. **اختبار الحساسية:** أشغّل الإجراء مرتين، مرة والورقة النشطة هي الصحيحة، ومرة وورقة أخرى نشطة. أي اختلاف في النتيجة يكشف اعتمادًا خفيًا فعليًا.

**اعتراض على Chatty GPT 5.6:** قلت إن إزالة هذه الاعتمادات تسبق الفصل المعماري. أتفق، لكن بشرط: لا تكتب Baseline على سلوك غير مستقر. خذ اللقطة المرجعية **بعد** خطوة 4، وإلا ستثبّت نتائج عشوائية على أنها «الصحيحة».

**نقطة لم تُطرح:** الأحداث المتسلسلة (`Worksheet_Change` و`Workbook_Open`). قد تُشغَّل ضمنيًا عند تعديل الخلايا من الكود فتغيّر البيانات دون أن تظهر في الإجراء. أجرّب بتعطيل `Application.EnableEvents` أثناء الاختبار، وأقارن النتيجة مع وضعه الطبيعي.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: أحداث Excel مثل `Worksheet_Change` قد تصنع آثارًا جانبية لا تظهر داخل الإجراء الذي نختبره.  
Chatty GPT 5.6: كيف تميّز عمليًا بين **حدث ضروري لسير البرنامج** وحدث يمثل اعتمادًا خفيًا يجب فصله، خصوصًا عندما يستدعي الحدث إجراءات أخرى متسلسلة؟

### Participant

الجميع يتوقف

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

تم. أتوقف هنا ولن أكمل النقاش أو أضيف أي رد على الجولة الحالية.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

تم إيقاف النقاش بناءً على طلب المستخدم. هل تريد **إنهاء الجلسة**، أم **إبقائها متوقفة مؤقتًا** للعودة إليها لاحقًا؟

### Participant

Option Explicit

'==========================================================
' سجل الأفكار والنتائج: مرجع ثابت لكل تجربة.
' F01 - تحرير تكليفين وإعادة تركيب الطاقم: طُبق في R8.
' النتيجة: وجد إصلاح 100، لكن رفضت المرحلة بسبب مقارنة الترتيب.
' F02 - مقارنة المحتوى دون ترتيب: طُبق في R9.
' النتيجة: اعتمد إصلاح 100 و115؛ بقي مقعدان، وتوقف الوقت.
' F03 - رفع البحث إلى 90 ثانية: تجربة R9 الثانية.
' النتيجة: بقي مقعدان؛ لم يسجل نقل فعلي للبيرنق 135.
' F04 - مقارنة الحالات المتغيرة وتقاسم الوقت وعمق 3: طُبق في R10.
' النتيجة: أصلح 147؛ بقي 135؛ انتهت 240 محاولة بعد بحث 86.1 ثانية.
' قياس R10: النسخ 63.3؛ المقارنة 14.9؛ التراجع 1.1 ثانية.
' F05 - نسخ حاويات القوائم وفصل سجل القيادة: جديد في R11.
' نتيجة R11: النسخ 15.0؛ المقارنة 3.5؛ إجمالي المرحلة 40.4 ثانية.
' المخالفات صفر؛ بقي قائد 135 ناقصًا.
' F06 - عمق 4 في الجولة الثالثة: جرب في R11، ولم يغط 135.
' توقفت المرحلة عند 240 محاولة بعد بحث 28.7 ثانية؛ البحث غير مكتمل.
' F07 - ذاكرة للمسارات الداخلية الفاشلة: لم تطبق بعد.
' السبب: يلزم مفتاح يشمل حالة الخطة والمسار والمنع والعمق؛
' تخزين فشل بسبب حد الوقت أو المحاولات قد يستبعد حلًا صالحًا.
' F08 - الحد الإجمالي 600 بدل 240: جرب؛ بقي قائد 135 ناقصًا.
' النتيجة: 253 محاولة، بحث 29.3 ثانية، إجمالي 40.6 ثانية؛ مخالفات صفر.
' سبب التوقف: حد الجولة الأخيرة 100 محاولة، وليس الوقت أو سقف 600.
' F09 - رفع حد الجولة الأخيرة إلى 400: بقي قائد 135 ناقصًا.
' F14 - تأكيد التصنيف نجح؛ بقي قائد 135 ناقصًا.
' تحديد المبيت من اسم الفندق ومدة المبيت في البيرنق فقط.
' يضيف التقرير دليل التصنيف لكل مرة تشغيل؛ النتيجة تنتظر التجربة.
' حدود الجولات الآن 20 ثم 60 ثم 400، والحد الإجمالي 600.
' الوقت 90 ثانية، والفحوص 20000، والأعماق 2 ثم 3 ثم 4 ثابتة.
' النتيجة الفعلية لكل فكرة تؤخذ من التقرير التالي، ولا تفترض مسبقًا.
'==========================================================


'==========================================================
' R19 - تطبيق مباشر لطريقة الخبير: المبيت زمنيًا قبل الراوندترب،
' وتكليف المبيت لا تنقله مرحلة الإصلاح بعد اعتماده.
' محرك توزيع الرحلات - المرحلة الثانية
'
' يبني خطة التوزيع في الذاكرة فقط، ولا يكتب في LINE.
' يعتمد على نجاح تحميل modDistributionReader أولًا.
'==========================================================

Public DE_Assignments As Collection
Private DE_LayoverDayLoads As Object
Public DE_RunPlans As Collection
Public DE_EmployeeStates As Object
Public DE_Warnings As Collection
Private DE_RTDayLoad As Object
Private DE_RepairOrderingChecks As Long
Private DE_RepairTransferSamples As Collection
Private DE_RepairTransferSampleKeys As Object
Private DE_RepairActivePath As Object
Private DE_RepairSampleKeys As Object
Private DE_RepairActiveDepth As Long
Private DE_RepairRootAttemptLimit As Long
Private DE_RepairDiagnosticCounts As Object
Private DE_RepairDiagnosticSamples As Collection
Private DE_RepairDepthReached As Boolean
Private DE_RepairRootAttemptStart As Long
Private DE_RepairAttempts As Long
Private Const DE_REPAIR_MAX_ATTEMPTS As Long = 600
Private DE_RepairRootDeadline As Double
Private DE_RepairCopySeconds As Double
Private DE_RepairCompareSeconds As Double
Private DE_RepairRollbackSeconds As Double
Private DE_RepairStartedAt As Double
Private DE_RepairChecks As Long
Private Const DE_REPAIR_MAX_SECONDS As Double = 90#
Private Const DE_REPAIR_MAX_CHECKS As Long = 20000
Private Const DE_REPAIR_DEPTH As Long = 2
Private DE_PreviousMonthRestCount As Long
Private DE_PreviousMonthRestOneDayCount As Long
Private DE_PreviousMonthRestTwoDaysCount As Long
Private DE_PreviousMonthRestThreeDaysCount As Long

Private Const DE_INFO As String = "INFO"
Private Const DE_PAIRING As String = "PAIRING"

Private Const DE_TYPE_FLIGHT As String = "FLIGHT"
Private Const DE_TYPE_OFFICE As String = "OFFICE"
Private Const DE_TYPE_EVENT As String = "EVENT"
Private Const DE_TYPE_SB As String = "SB"
Private Const DE_TYPE_RT As String = "RT"
Private Const DE_TYPE_PREVIOUS_MONTH_REST As String = _
    "PREVIOUS_MONTH_REST"

' الحد الأعلى المسموح به فوق المستهدف لكل موظف.
' يمنع إخفاء العجز بتكرار الرحلات الإضافية على الموظف نفسه.
Private Const DE_MAX_EXTRA_FLIGHTS_PER_EMPLOYEE As Long = 1

' تفضيلات تشغيلية لتباعد الرحلات. هذه ليست منعًا مطلقًا؛
' فالحد الإلزامي يبقى راحة 24 ساعة، بينما يستخدم المحرك
' هذه القيم لاختيار الموظف صاحب الجدول الأقل ازدحامًا.
Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Double = 72#
Private Const DE_ACCEPTABLE_FLIGHT_GAP_HOURS As Double = 48#

'==========================================================
' بناء خطة التوزيع كاملة في الذاكرة
'==========================================================
Public Function DistributionEngine_BuildPlan( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim CurrentStep As String
    Dim ReaderPassed As Boolean
    Dim ValidationPassed As Boolean

    On Error GoTo EngineError

    EnsureGregorianCalendar
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    DistributionEngine_Clear
    Set DE_Warnings = WarningResults

    CurrentStep = "تحميل مصادر بيانات التوزيع"

    If DR_Employees Is Nothing Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    ElseIf DR_Employees.Count = 0 Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    Else
        ReaderPassed = True
    End If

    If Not ReaderPassed Then Exit Function

    ValidationPassed = True

    CurrentStep = "تجهيز حالات الموظفين"
    If Not DE_InitializeEmployeeStates(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تحليل مرات تشغيل البيرنقات"
    If Not DE_InitializeRunPlans(ErrorResults, PassedResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "تثبيت ارتباطات الموظفين"
    If Not DE_RegisterEvents( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تثبيت جداول الاستاندباي"
    If Not DE_RegisterStandby(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تثبيت رحلات الإداريين"
    If Not DE_RegisterOfficeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    ' يجب حجز أيام الاحتياط قبل توزيع الرحلات العادية؛
    ' حتى تُبنى الرحلات حول الاحتياط وفترة الراحة التالية له.
    CurrentStep = "توزيع أيام الاحتياط"
    If Not DE_DistributeReserveDays( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    ' الاحتياط تكليف إلزامي. إذا تعذر حجز يوم مناسب له،
    ' لا يبدأ توزيع الرحلات على خطة ناقصة أو غير قابلة للتنفيذ.
    If Not ValidationPassed Then Exit Function

    ' الرحلات المطالب بها من الشهر السابق تُثبت قبل التوزيع
    ' العادي؛ لأنها حق مرحّل للموظف وتُعد رحلة إضافية فوق
    ' مستهدفه الأساسي، مع تطبيق الأهلية والتعارض وراحة 24 ساعة.
    CurrentStep = "توزيع الرحلات المطالب بها"
    If Not DE_DistributeCarryoverFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "توزيع أطقم الرحلات"
    If Not DE_DistributeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "مراجعة ترتيب القيادة والأفراد"
    If Not DE_ValidateRolePolicy(ErrorResults) Then
        ValidationPassed = False
    End If

    ' يضاف الملخص سواء نجحت الخطة أم ظهر عجز؛ حتى يوضح
    ' عدد الرحلات الإضافية المستخدمة فعليًا حسب المجموعة.
    DE_AddExtraFlightSummary WarningResults


    CurrentStep = "مراجعة توافق تأهيل الموظفين مع الناقلات"
    If ValidationPassed Then
        If Not DE_ValidateCarrierAssignments(ErrorResults) Then
            ValidationPassed = False
        End If
    End If

    If ValidationPassed Then
        PassedResults.Add _
            "نجح إنشاء خطة توزيع الرحلات في الذاكرة."

        PassedResults.Add _
            "عدد مرات التشغيل المخططة: " & _
            CStr(DE_RunPlans.Count) & _
            "، وعدد التكليفات: " & _
            CStr(DE_Assignments.Count) & "."

        PassedResults.Add _
            "لم يتم تعديل أي خلية في شيت LINE."

        PassedResults.Add _
            "تكليفات الرحلات حسب الناقل: السعودية " & _
            CStr(DE_CarrierAssignmentCount("SAUDIA")) & _
            "، ناس " & CStr(DE_CarrierAssignmentCount("NAS")) & _
            "، أديل " & CStr(DE_CarrierAssignmentCount("ADEAL")) & "."

        PassedResults.Add _
            "تمت تغطية الرحلات المطالب بها: " & _
            CStr(DE_CarryoverAssignedCount()) & _
            " من " & CStr(DR_CarryoverFlights.Count) & "."

        PassedResults.Add _
            "راحة بداية الشهر بسبب آخر رحلة في الشهر السابق: " & _
            "يوم واحد لعدد " & _
            CStr(DE_PreviousMonthRestOneDayCount) & _
            "، يومان لعدد " & _
            CStr(DE_PreviousMonthRestTwoDaysCount) & _
            "، ثلاثة أيام لعدد " & _
            CStr(DE_PreviousMonthRestThreeDaysCount) & _
            "، والإجمالي " & _
            CStr(DE_PreviousMonthRestCount) & " موظفًا."
    End If

    DistributionEngine_BuildPlan = ValidationPassed
    Exit Function

EngineError:
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    ErrorResults.Add _
        "تعذر بناء خطة التوزيع أثناء " & CurrentStep & _
        ". تفاصيل الخطأ: " & Err.Description
    DistributionEngine_BuildPlan = False

End Function

'==========================================================
' مسح خطة سابقة من الذاكرة
'==========================================================
Public Sub DistributionEngine_Clear()

    Set DE_Assignments = New Collection
    Set DE_RunPlans = New Collection
    Set DE_EmployeeStates = _
        CreateObject("Scripting.Dictionary")
    DE_EmployeeStates.CompareMode = vbTextCompare
    Set DE_Warnings = New Collection
    Set DE_RTDayLoad = CreateObject("Scripting.Dictionary")
    DE_RTDayLoad.CompareMode = vbTextCompare
    DE_PreviousMonthRestCount = 0
    DE_PreviousMonthRestOneDayCount = 0
    DE_PreviousMonthRestTwoDaysCount = 0
    DE_PreviousMonthRestThreeDaysCount = 0

End Sub

'==========================================================
' اختبار مستقل للمحرك من دون الكتابة في LINE
'==========================================================
Public Sub TestDistributionEngine()

    Dim Errors As Collection
    Dim Passed As Collection
    Dim Warnings As Collection
    Dim ResultPassed As Boolean

    Set Errors = New Collection
    Set Passed = New Collection
    Set Warnings = New Collection

    ResultPassed = DistributionEngine_BuildPlan( _
        Errors, Passed, Warnings)

    DE_WriteDistributionTestReport _
        ResultPassed, Errors, Warnings, Passed

    MsgBox _
        "تم إنشاء تقرير اختبار التوزيع في شيت " & _
        "DISTRIBUTION_REPORT.", _
        IIf(ResultPassed, vbInformation, vbExclamation), _
        "اختبار محرك التوزيع"

End Sub

'==========================================================
' إنشاء تقرير اختبار المحرك داخل شيت مستقل.
'
' لا يكتب هذا الإجراء أي شيء في LINE، ولا يحذف الشيت؛
' بل يمسح محتوى التقرير السابق ثم يكتب التقرير الجديد.
'==========================================================
Private Sub DE_WriteDistributionTestReport( _
    ByVal ResultPassed As Boolean, _
    ByVal ErrorResults As Collection, _
    ByVal WarningResults As Collection, _
    ByVal PassedResults As Collection)

    Const REPORT_SHEET As String = _
        "DISTRIBUTION_REPORT"

    Dim ws As Worksheet
    Dim NextRow As Long
    Dim SequenceNumber As Long
    Dim FinalRow As Long
    Dim ReportRange As Range
    Dim ReportWindow As Window
    Dim PreviousScreenUpdating As Boolean
    Dim ReportErrorNumber As Long
    Dim ReportErrorDescription As String

    PreviousScreenUpdating = Application.ScreenUpdating
    On Error GoTo ReportError

    Set ws = DE_GetOrCreateReportSheet(REPORT_SHEET)

    Application.ScreenUpdating = False

    ws.Visible = xlSheetVisible
    ws.Cells.Clear
    ws.DisplayRightToLeft = True

    ' عنوان التقرير.
    With ws.Range("A1:D1")
        .HorizontalAlignment = xlCenterAcrossSelection
        .VerticalAlignment = xlCenter
        .Font.Bold = True
        .Font.Size = 16
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
    End With

    ws.Range("A1").Value = _
        "تقرير اختبار محرك توزيع الرحلات"
    ws.Rows(1).RowHeight = 28

    ws.Range("A2").Value = "الشهر"
    ws.Range("B2").Value = _
        UCase$(MonthName(DR_ActiveMonth, True))
    ws.Range("C2").Value = "السنة"
    ws.Range("D2").Value = DR_ActiveYear

    ws.Range("A3").Value = "نتيجة الاختبار"
    If ResultPassed Then
        ws.Range("B3").Value = "نجح الاختبار"
        ws.Range("B3").Interior.Color = _
            RGB(198, 239, 206)
        ws.Range("B3").Font.Color = _
            RGB(0, 97, 0)
    Else
        ws.Range("B3").Value = "لم تكتمل خطة التوزيع"
        ws.Range("B3").Interior.Color = _
            RGB(255, 199, 206)
        ws.Range("B3").Font.Color = _
            RGB(156, 0, 6)
    End If

    ws.Range("C3").Value = "تاريخ التقرير"
    ws.Range("D3").NumberFormat = "@"
    ws.Range("D3").Value = _
        "'" & Format$(Now, "dd/mm/yyyy hh:mm")

    With ws.Range("A2:D3")
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlCenter
        .Font.Bold = True
    End With

    ' رؤوس أعمدة النتائج.
    ws.Range("A5").Value = "التسلسل"
    ws.Range("B5").Value = "التصنيف"
    ws.Range("C5").Value = "الحالة"
    ws.Range("D5").Value = "التفاصيل"

    With ws.Range("A5:D5")
        .Font.Bold = True
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlCenter
        .Borders.LineStyle = xlContinuous
    End With

    NextRow = 6
    SequenceNumber = 0

    DE_WriteReportCollection _
        ws, ErrorResults, "خطأ", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, WarningResults, "تنبيه", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, PassedResults, "سليم", _
        NextRow, SequenceNumber

    If SequenceNumber = 0 Then
        SequenceNumber = 1
        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = "معلومة"
        ws.Cells(NextRow, "C").Value = "لا توجد نتائج"
        ws.Cells(NextRow, "D").Value = _
            "لم يُرجع المحرك أي نتائج لعرضها."
        NextRow = NextRow + 1
    End If

    FinalRow = NextRow - 1
    Set ReportRange = ws.Range( _
        "A5:D" & CStr(FinalRow))

    With ReportRange
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlTop
    End With

    ws.Range("A6:C" & CStr(FinalRow)).HorizontalAlignment = _
        xlCenter
    ws.Range("D6:D" & CStr(FinalRow)).HorizontalAlignment = _
        xlRight
    ws.Range("D6:D" & CStr(FinalRow)).WrapText = True

    ws.Columns("A").ColumnWidth = 10
    ws.Columns("B").ColumnWidth = 16
    ws.Columns("C").ColumnWidth = 20
    ws.Columns("D").ColumnWidth = 95

    ws.Rows("2:3").AutoFit
    ws.Rows("5:" & CStr(FinalRow)).AutoFit

    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    ws.Range("A5:D" & CStr(FinalRow)).AutoFilter

    ' إعداد عرض التقرير اختياري ولا يوقف كتابة النتائج.
    On Error Resume Next
    ws.Parent.Activate
    ws.Activate
    Set ReportWindow = Application.ActiveWindow
    If Not ReportWindow Is Nothing Then
        ReportWindow.View = xlNormalView
        ReportWindow.FreezePanes = False
        ReportWindow.SplitRow = 0
        ReportWindow.SplitColumn = 0
        Err.Clear
        ws.Range("A6").Select
        If Err.Number = 0 Then
            ReportWindow.SplitRow = 5
            ReportWindow.FreezePanes = True
        End If
    End If
    Err.Clear
    On Error GoTo ReportError
    Application.ScreenUpdating = PreviousScreenUpdating
    Exit Sub
ReportError:
    ReportErrorNumber = Err.Number
    ReportErrorDescription = Err.Description
    Application.ScreenUpdating = PreviousScreenUpdating
    MsgBox "تعذر إخراج التقرير. رقم الخطأ: " & CStr(ReportErrorNumber) & _
        vbCrLf & ReportErrorDescription, vbExclamation, "تقرير التوزيع"

End Sub

'==========================================================
' إرجاع شيت التقرير أو إنشاؤه إذا لم يكن موجودًا.
'==========================================================
Private Function DE_GetOrCreateReportSheet( _
    ByVal SheetName As String) As Worksheet

    Dim ws As Worksheet

    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(SheetName)
    On Error GoTo 0

    If ws Is Nothing Then
        Set ws = ThisWorkbook.Worksheets.Add( _
            After:=ThisWorkbook.Worksheets( _
                ThisWorkbook.Worksheets.Count))
        ws.NAME = SheetName
    End If

    Set DE_GetOrCreateReportSheet = ws

End Function

'==========================================================
' كتابة مجموعة نتائج في تقرير الاختبار.
'==========================================================
Private Sub DE_WriteReportCollection( _
    ByVal ws As Worksheet, _
    ByVal Results As Collection, _
    ByVal DefaultCategory As String, _
    ByRef NextRow As Long, _
    ByRef SequenceNumber As Long)

    Dim Item As Variant
    Dim ResultText As String
    Dim CategoryText As String
    Dim StatusText As String

    If Results Is Nothing Then Exit Sub

    For Each Item In Results

        ResultText = CStr(Item)
        CategoryText = DefaultCategory
        StatusText = DefaultCategory

        If DE_IsShortageDiagnostic(ResultText) Then
            CategoryText = "تشخيص عجز"
            StatusText = "تفاصيل الموانع"
        ElseIf DefaultCategory = "خطأ" _
            And InStr(1, ResultText, _
                "عجز", vbTextCompare) > 0 Then
            CategoryText = "عجز"
            StatusText = "غير مغطى"
        ElseIf DefaultCategory = "خطأ" Then
            StatusText = "يحتاج معالجة"
        ElseIf DefaultCategory = "تنبيه" Then
            StatusText = "للمراجعة"
        ElseIf DefaultCategory = "سليم" Then
            StatusText = "مكتمل"
        End If

        SequenceNumber = SequenceNumber + 1

        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = CategoryText
        ws.Cells(NextRow, "C").Value = StatusText
        ws.Cells(NextRow, "D").Value = ResultText

        DE_ApplyReportRowColor _
            ws.Range("A" & CStr(NextRow) & _
                ":D" & CStr(NextRow)), _
            CategoryText

        NextRow = NextRow + 1

    Next Item

End Sub

'==========================================================
' تلوين صف التقرير حسب نوع النتيجة.
'==========================================================
Private Sub DE_ApplyReportRowColor( _
    ByVal TargetRange As Range, _
    ByVal CategoryText As String)

    Select Case CategoryText

        Case "خطأ"
            TargetRange.Interior.Color = _
                RGB(255, 199, 206)
            TargetRange.Font.Color = _
                RGB(156, 0, 6)

        Case "عجز", "تشخيص عجز"
            TargetRange.Interior.Color = _
                RGB(252, 229, 205)
            TargetRange.Font.Color = _
                RGB(156, 87, 0)

        Case "تنبيه"
            TargetRange.Interior.Color = _
                RGB(255, 242, 204)
            TargetRange.Font.Color = _
                RGB(127, 96, 0)

        Case "سليم"
            TargetRange.Interior.Color = _
                RGB(198, 239, 206)
            TargetRange.Font.Color = _
                RGB(0, 97, 0)

        Case Else
            TargetRange.Interior.Color = _
                RGB(217, 217, 217)
            TargetRange.Font.Color = _
                RGB(0, 0, 0)

    End Select

End Sub

'==========================================================
' التحقق من أن النص تقرير تشخيص عجز تفصيلي.
'==========================================================
Private Function DE_IsShortageDiagnostic( _
    ByVal ResultText As String) As Boolean

    DE_IsShortageDiagnostic = _
        (Left$(Trim$(ResultText), _
            Len("[تشخيص]")) = "[تشخيص]" _
        Or Left$(Trim$(ResultText), Len("[أهلية فعلية")) = "[أهلية فعلية")

End Function

'==========================================================
' عرض تقارير تشخيص العجز في رسائل منفصلة.
'==========================================================
Private Sub DE_ShowShortageDiagnostics( _
    ByVal ErrorResults As Collection)

    Dim Item As Variant
    Dim DiagnosticNumber As Long

    If ErrorResults Is Nothing Then Exit Sub

    For Each Item In ErrorResults

        If DE_IsShortageDiagnostic(CStr(Item)) Then
            DiagnosticNumber = DiagnosticNumber + 1

            DE_ShowLongDiagnostic _
                CStr(Item), _
                "تفاصيل عجز التوزيع " & _
                CStr(DiagnosticNumber)
        End If

    Next Item

End Sub

'==========================================================
' تقسيم التقرير الطويل إلى صفحات من دون قطع السطور.
'==========================================================
Private Sub DE_ShowLongDiagnostic( _
    ByVal DiagnosticText As String, _
    ByVal WindowTitle As String)

    Const MAX_PAGE_LENGTH As Long = 650

    Dim Lines As Variant
    Dim LineItem As Variant
    Dim CurrentPage As String
    Dim Pages As Collection
    Dim PageNumber As Long
    Dim CandidateText As String

    Set Pages = New Collection
    Lines = Split(DiagnosticText, vbCrLf)

    For Each LineItem In Lines

        If CurrentPage = vbNullString Then
            CandidateText = CStr(LineItem)
        Else
            CandidateText = CurrentPage & _
                vbCrLf & CStr(LineItem)
        End If

        If Len(CandidateText) > MAX_PAGE_LENGTH _
            And CurrentPage <> vbNullString Then

            Pages.Add CurrentPage
            CurrentPage = CStr(LineItem)
        Else
            CurrentPage = CandidateText
        End If

    Next LineItem

    If CurrentPage <> vbNullString Then
        Pages.Add CurrentPage
    End If

    For PageNumber = 1 To Pages.Count

        MsgBox _
            CStr(Pages(PageNumber)), _
            vbExclamation, _
            WindowTitle & " - الصفحة " & _
            CStr(PageNumber) & " من " & _
            CStr(Pages.Count)

    Next PageNumber

End Sub

'==========================================================
' تجهيز حالة كل موظف ومستهدف مجموعته
'==========================================================
Private Function DE_GroupFlightTarget(ByVal GroupCode As String) As Long
    Select Case UCase$(Trim$(GroupCode))
        Case "SSS", "SST": DE_GroupFlightTarget = 7
        Case "STS": DE_GroupFlightTarget = 8
        Case "STT": DE_GroupFlightTarget = 10
    End Select
End Function
Private Function DE_InitializeEmployeeStates( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim EmployeeRecord As Object
    Dim State As Object
    Dim Targets As Object
    Dim GroupCode As String
    Dim BusyPeriods As Collection
    Dim EmployeeAssignments As Collection
    Dim EventDates As Object
    Dim PreviousLastFlightDate As Date
    Dim PreviousMonthEnd As Date
    Dim NewMonthStart As Date
    Dim PreviousMonthRestEnd As Date
    Dim PreviousMonthRestDays As Long
    Dim PreviousTripType As String

    Set Targets = DE_ReadGroupTargets(ErrorResults)
    If Targets Is Nothing Then Exit Function

    For Each EmployeeKey In DR_Employees.Keys

        Set EmployeeRecord = DR_Employees(EmployeeKey)
        GroupCode = UCase$(CStr(EmployeeRecord("GroupCode")))

        If Not Targets.Exists(GroupCode) Then
            ErrorResults.Add _
                "لا يوجد مستهدف للمجموعة " & GroupCode & _
                " الخاصة بالموظف " & CStr(EmployeeKey) & "."
            Exit Function
        End If

        Set State = DE_NewRecord()
        State.Add "EmployeeNumber", CStr(EmployeeKey)
        State.Add "EmployeeName", _
            CStr(EmployeeRecord("EmployeeName"))
        State.Add "GroupCode", GroupCode
        State.Add "CarrierProfile", _
            UCase$(CStr(EmployeeRecord("CarrierProfile")))
        State.Add "InfoRow", CLng(EmployeeRecord("InfoRow"))
        State.Add "LineRow", CLng(EmployeeRecord("LineRow"))
        State.Add "IsOffice", CBool(EmployeeRecord("IsOffice"))
        State.Add "IsHold", CBool(EmployeeRecord("IsHold"))
        State.Add "IsStandby", _
            DR_SBByEmployee.Exists(CStr(EmployeeKey))
        State.Add "OriginalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "NormalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "FlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "OriginalLayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LongLayoverTarget", 0&
        State.Add "ShortLayoverTarget", 0&
        State.Add "LongLayoverCount", 0&
        State.Add "ShortLayoverCount", 0&
        State.Add "EventDayCount", 0&
        State.Add "LongestEventDays", 0&
        State.Add "TargetAdjustmentReason", vbNullString
        State.Add "IsNoFlightMonth", False
        State.Add "SaudiaTarget", 0&
        State.Add "SpecialCarrierTarget", 0&
        State.Add "SpecialCarrierCode", vbNullString
        State.Add "SaudiaCount", 0&
        State.Add "SpecialCarrierCount", 0&
        State.Add "FlightCount", 0&
        State.Add "LayoverCount", 0&
        State.Add "CarryoverTarget", _
            DE_EmployeeCarryoverCount(CStr(EmployeeKey))
        State.Add "CarryoverAssigned", 0&
        State.Add "CarryoverLayoverCount", 0&
        State.Add "CarryoverLongLayoverCount", 0&
        State.Add "CarryoverShortLayoverCount", 0&
        State.Add "CarryoverSaudiaTarget", _
            DE_EmployeeCarryoverCarrierCount( _
                CStr(EmployeeKey), "SAUDIA")
        State.Add "CarryoverSpecialTarget", _
            DE_EmployeeCarryoverSpecialCount( _
                CStr(EmployeeKey))
        State.Add "LeaderCount", 0&
        State.Add "ReserveCount", 0&
        State.Add "HasPreviousMonthEndRest", False
        ' عدد الرحلات التي أضيفت فوق مستهدف المجموعة لمعالجة العجز
        State.Add "ExtraFlightCount", 0&
        Set BusyPeriods = New Collection
        Set EmployeeAssignments = New Collection
        Set EventDates = CreateObject("Scripting.Dictionary")
        EventDates.CompareMode = vbTextCompare
        State.Add "BusyPeriods", BusyPeriods
        State.Add "Assignments", EmployeeAssignments
        State.Add "EventDates", EventDates

        ' إذا كانت آخر رحلة للموظف في آخر يوم من الشهر السابق،
        ' تُحجز راحة بداية الشهر حسب تصنيف الرحلة السابقة:
        '   LAY = ثلاثة أيام، LNG أو MED = يومان، SHT = يوم واحد.
        ' وتستمر بقية شروط الراحة والتعارض دون أي تغيير.
        If EmployeeRecord.Exists("HasPreviousLastFlight") Then

            If CBool(EmployeeRecord("HasPreviousLastFlight")) Then

                PreviousLastFlightDate = CDate(CDbl( _
                    EmployeeRecord("PreviousLastFlightDate")))

                PreviousMonthEnd = DateSerial( _
                    DR_ActiveYear, DR_ActiveMonth, 0)

                If Int(CDbl(PreviousLastFlightDate)) = _
                    Int(CDbl(PreviousMonthEnd)) Then

                    NewMonthStart = DateSerial( _
                        DR_ActiveYear, DR_ActiveMonth, 1)

                    PreviousMonthRestDays = _
                        DE_GetPreviousMonthRestDays( _
                            EmployeeRecord, PreviousTripType)

                    PreviousMonthRestEnd = DateAdd( _
                        "d", PreviousMonthRestDays, NewMonthStart)

                    DE_AddBusyPeriod _
                        State, _
                        NewMonthStart, _
                        PreviousMonthRestEnd, _
                        DE_TYPE_PREVIOUS_MONTH_REST, _
                        False, _
                        "راحة بداية الشهر لمدة " & _
                        DE_ArabicDayPhrase(PreviousMonthRestDays) & _
                        " بسبب رحلة " & PreviousTripType & _
                        " في آخر يوم من الشهر السابق"

                    State("HasPreviousMonthEndRest") = True
                    DE_PreviousMonthRestCount = _
                        DE_PreviousMonthRestCount + 1

                    Select Case PreviousMonthRestDays
                        Case 1
                            DE_PreviousMonthRestOneDayCount = _
                                DE_PreviousMonthRestOneDayCount + 1
                        Case 2
                            DE_PreviousMonthRestTwoDaysCount = _
                                DE_PreviousMonthRestTwoDaysCount + 1
                        Case 3
                            DE_PreviousMonthRestThreeDaysCount = _
                                DE_PreviousMonthRestThreeDaysCount + 1
                    End Select

                End If

            End If

        End If

        DE_EmployeeStates.Add CStr(EmployeeKey), State

    Next EmployeeKey

    DE_InitializeEmployeeStates = True

End Function

'==========================================================
' حساب عدد المطالبات المسجلة لموظف
'==========================================================
Private Function DE_EmployeeCarryoverCount( _
    ByVal EmployeeNumber As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCount = _
                DE_EmployeeCarryoverCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' حساب مطالبات موظف لناقل محدد
'==========================================================
Private Function DE_EmployeeCarryoverCarrierCount( _
    ByVal EmployeeNumber As String, _
    ByVal CarrierCode As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 _
            And StrComp(CStr(CarryoverRecord("CarrierCode")), _
                CarrierCode, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCarrierCount = _
                DE_EmployeeCarryoverCarrierCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' حساب مطالبات الموظف للناقل المتخصص في ملفه
'==========================================================
Private Function DE_EmployeeCarryoverSpecialCount( _
    ByVal EmployeeNumber As String) As Long

    Dim EmployeeRecord As Object
    Dim SpecialCarrier As String

    If Not DR_Employees.Exists(EmployeeNumber) Then Exit Function
    Set EmployeeRecord = DR_Employees(EmployeeNumber)

    Select Case UCase$(CStr(EmployeeRecord("CarrierProfile")))
        Case "SAUDIA_NAS": SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL": SpecialCarrier = "ADEAL"
        Case Else: Exit Function
    End Select

    DE_EmployeeCarryoverSpecialCount = _
        DE_EmployeeCarryoverCarrierCount( _
            EmployeeNumber, SpecialCarrier)

End Function

'==========================================================
' قراءة مستهدف الرحلات والمبيت من INFO!K:M
'==========================================================
Private Function DE_ReadGroupTargets( _
    ByRef ErrorResults As Collection) As Object

    Dim ws As Worksheet
    Dim Targets As Object
    Dim LastRow As Long
    Dim CurrentRow As Long
    Dim GroupCode As String
    Dim FlightsText As String
    Dim LayoversText As String
    Dim Target As Object

    Set ws = ThisWorkbook.Worksheets(DE_INFO)
    Set Targets = CreateObject("Scripting.Dictionary")
    Targets.CompareMode = vbTextCompare

    LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row

    For CurrentRow = 1 To LastRow

        GroupCode = UCase$(DE_Text( _
            ws.Cells(CurrentRow, "K").Value2))

        If DE_IsGroupCode(GroupCode) Then

            FlightsText = DE_Text( _
                ws.Cells(CurrentRow, "L").Value2)
            LayoversText = DE_Text( _
                ws.Cells(CurrentRow, "M").Value2)

            If Not DE_IsDigits(FlightsText) _
                Or Not DE_IsDigits(LayoversText) Then
                ErrorResults.Add _
                    "مستهدف المجموعة " & GroupCode & _
                    " غير صحيح في INFO!K:M."
                Exit Function
            End If

            Set Target = DE_NewRecord()
            Target.Add "Flights", CLng(FlightsText)
            Target.Add "Layovers", CLng(LayoversText)

            If Targets.Exists(GroupCode) Then
                Targets.Remove GroupCode
            End If
            Targets.Add GroupCode, Target

        End If

    Next CurrentRow

    If Targets.Count <> 4 Then
        ErrorResults.Add _
            "تعذر قراءة مستهدفات المجموعات الأربع من INFO!K:M."
        Exit Function
    End If

    Set DE_ReadGroupTargets = Targets

End Function

'==========================================================
' تحليل كل مرة تشغيل وإضافة تفاصيلها اللازمة للتوزيع
'==========================================================
Private Function DE_InitializeRunPlans( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection) As Boolean

    Dim ws As Worksheet
    Dim SourceRun As Object
    Dim RunPlan As Object
    Dim SortedRuns() As Object
    Dim RunCount As Long
    Dim Index As Long
    Dim SwapIndex As Long
    Dim TempRun As Object

    Set ws = ThisWorkbook.Worksheets(DE_PAIRING)
    RunCount = DR_PairingRuns.Count

    If RunCount = 0 Then
        ErrorResults.Add "لا توجد مرات تشغيل جاهزة للتوزيع."
        Exit Function
    End If

    ReDim SortedRuns(1 To RunCount)

    For Index = 1 To RunCount
        Set SourceRun = DR_PairingRuns(Index)
        Set RunPlan = DE_BuildRunPlan( _
            ws, SourceRun, ErrorResults)

        If RunPlan Is Nothing Then Exit Function
        Set SortedRuns(Index) = RunPlan
        PassedResults.Add "[تصنيف البيرنق] " & _
            CStr(RunPlan("PairingNumber")) & " بتاريخ " & _
            Format$(CDate(RunPlan("OperatingDate")), "dd/mm/yyyy") & _
            ": " & CStr(RunPlan("LayoverEvidence"))
    Next Index

    ' ترتيب زمني ثابت؛ وعند تساوي التاريخ يرتب برقم البيرنق.
    For Index = 1 To RunCount - 1
        For SwapIndex = Index + 1 To RunCount
            If DE_RunComesAfter( _
                SortedRuns(Index), SortedRuns(SwapIndex)) Then
                Set TempRun = SortedRuns(Index)
                Set SortedRuns(Index) = SortedRuns(SwapIndex)
                Set SortedRuns(SwapIndex) = TempRun
            End If
        Next SwapIndex
    Next Index

    For Index = 1 To RunCount
        SortedRuns(Index)("RunIndex") = Index
        DE_RunPlans.Add SortedRuns(Index)
    Next Index

    DE_InitializeRunPlans = True

End Function

Private Function DE_BuildRunPlan( _
    ByVal ws As Worksheet, _
    ByVal SourceRun As Object, _
    ByRef ErrorResults As Collection) As Object

    Dim StartRow As Long
    Dim CreditRow As Long
    Dim StartColumn As Long
    Dim EndColumn As Long
    Dim DetailRow As Long
    Dim FirstFlightRow As Long
    Dim LastFlightRow As Long
    Dim LayoverRow As Long
    Dim HotelName As String
    Dim CandidateHotel As String
    Dim HotelColumn As Long
    Dim DurationText As String
    Dim FlightDayCode As String
    Dim FirstDayCode As String
    Dim LastDayCode As String
    Dim StationCode As String
    Dim LayoverStation As String
    Dim LayoverClass As String
    Dim LayoverHours As Double
    Dim TripType As String
    Dim RequiredCrew As Long
    Dim StartClock As Double
    Dim EndClock As Double
    Dim DayOffset As Long
    Dim OperatingDate As Date
    Dim StartDateTime As Date
    Dim EndDateTime As Date
    Dim RunPlan As Object
    Dim AssignedEmployees As Collection

    StartRow = CLng(SourceRun("StartRow"))
    CreditRow = CLng(SourceRun("CreditRow"))
    StartColumn = CLng(SourceRun("StartColumn"))
    EndColumn = CLng(SourceRun("EndColumn"))
    FirstDayCode = UCase$(CStr(SourceRun("DayCode")))
    OperatingDate = CDate(CDbl(SourceRun("OperatingDate")))

    For DetailRow = StartRow + 2 To CreditRow - 1

        FlightDayCode = UCase$(DE_Text( _
            ws.Cells(DetailRow, StartColumn).Value2))

        If DE_IsDayCode(FlightDayCode) Then
            If FirstFlightRow = 0 Then FirstFlightRow = DetailRow
            LastFlightRow = DetailRow
        ElseIf DE_RowHasData( _
            ws, DetailRow, StartColumn, EndColumn) Then

            ' اسم الفندق يقع قبل عمودي محطة المبيت ومدته.
            ' تجاهل الصفوف الفارغة والملاحظات التي لا تحمل بيانات مبيت.
            CandidateHotel = vbNullString
            For HotelColumn = StartColumn To StartColumn + 7
                If Not IsError(ws.Cells(DetailRow, HotelColumn).Value2) Then
                    DurationText = DE_Text( _
                        ws.Cells(DetailRow, HotelColumn).Value2)
                    If DurationText <> vbNullString Then
                        If Not IsNumeric(DurationText) Then
                            If CandidateHotel <> vbNullString Then _
                                CandidateHotel = CandidateHotel & " "
                            CandidateHotel = CandidateHotel & DurationText
                        End If
                    End If
                End If
            Next HotelColumn

            DurationText = DE_Text( _
                ws.Cells(DetailRow, StartColumn + 9).Value2)

            ' لا يكفي اختلاف يوم العودة أو اسم المحطة لإثبات المبيت.
            If CandidateHotel <> vbNullString Or _
                DurationText <> vbNullString Then
                If CandidateHotel = vbNullString Then
                    ErrorResults.Add "مدة مبيت دون اسم فندق في البيرنق " & _
                        CStr(SourceRun("PairingNumber")) & _
                        "، صف " & CStr(DetailRow) & ". راجع بيانات البيرنق."
                    Exit Function
                End If
                If LayoverRow > 0 Then
                    ErrorResults.Add "يوجد أكثر من سطر مبيت في البيرنق " & _
                        CStr(SourceRun("PairingNumber")) & _
                        ". يلزم دعم المبيت المتعدد قبل توزيعه."
                    Exit Function
                End If
                LayoverRow = DetailRow
                HotelName = CandidateHotel
            End If
        End If

    Next DetailRow

    If FirstFlightRow = 0 Or LastFlightRow = 0 Then
        ErrorResults.Add _
            "تعذر قراءة قطاعات البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    StationCode = UCase$(DE_Text( _
        ws.Cells(FirstFlightRow, StartColumn + 6).Value2))

    If StationCode = "JED" Then
        StationCode = UCase$(DE_Text( _
            ws.Cells(LastFlightRow, StartColumn + 3).Value2))
    End If

    If LayoverRow > 0 Then
        LayoverStation = UCase$(DE_Text( _
            ws.Cells(LayoverRow, StartColumn + 8).Value2))
        If LayoverStation <> vbNullString Then
            StationCode = LayoverStation
        End If

        If Not DE_ReadDurationHours( _
            ws.Cells(LayoverRow, StartColumn + 9), _
            LayoverHours) Then
            ErrorResults.Add _
                "تعذر قراءة ساعات المبيت للبيرنق " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours <= 0# Or LayoverStation = vbNullString Then
            ErrorResults.Add _
                "بيانات المبيت ناقصة أو مدته غير موجبة للبيرنق " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours >= 50# Then
            LayoverClass = "LONG"
        Else
            LayoverClass = "SHORT"
        End If
    End If

    If Not DR_CrewRequirements.Exists(StationCode) Then
        ErrorResults.Add _
            "المحطة " & StationCode & _
            " للبيرنق " & CStr(SourceRun("PairingNumber")) & _
            " غير موجودة ضمن متطلبات INFO!G:I."
        Exit Function
    End If

    RequiredCrew = CLng( _
        DR_CrewRequirements(StationCode)("CrewCount"))
    TripType = CStr( _
        DR_CrewRequirements(StationCode)("TripType"))

    ' عدد أفراد المبيت مستقل عن عدد أفراد الراوندترب للمحطة.
    If LayoverRow > 0 Then RequiredCrew = 2

    ' استثناء عدد الطاقم اختياري ومقيد بالشهر والسنة والبيرنق.
    If Not DE_ResolveCrewOverride( _
        CStr(SourceRun("PairingNumber")), OperatingDate, _
        RequiredCrew, ErrorResults) Then Exit Function


    If Not DE_ReadClock( _
        ws.Cells(FirstFlightRow, StartColumn + 4), StartClock) Then
        ErrorResults.Add _
            "تعذر قراءة وقت بداية البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    If Not DE_ReadClock( _
        ws.Cells(LastFlightRow, StartColumn + 7), EndClock) Then
        ErrorResults.Add _
            "تعذر قراءة وقت نهاية البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    LastDayCode = UCase$(DE_Text( _
        ws.Cells(LastFlightRow, StartColumn).Value2))

    DayOffset = DE_DayOffset(FirstDayCode, LastDayCode)

    If DayOffset = 0 Then
        If EndClock < DE_CellClockValue( _
            ws.Cells(LastFlightRow, StartColumn + 4)) Then
            DayOffset = 1
        End If
    End If

    StartDateTime = OperatingDate + StartClock
    EndDateTime = OperatingDate + DayOffset + EndClock

    If EndDateTime <= StartDateTime Then
        EndDateTime = EndDateTime + 1
    End If

    Set RunPlan = DE_NewRecord()
    RunPlan.Add "RunIndex", 0&
    RunPlan.Add "PairingNumber", _
        CStr(SourceRun("PairingNumber"))
    ' رحلات حلب ALP تُغطى حصريًا بواسطة المؤهلين لرحلات ناس،
    ' حتى لو كانت واقعة قبل عنوان FLY NAS في شيت PAIRING.
    If StationCode = "ALP" Then
        RunPlan.Add "CarrierCode", "NAS"
    Else
        RunPlan.Add "CarrierCode", _
            UCase$(CStr(SourceRun("CarrierCode")))
    End If
    RunPlan.Add "OperatingDate", CDbl(OperatingDate)
    RunPlan.Add "StartDateTime", CDbl(StartDateTime)
    RunPlan.Add "EndDateTime", CDbl(EndDateTime)
    RunPlan.Add "ReturnDayOffset", DayOffset
    RunPlan.Add "StationCode", StationCode
    RunPlan.Add "TripType", TripType
    RunPlan.Add "IsLayover", (LayoverRow > 0)
    RunPlan.Add "HotelName", HotelName
    If LayoverRow > 0 Then
        RunPlan.Add "LayoverEvidence", _
            "مبيت؛ الفندق: " & HotelName & _
            "؛ المدة بالساعات: " & Format$(LayoverHours, "0.00") & _
            "؛ صف المصدر: " & CStr(LayoverRow)
    Else
        RunPlan.Add "LayoverEvidence", _
            "راوندترب؛ لا يوجد سطر فندق ومدة مبيت في البيرنق."
    End If
    RunPlan.Add "LayoverHours", LayoverHours
    RunPlan.Add "LayoverClass", LayoverClass
    RunPlan.Add "RequiredCrew", RequiredCrew
    RunPlan.Add "StartRow", StartRow
    RunPlan.Add "CreditRow", CreditRow
    RunPlan.Add "StartColumn", StartColumn
    Set AssignedEmployees = New Collection
    RunPlan.Add "AssignedEmployees", AssignedEmployees
    RunPlan.Add "LeaderEmployee", vbNullString
    RunPlan.Add "OfficeCount", 0&

    Set DE_BuildRunPlan = RunPlan

End Function

'==========================================================
' تثبيت الارتباطات باعتبارها فترات منع
'==========================================================
Private Function DE_RegisterEvents( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EventRecord As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim StartDate As Date
    Dim EndDate As Date
    Dim MonthStart As Date
    Dim MonthEnd As Date
    Dim ActiveStart As Date
    Dim ActiveEnd As Date
    Dim DateCursor As Date
    Dim DateKey As String
    Dim EventDates As Object

    MonthStart = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth, 1)
    MonthEnd = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth + 1, 0)

    For Each EventRecord In DR_Events

        EmployeeNumber = CStr(EventRecord("EmployeeNumber"))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "موظف الارتباط رقم " & EmployeeNumber & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)
        StartDate = CDate(CDbl(EventRecord("StartDate")))
        EndDate = CDate(CDbl(EventRecord("EndDate"))) + 1

        DE_AddBusyPeriod State, StartDate, EndDate, _
            DE_TYPE_EVENT, False, _
            CStr(EventRecord("EventName"))

        ActiveStart = StartDate
        If ActiveStart < MonthStart Then ActiveStart = MonthStart

        ActiveEnd = EndDate - 1
        If ActiveEnd > MonthEnd Then ActiveEnd = MonthEnd

        If ActiveStart <= ActiveEnd Then
            Set EventDates = State("EventDates")

            If CLng(CDbl(ActiveEnd) - _
                CDbl(ActiveStart) + 1) > _
                CLng(State("LongestEventDays")) Then

                State("LongestEventDays") = _
                    CLng(CDbl(ActiveEnd) - _
                    CDbl(ActiveStart) + 1)
            End If

            For DateCursor = ActiveStart To ActiveEnd
                DateKey = CStr(CLng(CDbl(DateCursor)))
                If Not EventDates.Exists(DateKey) Then
                    EventDates.Add DateKey, True
                End If
            Next DateCursor
        End If

    Next EventRecord

    If Not DE_ApplyEventTargetAdjustments( _
        ErrorResults, WarningResults) Then Exit Function

    DE_RegisterEvents = True

End Function

'==========================================================
' تحديد راحة بداية الشهر من تصنيف آخر رحلة في الشهر السابق
'==========================================================
Private Function DE_GetPreviousMonthRestDays( _
    ByVal EmployeeRecord As Object, _
    ByRef PreviousTripType As String) As Long

    Dim StationCode As String
    Dim SeparatorPosition As Long
    Dim Requirement As Object

    PreviousTripType = vbNullString

    If EmployeeRecord.Exists("PreviousLastStation") Then
        StationCode = UCase$(Trim$(CStr( _
            EmployeeRecord("PreviousLastStation"))))
    End If

    ' قد تُحفظ الرحلة السابقة مثل DAC-MED أو DAC MED؛
    ' التصنيف يعتمد على محطة الذهاب الأساسية.
    StationCode = Replace$(StationCode, "/", "-")
    StationCode = Replace$(StationCode, " ", "-")

    Do While InStr(1, StationCode, "--", vbBinaryCompare) > 0
        StationCode = Replace$(StationCode, "--", "-")
    Loop

    SeparatorPosition = InStr(1, StationCode, "-", vbTextCompare)
    If SeparatorPosition > 1 Then
        StationCode = Left$(StationCode, SeparatorPosition - 1)
    ElseIf Len(StationCode) = 6 Then
        StationCode = Left$(StationCode, 3)
    End If

    If Not DR_CrewRequirements Is Nothing Then
        If DR_CrewRequirements.Exists(StationCode) Then
            Set Requirement = DR_CrewRequirements(StationCode)
            PreviousTripType = UCase$(Trim$(CStr( _
                Requirement("TripType"))))
        End If
    End If

    Select Case PreviousTripType
        Case "LAY"
            DE_GetPreviousMonthRestDays = 3
        Case "LNG", "MED"
            DE_GetPreviousMonthRestDays = 2
        Case "SHT"
            DE_GetPreviousMonthRestDays = 1
        Case Else
            ' إذا تعذر التعرف على التصنيف، نستخدم يومين كحل آمن
            ' ونسجل تنبيهًا واضحًا بدل إيقاف خطة التوزيع بالكامل.
            DE_GetPreviousMonthRestDays = 2
            PreviousTripType = "غير مصنفة"

            If Not DE_Warnings Is Nothing Then
                DE_Warnings.Add _
                    "تعذر تحديد تصنيف آخر رحلة للموظف " & _
                    CStr(EmployeeRecord("EmployeeNumber")) & _
                    " من المحطة [" & StationCode & _
                    "]. تم اعتماد يومين راحة بصورة احتياطية."
            End If
    End Select

End Function

'==========================================================
' صياغة كلمة يوم في وصف فترة الراحة
'==========================================================
Private Function DE_ArabicDayPhrase( _
    ByVal DayCount As Long) As String

    Select Case DayCount
        Case 1
            DE_ArabicDayPhrase = "يوم واحد"
        Case 2
            DE_ArabicDayPhrase = "يومين"
        Case Else
            DE_ArabicDayPhrase = CStr(DayCount) & " أيام"
    End Select

End Function

'==========================================================
' تعديل مستهدف الموظف حسب مجموع أيام ارتباطاته في الشهر
'
' المدد المعتمدة:
'   10 أيام: خصم رحلتين.
'   15 يومًا: نصف المستهدف مع التقريب للأعلى.
'   30 يومًا: منع كامل من الرحلات والمبيت والاحتياط.
'==========================================================
Private Function DE_ApplyEventTargetAdjustments( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim EventDayCount As Long
    Dim OriginalFlights As Long
    Dim AdjustedFlights As Long
    Dim OriginalLayovers As Long
    Dim AdjustedLayovers As Long
    Dim DaysInMonth As Long
    Dim CarryoverTarget As Long

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DE_EmployeeStates.Keys

        Set State = DE_EmployeeStates(EmployeeKey)
        EventDayCount = CLng(State("LongestEventDays"))
        OriginalFlights = CLng(State("OriginalFlightTarget"))
        OriginalLayovers = CLng(State("OriginalLayoverTarget"))
        AdjustedFlights = OriginalFlights
        CarryoverTarget = CLng(State("CarryoverTarget"))
        State("EventDayCount") = State("EventDates").Count
        State("TargetAdjustmentReason") = vbNullString
        State("IsNoFlightMonth") = False

        Select Case EventDayCount

            Case 0 To 9
                ' يمنع الارتباط أيامه فقط، ولا يخفض المستهدف.

            Case 10
                AdjustedFlights = OriginalFlights - 2
                If AdjustedFlights < 0 Then AdjustedFlights = 0
                State("TargetAdjustmentReason") = _
                    "ارتباط 10 أيام: خصم رحلتين"

            Case 15
                AdjustedFlights = (OriginalFlights + 1) \ 2
                State("TargetAdjustmentReason") = _
                    "ارتباط 15 يومًا: نصف المستهدف"

            Case Is >= 30
                AdjustedFlights = 0
                State("IsNoFlightMonth") = True
                State("TargetAdjustmentReason") = _
                    "ارتباط 30 يومًا: منع شهري كامل"

            Case Else
                WarningResults.Add _
                    "مدة ارتباط الموظف " & _
                    CStr(State("EmployeeNumber")) & _
                    " داخل الشهر هي " & _
                    CStr(EventDayCount) & _
                    " يومًا، وهي ليست من المدد المعتمدة " & _
                    "10 أو 15 أو 30 يومًا؛ مُنعت أيام " & _
                    "الارتباط دون تخفيض المستهدف."

        End Select

        If OriginalFlights > 0 _
            And OriginalLayovers > 0 _
            And AdjustedFlights > 0 Then

            AdjustedLayovers = _
                ((OriginalLayovers * AdjustedFlights) + _
                OriginalFlights - 1) \ OriginalFlights
        Else
            AdjustedLayovers = 0
        End If

        If AdjustedLayovers > AdjustedFlights Then
            AdjustedLayovers = AdjustedFlights
        End If

        ' الإداري والموقوف وموظف الاستاندباي لا يدخلون في
        ' التوزيع الآلي ولا الاحتياط. رحلات الإداري تُثبت فقط
        ' من OFFICE_DATA، وموظف SB يُسجل له تناوبه الشهري فقط.
        If CBool(State("IsOffice")) _
            Or CBool(State("IsHold")) _
            Or CBool(State("IsStandby")) Then

            AdjustedFlights = 0
            AdjustedLayovers = 0
            CarryoverTarget = 0
        End If

        State("NormalFlightTarget") = AdjustedFlights
        State("FlightTarget") = _
            AdjustedFlights + CarryoverTarget
        State("LayoverTarget") = AdjustedLayovers

        If AdjustedLayovers > 0 Then
            State("LongLayoverTarget") = 1
            State("ShortLayoverTarget") = _
                AdjustedLayovers - 1
        Else
            State("LongLayoverTarget") = 0
            State("ShortLayoverTarget") = 0
        End If

        DE_SetEmployeeCarrierTargets State

    Next EmployeeKey

    DE_ApplyEventTargetAdjustments = True

End Function

'==========================================================
' تقسيم مستهدف الموظف بين السعودية والناقل المتخصص
'==========================================================
Private Sub DE_SetEmployeeCarrierTargets( _
    ByVal State As Object)

    Dim TotalTarget As Long
    Dim SaudiaTarget As Long
    Dim SpecialTarget As Long
    Dim SpecialCarrier As String
    Dim SaudiaDemand As Long
    Dim SpecialDemand As Long

    ' يقسم المستهدف العادي أولًا، ثم تضاف مطالبات الشهر
    ' السابق إلى ناقلها المحدد دون تغيير توازن الرحلات العادية.
    TotalTarget = CLng(State("NormalFlightTarget"))

    Select Case UCase$(CStr(State("CarrierProfile")))
        Case "SAUDIA_NAS"
            SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL"
            SpecialCarrier = "ADEAL"
        Case Else
            SpecialCarrier = vbNullString
    End Select

    If SpecialCarrier = vbNullString Then
        SaudiaTarget = TotalTarget
        SpecialTarget = 0
    Else
        SaudiaTarget = TotalTarget \ 2
        SpecialTarget = TotalTarget \ 2

        If TotalTarget Mod 2 <> 0 Then
            SaudiaDemand = DE_CarrierRunCount("SAUDIA")
            SpecialDemand = DE_CarrierRunCount(SpecialCarrier)

            If SaudiaDemand > SpecialDemand Then
                SaudiaTarget = SaudiaTarget + 1
            Else
                SpecialTarget = SpecialTarget + 1
            End If
        End If
    End If

    State("SaudiaTarget") = SaudiaTarget
    State("SpecialCarrierTarget") = SpecialTarget
    State("SpecialCarrierCode") = SpecialCarrier

    State("SaudiaTarget") = _
        CLng(State("SaudiaTarget")) + _
        CLng(State("CarryoverSaudiaTarget"))

    State("SpecialCarrierTarget") = _
        CLng(State("SpecialCarrierTarget")) + _
        CLng(State("CarryoverSpecialTarget"))

End Sub

Private Function DE_CarrierRunCount( _
    ByVal CarrierCode As String) As Long

    Dim RunPlan As Object

    For Each RunPlan In DE_RunPlans
        If StrComp(CStr(RunPlan("CarrierCode")), _
            CarrierCode, vbTextCompare) = 0 Then
            DE_CarrierRunCount = _
                DE_CarrierRunCount + 1
        End If
    Next RunPlan

End Function

'==========================================================
' إنشاء تناوب SB-1 / SB-2 / SB-3 طوال الشهر
'==========================================================
Private Function DE_RegisterStandby( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim SBGroup As String
    Dim GroupNumber As Long
    Dim DayNumber As Long
    Dim DaysInMonth As Long
    Dim DutyDate As Date

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DR_SBByEmployee.Keys

        If Not DE_EmployeeStates.Exists(CStr(EmployeeKey)) Then
            ErrorResults.Add _
                "موظف الاستاندباي رقم " & CStr(EmployeeKey) & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set State = DE_EmployeeStates(CStr(EmployeeKey))

        State("IsStandby") = True
        SBGroup = UCase$(CStr(DR_SBByEmployee(EmployeeKey)))

        Select Case SBGroup
            Case "SB-1": GroupNumber = 1
            Case "SB-2": GroupNumber = 2
            Case "SB-3": GroupNumber = 3
            Case Else
                ErrorResults.Add _
                    "رمز الاستاندباي " & SBGroup & _
                    " غير صحيح للموظف " & CStr(EmployeeKey) & "."
                Exit Function
        End Select

        For DayNumber = GroupNumber To DaysInMonth Step 3
            DutyDate = DateSerial( _
                DR_ActiveYear, DR_ActiveMonth, DayNumber)

            DE_AddAssignment _
                State, Nothing, DE_TYPE_SB, SBGroup, _
                DutyDate, DutyDate + 1, False, False
        Next DayNumber

    Next EmployeeKey

    DE_RegisterStandby = True

End Function

'==========================================================
' تثبيت رحلات الإداريين قبل التوزيع الآلي
'==========================================================
Private Function DE_RegisterOfficeFlights( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim OfficeRecord As Object
    Dim RunPlan As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim PairingNumber As String
    Dim FlightDate As Date
    Dim IsLeader As Boolean
    Dim GroupCode As String
    Dim ExistingLeader As String
    Dim ExistingLeaderState As Object
    Dim ConflictDetails As String

    For Each OfficeRecord In DR_OfficeFlights

        EmployeeNumber = CStr(OfficeRecord("EmployeeNumber"))
        PairingNumber = CStr(OfficeRecord("PairingNumber"))
        FlightDate = CDate(CDbl(OfficeRecord("FlightDate")))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set RunPlan = DE_FindRun(PairingNumber, FlightDate)
        If RunPlan Is Nothing Then
            ErrorResults.Add _
                "تعذر إيجاد تشغيل البيرنق " & PairingNumber & _
                " بتاريخ " & Format$(FlightDate, "dd/mm/yyyy") & _
                " الخاص بالإداري " & EmployeeNumber & "."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)

        If CBool(State("IsNoFlightMonth")) Then
            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " لديه ارتباط مدته 30 يومًا، ولا يجوز " & _
                "تثبيت رحلة له خلال الشهر."
            Exit Function
        End If

        If Not DE_EmployeeCanCoverCarrier( _
            CStr(State("CarrierProfile")), _
            CStr(RunPlan("CarrierCode"))) Then

            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " مؤهل بصفة " & CStr(State("CarrierProfile")) & _
                "، ولا يستطيع تغطية البيرنق " & PairingNumber & _
                " التابع للناقل " & _
                CStr(RunPlan("CarrierCode")) & "."
            Exit Function

        End If

        ConflictDetails = vbNullString

        If Not DE_IsAvailable( _
            State, CDate(RunPlan("StartDateTime")), _
            CDate(RunPlan("EndDateTime")), _
            ConflictDetails) Then

            ErrorResults.Add _
                "رحلة الإداري رقم " & EmployeeNumber & _
                "، البيرنق " & PairingNumber & _
                "، بتاريخ " & _
                Format$(FlightDate, "dd/mm/yyyy") & _
                " تتعارض مع فترة مسجلة مسبقًا." & _
                vbCrLf & _
                "الفترة الجديدة: " & _
                Format$(CDate(RunPlan("StartDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                " إلى " & _
                Format$(CDate(RunPlan("EndDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                vbCrLf & ConflictDetails

            Exit Function
        End If

        GroupCode = CStr(State("GroupCode"))
        ExistingLeader = CStr(RunPlan("LeaderEmployee"))
        IsLeader = False

        Select Case GroupCode
            Case "SSS", "SST"

                If ExistingLeader <> vbNullString Then
               

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

الكود المرسل يغيّر النقاش من مبادئ عامة إلى تشخيص محرك توزيع فعلي، لكن النص ظاهر أنه مقطوع قبل اكتماله.  
هل تريد أن نبدأ بتحليل **سبب بقاء قائد 135 ناقصًا**، أم **مراجعة R19 معماريًا وأدائيًا**، أم ترسل بقية الكود أولًا؟

### Participant

انظرو وافهمو طريقة عمل الكود مع التعديلات اللي قلتها لكم بالاخير

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

المطلوب الآن فهم تدفق الكود كاملًا وربطه بسجل التعديلات الأخير، خصوصًا منطق **المبيت قبل الراوندترب** ومنع نقل تكليف المبيت بعد اعتماده. Auto Claude: حلّل التنفيذ من البداية للنهاية، وحدد هل التعديلات الأخيرة مطبقة فعليًا في المسار التنفيذي أم توجد ثغرات أو تعارضات بينها وبين مرحلة الإصلاح.

### Auto Claude (Claude Sonnet 5.5)

ما أقدر أحلل التنفيذ من البداية للنهاية، لأن الكود وصلني مقطوعًا. اللي وصلني هو سجل الأفكار F01–F14 وترويسة R19 وتعريفات المتغيرات العامة فقط. ما وصلتني الإجراءات نفسها، ولا أي دالة تتعلق بالمبيت أو الإصلاح أو النقل. أي حكم الحين على «مطبّق فعليًا» أو «فيه تعارض» يكون تخمينًا، وما أبي أعطيك تخمينًا على أنه تحليل.

**اللي أقدر أقوله من السجل:**
- F08 يثبت أن توقف قائد 135 ما كان بسبب الوقت ولا سقف 600، بل بسبب حد الجولة الأخيرة (100 محاولة). F09 رفعه إلى 400 وبقي النقص. معناه أن المشكلة غالبًا **بنيوية** وليست في الحدود، فرفع الحدود وحده ما راح يحلها.
- F07 معلّق، وسببه منطقي: تخزين المسارات الفاشلة بدون مفتاح كامل (الخطة والمسار والمنع والعمق) يستبعد حلولًا صالحة.
- ترويسة R19 تقول إن المبيت قبل الراوندترب وإن مرحلة الإصلاح لا تنقل تكليف المبيت بعد اعتماده. هذا يتحقق بالكود، وما أقدر أؤكده من الترويسة.

**شيء ممكن يغيّر الاستنتاج:** إذا ثبّتنا تكليف المبيت وصار غير قابل للنقل، فقد يكون هذا نفسه هو سبب بقاء 135 ناقصًا، لأن الإصلاح فقد المرونة اللي كان يحتاجها. أقترح فحص هذا الاحتمال أولًا.

**المطلوب منك:** أرسل الكود على أجزاء، وابدأ بالإجراءات الرئيسية للمبيت ومرحلة الإصلاح ودالة قفل التكليف. وإذا عندك تقرير آخر تشغيل لـR19، أرسله معه، لأن النتيجة الفعلية تؤخذ منه كما كتبت في السجل.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة الحاسمة: **تثبيت المبيت نفسه قد يقلّل مساحة الحلول المتاحة للإصلاح**، لكن لا يمكن إثبات ذلك من الجزء المقطوع. Chatty GPT 5.6: قيّم هذا الاحتمال منطقيًا، وحدد بالضبط أي إجراءات/دوال من R19 يجب فحصها لإثبات أو نفي أن قفل المبيت هو سبب بقاء 135 ناقصًا.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

منطقيًا احتمال أن **قفل المبيت هو سبب بقاء 135 ناقصًا موجود، لكنه ليس الاحتمال الأول عندي**. سجل التجارب يقول إن 135 كان ناقصًا بالفعل قبل R19؛ لذلك لا يصح تحميل التعديل الأخير المشكلة إلا إذا غيّر مسارًا كان قادرًا سابقًا على إصلاح 135 ولم ينجح فقط بسبب حدود البحث.

لإثبات ذلك من R19 أريد تتبع أربع نقاط تحديدًا:

1. **مرحلة إنشاء المبيت:** الإجراء الذي يختار طاقم رحلات المبيت قبل RoundTrip. نحتاج معرفة لماذا اختار هؤلاء الموظفين تحديدًا وهل استهلك شخصًا يحتاجه 135 لاحقًا.
2. **مرحلة RoundTrip:** هل ترى الموظفين المرتبطين بالمبيت كـ unavailable فعلًا، أم ما زالت تحاول استخدامهم ثم ترفضهم لاحقًا؟
3. **محرك الإصلاح:** خصوصًا الإجراءات التي تنشئ النقل/التبديل وتفحص صلاحية الخطة. أريد مكان الشرط الذي يقول فعليًا: «هذا Assignment مبيت → ممنوع نقله».
4. **حالة الموظف:** أي دوال تحدث `DE_EmployeeStates` بعد اعتماد المبيت؛ لأن الخلل قد لا يكون في القفل نفسه، بل في أن حالة الموظف بعد المبيت تمنع استخدامه في يوم آخر رغم أنه متاح.

وأضيف احتمالًا أهم لم يذكره Auto Claude: **ترتيب المبيت قبل RoundTrip قد يسبب المشكلة حتى بدون القفل.** إذا كان اختيار المبيت Greedy ويأخذ أفضل موظف محليًا دون مراعاة احتياجات الرحلات اللاحقة، فقد يصل RoundTrip إلى 135 ولا يوجد له قائد صالح أصلًا؛ عندها منع الإصلاح من تحريك المبيت مجرد يمنع المحرك من تصحيح قرار سابق.

لذلك أريد الكود الذي يبدأ من **بناء خطة المبيت → RoundTrip → Repair → تحديث EmployeeStates**. من هذا المسار نستطيع تحديد أين يُحاصر 135 بدل التخمين.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة الأهم: قد تكون المشكلة في **الاختيار الجشع للمبيت قبل RoundTrip** لا في قفل المبيت نفسه؛ أي أن الإصلاح يرث خطة خُنقت مبكرًا. Auto Claude: كيف تختبر هذا الاحتمال بأقل تعديل ممكن، وتفرّق في التقرير بين «135 بلا قائد بسبب اختيار المبيت» وبين «135 له حل لكن Repair لم يجده»؟

### Auto Claude (Claude Sonnet 5.5)

أتفق مع Chatty GPT 5.6 أن الاختيار الجشع للمبيت احتمال جدي، وأن 135 كان ناقصًا قبل R19. لكن ما زلنا نتكلم بدون الكود الفعلي، فهذا اختبار مقترح وليس نتيجة.

**أقل تعديل ممكن: تشخيص فقط، بدون تغيير السلوك.**

1. **تجربة الجذور الحرة:** عند أول فشل لتغطية 135، شغّل بحثًا منفصلًا يعامل المبيت كقابل للنقل (يرفع القفل فقط داخل التشخيص). تُسجَّل النتيجة ولا تُعتمد في الخطة.
2. **تجربة الاستبعاد:** أعد بناء الخطة بعد منع المبيت من اختيار المرشحين الذين يؤهلهم 135 فقط. هذا يكشف هل المبيت استهلك قائده.

**قراءة النتيجة في التقرير:**
- لا يوجد أي موظف مؤهل لـ135 بعد استبعاد المرتبطين بالمبيت → **135 بلا قائد بسبب المبيت**.
- يوجد مرشح مؤهل غير مشغول لكن Repair لم يصل له → **حل موجود والإصلاح لم يجده**.
- يظهر الحل فقط عند رفع القفل → **القفل هو السبب**.

وأقترح أن يطبع التقرير لكل نقص: قائمة المؤهلين، وسبب رفض كل واحد (مبيت، تعارض راحة، حمل يوم، منع).

**شرط لم يُذكر:** قد يكون 135 غير قابل للتغطية فعلًا بالقيود الحالية. أثبت أولًا بفحص جدوى بسيط (هل يوجد أي مرشح مؤهل أصلًا) قبل الحكم على الإصلاح.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة الحاسمة: قبل اتهام ترتيب المبيت أو Repair يجب إثبات أن **135 قابل للتغطية أصلًا تحت القيود الحالية**. Chatty GPT 5.6: صمّم فحص جدوى تشخيصيًا لا يغيّر الخطة، ويحدد لكل مرشح لـ135 أي قيد أسقطه، مع التمييز بين «مستحيل فعليًا» و«الحل يتطلب سلسلة تبديلات».

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

أتفق مع Auto Claude في فحص الجدوى، لكن عندي اعتراض مهم: **وجود مرشح مؤهل لـ135 لا يعني أن 135 قابل للتغطية في الخطة كاملة**. قد يكون المرشح مشغولًا، ويمكن تحريره فقط بنقل موظف ثانٍ، وهذا الثاني يحتاج ثالثًا؛ أي أن الحل سلسلة تبديلات.

لذلك أبني فحصًا تشخيصيًا **Read-Only** لا يعدّل `DE_Assignments`:

لكل موظف يمكنه شغل مقعد القائد في 135 نسجل:

`Employee → Qualified? → CurrentAssignment → RejectReason → Movable?`

ويكون سبب السقوط محددًا، مثل: التصنيف، الوقت/التداخل، الراحة، الحمل اليومي، المبيت المقفل، أو قيد آخر موجود فعلًا في R19.

ثم نصنف النتيجة إلى ثلاث حالات:

**A — DIRECT:** يوجد مرشح صالح وفارغ الآن.  
هنا المشكلة في التوزيع الأساسي إذا بقي 135 ناقصًا.

**B — CHAIN:** لا يوجد مرشح مباشر، لكن يوجد مرشح صالح مشغول وتكليفه يمكن تغطيته بموظف آخر، ونكرر ذلك حتى العمق الذي يستخدمه Repair.  
هنا **الحل موجود كسلسلة تبديلات**؛ فإذا لم يجده Repair فالمشكلة في البحث أو شروطه.

**C — INFEASIBLE:** كل المسارات تنتهي بقيد غير قابل للحل، وليس فقط بانتهاء حد المحاولات أو الوقت.  
عندها فقط أقبل عبارة «135 مستحيل تحت القيود الحالية».

وأضيف اختبارًا أراه حاسمًا: الفحص يجب أن يعمل على **نسختين من الحالة**: قبل توزيع المبيت مباشرة، وبعد تثبيت المبيت. إذا تحولت 135 من `DIRECT/CHAIN` قبل المبيت إلى `INFEASIBLE` بعده، صار عندنا دليل أن قرار المبيت هو الذي أغلق الحل، بدل الاستمرار في تعديل حدود Repair عشوائيًا.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة الحاسمة: مقارنة حالة 135 **قبل المبيت وبعد تثبيته** تستطيع عزل أثر قرار المبيت عن قصور Repair. Auto Claude: هل هذا الاختبار وحده كافٍ لإثبات السببية، أم قد تغيّر مرحلة المبيت حالات أخرى مثل الأحمال والراحة فتجعل النتيجة مضللة؟ صمّم المقارنة التي تعزل أثر كل قيد.

### Auto Claude (Claude Sonnet 5.5)

لا، مقارنة قبل/بعد المبيت وحدها غير كافية لإثبات السببية. أتفق مع Chatty GPT 5.6 أنها أداة عزل جيدة، لكن مرحلة المبيت تغيّر أكثر من متغير في نفس الوقت: تقفل تكليفات، وتزيد حمل الأيام، وتستهلك أيام الراحة، وتغيّر `DE_EmployeeStates`. فإذا انتقلت 135 من CHAIN إلى INFEASIBLE، لا نعرف أي تغيير فعل ذلك.

**التصميم الذي يعزل كل قيد (تجارب إلغاء واحدة في كل مرة، قراءة فقط):**

1. **خط الأساس:** الحالة قبل المبيت، ونسجل تصنيف 135.
2. **بعد المبيت كاملًا:** نسجل التصنيف.
3. **ثلاث نسخ بعد المبيت، في كل نسخة نُلغي أثرًا واحدًا فقط:**
   - رفع قفل النقل فقط (القفل هو المتهم).
   - إعادة أحمال الأيام لقيمها قبل المبيت (الحمل هو المتهم).
   - إعادة حالة الراحة للموظفين المرتبطين بالمبيت (الراحة هي المتهمة).
4. **القراءة:** القيد الذي يعيد 135 إلى DIRECT أو CHAIN عند إلغائه وحده هو السبب. وإذا لزم إلغاء اثنين معًا، فالسبب تفاعل بينهما، وهذا يُسجَّل كنتيجة مستقلة.

**نقطة لم تُطرح:** يجب أن يكون الفحص حتميًا. إذا كان ترتيب المرشحين أو اختيار المبيت يعتمد على ترتيب `Collection` أو على أي مصدر عشوائية، فقد تختلف النتيجة بين تشغيلين لنفس المدخلات. لذلك أشغّل خط الأساس مرتين أولًا وأقارن التقريرين. وبما أنني لم أرَ الكود، هذا شرط تحقق وليس ادعاءً بوجود المشكلة.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: قبل تشخيص 135 يجب التأكد أن المحرك **حتمي** وأن نفس المدخلات تنتج نفس الخطة، وإلا تصبح المقارنات مضللة. Chatty GPT 5.6: ما أقل Instrumentation نضيفه لـR19 لتسجيل ترتيب القرارات والمرشحين بحيث نعرف هل الاختلاف سببه عدم الحتمية أم منطق القيود، دون إبطاء المحرك بشكل مؤثر؟

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

أختلف قليلًا مع Auto Claude هنا: قبل إضافة تجارب تشخيصية كثيرة، نحتاج أولًا **بصمة تنفيذ خفيفة جدًا** تثبت أن تشغيلين متطابقين يسلكان المسار نفسه.

أضيف إلى R19 سجلًا في الذاكرة فقط، ولا نكتب شيئًا إلى الشيت أثناء البحث. كل قرار مهم يسجل سطرًا مختصرًا بالشكل:

`Stage | Trip | Seat | Candidate | Decision | Reason`

لكن لا أسجل كل فحص داخلي؛ هذا سيضخم التقرير ويؤثر في الأداء. أسجل فقط:

- ترتيب الرحلات عند بدء كل مرحلة.
- ترتيب المرشحين النهائي لكل مقعد قبل اختيار أحدهم.
- المرشح الذي تم اختياره وسبب الاختيار.
- في Repair: رقم المحاولة، التكليف المراد تحريره، النقل المقبول/المرفوض وسبب الرفض.
- أي تغيير في حالة 135 تحديدًا.

ثم نحسب في نهاية التشغيل **بصمة نصية** لهذا التسلسل ونقارن تشغيلين بنفس البيانات.

إذا اختلفت البصمة **قبل أول قرار مختلف**، نرجع إلى مصدر ترتيب المرشحين أو الحالة التي دخلت المرحلة. أما إذا تطابق التسلسل حتى نقطة معينة ثم رفض أحد التشغيلين مرشحًا قبله الآخر، فالمتهم هو **حالة/قيد تغير بين التشغيلين** وليس ترتيب البحث.

والشرط الجديد الذي أبحث عنه تحديدًا في VBA: أي ترتيب مبني ضمنيًا على `Dictionary.Keys` أو على تجميع بيانات لم يجرِ له **Sort صريح**. لا أفترض أنه سبب المشكلة قبل رؤية R19، لكن لا ينبغي لمحرك توزيع حساس أن يعتمد على ترتيب ضمني أصلًا.

بهذه الإضافة نحصل على شيء أهم من تقرير «135 فشل»: نعرف **أول نقطة بالضبط انحرف عندها التشغيلان**، وبدون تغيير منطق التوزيع نفسه.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: الاعتماد على ترتيب ضمني للمرشحين قد يجعل المحرك غير حتمي حتى لو كانت القيود صحيحة. Auto Claude: قبل إضافة سجل جديد، هل يمكن أن يكون **التسجيل نفسه مؤثرًا في الأداء أو توقيت Repair** فيغيّر النتيجة؟ اقترح طريقة إثبات الحتمية بأقل تدخل ممكن.

### Auto Claude (Claude Sonnet 5.5)

نعم، التسجيل نفسه ممكن يغيّر النتيجة، وهذا أهم من تكلفته على الأداء. اللي يهم هنا **الوقت**: السجل F04 وF06 يقولان إن البحث يتوقف عند حد الوقت (90 ثانية) أو حد المحاولات. إذا أبطأ التسجيل كل محاولة، يتغير عدد المحاولات اللي تتم قبل انتهاء الوقت، فتتغير النتيجة حتى لو المنطق ثابت. وهذا يؤثر فقط في الحالات اللي تنتهي بحد الوقت، وليس بحد المحاولات.

**أقل تدخل لإثبات الحتمية:**

1. **قارن بدون تسجيل أولًا:** شغّل نفس المدخلات مرتين وقارن مخرجات الخطة النهائية فقط (التكليفات مرتبة). هذا لا يضيف أي كود.
2. **اجعل التشخيص مبنيًا على عدّاد المحاولات لا الساعة:** أثناء التشخيص، ارفع حد الوقت كثيرًا واعتمد على حد المحاولات وحده. هكذا يزول أثر السرعة.
3. **سجّل في مصفوفة مسبقة الحجز** (لا `Collection` ولا تجميع نصوص)، واحسب البصمة في النهاية. هذا أخف بكثير.

**اعتراض على Chatty GPT 5.6:** بصمة التسلسل الكامل قد تختلف بين تشغيلين لأسباب غير مؤثرة (مثل رقم المحاولة عند حد الوقت). الأفضل أن تُقارن البصمة حتى أول قرار اختيار فقط، لا التسلسل كله.

وهذا كله ما زال قبل رؤية الكود الفعلي، فأحتاج الإجراءات نفسها لأحكم.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: حدود الوقت تجعل **الأداء جزءًا من سلوك الخوارزمية نفسه**؛ أي تحسين أو تسجيل قد يغيّر الحل الناتج. Chatty GPT 5.6: هل ترى أن الاعتماد على 90 ثانية أصلًا تصميم غير مناسب لمحرك توزيع كهذا؟ اقترح معيار توقف حتمي يحافظ على الأداء ولا يجعل نفس البيانات تنتج نتائج مختلفة حسب سرعة الجهاز.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

نعم، أرى أن جعل **90 ثانية معيار التوقف الأساسي** تصميم غير مناسب لمحرك توزيع نريد منه نتيجة قابلة للتكرار. الزمن يصلح كـ **حارس أمان** لمنع التعليق، لا كجزء يقرر مقدار مساحة الحلول التي سنبحثها.

وأوافق Auto Claude على أن التسجيل قد يغيّر عدد المحاولات إذا كان الوقت هو الحاكم؛ وهذه بحد ذاتها علامة أن معيار التوقف يحتاج تعديلًا.

أقترح أن يصبح التوقف حتميًا بهذا الشكل:

**الأساس = ميزانية بحث Search Budget، وليس ثواني.**

مثلًا وفق حدود R19 الحالية نفسها:
`Round1 = 20`
`Round2 = 60`
`Round3 = 400`
مع سقف إجمالي للمحاولات/العقد المفحوصة.

كل تشغيل لنفس الحالة يزور **نفس العدد الأقصى من حالات البحث وبنفس ترتيب المرشحين**. ويبقى حد الزمن 90 ثانية فقط كـ Emergency Stop، ويظهر في التقرير:

`STOP = SEARCH_LIMIT`  
أو  
`STOP = TIME_SAFETY`

وهنا عندي اعتراض إضافي: **عدد المحاولات وحده أيضًا ليس مثاليًا** إذا كانت محاولة واحدة تستطيع داخليًا فحص عدد متغير جدًا من الحالات.

الأفضل أن نعد وحدة عمل أدق، مثل:

`NodesVisited`

أي كل حالة خطة فعلية دخل إليها البحث أو كل انتقال صالح جرى تقييمه. عندها نقول مثلًا: الجولة الثالثة لها ميزانية `N` عقدة، وليس `N` ثانية.

وهناك شرط أهم: حتى هذا لن يجعل المحرك حتميًا إذا لم يكن **ترتيب الرحلات والمرشحين والتبادلات حتميًا**. لذلك التصميم الصحيح عندي:

**ترتيب ثابت + Search Budget ثابت + Tie-Breaker ثابت + الزمن حارس أمان فقط.**

بعدها إذا بقي 135 ناقصًا، يصبح تقرير «استنفد البحث» ذا معنى هندسي حقيقي، وليس «الجهاز لم يكن سريعًا بما يكفي هذه المرة».

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: لا يمكن الحكم على سبب نقص 135 قبل رؤية إجراءات R19 الفعلية، لكن التشخيص الصحيح يجب أن يسبق أي تعديل جديد على الخوارزمية.

**Agreed**
- يجب أولًا إثبات هل 135 قابل للتغطية مباشرة أو عبر سلسلة تبديلات أم مستحيل تحت القيود الحالية.
- يجب عزل أثر المبيت عن القفل والراحة والحمل بدل زيادة حدود Repair عشوائيًا.
- البحث ينبغي أن يعتمد على ترتيب وميزانية عمل حتميين، مع الزمن كحارس أمان فقط.

**Still disputed**
- هل السبب الأقرب هو الاختيار الجشع للمبيت، قفل المبيت، حالة الموظفين، أم قصور Repair؛ لا يوجد كود كافٍ للحسم.
- Auto Claude يفضّل تشخيصًا تدريجيًا بأقل تدخل، بينما Chatty GPT 5.6 يركز على تتبع مسار البحث وتصنيف DIRECT/CHAIN/INFEASIBLE.

**Why**
- 135 بقي ناقصًا عبر تجارب سابقة رغم رفع حدود البحث، ما يجعل مجرد زيادة المحاولات تفسيرًا غير كافٍ.
- R19 المرسل كان مقطوعًا قبل الإجراءات التي تنفذ المبيت وRoundTrip وRepair وتحديث `DE_EmployeeStates`.

**What would change it**
- توفير المسار الفعلي من إنشاء المبيت ثم RoundTrip ثم Repair ثم تحديث `DE_EmployeeStates` سيتيح تحديد سبب 135 بدل الاستنتاج من سجل التجارب.
- تقرير تشخيص يوضح مرشحي 135 وأسباب رفضهم قبل المبيت وبعده سيحسم أين أُغلق مسار الحل.

### Participant

ايش المطلوب مني اعطائكم اياه  لتجدو الحل في هذه المشكله

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Auto Claude: حدّد للمستخدم **بالضبط** ما الذي يرسله الآن وبأي ترتيب، مع أسماء الإجراءات أو أجزاء الكود المطلوبة والبيانات/التقرير اللازمين، بحيث لا يضطر لإرسال المشروع كاملًا ولا نعود نطلب منه أجزاء إضافية واحدة تلو الأخرى.

### Auto Claude (Claude Sonnet 5.5)

المطلوب منك خمسة أشياء، بهذا الترتيب، بدون إرسال المشروع كاملًا:

1. **بقية R19 كاملة**، لأن اللي وصلني انقطع بعد تعريفات المتغيرات العامة. إذا كانت طويلة، أرسلها على رسالتين متتاليتين مع ترقيم (1/2، 2/2).

2. **الإجراء الرئيسي** اللي يستدعي المراحل بالترتيب (المبيت ثم RoundTrip ثم Repair)، وأي إجراء يحدّث `DE_EmployeeStates` بعد كل مرحلة.

3. **دالة اختيار طاقم المبيت** ودالة الراوندترب، مع الدالة اللي تقفل تكليف المبيت بعد اعتماده.

4. **محرك الإصلاح كله**: الحلقات الثلاث (20 ثم 60 ثم 400)، ودالة النقل أو التبديل، ودالة فحص صلاحية الخطة. وأهم شيء أبي أشوفه فيه الشرط اللي يمنع نقل تكليف المبيت.

5. **تقرير آخر تشغيل لـR19**، خصوصًا الأسطر الخاصة بالقائد 135: عدد المحاولات، سبب التوقف، والمخالفات.

**معلومة إضافية تفيد:** وش القيود الفعلية على مقعد قائد 135 (التصنيف، الراحة، الحمل اليومي)، وكم موظف مؤهل له في بيانات الاختبار. وإذا عندك نسخة بيانات تعطي 135 كاملًا في R18 أو قبله، فاذكر ذلك.

بعد ما توصلني هذه الأجزاء، أقدر أحدد هل المشكلة في اختيار المبيت، أو القفل، أو بحث Repair، بدل التخمين.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: راجع قائمة Auto Claude من منظور عملي للمستخدم: هل إرسال **R19 كاملًا + تقرير آخر تشغيل** يكفي وحده غالبًا لاستخراج بقية الإجراءات والقيود، أم توجد معلومات خارج R19 لا يمكن استنتاجها ويجب طلبها الآن تحديدًا لتجنب إرهاق المستخدم بطلبات مكررة؟

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

أبسطها عليك أكثر من قائمة Auto Claude: **لا ترسل خمسة أشياء منفصلة الآن.**

أرسل لنا فقط:

1. **موديول R19 كاملًا من أول `Option Explicit` إلى آخر سطر `End Sub` أو `End Function` في الموديول.** إذا كان طويلًا جدًا، قسمه رسائل `R19-1`، `R19-2`، `R19-3` بدون حذف أي جزء.
2. **تقرير آخر تشغيل كامل لـR19** كما خرج من البرنامج، وليس فقط أسطر 135.

من هذين الاثنين نستطيع نحن استخراج أسماء إجراءات المبيت وRoundTrip وRepair والقفل وتتبع `DE_EmployeeStates` بدل أن أجعلك تبحث عنها بنفسك.

الشيء الوحيد الذي قد نحتاجه خارج R19 هو **الموديول الذي يحمّل البيانات ويحدد أهلية الموظفين/التصنيفات**، مثل `modDistributionReader` الذي تشير إليه ترويسة الكود. لكن **لا ترسله الآن**. سنطلبه فقط إذا وجدنا أن قرار رفض قائد محتمل لـ135 صادر من دالة خارج R19.

وأختلف مع Auto Claude في طلبك الآن تحديد قيود قائد 135 وعدد المؤهلين له؛ **لا أريدك تستنتجها يدويًا**. الأفضل أن نستخرجها من الكود والبيانات حتى لا نبني التشخيص على افتراض بشري قد يكون هو نفسه الخطأ.

إذًا الخطوة التالية فقط: **R19 كامل + تقرير آخر تشغيل كامل**. بعدها نحدد نحن مكان الخلل وما الجزء التالي المطلوب، إن احتجناه أصلًا.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: المطلوب منك الآن فقط **موديول R19 كاملًا + تقرير آخر تشغيل كامل**؛ لا تبحث يدويًا عن أي شيء آخر.

**Agreed**
- R19 يجب أن يصل كاملًا دون حذف، ويمكن تقسيمه إلى عدة رسائل مرقمة.
- تقرير التشغيل الكامل مهم لتتبع سبب بقاء 135 ناقصًا.
- أي موديول خارجي سنطلبه لاحقًا فقط إذا أثبت R19 أنه يعتمد عليه في القرار.

**Still disputed**
- Auto Claude طلب معلومات إضافية منذ البداية، بينما Chatty GPT 5.6 يرى أن استخراجها من الكود أدق وأقل عبئًا عليك.

**Why**
- R19 الكامل سيكشف إجراءات المبيت وRoundTrip وRepair والقفل وتحديث حالات الموظفين.
- تجنب الاستنتاج اليدوي لقيود 135 يقلل احتمال إدخال معلومة غير مطابقة لما ينفذه الكود فعليًا.

**What would change it**
- إذا ظهر داخل R19 أن أهلية الموظفين أو تصنيفاتهم تُحسم في موديول خارجي، سنطلب ذلك الموديول تحديدًا.

### Participant

هل تريدون جميع الاكواد اللي في الفورمات واللي في الموديولات ..؟

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Auto Claude: أجب المستخدم مباشرة: هل نحتاج **كل أكواد الفورمات والموديولات** أم يكفي موديول R19 وتقرير التشغيل الآن؟ وإذا كان اسم R19 مجرد رقم نسخة وليس اسم موديول فعلي، وضّح له كيف يحدد الكود المقصود بدون مصطلحات تقنية مربكة.

### Auto Claude (Claude Sonnet 5.5)

لا، ما نحتاج كل أكواد الفورمات والموديولات. يكفي الآن شيئان فقط:

1. **الموديول اللي فيه الكود اللي أرسلته** (اللي يبدأ بـ`Option Explicit` وفيه التعليق "R19 - تطبيق مباشر لطريقة الخبير"). كامل، من أول سطر إلى آخر سطر، بدون حذف.
2. **تقرير آخر تشغيل كامل** كما طلع من البرنامج.

**كيف تحدد الموديول:** R19 غالبًا رقم نسخة في التعليق، وليس اسم الموديول. افتح محرر VBA (Alt+F11)، وفي القائمة اليسرى ابحث عن الموديول اللي يحتوي هذا التعليق، أو اضغط Ctrl+F وابحث عن `R19`، وسيوصلك له مباشرة. إذا طلع الكود طويلًا، انسخه على رسائل مرقمة (1/3، 2/3، 3/3).

**الفورمات:** لا نحتاجها، لأن المشكلة في محرك التوزيع، والفورم غالبًا مجرد زر يشغّله.

**موديول القراءة:** `modDistributionReader` اللي ذُكر في الترويسة، لا ترسله الآن. إذا ظهر في الكود أن أهلية الموظفين تُحسم فيه، نطلبه بعدها.

أتفق مع Chatty GPT 5.6 أن طلب شيئين فقط أبسط عليك من قائمتي الأولى، وأتراجع عن طلب قيود 135 يدويًا. الكود والتقرير يكفيان لاستخراجها.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: لا ترسل جميع الأكواد؛ أرسل فقط **الموديول الكامل الذي يحتوي تعليق R19 + تقرير آخر تشغيل كامل**.

**Agreed**
- لا نحتاج أكواد الفورمات الآن.
- لا نحتاج بقية الموديولات الآن.
- إذا كان الكود طويلًا، أرسله على أجزاء مرقمة دون حذف أي سطر.

**Why**
- هذان الجزءان يكفيان لبدء تشخيص مشكلة 135 وتتبع مسار التوزيع والإصلاح.
- إذا كشف الكود اعتمادًا مؤثرًا على موديول آخر، سنحدد لك اسمه ونطلبه وقتها فقط.

**What would change it**
- ظهور أن أهلية الموظفين أو أحد القيود المهمة يُحسب خارج هذا الموديول سيجعلنا نطلب ذلك الجزء تحديدًا.

### Participant

واين اجد r19 هذا

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: وضّح للمستخدم خطوة بخطوة وبأبسط طريقة أين يجد النص `R19` داخل محرر VBA، وكيف يعرف **اسم الموديول الحقيقي** الذي يحتويه، لأن R19 مجرد تعليق داخل الكود وليس بالضرورة اسم الموديول.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

`R19` **ليس اسم موديول عندك**؛ هو مجرد اسم النسخة الذي كتبناه داخل تعليق في الكود، مثل:

```vb
' R19 - تطبيق مباشر لطريقة الخبير
```

للعثور على الموديول الحقيقي:

1. افتح ملف Excel واضغط **Alt + F11**.
2. داخل محرر VBA اضغط **Ctrl + F**.
3. اكتب:
   ```text
   R19
   ```
4. في البحث اختر **Current Project** إذا ظهر هذا الخيار، ثم **Find Next**.
5. عندما ينقلك إلى السطر الذي يحتوي:
   ```vb
   ' R19 - تطبيق مباشر لطريقة الخبير
   ```
   انظر إلى النافذة اليسرى **Project Explorer**. سيكون أحد الموديولات محددًا، مثل `Module1` أو `modDistributionEngine` أو اسم آخر. **هذا هو الموديول الذي نريده.**

إذا أردت الأسهل ولا تريد البحث أصلًا: **صوّر لي نافذة VBA والجزء الأيسر الذي تظهر فيه أسماء الموديولات**، وأنا أحدد لك بالضبط أي واحد تفتحه.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: `R19` ليس موديولًا مستقلًا؛ ابحث عن كلمة `R19` داخل مشروع VBA، والموديول الذي يفتح عند العثور عليها هو المطلوب.

**Agreed**
- افتح VBA بـ **Alt+F11** ثم ابحث بـ **Ctrl+F** عن `R19`.
- أرسل الموديول الذي يحتوي هذا التعليق كاملًا.
- إذا لم تجده بسهولة، أرسل صورة لنافذة VBA يظهر فيها الجانب الأيسر وأسماء الموديولات.

**Why**
- اسم `R19` موجود داخل تعليق في الكود، لذلك قد يكون اسم الموديول الحقيقي مختلفًا تمامًا.

### Participant

'==========================================================
' R19 - ÊØÈíÞ ãÈÇÔÑ áØÑíÞÉ ÇáÎÈíÑ: ÇáãÈíÊ ÒãäíðÇ ÞÈá ÇáÑÇæäÏÊÑÈ¡
' æÊßáíÝ ÇáãÈíÊ áÇ ÊäÞáå ãÑÍáÉ ÇáÅÕáÇÍ ÈÚÏ ÇÚÊãÇÏå.
' ãÍÑß ÊæÒíÚ ÇáÑÍáÇÊ - ÇáãÑÍáÉ ÇáËÇäíÉ
'
' íÈäí ÎØÉ ÇáÊæÒíÚ Ýí ÇáÐÇßÑÉ ÝÞØ¡ æáÇ íßÊÈ Ýí LINE.
' íÚÊãÏ Úáì äÌÇÍ ÊÍãíá modDistributionReader ÃæáðÇ.
'==========================================================

Public DE_Assignments As Collection
Private DE_LayoverDayLoads As Object
Public DE_RunPlans As Collection
Public DE_EmployeeStates As Object
Public DE_Warnings As Collection
Private DE_RTDayLoad As Object
Private DE_RepairOrderingChecks As Long
Private DE_RepairTransferSamples As Collection
Private DE_RepairTransferSampleKeys As Object
Private DE_RepairActivePath As Object
Private DE_RepairSampleKeys As Object
Private DE_RepairActiveDepth As Long
Private DE_RepairRootAttemptLimit As Long
Private DE_RepairDiagnosticCounts As Object
Private DE_RepairDiagnosticSamples As Collection
Private DE_RepairDepthReached As Boolean
Private DE_RepairRootAttemptStart As Long
Private DE_RepairAttempts As Long
Private Const DE_REPAIR_MAX_ATTEMPTS As Long = 600
Private DE_RepairRootDeadline As Double
Private DE_RepairCopySeconds As Double
Private DE_RepairCompareSeconds As Double
Private DE_RepairRollbackSeconds As Double
Private DE_RepairStartedAt As Double
Private DE_RepairChecks As Long
Private Const DE_REPAIR_MAX_SECONDS As Double = 90#
Private Const DE_REPAIR_MAX_CHECKS As Long = 20000
Private Const DE_REPAIR_DEPTH As Long = 2
Private DE_PreviousMonthRestCount As Long
Private DE_PreviousMonthRestOneDayCount As Long
Private DE_PreviousMonthRestTwoDaysCount As Long
Private DE_PreviousMonthRestThreeDaysCount As Long

Private Const DE_INFO As String = "INFO"
Private Const DE_PAIRING As String = "PAIRING"

Private Const DE_TYPE_FLIGHT As String = "FLIGHT"
Private Const DE_TYPE_OFFICE As String = "OFFICE"
Private Const DE_TYPE_EVENT As String = "EVENT"
Private Const DE_TYPE_SB As String = "SB"
Private Const DE_TYPE_RT As String = "RT"
Private Const DE_TYPE_PREVIOUS_MONTH_REST As String = _
    "PREVIOUS_MONTH_REST"

' ÇáÍÏ ÇáÃÚáì ÇáãÓãæÍ Èå ÝæÞ ÇáãÓÊåÏÝ áßá ãæÙÝ.
' íãäÚ ÅÎÝÇÁ ÇáÚÌÒ ÈÊßÑÇÑ ÇáÑÍáÇÊ ÇáÅÖÇÝíÉ Úáì ÇáãæÙÝ äÝÓå.
Private Const DE_MAX_EXTRA_FLIGHTS_PER_EMPLOYEE As Long = 1

' ÊÝÖíáÇÊ ÊÔÛíáíÉ áÊÈÇÚÏ ÇáÑÍáÇÊ. åÐå áíÓÊ ãäÚðÇ ãØáÞðÇº
' ÝÇáÍÏ ÇáÅáÒÇãí íÈÞì ÑÇÍÉ 24 ÓÇÚÉ¡ ÈíäãÇ íÓÊÎÏã ÇáãÍÑß
' åÐå ÇáÞíã áÇÎÊíÇÑ ÇáãæÙÝ ÕÇÍÈ ÇáÌÏæá ÇáÃÞá ÇÒÏÍÇãðÇ.
Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Double = 72#
Private Const DE_ACCEPTABLE_FLIGHT_GAP_HOURS As Double = 48#

'==========================================================
' ÈäÇÁ ÎØÉ ÇáÊæÒíÚ ßÇãáÉ Ýí ÇáÐÇßÑÉ
'==========================================================
Public Function DistributionEngine_BuildPlan( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim CurrentStep As String
    Dim ReaderPassed As Boolean
    Dim ValidationPassed As Boolean

    On Error GoTo EngineError

    EnsureGregorianCalendar
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    DistributionEngine_Clear
    Set DE_Warnings = WarningResults

    CurrentStep = "ÊÍãíá ãÕÇÏÑ ÈíÇäÇÊ ÇáÊæÒíÚ"

    If DR_Employees Is Nothing Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    ElseIf DR_Employees.Count = 0 Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    Else
        ReaderPassed = True
    End If

    If Not ReaderPassed Then Exit Function

    ValidationPassed = True

    CurrentStep = "ÊÌåíÒ ÍÇáÇÊ ÇáãæÙÝíä"
    If Not DE_InitializeEmployeeStates(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "ÊÍáíá ãÑÇÊ ÊÔÛíá ÇáÈíÑäÞÇÊ"
    If Not DE_InitializeRunPlans(ErrorResults, PassedResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "ÊËÈíÊ ÇÑÊÈÇØÇÊ ÇáãæÙÝíä"
    If Not DE_RegisterEvents( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "ÊËÈíÊ ÌÏÇæá ÇáÇÓÊÇäÏÈÇí"
    If Not DE_RegisterStandby(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "ÊËÈíÊ ÑÍáÇÊ ÇáÅÏÇÑííä"
    If Not DE_RegisterOfficeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    ' íÌÈ ÍÌÒ ÃíÇã ÇáÇÍÊíÇØ ÞÈá ÊæÒíÚ ÇáÑÍáÇÊ ÇáÚÇÏíÉº
    ' ÍÊì ÊõÈäì ÇáÑÍáÇÊ Íæá ÇáÇÍÊíÇØ æÝÊÑÉ ÇáÑÇÍÉ ÇáÊÇáíÉ áå.
    CurrentStep = "ÊæÒíÚ ÃíÇã ÇáÇÍÊíÇØ"
    If Not DE_DistributeReserveDays( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    ' ÇáÇÍÊíÇØ ÊßáíÝ ÅáÒÇãí. ÅÐÇ ÊÚÐÑ ÍÌÒ íæã ãäÇÓÈ áå¡
    ' áÇ íÈÏÃ ÊæÒíÚ ÇáÑÍáÇÊ Úáì ÎØÉ äÇÞÕÉ Ãæ ÛíÑ ÞÇÈáÉ ááÊäÝíÐ.
    If Not ValidationPassed Then Exit Function

    ' ÇáÑÍáÇÊ ÇáãØÇáÈ ÈåÇ ãä ÇáÔåÑ ÇáÓÇÈÞ ÊõËÈÊ ÞÈá ÇáÊæÒíÚ
    ' ÇáÚÇÏíº áÃäåÇ ÍÞ ãÑÍøá ááãæÙÝ æÊõÚÏ ÑÍáÉ ÅÖÇÝíÉ ÝæÞ
    ' ãÓÊåÏÝå ÇáÃÓÇÓí¡ ãÚ ÊØÈíÞ ÇáÃåáíÉ æÇáÊÚÇÑÖ æÑÇÍÉ 24 ÓÇÚÉ.
    CurrentStep = "ÊæÒíÚ ÇáÑÍáÇÊ ÇáãØÇáÈ ÈåÇ"
    If Not DE_DistributeCarryoverFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "ÊæÒíÚ ÃØÞã ÇáÑÍáÇÊ"
    If Not DE_DistributeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "ãÑÇÌÚÉ ÊÑÊíÈ ÇáÞíÇÏÉ æÇáÃÝÑÇÏ"
    If Not DE_ValidateRolePolicy(ErrorResults) Then
        ValidationPassed = False
    End If

    ' íÖÇÝ ÇáãáÎÕ ÓæÇÁ äÌÍÊ ÇáÎØÉ Ãã ÙåÑ ÚÌÒº ÍÊì íæÖÍ
    ' ÚÏÏ ÇáÑÍáÇÊ ÇáÅÖÇÝíÉ ÇáãÓÊÎÏãÉ ÝÚáíðÇ ÍÓÈ ÇáãÌãæÚÉ.
    DE_AddExtraFlightSummary WarningResults


    CurrentStep = "ãÑÇÌÚÉ ÊæÇÝÞ ÊÃåíá ÇáãæÙÝíä ãÚ ÇáäÇÞáÇÊ"
    If ValidationPassed Then
        If Not DE_ValidateCarrierAssignments(ErrorResults) Then
            ValidationPassed = False
        End If
    End If

    If ValidationPassed Then
        PassedResults.Add _
            "äÌÍ ÅäÔÇÁ ÎØÉ ÊæÒíÚ ÇáÑÍáÇÊ Ýí ÇáÐÇßÑÉ."

        PassedResults.Add _
            "ÚÏÏ ãÑÇÊ ÇáÊÔÛíá ÇáãÎØØÉ: " & _
            CStr(DE_RunPlans.Count) & _
            "¡ æÚÏÏ ÇáÊßáíÝÇÊ: " & _
            CStr(DE_Assignments.Count) & "."

        PassedResults.Add _
            "áã íÊã ÊÚÏíá Ãí ÎáíÉ Ýí ÔíÊ LINE."

        PassedResults.Add _
            "ÊßáíÝÇÊ ÇáÑÍáÇÊ ÍÓÈ ÇáäÇÞá: ÇáÓÚæÏíÉ " & _
            CStr(DE_CarrierAssignmentCount("SAUDIA")) & _
            "¡ äÇÓ " & CStr(DE_CarrierAssignmentCount("NAS")) & _
            "¡ ÃÏíá " & CStr(DE_CarrierAssignmentCount("ADEAL")) & "."

        PassedResults.Add _
            "ÊãÊ ÊÛØíÉ ÇáÑÍáÇÊ ÇáãØÇáÈ ÈåÇ: " & _
            CStr(DE_CarryoverAssignedCount()) & _
            " ãä " & CStr(DR_CarryoverFlights.Count) & "."

        PassedResults.Add _
            "ÑÇÍÉ ÈÏÇíÉ ÇáÔåÑ ÈÓÈÈ ÂÎÑ ÑÍáÉ Ýí ÇáÔåÑ ÇáÓÇÈÞ: " & _
            "íæã æÇÍÏ áÚÏÏ " & _
            CStr(DE_PreviousMonthRestOneDayCount) & _
            "¡ íæãÇä áÚÏÏ " & _
            CStr(DE_PreviousMonthRestTwoDaysCount) & _
            "¡ ËáÇËÉ ÃíÇã áÚÏÏ " & _
            CStr(DE_PreviousMonthRestThreeDaysCount) & _
            "¡ æÇáÅÌãÇáí " & _
            CStr(DE_PreviousMonthRestCount) & " ãæÙÝðÇ."
    End If

    DistributionEngine_BuildPlan = ValidationPassed
    Exit Function

EngineError:
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    ErrorResults.Add _
        "ÊÚÐÑ ÈäÇÁ ÎØÉ ÇáÊæÒíÚ ÃËäÇÁ " & CurrentStep & _
        ". ÊÝÇÕíá ÇáÎØÃ: " & Err.Description
    DistributionEngine_BuildPlan = False

End Function

'==========================================================
' ãÓÍ ÎØÉ ÓÇÈÞÉ ãä ÇáÐÇßÑÉ
'==========================================================
Public Sub DistributionEngine_Clear()

    Set DE_Assignments = New Collection
    Set DE_RunPlans = New Collection
    Set DE_EmployeeStates = _
        CreateObject("Scripting.Dictionary")
    DE_EmployeeStates.CompareMode = vbTextCompare
    Set DE_Warnings = New Collection
    Set DE_RTDayLoad = CreateObject("Scripting.Dictionary")
    DE_RTDayLoad.CompareMode = vbTextCompare
    DE_PreviousMonthRestCount = 0
    DE_PreviousMonthRestOneDayCount = 0
    DE_PreviousMonthRestTwoDaysCount = 0
    DE_PreviousMonthRestThreeDaysCount = 0

End Sub

'==========================================================
' ÇÎÊÈÇÑ ãÓÊÞá ááãÍÑß ãä Ïæä ÇáßÊÇÈÉ Ýí LINE
'==========================================================
Public Sub TestDistributionEngine()

    Dim Errors As Collection
    Dim Passed As Collection
    Dim Warnings As Collection
    Dim ResultPassed As Boolean

    Set Errors = New Collection
    Set Passed = New Collection
    Set Warnings = New Collection

    ResultPassed = DistributionEngine_BuildPlan( _
        Errors, Passed, Warnings)

    DE_WriteDistributionTestReport _
        ResultPassed, Errors, Warnings, Passed

    MsgBox _
        "Êã ÅäÔÇÁ ÊÞÑíÑ ÇÎÊÈÇÑ ÇáÊæÒíÚ Ýí ÔíÊ " & _
        "DISTRIBUTION_REPORT.", _
        IIf(ResultPassed, vbInformation, vbExclamation), _
        "ÇÎÊÈÇÑ ãÍÑß ÇáÊæÒíÚ"

End Sub

'==========================================================
' ÅäÔÇÁ ÊÞÑíÑ ÇÎÊÈÇÑ ÇáãÍÑß ÏÇÎá ÔíÊ ãÓÊÞá.
'
' áÇ íßÊÈ åÐÇ ÇáÅÌÑÇÁ Ãí ÔíÁ Ýí LINE¡ æáÇ íÍÐÝ ÇáÔíÊº
' Èá íãÓÍ ãÍÊæì ÇáÊÞÑíÑ ÇáÓÇÈÞ Ëã íßÊÈ ÇáÊÞÑíÑ ÇáÌÏíÏ.
'==========================================================
Private Sub DE_WriteDistributionTestReport( _
    ByVal ResultPassed As Boolean, _
    ByVal ErrorResults As Collection, _
    ByVal WarningResults As Collection, _
    ByVal PassedResults As Collection)

    Const REPORT_SHEET As String = _
        "DISTRIBUTION_REPORT"

    Dim ws As Worksheet
    Dim NextRow As Long
    Dim SequenceNumber As Long
    Dim FinalRow As Long
    Dim ReportRange As Range
    Dim ReportWindow As Window
    Dim PreviousScreenUpdating As Boolean
    Dim ReportErrorNumber As Long
    Dim ReportErrorDescription As String

    PreviousScreenUpdating = Application.ScreenUpdating
    On Error GoTo ReportError

    Set ws = DE_GetOrCreateReportSheet(REPORT_SHEET)

    Application.ScreenUpdating = False

    ws.Visible = xlSheetVisible
    ws.Cells.Clear
    ws.DisplayRightToLeft = True

    ' ÚäæÇä ÇáÊÞÑíÑ.
    With ws.Range("A1:D1")
        .HorizontalAlignment = xlCenterAcrossSelection
        .VerticalAlignment = xlCenter
        .Font.Bold = True
        .Font.Size = 16
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
    End With

    ws.Range("A1").Value = _
        "ÊÞÑíÑ ÇÎÊÈÇÑ ãÍÑß ÊæÒíÚ ÇáÑÍáÇÊ"
    ws.Rows(1).RowHeight = 28

    ws.Range("A2").Value = "ÇáÔåÑ"
    ws.Range("B2").Value = _
        UCase$(MonthName(DR_ActiveMonth, True))
    ws.Range("C2").Value = "ÇáÓäÉ"
    ws.Range("D2").Value = DR_ActiveYear

    ws.Range("A3").Value = "äÊíÌÉ ÇáÇÎÊÈÇÑ"
    If ResultPassed Then
        ws.Range("B3").Value = "äÌÍ ÇáÇÎÊÈÇÑ"
        ws.Range("B3").Interior.Color = _
            RGB(198, 239, 206)
        ws.Range("B3").Font.Color = _
            RGB(0, 97, 0)
    Else
        ws.Range("B3").Value = "áã ÊßÊãá ÎØÉ ÇáÊæÒíÚ"
        ws.Range("B3").Interior.Color = _
            RGB(255, 199, 206)
        ws.Range("B3").Font.Color = _
            RGB(156, 0, 6)
    End If

    ws.Range("C3").Value = "ÊÇÑíÎ ÇáÊÞÑíÑ"
    ws.Range("D3").NumberFormat = "@"
    ws.Range("D3").Value = _
        "'" & Format$(Now, "dd/mm/yyyy hh:mm")

    With ws.Range("A2:D3")
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlCenter
        .Font.Bold = True
    End With

    ' ÑÄæÓ ÃÚãÏÉ ÇáäÊÇÆÌ.
    ws.Range("A5").Value = "ÇáÊÓáÓá"
    ws.Range("B5").Value = "ÇáÊÕäíÝ"
    ws.Range("C5").Value = "ÇáÍÇáÉ"
    ws.Range("D5").Value = "ÇáÊÝÇÕíá"

    With ws.Range("A5:D5")
        .Font.Bold = True
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlCenter
        .Borders.LineStyle = xlContinuous
    End With

    NextRow = 6
    SequenceNumber = 0

    DE_WriteReportCollection _
        ws, ErrorResults, "ÎØÃ", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, WarningResults, "ÊäÈíå", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, PassedResults, "Óáíã", _
        NextRow, SequenceNumber

    If SequenceNumber = 0 Then
        SequenceNumber = 1
        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = "ãÚáæãÉ"
        ws.Cells(NextRow, "C").Value = "áÇ ÊæÌÏ äÊÇÆÌ"
        ws.Cells(NextRow, "D").Value = _
            "áã íõÑÌÚ ÇáãÍÑß Ãí äÊÇÆÌ áÚÑÖåÇ."
        NextRow = NextRow + 1
    End If

    FinalRow = NextRow - 1
    Set ReportRange = ws.Range( _
        "A5:D" & CStr(FinalRow))

    With ReportRange
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlTop
    End With

    ws.Range("A6:C" & CStr(FinalRow)).HorizontalAlignment = _
        xlCenter
    ws.Range("D6:D" & CStr(FinalRow)).HorizontalAlignment = _
        xlRight
    ws.Range("D6:D" & CStr(FinalRow)).WrapText = True

    ws.Columns("A").ColumnWidth = 10
    ws.Columns("B").ColumnWidth = 16
    ws.Columns("C").ColumnWidth = 20
    ws.Columns("D").ColumnWidth = 95

    ws.Rows("2:3").AutoFit
    ws.Rows("5:" & CStr(FinalRow)).AutoFit

    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    ws.Range("A5:D" & CStr(FinalRow)).AutoFilter

    ' ÅÚÏÇÏ ÚÑÖ ÇáÊÞÑíÑ ÇÎÊíÇÑí æáÇ íæÞÝ ßÊÇÈÉ ÇáäÊÇÆÌ.
    On Error Resume Next
    ws.Parent.Activate
    ws.Activate
    Set ReportWindow = Application.ActiveWindow
    If Not ReportWindow Is Nothing Then
        ReportWindow.View = xlNormalView
        ReportWindow.FreezePanes = False
        ReportWindow.SplitRow = 0
        ReportWindow.SplitColumn = 0
        Err.Clear
        ws.Range("A6").Select
        If Err.Number = 0 Then
            ReportWindow.SplitRow = 5
            ReportWindow.FreezePanes = True
        End If
    End If
    Err.Clear
    On Error GoTo ReportError
    Application.ScreenUpdating = PreviousScreenUpdating
    Exit Sub
ReportError:
    ReportErrorNumber = Err.Number
    ReportErrorDescription = Err.Description
    Application.ScreenUpdating = PreviousScreenUpdating
    MsgBox "ÊÚÐÑ ÅÎÑÇÌ ÇáÊÞÑíÑ. ÑÞã ÇáÎØÃ: " & CStr(ReportErrorNumber) & _
        vbCrLf & ReportErrorDescription, vbExclamation, "ÊÞÑíÑ ÇáÊæÒíÚ"

End Sub

'==========================================================
' ÅÑÌÇÚ ÔíÊ ÇáÊÞÑíÑ Ãæ ÅäÔÇÄå ÅÐÇ áã íßä ãæÌæÏðÇ.
'==========================================================
Private Function DE_GetOrCreateReportSheet( _
    ByVal SheetName As String) As Worksheet

    Dim ws As Worksheet

    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(SheetName)
    On Error GoTo 0

    If ws Is Nothing Then
        Set ws = ThisWorkbook.Worksheets.Add( _
            After:=ThisWorkbook.Worksheets( _
                ThisWorkbook.Worksheets.Count))
        ws.NAME = SheetName
    End If

    Set DE_GetOrCreateReportSheet = ws

End Function

'==========================================================
' ßÊÇÈÉ ãÌãæÚÉ äÊÇÆÌ Ýí ÊÞÑíÑ ÇáÇÎÊÈÇÑ.
'==========================================================
Private Sub DE_WriteReportCollection( _
    ByVal ws As Worksheet, _
    ByVal Results As Collection, _
    ByVal DefaultCategory As String, _
    ByRef NextRow As Long, _
    ByRef SequenceNumber As Long)

    Dim Item As Variant
    Dim ResultText As String
    Dim CategoryText As String
    Dim StatusText As String

    If Results Is Nothing Then Exit Sub

    For Each Item In Results

        ResultText = CStr(Item)
        CategoryText = DefaultCategory
        StatusText = DefaultCategory

        If DE_IsShortageDiagnostic(ResultText) Then
            CategoryText = "ÊÔÎíÕ ÚÌÒ"
            StatusText = "ÊÝÇÕíá ÇáãæÇäÚ"
        ElseIf DefaultCategory = "ÎØÃ" _
            And InStr(1, ResultText, _
                "ÚÌÒ", vbTextCompare) > 0 Then
            CategoryText = "ÚÌÒ"
            StatusText = "ÛíÑ ãÛØì"
        ElseIf DefaultCategory = "ÎØÃ" Then
            StatusText = "íÍÊÇÌ ãÚÇáÌÉ"
        ElseIf DefaultCategory = "ÊäÈíå" Then
            StatusText = "ááãÑÇÌÚÉ"
        ElseIf DefaultCategory = "Óáíã" Then
            StatusText = "ãßÊãá"
        End If

        SequenceNumber = SequenceNumber + 1

        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = CategoryText
        ws.Cells(NextRow, "C").Value = StatusText
        ws.Cells(NextRow, "D").Value = ResultText

        DE_ApplyReportRowColor _
            ws.Range("A" & CStr(NextRow) & _
                ":D" & CStr(NextRow)), _
            CategoryText

        NextRow = NextRow + 1

    Next Item

End Sub

'==========================================================
' Êáæíä ÕÝ ÇáÊÞÑíÑ ÍÓÈ äæÚ ÇáäÊíÌÉ.
'==========================================================
Private Sub DE_ApplyReportRowColor( _
    ByVal TargetRange As Range, _
    ByVal CategoryText As String)

    Select Case CategoryText

        Case "ÎØÃ"
            TargetRange.Interior.Color = _
                RGB(255, 199, 206)
            TargetRange.Font.Color = _
                RGB(156, 0, 6)

        Case "ÚÌÒ", "ÊÔÎíÕ ÚÌÒ"
            TargetRange.Interior.Color = _
                RGB(252, 229, 205)
            TargetRange.Font.Color = _
                RGB(156, 87, 0)

        Case "ÊäÈíå"
            TargetRange.Interior.Color = _
                RGB(255, 242, 204)
            TargetRange.Font.Color = _
                RGB(127, 96, 0)

        Case "Óáíã"
            TargetRange.Interior.Color = _
                RGB(198, 239, 206)
            TargetRange.Font.Color = _
                RGB(0, 97, 0)

        Case Else
            TargetRange.Interior.Color = _
                RGB(217, 217, 217)
            TargetRange.Font.Color = _
                RGB(0, 0, 0)

    End Select

End Sub

'==========================================================
' ÇáÊÍÞÞ ãä Ãä ÇáäÕ ÊÞÑíÑ ÊÔÎíÕ ÚÌÒ ÊÝÕíáí.
'==========================================================
Private Function DE_IsShortageDiagnostic( _
    ByVal ResultText As String) As Boolean

    DE_IsShortageDiagnostic = _
        (Left$(Trim$(ResultText), _
            Len("[ÊÔÎíÕ]")) = "[ÊÔÎíÕ]" _
        Or Left$(Trim$(ResultText), Len("[ÃåáíÉ ÝÚáíÉ")) = "[ÃåáíÉ ÝÚáíÉ")

End Function

'==========================================================
' ÚÑÖ ÊÞÇÑíÑ ÊÔÎíÕ ÇáÚÌÒ Ýí ÑÓÇÆá ãäÝÕáÉ.
'==========================================================
Private Sub DE_ShowShortageDiagnostics( _
    ByVal ErrorResults As Collection)

    Dim Item As Variant
    Dim DiagnosticNumber As Long

    If ErrorResults Is Nothing Then Exit Sub

    For Each Item In ErrorResults

        If DE_IsShortageDiagnostic(CStr(Item)) Then
            DiagnosticNumber = DiagnosticNumber + 1

            DE_ShowLongDiagnostic _
                CStr(Item), _
                "ÊÝÇÕíá ÚÌÒ ÇáÊæÒíÚ " & _
                CStr(DiagnosticNumber)
        End If

    Next Item

End Sub

'==========================================================
' ÊÞÓíã ÇáÊÞÑíÑ ÇáØæíá Åáì ÕÝÍÇÊ ãä Ïæä ÞØÚ ÇáÓØæÑ.
'==========================================================
Private Sub DE_ShowLongDiagnostic( _
    ByVal DiagnosticText As String, _
    ByVal WindowTitle As String)

    Const MAX_PAGE_LENGTH As Long = 650

    Dim Lines As Variant
    Dim LineItem As Variant
    Dim CurrentPage As String
    Dim Pages As Collection
    Dim PageNumber As Long
    Dim CandidateText As String

    Set Pages = New Collection
    Lines = Split(DiagnosticText, vbCrLf)

    For Each LineItem In Lines

        If CurrentPage = vbNullString Then
            CandidateText = CStr(LineItem)
        Else
            CandidateText = CurrentPage & _
                vbCrLf & CStr(LineItem)
        End If

        If Len(CandidateText) > MAX_PAGE_LENGTH _
            And CurrentPage <> vbNullString Then

            Pages.Add CurrentPage
            CurrentPage = CStr(LineItem)
        Else
            CurrentPage = CandidateText
        End If

    Next LineItem

    If CurrentPage <> vbNullString Then
        Pages.Add CurrentPage
    End If

    For PageNumber = 1 To Pages.Count

        MsgBox _
            CStr(Pages(PageNumber)), _
            vbExclamation, _
            WindowTitle & " - ÇáÕÝÍÉ " & _
            CStr(PageNumber) & " ãä " & _
            CStr(Pages.Count)

    Next PageNumber

End Sub

'==========================================================
' ÊÌåíÒ ÍÇáÉ ßá ãæÙÝ æãÓÊåÏÝ ãÌãæÚÊå
'==========================================================
Private Function DE_GroupFlightTarget(ByVal GroupCode As String) As Long
    Select Case UCase$(Trim$(GroupCode))
        Case "SSS", "SST": DE_GroupFlightTarget = 7
        Case "STS": DE_GroupFlightTarget = 8
        Case "STT": DE_GroupFlightTarget = 10
    End Select
End Function
Private Function DE_InitializeEmployeeStates( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim EmployeeRecord As Object
    Dim State As Object
    Dim Targets As Object
    Dim GroupCode As String
    Dim BusyPeriods As Collection
    Dim EmployeeAssignments As Collection
    Dim EventDates As Object
    Dim PreviousLastFlightDate As Date
    Dim PreviousMonthEnd As Date
    Dim NewMonthStart As Date
    Dim PreviousMonthRestEnd As Date
    Dim PreviousMonthRestDays As Long
    Dim PreviousTripType As String

    Set Targets = DE_ReadGroupTargets(ErrorResults)
    If Targets Is Nothing Then Exit Function

    For Each EmployeeKey In DR_Employees.Keys

        Set EmployeeRecord = DR_Employees(EmployeeKey)
        GroupCode = UCase$(CStr(EmployeeRecord("GroupCode")))

        If Not Targets.Exists(GroupCode) Then
            ErrorResults.Add _
                "áÇ íæÌÏ ãÓÊåÏÝ ááãÌãæÚÉ " & GroupCode & _
                " ÇáÎÇÕÉ ÈÇáãæÙÝ " & CStr(EmployeeKey) & "."
            Exit Function
        End If

        Set State = DE_NewRecord()
        State.Add "EmployeeNumber", CStr(EmployeeKey)
        State.Add "EmployeeName", _
            CStr(EmployeeRecord("EmployeeName"))
        State.Add "GroupCode", GroupCode
        State.Add "CarrierProfile", _
            UCase$(CStr(EmployeeRecord("CarrierProfile")))
        State.Add "InfoRow", CLng(EmployeeRecord("InfoRow"))
        State.Add "LineRow", CLng(EmployeeRecord("LineRow"))
        State.Add "IsOffice", CBool(EmployeeRecord("IsOffice"))
        State.Add "IsHold", CBool(EmployeeRecord("IsHold"))
        State.Add "IsStandby", _
            DR_SBByEmployee.Exists(CStr(EmployeeKey))
        State.Add "OriginalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "NormalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "FlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "OriginalLayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LongLayoverTarget", 0&
        State.Add "ShortLayoverTarget", 0&
        State.Add "LongLayoverCount", 0&
        State.Add "ShortLayoverCount", 0&
        State.Add "EventDayCount", 0&
        State.Add "LongestEventDays", 0&
        State.Add "TargetAdjustmentReason", vbNullString
        State.Add "IsNoFlightMonth", False
        State.Add "SaudiaTarget", 0&
        State.Add "SpecialCarrierTarget", 0&
        State.Add "SpecialCarrierCode", vbNullString
        State.Add "SaudiaCount", 0&
        State.Add "SpecialCarrierCount", 0&
        State.Add "FlightCount", 0&
        State.Add "LayoverCount", 0&
        State.Add "CarryoverTarget", _
            DE_EmployeeCarryoverCount(CStr(EmployeeKey))
        State.Add "CarryoverAssigned", 0&
        State.Add "CarryoverLayoverCount", 0&
        State.Add "CarryoverLongLayoverCount", 0&
        State.Add "CarryoverShortLayoverCount", 0&
        State.Add "CarryoverSaudiaTarget", _
            DE_EmployeeCarryoverCarrierCount( _
                CStr(EmployeeKey), "SAUDIA")
        State.Add "CarryoverSpecialTarget", _
            DE_EmployeeCarryoverSpecialCount( _
                CStr(EmployeeKey))
        State.Add "LeaderCount", 0&
        State.Add "ReserveCount", 0&
        State.Add "HasPreviousMonthEndRest", False
        ' ÚÏÏ ÇáÑÍáÇÊ ÇáÊí ÃÖíÝÊ ÝæÞ ãÓÊåÏÝ ÇáãÌãæÚÉ áãÚÇáÌÉ ÇáÚÌÒ
        State.Add "ExtraFlightCount", 0&
        Set BusyPeriods = New Collection
        Set EmployeeAssignments = New Collection
        Set EventDates = CreateObject("Scripting.Dictionary")
        EventDates.CompareMode = vbTextCompare
        State.Add "BusyPeriods", BusyPeriods
        State.Add "Assignments", EmployeeAssignments
        State.Add "EventDates", EventDates

        ' ÅÐÇ ßÇäÊ ÂÎÑ ÑÍáÉ ááãæÙÝ Ýí ÂÎÑ íæã ãä ÇáÔåÑ ÇáÓÇÈÞ¡
        ' ÊõÍÌÒ ÑÇÍÉ ÈÏÇíÉ ÇáÔåÑ ÍÓÈ ÊÕäíÝ ÇáÑÍáÉ ÇáÓÇÈÞÉ:
        '   LAY = ËáÇËÉ ÃíÇã¡ LNG Ãæ MED = íæãÇä¡ SHT = íæã æÇÍÏ.
        ' æÊÓÊãÑ ÈÞíÉ ÔÑæØ ÇáÑÇÍÉ æÇáÊÚÇÑÖ Ïæä Ãí ÊÛííÑ.
        If EmployeeRecord.Exists("HasPreviousLastFlight") Then

            If CBool(EmployeeRecord("HasPreviousLastFlight")) Then

                PreviousLastFlightDate = CDate(CDbl( _
                    EmployeeRecord("PreviousLastFlightDate")))

                PreviousMonthEnd = DateSerial( _
                    DR_ActiveYear, DR_ActiveMonth, 0)

                If Int(CDbl(PreviousLastFlightDate)) = _
                    Int(CDbl(PreviousMonthEnd)) Then

                    NewMonthStart = DateSerial( _
                        DR_ActiveYear, DR_ActiveMonth, 1)

                    PreviousMonthRestDays = _
                        DE_GetPreviousMonthRestDays( _
                            EmployeeRecord, PreviousTripType)

                    PreviousMonthRestEnd = DateAdd( _
                        "d", PreviousMonthRestDays, NewMonthStart)

                    DE_AddBusyPeriod _
                        State, _
                        NewMonthStart, _
                        PreviousMonthRestEnd, _
                        DE_TYPE_PREVIOUS_MONTH_REST, _
                        False, _
                        "ÑÇÍÉ ÈÏÇíÉ ÇáÔåÑ áãÏÉ " & _
                        DE_ArabicDayPhrase(PreviousMonthRestDays) & _
                        " ÈÓÈÈ ÑÍáÉ " & PreviousTripType & _
                        " Ýí ÂÎÑ íæã ãä ÇáÔåÑ ÇáÓÇÈÞ"

                    State("HasPreviousMonthEndRest") = True
                    DE_PreviousMonthRestCount = _
                        DE_PreviousMonthRestCount + 1

                    Select Case PreviousMonthRestDays
                        Case 1
                            DE_PreviousMonthRestOneDayCount = _
                                DE_PreviousMonthRestOneDayCount + 1
                        Case 2
                            DE_PreviousMonthRestTwoDaysCount = _
                                DE_PreviousMonthRestTwoDaysCount + 1
                        Case 3
                            DE_PreviousMonthRestThreeDaysCount = _
                                DE_PreviousMonthRestThreeDaysCount + 1
                    End Select

                End If

            End If

        End If

        DE_EmployeeStates.Add CStr(EmployeeKey), State

    Next EmployeeKey

    DE_InitializeEmployeeStates = True

End Function

'==========================================================
' ÍÓÇÈ ÚÏÏ ÇáãØÇáÈÇÊ ÇáãÓÌáÉ áãæÙÝ
'==========================================================
Private Function DE_EmployeeCarryoverCount( _
    ByVal EmployeeNumber As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCount = _
                DE_EmployeeCarryoverCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' ÍÓÇÈ ãØÇáÈÇÊ ãæÙÝ áäÇÞá ãÍÏÏ
'==========================================================
Private Function DE_EmployeeCarryoverCarrierCount( _
    ByVal EmployeeNumber As String, _
    ByVal CarrierCode As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 _
            And StrComp(CStr(CarryoverRecord("CarrierCode")), _
                CarrierCode, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCarrierCount = _
                DE_EmployeeCarryoverCarrierCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' ÍÓÇÈ ãØÇáÈÇÊ ÇáãæÙÝ ááäÇÞá ÇáãÊÎÕÕ Ýí ãáÝå
'==========================================================
Private Function DE_EmployeeCarryoverSpecialCount( _
    ByVal EmployeeNumber As String) As Long

    Dim EmployeeRecord As Object
    Dim SpecialCarrier As String

    If Not DR_Employees.Exists(EmployeeNumber) Then Exit Function
    Set EmployeeRecord = DR_Employees(EmployeeNumber)

    Select Case UCase$(CStr(EmployeeRecord("CarrierProfile")))
        Case "SAUDIA_NAS": SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL": SpecialCarrier = "ADEAL"
        Case Else: Exit Function
    End Select

    DE_EmployeeCarryoverSpecialCount = _
        DE_EmployeeCarryoverCarrierCount( _
            EmployeeNumber, SpecialCarrier)

End Function

'==========================================================
' ÞÑÇÁÉ ãÓÊåÏÝ ÇáÑÍáÇÊ æÇáãÈíÊ ãä INFO!K:M
'==========================================================
Private Function DE_ReadGroupTargets( _
    ByRef ErrorResults As Collection) As Object

    Dim ws As Worksheet
    Dim Targets As Object
    Dim LastRow As Long
    Dim CurrentRow As Long
    Dim GroupCode As String
    Dim FlightsText As String
    Dim LayoversText As String
    Dim Target As Object

    Set ws = ThisWorkbook.Worksheets(DE_INFO)
    Set Targets = CreateObject("Scripting.Dictionary")
    Targets.CompareMode = vbTextCompare

    LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row

    For CurrentRow = 1 To LastRow

        GroupCode = UCase$(DE_Text( _
            ws.Cells(CurrentRow, "K").Value2))

        If DE_IsGroupCode(GroupCode) Then

            FlightsText = DE_Text( _
                ws.Cells(CurrentRow, "L").Value2)
            LayoversText = DE_Text( _
                ws.Cells(CurrentRow, "M").Value2)

            If Not DE_IsDigits(FlightsText) _
                Or Not DE_IsDigits(LayoversText) Then
                ErrorResults.Add _
                    "ãÓÊåÏÝ ÇáãÌãæÚÉ " & GroupCode & _
                    " ÛíÑ ÕÍíÍ Ýí INFO!K:M."
                Exit Function
            End If

            Set Target = DE_NewRecord()
            Target.Add "Flights", CLng(FlightsText)
            Target.Add "Layovers", CLng(LayoversText)

            If Targets.Exists(GroupCode) Then
                Targets.Remove GroupCode
            End If
            Targets.Add GroupCode, Target

        End If

    Next CurrentRow

    If Targets.Count <> 4 Then
        ErrorResults.Add _
            "ÊÚÐÑ ÞÑÇÁÉ ãÓÊåÏÝÇÊ ÇáãÌãæÚÇÊ ÇáÃÑÈÚ ãä INFO!K:M."
        Exit Function
    End If

    Set DE_ReadGroupTargets = Targets

End Function

'==========================================================
' ÊÍáíá ßá ãÑÉ ÊÔÛíá æÅÖÇÝÉ ÊÝÇÕíáåÇ ÇááÇÒãÉ ááÊæÒíÚ
'==========================================================
Private Function DE_InitializeRunPlans( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection) As Boolean

    Dim ws As Worksheet
    Dim SourceRun As Object
    Dim RunPlan As Object
    Dim SortedRuns() As Object
    Dim RunCount As Long
    Dim Index As Long
    Dim SwapIndex As Long
    Dim TempRun As Object

    Set ws = ThisWorkbook.Worksheets(DE_PAIRING)
    RunCount = DR_PairingRuns.Count

    If RunCount = 0 Then
        ErrorResults.Add "áÇ ÊæÌÏ ãÑÇÊ ÊÔÛíá ÌÇåÒÉ ááÊæÒíÚ."
        Exit Function
    End If

    ReDim SortedRuns(1 To RunCount)

    For Index = 1 To RunCount
        Set SourceRun = DR_PairingRuns(Index)
        Set RunPlan = DE_BuildRunPlan( _
            ws, SourceRun, ErrorResults)

        If RunPlan Is Nothing Then Exit Function
        Set SortedRuns(Index) = RunPlan
        PassedResults.Add "[ÊÕäíÝ ÇáÈíÑäÞ] " & _
            CStr(RunPlan("PairingNumber")) & " ÈÊÇÑíÎ " & _
            Format$(CDate(RunPlan("OperatingDate")), "dd/mm/yyyy") & _
            ": " & CStr(RunPlan("LayoverEvidence"))
    Next Index

    ' ÊÑÊíÈ Òãäí ËÇÈÊº æÚäÏ ÊÓÇæí ÇáÊÇÑíÎ íÑÊÈ ÈÑÞã ÇáÈíÑäÞ.
    For Index = 1 To RunCount - 1
        For SwapIndex = Index + 1 To RunCount
            If DE_RunComesAfter( _
                SortedRuns(Index), SortedRuns(SwapIndex)) Then
                Set TempRun = SortedRuns(Index)
                Set SortedRuns(Index) = SortedRuns(SwapIndex)
                Set SortedRuns(SwapIndex) = TempRun
            End If
        Next SwapIndex
    Next Index

    For Index = 1 To RunCount
        SortedRuns(Index)("RunIndex") = Index
        DE_RunPlans.Add SortedRuns(Index)
    Next Index

    DE_InitializeRunPlans = True

End Function

Private Function DE_BuildRunPlan( _
    ByVal ws As Worksheet, _
    ByVal SourceRun As Object, _
    ByRef ErrorResults As Collection) As Object

    Dim StartRow As Long
    Dim CreditRow As Long
    Dim StartColumn As Long
    Dim EndColumn As Long
    Dim DetailRow As Long
    Dim FirstFlightRow As Long
    Dim LastFlightRow As Long
    Dim LayoverRow As Long
    Dim HotelName As String
    Dim CandidateHotel As String
    Dim HotelColumn As Long
    Dim DurationText As String
    Dim FlightDayCode As String
    Dim FirstDayCode As String
    Dim LastDayCode As String
    Dim StationCode As String
    Dim LayoverStation As String
    Dim LayoverClass As String
    Dim LayoverHours As Double
    Dim TripType As String
    Dim RequiredCrew As Long
    Dim StartClock As Double
    Dim EndClock As Double
    Dim DayOffset As Long
    Dim OperatingDate As Date
    Dim StartDateTime As Date
    Dim EndDateTime As Date
    Dim RunPlan As Object
    Dim AssignedEmployees As Collection

    StartRow = CLng(SourceRun("StartRow"))
    CreditRow = CLng(SourceRun("CreditRow"))
    StartColumn = CLng(SourceRun("StartColumn"))
    EndColumn = CLng(SourceRun("EndColumn"))
    FirstDayCode = UCase$(CStr(SourceRun("DayCode")))
    OperatingDate = CDate(CDbl(SourceRun("OperatingDate")))

    For DetailRow = StartRow + 2 To CreditRow - 1

        FlightDayCode = UCase$(DE_Text( _
            ws.Cells(DetailRow, StartColumn).Value2))

        If DE_IsDayCode(FlightDayCode) Then
            If FirstFlightRow = 0 Then FirstFlightRow = DetailRow
            LastFlightRow = DetailRow
        ElseIf DE_RowHasData( _
            ws, DetailRow, StartColumn, EndColumn) Then

            ' ÇÓã ÇáÝäÏÞ íÞÚ ÞÈá ÚãæÏí ãÍØÉ ÇáãÈíÊ æãÏÊå.
            ' ÊÌÇåá ÇáÕÝæÝ ÇáÝÇÑÛÉ æÇáãáÇÍÙÇÊ ÇáÊí áÇ ÊÍãá ÈíÇäÇÊ ãÈíÊ.
            CandidateHotel = vbNullString
            For HotelColumn = StartColumn To StartColumn + 7
                If Not IsError(ws.Cells(DetailRow, HotelColumn).Value2) Then
                    DurationText = DE_Text( _
                        ws.Cells(DetailRow, HotelColumn).Value2)
                    If DurationText <> vbNullString Then
                        If Not IsNumeric(DurationText) Then
                            If CandidateHotel <> vbNullString Then _
                                CandidateHotel = CandidateHotel & " "
                            CandidateHotel = CandidateHotel & DurationText
                        End If
                    End If
                End If
            Next HotelColumn

            DurationText = DE_Text( _
                ws.Cells(DetailRow, StartColumn + 9).Value2)

            ' áÇ íßÝí ÇÎÊáÇÝ íæã ÇáÚæÏÉ Ãæ ÇÓã ÇáãÍØÉ áÅËÈÇÊ ÇáãÈíÊ.
            If CandidateHotel <> vbNullString Or _
                DurationText <> vbNullString Then
                If CandidateHotel = vbNullString Then
                    ErrorResults.Add "ãÏÉ ãÈíÊ Ïæä ÇÓã ÝäÏÞ Ýí ÇáÈíÑäÞ " & _
                        CStr(SourceRun("PairingNumber")) & _
                        "¡ ÕÝ " & CStr(DetailRow) & ". ÑÇÌÚ ÈíÇäÇÊ ÇáÈíÑäÞ."
                    Exit Function
                End If
                If LayoverRow > 0 Then
                    ErrorResults.Add "íæÌÏ ÃßËÑ ãä ÓØÑ ãÈíÊ Ýí ÇáÈíÑäÞ " & _
                        CStr(SourceRun("PairingNumber")) & _
                        ". íáÒã ÏÚã ÇáãÈíÊ ÇáãÊÚÏÏ ÞÈá ÊæÒíÚå."
                    Exit Function
                End If
                LayoverRow = DetailRow
                HotelName = CandidateHotel
            End If
        End If

    Next DetailRow

    If FirstFlightRow = 0 Or LastFlightRow = 0 Then
        ErrorResults.Add _
            "ÊÚÐÑ ÞÑÇÁÉ ÞØÇÚÇÊ ÇáÈíÑäÞ " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    StationCode = UCase$(DE_Text( _
        ws.Cells(FirstFlightRow, StartColumn + 6).Value2))

    If StationCode = "JED" Then
        StationCode = UCase$(DE_Text( _
            ws.Cells(LastFlightRow, StartColumn + 3).Value2))
    End If

    If LayoverRow > 0 Then
        LayoverStation = UCase$(DE_Text( _
            ws.Cells(LayoverRow, StartColumn + 8).Value2))
        If LayoverStation <> vbNullString Then
            StationCode = LayoverStation
        End If

        If Not DE_ReadDurationHours( _
            ws.Cells(LayoverRow, StartColumn + 9), _
            LayoverHours) Then
            ErrorResults.Add _
                "ÊÚÐÑ ÞÑÇÁÉ ÓÇÚÇÊ ÇáãÈíÊ ááÈíÑäÞ " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours <= 0# Or LayoverStation = vbNullString Then
            ErrorResults.Add _
                "ÈíÇäÇÊ ÇáãÈíÊ äÇÞÕÉ Ãæ ãÏÊå ÛíÑ ãæÌÈÉ ááÈíÑäÞ " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours >= 50# Then
            LayoverClass = "LONG"
        Else
            LayoverClass = "SHORT"
        End If
    End If

    If Not DR_CrewRequirements.Exists(StationCode) Then
        ErrorResults.Add _
            "ÇáãÍØÉ " & StationCode & _
            " ááÈíÑäÞ " & CStr(SourceRun("PairingNumber")) & _
            " ÛíÑ ãæÌæÏÉ Öãä ãÊØáÈÇÊ INFO!G:I."
        Exit Function
    End If

    RequiredCrew = CLng( _
        DR_CrewRequirements(StationCode)("CrewCount"))
    TripType = CStr( _
        DR_CrewRequirements(StationCode)("TripType"))

    ' ÚÏÏ ÃÝÑÇÏ ÇáãÈíÊ ãÓÊÞá Úä ÚÏÏ ÃÝÑÇÏ ÇáÑÇæäÏÊÑÈ ááãÍØÉ.
    If LayoverRow > 0 Then RequiredCrew = 2

    ' ÇÓÊËäÇÁ ÚÏÏ ÇáØÇÞã ÇÎÊíÇÑí æãÞíÏ ÈÇáÔåÑ æÇáÓäÉ æÇáÈíÑäÞ.
    If Not DE_ResolveCrewOverride( _
        CStr(SourceRun("PairingNumber")), OperatingDate, _
        RequiredCrew, ErrorResults) Then Exit Function


    If Not DE_ReadClock( _
        ws.Cells(FirstFlightRow, StartColumn + 4), StartClock) Then
        ErrorResults.Add _
            "ÊÚÐÑ ÞÑÇÁÉ æÞÊ ÈÏÇíÉ ÇáÈíÑäÞ " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    If Not DE_ReadClock( _
        ws.Cells(LastFlightRow, StartColumn + 7), EndClock) Then
        ErrorResults.Add _
            "ÊÚÐÑ ÞÑÇÁÉ æÞÊ äåÇíÉ ÇáÈíÑäÞ " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    LastDayCode = UCase$(DE_Text( _
        ws.Cells(LastFlightRow, StartColumn).Value2))

    DayOffset = DE_DayOffset(FirstDayCode, LastDayCode)

    If DayOffset = 0 Then
        If EndClock < DE_CellClockValue( _
            ws.Cells(LastFlightRow, StartColumn + 4)) Then
            DayOffset = 1
        End If
    End If

    StartDateTime = OperatingDate + StartClock
    EndDateTime = OperatingDate + DayOffset + EndClock

    If EndDateTime <= StartDateTime Then
        EndDateTime = EndDateTime + 1
    End If

    Set RunPlan = DE_NewRecord()
    RunPlan.Add "RunIndex", 0&
    RunPlan.Add "PairingNumber", _
        CStr(SourceRun("PairingNumber"))
    ' ÑÍáÇÊ ÍáÈ ALP ÊõÛØì ÍÕÑíðÇ ÈæÇÓØÉ ÇáãÄåáíä áÑÍáÇÊ äÇÓ¡
    ' ÍÊì áæ ßÇäÊ æÇÞÚÉ ÞÈá ÚäæÇä FLY NAS Ýí ÔíÊ PAIRING.
    If StationCode = "ALP" Then
        RunPlan.Add "CarrierCode", "NAS"
    Else
        RunPlan.Add "CarrierCode", _
            UCase$(CStr(SourceRun("CarrierCode")))
    End If
    RunPlan.Add "OperatingDate", CDbl(OperatingDate)
    RunPlan.Add "StartDateTime", CDbl(StartDateTime)
    RunPlan.Add "EndDateTime", CDbl(EndDateTime)
    RunPlan.Add "ReturnDayOffset", DayOffset
    RunPlan.Add "StationCode", StationCode
    RunPlan.Add "TripType", TripType
    RunPlan.Add "IsLayover", (LayoverRow > 0)
    RunPlan.Add "HotelName", HotelName
    If LayoverRow > 0 Then
        RunPlan.Add "LayoverEvidence", _
            "ãÈíÊº ÇáÝäÏÞ: " & HotelName & _
            "º ÇáãÏÉ ÈÇáÓÇÚÇÊ: " & Format$(LayoverHours, "0.00") & _
            "º ÕÝ ÇáãÕÏÑ: " & CStr(LayoverRow)
    Else
        RunPlan.Add "LayoverEvidence", _
            "ÑÇæäÏÊÑÈº áÇ íæÌÏ ÓØÑ ÝäÏÞ æãÏÉ ãÈíÊ Ýí ÇáÈíÑäÞ."
    End If
    RunPlan.Add "LayoverHours", LayoverHours
    RunPlan.Add "LayoverClass", LayoverClass
    RunPlan.Add "RequiredCrew", RequiredCrew
    RunPlan.Add "StartRow", StartRow
    RunPlan.Add "CreditRow", CreditRow
    RunPlan.Add "StartColumn", StartColumn
    Set AssignedEmployees = New Collection
    RunPlan.Add "AssignedEmployees", AssignedEmployees
    RunPlan.Add "LeaderEmployee", vbNullString
    RunPlan.Add "OfficeCount", 0&

    Set DE_BuildRunPlan = RunPlan

End Function

'==========================================================
' ÊËÈíÊ ÇáÇÑÊÈÇØÇÊ ÈÇÚÊÈÇÑåÇ ÝÊÑÇÊ ãäÚ
'==========================================================
Private Function DE_RegisterEvents( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EventRecord As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim StartDate As Date
    Dim EndDate As Date
    Dim MonthStart As Date
    Dim MonthEnd As Date
    Dim ActiveStart As Date
    Dim ActiveEnd As Date
    Dim DateCursor As Date
    Dim DateKey As String
    Dim EventDates As Object

    MonthStart = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth, 1)
    MonthEnd = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth + 1, 0)

    For Each EventRecord In DR_Events

        EmployeeNumber = CStr(EventRecord("EmployeeNumber"))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "ãæÙÝ ÇáÇÑÊÈÇØ ÑÞã " & EmployeeNumber & _
                " ÛíÑ ãæÌæÏ Öãä ÍÇáÇÊ ÇáÊæÒíÚ."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)
        StartDate = CDate(CDbl(EventRecord("StartDate")))
        EndDate = CDate(CDbl(EventRecord("EndDate"))) + 1

        DE_AddBusyPeriod State, StartDate, EndDate, _
            DE_TYPE_EVENT, False, _
            CStr(EventRecord("EventName"))

        ActiveStart = StartDate
        If ActiveStart < MonthStart Then ActiveStart = MonthStart

        ActiveEnd = EndDate - 1
        If ActiveEnd > MonthEnd Then ActiveEnd = MonthEnd

        If ActiveStart <= ActiveEnd Then
            Set EventDates = State("EventDates")

            If CLng(CDbl(ActiveEnd) - _
                CDbl(ActiveStart) + 1) > _
                CLng(State("LongestEventDays")) Then

                State("LongestEventDays") = _
                    CLng(CDbl(ActiveEnd) - _
                    CDbl(ActiveStart) + 1)
            End If

            For DateCursor = ActiveStart To ActiveEnd
                DateKey = CStr(CLng(CDbl(DateCursor)))
                If Not EventDates.Exists(DateKey) Then
                    EventDates.Add DateKey, True
                End If
            Next DateCursor
        End If

    Next EventRecord

    If Not DE_ApplyEventTargetAdjustments( _
        ErrorResults, WarningResults) Then Exit Function

    DE_RegisterEvents = True

End Function

'==========================================================
' ÊÍÏíÏ ÑÇÍÉ ÈÏÇíÉ ÇáÔåÑ ãä ÊÕäíÝ ÂÎÑ ÑÍáÉ Ýí ÇáÔåÑ ÇáÓÇÈÞ
'==========================================================
Private Function DE_GetPreviousMonthRestDays( _
    ByVal EmployeeRecord As Object, _
    ByRef PreviousTripType As String) As Long

    Dim StationCode As String
    Dim SeparatorPosition As Long
    Dim Requirement As Object

    PreviousTripType = vbNullString

    If EmployeeRecord.Exists("PreviousLastStation") Then
        StationCode = UCase$(Trim$(CStr( _
            EmployeeRecord("PreviousLastStation"))))
    End If

    ' ÞÏ ÊõÍÝÙ ÇáÑÍáÉ ÇáÓÇÈÞÉ ãËá DAC-MED Ãæ DAC MEDº
    ' ÇáÊÕäíÝ íÚÊãÏ Úáì ãÍØÉ ÇáÐåÇÈ ÇáÃÓÇÓíÉ.
    StationCode = Replace$(StationCode, "/", "-")
    StationCode = Replace$(StationCode, " ", "-")

    Do While InStr(1, StationCode, "--", vbBinaryCompare) > 0
        StationCode = Replace$(StationCode, "--", "-")
    Loop

    SeparatorPosition = InStr(1, StationCode, "-", vbTextCompare)
    If SeparatorPosition > 1 Then
        StationCode = Left$(StationCode, SeparatorPosition - 1)
    ElseIf Len(StationCode) = 6 Then
        StationCode = Left$(StationCode, 3)
    End If

    If Not DR_CrewRequirements Is Nothing Then
        If DR_CrewRequirements.Exists(StationCode) Then
            Set Requirement = DR_CrewRequirements(StationCode)
            PreviousTripType = UCase$(Trim$(CStr( _
                Requirement("TripType"))))
        End If
    End If

    Select Case PreviousTripType
        Case "LAY"
            DE_GetPreviousMonthRestDays = 3
        Case "LNG", "MED"
            DE_GetPreviousMonthRestDays = 2
        Case "SHT"
            DE_GetPreviousMonthRestDays = 1
        Case Else
            ' ÅÐÇ ÊÚÐÑ ÇáÊÚÑÝ Úáì ÇáÊÕäíÝ¡ äÓÊÎÏã íæãíä ßÍá Âãä
            ' æäÓÌá ÊäÈíåðÇ æÇÖÍðÇ ÈÏá ÅíÞÇÝ ÎØÉ ÇáÊæÒíÚ ÈÇáßÇãá.
            DE_GetPreviousMonthRestDays = 2
            PreviousTripType = "ÛíÑ ãÕäÝÉ"

            If Not DE_Warnings Is Nothing Then
                DE_Warnings.Add _
                    "ÊÚÐÑ ÊÍÏíÏ ÊÕäíÝ ÂÎÑ ÑÍáÉ ááãæÙÝ " & _
                    CStr(EmployeeRecord("EmployeeNumber")) & _
                    " ãä ÇáãÍØÉ [" & StationCode & _
                    "]. Êã ÇÚÊãÇÏ íæãíä ÑÇÍÉ ÈÕæÑÉ ÇÍÊíÇØíÉ."
            End If
    End Select

End Function

'==========================================================
' ÕíÇÛÉ ßáãÉ íæã Ýí æÕÝ ÝÊÑÉ ÇáÑÇÍÉ
'==========================================================
Private Function DE_ArabicDayPhrase( _
    ByVal DayCount As Long) As String

    Select Case DayCount
        Case 1
            DE_ArabicDayPhrase = "íæã æÇÍÏ"
        Case 2
            DE_ArabicDayPhrase = "íæãíä"
        Case Else
            DE_ArabicDayPhrase = CStr(DayCount) & " ÃíÇã"
    End Select

End Function

'==========================================================
' ÊÚÏíá ãÓÊåÏÝ ÇáãæÙÝ ÍÓÈ ãÌãæÚ ÃíÇã ÇÑÊÈÇØÇÊå Ýí ÇáÔåÑ
'
' ÇáãÏÏ ÇáãÚÊãÏÉ:
'   10 ÃíÇã: ÎÕã ÑÍáÊíä.
'   15 íæãðÇ: äÕÝ ÇáãÓÊåÏÝ ãÚ ÇáÊÞÑíÈ ááÃÚáì.
'   30 íæãðÇ: ãäÚ ßÇãá ãä ÇáÑÍáÇÊ æÇáãÈíÊ æÇáÇÍÊíÇØ.
'==========================================================
Private Function DE_ApplyEventTargetAdjustments( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim EventDayCount As Long
    Dim OriginalFlights As Long
    Dim AdjustedFlights As Long
    Dim OriginalLayovers As Long
    Dim AdjustedLayovers As Long
    Dim DaysInMonth As Long
    Dim CarryoverTarget As Long

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DE_EmployeeStates.Keys

        Set State = DE_EmployeeStates(EmployeeKey)
        EventDayCount = CLng(State("LongestEventDays"))
        OriginalFlights = CLng(State("OriginalFlightTarget"))
        OriginalLayovers = CLng(State("OriginalLayoverTarget"))
        AdjustedFlights = OriginalFlights
        CarryoverTarget = CLng(State("CarryoverTarget"))
        State("EventDayCount") = State("EventDates").Count
        State("TargetAdjustmentReason") = vbNullString
        State("IsNoFlightMonth") = False

        Select Case EventDayCount

            Case 0 To 9
                ' íãäÚ ÇáÇÑÊÈÇØ ÃíÇãå ÝÞØ¡ æáÇ íÎÝÖ ÇáãÓÊåÏÝ.

            Case 10
                AdjustedFlights = OriginalFlights - 2
                If AdjustedFlights < 0 Then AdjustedFlights = 0
                State("TargetAdjustmentReason") = _
                    "ÇÑÊÈÇØ 10 ÃíÇã: ÎÕã ÑÍáÊíä"

            Case 15
                AdjustedFlights = (OriginalFlights + 1) \ 2
                State("TargetAdjustmentReason") = _
                    "ÇÑÊÈÇØ 15 íæãðÇ: äÕÝ ÇáãÓÊåÏÝ"

            Case Is >= 30
                AdjustedFlights = 0
                State("IsNoFlightMonth") = True
                State("TargetAdjustmentReason") = _
                    "ÇÑÊÈÇØ 30 íæãðÇ: ãäÚ ÔåÑí ßÇãá"

            Case Else
                WarningResults.Add _
                    "ãÏÉ ÇÑÊÈÇØ ÇáãæÙÝ " & _
                    CStr(State("EmployeeNumber")) & _
                    " ÏÇÎá ÇáÔåÑ åí " & _
                    CStr(EventDayCount) & _
                    " íæãðÇ¡ æåí áíÓÊ ãä ÇáãÏÏ ÇáãÚÊãÏÉ " & _
                    "10 Ãæ 15 Ãæ 30 íæãðÇº ãõäÚÊ ÃíÇã " & _
                    "ÇáÇÑÊÈÇØ Ïæä ÊÎÝíÖ ÇáãÓÊåÏÝ."

        End Select

        If OriginalFlights > 0 _
            And OriginalLayovers > 0 _
            And AdjustedFlights > 0 Then

            AdjustedLayovers = _
                ((OriginalLayovers * AdjustedFlights) + _
                OriginalFlights - 1) \ OriginalFlights
        Else
            AdjustedLayovers = 0
        End If

        If AdjustedLayovers > AdjustedFlights Then
            AdjustedLayovers = AdjustedFlights
        End If

        ' ÇáÅÏÇÑí æÇáãæÞæÝ æãæÙÝ ÇáÇÓÊÇäÏÈÇí áÇ íÏÎáæä Ýí
        ' ÇáÊæÒíÚ ÇáÂáí æáÇ ÇáÇÍÊíÇØ. ÑÍáÇÊ ÇáÅÏÇÑí ÊõËÈÊ ÝÞØ
        ' ãä OFFICE_DATA¡ æãæÙÝ SB íõÓÌá áå ÊäÇæÈå ÇáÔåÑí ÝÞØ.
        If CBool(State("IsOffice")) _
            Or CBool(State("IsHold")) _
            Or CBool(State("IsStandby")) Then

            AdjustedFlights = 0
            AdjustedLayovers = 0
            CarryoverTarget = 0
        End If

        State("NormalFlightTarget") = AdjustedFlights
        State("FlightTarget") = _
            AdjustedFlights + CarryoverTarget
        State("LayoverTarget") = AdjustedLayovers

        If AdjustedLayovers > 0 Then
            State("LongLayoverTarget") = 1
            State("ShortLayoverTarget") = _
                AdjustedLayovers - 1
        Else
            State("LongLayoverTarget") = 0
            State("ShortLayoverTarget") = 0
        End If

        DE_SetEmployeeCarrierTargets State

    Next EmployeeKey

    DE_ApplyEventTargetAdjustments = True

End Function

'==========================================================
' ÊÞÓíã ãÓÊåÏÝ ÇáãæÙÝ Èíä ÇáÓÚæÏíÉ æÇáäÇÞá ÇáãÊÎÕÕ
'==========================================================
Private Sub DE_SetEmployeeCarrierTargets( _
    ByVal State As Object)

    Dim TotalTarget As Long
    Dim SaudiaTarget As Long
    Dim SpecialTarget As Long
    Dim SpecialCarrier As String
    Dim SaudiaDemand As Long
    Dim SpecialDemand As Long

    ' íÞÓã ÇáãÓÊåÏÝ ÇáÚÇÏí ÃæáðÇ¡ Ëã ÊÖÇÝ ãØÇáÈÇÊ ÇáÔåÑ
    ' ÇáÓÇÈÞ Åáì äÇÞáåÇ ÇáãÍÏÏ Ïæä ÊÛííÑ ÊæÇÒä ÇáÑÍáÇÊ ÇáÚÇÏíÉ.
    TotalTarget = CLng(State("NormalFlightTarget"))

    Select Case UCase$(CStr(State("CarrierProfile")))
        Case "SAUDIA_NAS"
            SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL"
            SpecialCarrier = "ADEAL"
        Case Else
            SpecialCarrier = vbNullString
    End Select

    If SpecialCarrier = vbNullString Then
        SaudiaTarget = TotalTarget
        SpecialTarget = 0
    Else
        SaudiaTarget = TotalTarget \ 2
        SpecialTarget = TotalTarget \ 2

        If TotalTarget Mod 2 <> 0 Then
            SaudiaDemand = DE_CarrierRunCount("SAUDIA")
            SpecialDemand = DE_CarrierRunCount(SpecialCarrier)

            If SaudiaDemand > SpecialDemand Then
                SaudiaTarget = SaudiaTarget + 1
            Else
                SpecialTarget = SpecialTarget + 1
            End If
        End If
    End If

    State("SaudiaTarget") = SaudiaTarget
    State("SpecialCarrierTarget") = SpecialTarget
    State("SpecialCarrierCode") = SpecialCarrier

    State("SaudiaTarget") = _
        CLng(State("SaudiaTarget")) + _
        CLng(State("CarryoverSaudiaTarget"))

    State("SpecialCarrierTarget") = _
        CLng(State("SpecialCarrierTarget")) + _
        CLng(State("CarryoverSpecialTarget"))

End Sub

Private Function DE_CarrierRunCount( _
    ByVal CarrierCode As String) As Long

    Dim RunPlan As Object

    For Each RunPlan In DE_RunPlans
        If StrComp(CStr(RunPlan("CarrierCode")), _
            CarrierCode, vbTextCompare) = 0 Then
            DE_CarrierRunCount = _
                DE_CarrierRunCount + 1
        End If
    Next RunPlan

End Function

'==========================================================
' ÅäÔÇÁ ÊäÇæÈ SB-1 / SB-2 / SB-3 ØæÇá ÇáÔåÑ
'==========================================================
Private Function DE_RegisterStandby( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim SBGroup As String
    Dim GroupNumber As Long
    Dim DayNumber As Long
    Dim DaysInMonth As Long
    Dim DutyDate As Date

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DR_SBByEmployee.Keys

        If Not DE_EmployeeStates.Exists(CStr(EmployeeKey)) Then
            ErrorResults.Add _
                "ãæÙÝ ÇáÇÓÊÇäÏÈÇí ÑÞã " & CStr(EmployeeKey) & _
                " ÛíÑ ãæÌæÏ Öãä ÍÇáÇÊ ÇáÊæÒíÚ."
            Exit Function
        End If

        Set State = DE_EmployeeStates(CStr(EmployeeKey))

        State("IsStandby") = True
        SBGroup = UCase$(CStr(DR_SBByEmployee(EmployeeKey)))

        Select Case SBGroup
            Case "SB-1": GroupNumber = 1
            Case "SB-2": GroupNumber = 2
            Case "SB-3": GroupNumber = 3
            Case Else
                ErrorResults.Add _
                    "ÑãÒ ÇáÇÓÊÇäÏÈÇí " & SBGroup & _
                    " ÛíÑ ÕÍíÍ ááãæÙÝ " & CStr(EmployeeKey) & "."
                Exit Function
        End Select

        For DayNumber = GroupNumber To DaysInMonth Step 3
            DutyDate = DateSerial( _
                DR_ActiveYear, DR_ActiveMonth, DayNumber)

            DE_AddAssignment _
                State, Nothing, DE_TYPE_SB, SBGroup, _
                DutyDate, DutyDate + 1, False, False
        Next DayNumber

    Next EmployeeKey

    DE_RegisterStandby = True

End Function

'==========================================================
' ÊËÈíÊ ÑÍáÇÊ ÇáÅÏÇÑííä ÞÈá ÇáÊæÒíÚ ÇáÂáí
'==========================================================
Private Function DE_RegisterOfficeFlights( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim OfficeRecord As Object
    Dim RunPlan As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim PairingNumber As String
    Dim FlightDate As Date
    Dim IsLeader As Boolean
    Dim GroupCode As String
    Dim ExistingLeader As String
    Dim ExistingLeaderState As Object
    Dim ConflictDetails As String

    For Each OfficeRecord In DR_OfficeFlights

        EmployeeNumber = CStr(OfficeRecord("EmployeeNumber"))
        PairingNumber = CStr(OfficeRecord("PairingNumber"))
        FlightDate = CDate(CDbl(OfficeRecord("FlightDate")))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "ÇáÅÏÇÑí ÑÞã " & EmployeeNumber & _
                " ÛíÑ ãæÌæÏ Öãä ÍÇáÇÊ ÇáÊæÒíÚ."
            Exit Function
        End If

        Set RunPlan = DE_FindRun(PairingNumber, FlightDate)
        If RunPlan Is Nothing Then
            ErrorResults.Add _
                "ÊÚÐÑ ÅíÌÇÏ ÊÔÛíá ÇáÈíÑäÞ " & PairingNumber & _
                " ÈÊÇÑíÎ " & Format$(FlightDate, "dd/mm/yyyy") & _
                " ÇáÎÇÕ ÈÇáÅÏÇÑí " & EmployeeNumber & "."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)

        If CBool(State("IsNoFlightMonth")) Then
            ErrorResults.Add _
                "ÇáÅÏÇÑí ÑÞã " & EmployeeNumber & _
                " áÏíå ÇÑÊÈÇØ ãÏÊå 30 íæãðÇ¡ æáÇ íÌæÒ " & _
                "ÊËÈíÊ ÑÍáÉ áå ÎáÇá ÇáÔåÑ."
            Exit Function
        End If

        If Not DE_EmployeeCanCoverCarrier( _
            CStr(State("CarrierProfile")), _
            CStr(RunPlan("CarrierCode"))) Then

            ErrorResults.Add _
                "ÇáÅÏÇÑí ÑÞã " & EmployeeNumber & _
                " ãÄåá ÈÕÝÉ " & CStr(State("CarrierProfile")) & _
                "¡ æáÇ íÓÊØíÚ ÊÛØíÉ ÇáÈíÑäÞ " & PairingNumber & _
                " ÇáÊÇÈÚ ááäÇÞá " & _
                CStr(RunPlan("CarrierCode")) & "."
            Exit Function

        End If

        ConflictDetails = vbNullString

        If Not DE_IsAvailable( _
            State, CDate(RunPlan("StartDateTime")), _
            CDate(RunPlan("EndDateTime")), _
            ConflictDetails) Then

            ErrorResults.Add _
                "ÑÍáÉ ÇáÅÏÇÑí ÑÞã " & EmployeeNumber & _
                "¡ ÇáÈíÑäÞ " & PairingNumber & _
                "¡ ÈÊÇÑíÎ " & _
                Format$(FlightDate, "dd/mm/yyyy") & _
                " ÊÊÚÇÑÖ ãÚ ÝÊÑÉ ãÓÌáÉ ãÓÈÞðÇ." & _
                vbCrLf & _
                "ÇáÝÊÑÉ ÇáÌÏíÏÉ: " & _
                Format$(CDate(RunPlan("StartDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                " Åáì " & _
                Format$(CDate(RunPlan("EndDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                vbCrLf & ConflictDetails

            Exit Function
        End If

        GroupCode = CStr(State("GroupCode"))
        ExistingLeader = CStr(RunPlan("LeaderEmployee"))
        IsLeader = False

        Select Case GroupCode
            Case "SSS", "SST"

                If ExistingLeader <> vbNullString Then
                    Set ExistingLeaderState = _
                        DE_EmployeeStates(ExistingLeader)

                    If CStr(ExistingLeaderState("GroupCode")) = "STS" Then
                        DE_DemoteLeader _
                            ExistingLeaderState, RunPlan
                        ExistingLeader = vbNullString
                    End If
                End If

                IsLeader = True

            Case "STS"
                IsLeader = (ExistingLeader = vbNullString)
        End Select

        If IsLeader Then
            If ExistingLeader <> vbNullString Then
                WarningResults.Add _
                    "ÇáÈíÑäÞ " & PairingNumber & _
                    " ÈÊÇÑíÎ " & Format$(FlightDate, "dd/mm/yyyy") & _
                    " íÍÊæí ÃßËÑ ãä ÞÇÆÏ ÅÏÇÑí æÝÞ ÇáÇÎÊíÇÑ ÇáãÚÊãÏ."
            Else
                RunPlan("LeaderEmployee") = EmployeeNumber
            End If
        End If

        DE_AddAssignment _
            State, RunPlan, DE_TYPE_OFFICE, PairingNumber, _
            CDate(RunPlan("StartDateTime")), _
            CDate(RunPlan("EndDateTime")), _
            CBool(RunPlan("IsLayover")), IsLeader

        RunPlan("OfficeCount") = _
            CLng(RunPlan("OfficeCount")) + 1

        If RunPlan("AssignedEmployees").Count > _
            CLng(RunPlan("RequiredCrew")) Then
            WarningResults.Add _
                "ÚÏÏ ÇáÅÏÇÑííä ÇáãÎÊÇÑíä Úáì ÇáÈíÑäÞ " & _
                PairingNumber & " ÊÌÇæÒ ÚÏÏ ÇáØÇÞã ÇáãØáæÈ."
        End If

    Next OfficeRecord

    DE_RegisterOfficeFlights = True

End Function

Private Sub DE_DemoteLeader( _
    ByVal State As Object, _
    ByVal RunPlan As Object)

    Dim Assignment As Object

    For Each Assignment In State("Assignments")
        If CLng(Assignment("RunIndex"))

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: هذا هو كود R19 المطلوب فعلًا، لكنه وصل **مقطوعًا مرة أخرى** عند `DE_PREFERRED_FLIGHT_GAP_HOURS`. Auto Claude: أعط المستخدم أبسط طريقة لإرسال الموديول كاملًا دون أن تقطعه المحادثة، مع طريقة واضحة لتقسيمه بحيث نعرف أن جميع الأجزاء وصلت بالترتيب.

### Auto Claude (Claude Sonnet 5.5)

وصلني الكود المطلوب، لكنه انقطع مرة ثانية عند `DE_PREFERRED_FLIGHT_GAP_HOURS`، قبل أي إجراء فعلي. وفيه مشكلة ثانية: التعليقات العربية وصلت رموزًا مشوهة، وهذا لا يضر الكود نفسه لكنه يخفي شرح المنطق.

**أبسط طريقة لإرسال الموديول كاملًا:**

1. **أرسله ملفًا بدل النسخ واللصق.** في محرر VBA: كليك يمين على الموديول ثم **Export File** (ينتج ملف `.bas`). ارفعه كمرفق في المحادثة. هذا يتجاوز الانقطاع ويحفظ الترميز.
2. **إذا ما تيسر رفع الملف:** قسّم النص إلى أجزاء من نحو 300 سطر، وابدأ كل جزء بسطر مثل `--- جزء 1 من 4 ---`، وأنهِ آخر جزء بـ`--- نهاية الموديول ---`. هكذا نعرف أن الأجزاء كلها وصلت وبالترتيب.
3. **تقرير آخر تشغيل:** أرسله بنفس الطريقة، كنص أو لقطة شاشة.

**ملاحظة:** إذا كان الموديول ضخمًا جدًا (آلاف الأسطر)، أرسل الأجزاء حسب الأولوية: إجراءات المبيت، ثم الإصلاح.

بدون الإجراءات نفسها ما أقدر أحكم على شيء بعد.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: هل توافق أن **تصدير الموديول كملف `.bas` ورفعه هنا** هو الخيار الأفضل؟ وضّح للمستخدم فقط خطوات التصدير الدقيقة في VBA وكيف يتأكد أنه صدّر الموديول الصحيح، بدون بدائل إضافية تربكه.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

نعم، **ملف `.bas` هو الأفضل**؛ لأنه يعطينا الموديول كاملًا بدون انقطاع النص.

اعمل التالي فقط:

1. افتح VBA بـ **Alt + F11**.
2. من القائمة اليسرى اضغط على **الموديول الذي وجدت داخله تعليق `R19`**.
3. تأكد منه بفتح الموديول ورؤية بداية الكود التي أرسلتها الآن:
```vb
' R19 - ...
Public DE_Assignments As Collection
```
4. ارجع لاسم الموديول في القائمة اليسرى واضغط عليه **كليك يمين**.
5. اختر **Export File...**
6. ستظهر نافذة الحفظ. لا تغيّر نوع الملف؛ سيُحفظ بامتداد:
```text
.bas
```
7. اختر سطح المكتب مثلًا واضغط **Save**.
8. ارفع ملف الـ `.bas` هنا في المحادثة.

**لا تعدّل الكود ولا تحذف منه شيئًا قبل التصدير.** أرسله كما هو تمامًا، وأنا وAuto Claude نقرأه كاملًا ونحدد مسار مشكلة 135.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: صدّر الموديول الذي يحتوي `R19` كملف `.bas` وارفعه هنا كما هو؛ هذه أفضل طريقة لوصول الكود كاملًا.

**Agreed**
- افتح VBA وحدد الموديول الذي يحتوي تعليق `R19`.
- كليك يمين على اسم الموديول ثم **Export File...** واحفظ ملف `.bas`.
- لا تعدّل أو تحذف أي جزء من الكود قبل التصدير.

**What would change it**
- بعد استلام ملف `.bas` نقرأ المحرك كاملًا، ثم نحدد إن كنا نحتاج أي موديول إضافي.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Auto Claude: قبل أن نبدأ التحليل بعد رفع ملف `.bas`، ما أقل تقرير تشغيل نحتاجه بجانب الكود حتى نستطيع ربط مشكلة الرحلة 135 بمسار التنفيذ الفعلي، بدل تحليل الكود نظريًا فقط؟

### Participant

كيف يمكنني رفع ملف هنا

### Auto Claude (Claude Sonnet 5.5)

أرفع الملف من نفس المربع اللي تكتب فيه الرسائل، وطريقته تختلف حسب جهازك. وما أعرف بالضبط شكل واجهة هذه الغرفة، فإذا ما لقيت الزر المذكور، قل لي وش تشوف بجانب مربع الكتابة.

**على الكمبيوتر:**
1. ابحث بجانب مربع الكتابة عن أيقونة **مشبك ورق 📎** أو علامة **+**.
2. اضغط عليها واختر **Upload a file** (رفع ملف).
3. اختر ملف `.bas` من سطح المكتب واضغط **Open**.
4. انتظر حتى يظهر اسم الملف داخل مربع الكتابة، ثم أرسل الرسالة.

**على الجوال:** نفس الأيقونة (+ أو المشبك)، ثم اختر **Files** وحدد الملف.

**إذا ما ظهر خيار الرفع أو رفض الملف `.bas`:**
- غيّر امتداد الملف إلى `.txt` (مثلًا `engine.bas` يصير `engine.txt`) وارفعه، فبعض الأنظمة تقبل النصوص فقط.
- وإذا ما نفع، افتح الملف بالمفكرة (Notepad)، وانسخ النص على أجزاء مرقمة (1/4، 2/4...) وأرسلها.

**بجانب الكود:** لو تقدر، أرسل تقرير آخر تشغيل كامل، لأننا نحتاج أسطر القائد 135 فيه (عدد المحاولات وسبب التوقف).

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: المستخدم على الكمبيوتر؛ أعطه **طريقة واحدة فقط وأسهلها** لرفع ملف `.bas` هنا من واجهة ChatGPT، وإذا كان السحب والإفلات مباشرة داخل المحادثة يعمل فاجعله الخيار الأول لتجنب تشتيته بالبدائل.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

أسهل طريقة على الكمبيوتر:

افتح المجلد الذي حفظت فيه ملف **`.bas`**، ثم بالماوس **اسحب الملف نفسه وأسقطه مباشرة داخل مربع المحادثة هنا**، في نفس المكان الذي تكتب فيه رسالتك.

بعد أن يظهر اسم الملف كمرفق، اضغط **إرسال** فقط.

لا تفتح الملف ولا تغيّر امتداده ولا تضغطه. أرسله كما هو.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: اسحب ملف `.bas` من المجلد وأسقطه مباشرة داخل مربع المحادثة، ثم أرسله.

**Agreed**
- لا تفتح الملف ولا تعدّله.
- لا تغيّر امتداده ولا تضغطه.
- بعد ظهور اسم الملف كمرفق، اضغط إرسال.

**What would change it**
- إذا رفضت الواجهة الملف فعلًا، ننتقل وقتها لطريقة بديلة واحدة فقط.

### Participant

تظهر لي علامة ممنوع

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: واجهة المحادثة ترفض إسقاط ملف `.bas` مباشرة. Auto Claude: أعط المستخدم **بديلًا واحدًا فقط** هو الأسهل والأضمن لإرسال نفس الملف دون فقدان محتواه أو تشويه العربية.

### Participant

برسل لكم الكود على جزئين
بس لو تقولون لي الي اي جزء توقف بالضبط لاكمل لكم المتبقي منه

### Auto Claude (Claude Sonnet 5.5)

الكود الأول وصلني مقطوعًا عند هذا السطر بالضبط:

`Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Doubl`

الكلمة الأخيرة ناقصة (المفروض `Double`). فابدأ الجزء الثاني من هذا السطر نفسه، وأعد كتابته كاملًا:

`Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Double = ...`

ثم أكمل كل ما بعده بدون حذف.

**ملاحظات تسهّل الإرسال:**
1. ابدأ كل رسالة بسطر `--- جزء 1 من 2 ---` والثانية `--- جزء 2 من 2 ---`، وفي آخر الثانية اكتب `--- نهاية الموديول ---`.
2. إذا انقطع الجزء الثاني أيضًا، سأقول لك آخر سطر وصل، وترسل من عنده.
3. التعليقات العربية تصل رموزًا مشوهة. لا يضر المنطق، لكن لو تقدر تنسخ النص من محرر VBA مباشرة بدل الملف الخارجي فقد يتحسن.

وبعد الكود أرسل تقرير آخر تشغيل كامل، خصوصًا أسطر القائد 135.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: أكّد نقطة البداية للجزء المتبقي، وهل الأفضل أن يعيد المستخدم السطر المقطوع كاملًا مع بضعة أسطر قبله لضمان عدم فقدان أي سطر عند نقطة الفصل؟

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

نعم. **ابدأ الجزء المتبقي من قبل مكان الانقطاع بقليل** حتى نضمن عدم سقوط أي سطر بين الجزأين.

آخر ما وصلنا كان:

```vb
Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Doubl
```

لذلك عند إرسال الجزء الثاني، ارجع في الكود **حوالي 5 أسطر قبل هذا السطر**، ثم انسخ من هناك إلى **نهاية الموديول بالكامل**.

لا يضر تكرار هذه الأسطر الخمسة؛ بالعكس، سيساعدنا على التأكد أن الجزأين متصلان بشكل صحيح.

وفي آخر الرسالة اكتب فقط:

`--- نهاية الموديول ---`

وبذلك نعرف يقينًا أن R19 وصل كاملًا.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: أرسل الجزء الثاني بدءًا من نحو **5 أسطر قبل** `Private Const DE_PREFERRED_FLIGHT_GAP_HOURS...` وحتى آخر سطر في الموديول.

**Agreed**
- تكرار عدة أسطر بين الجزأين مطلوب للتأكد من عدم فقدان شيء.
- أكمل النسخ حتى نهاية الموديول دون حذف.
- اختم الرسالة بـ `--- نهاية الموديول ---`.

### Participant

' تفضيلات تشغيلية لتباعد الرحلات. هذه ليست منعًا مطلقًا؛
' فالحد الإلزامي يبقى راحة 24 ساعة، بينما يستخدم المحرك
' هذه القيم لاختيار الموظف صاحب الجدول الأقل ازدحامًا.
Private Const DE_PREFERRED_FLIGHT_GAP_HOURS As Double = 72#
Private Const DE_ACCEPTABLE_FLIGHT_GAP_HOURS As Double = 48#

'==========================================================
' بناء خطة التوزيع كاملة في الذاكرة
'==========================================================
Public Function DistributionEngine_BuildPlan( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim CurrentStep As String
    Dim ReaderPassed As Boolean
    Dim ValidationPassed As Boolean

    On Error GoTo EngineError

    EnsureGregorianCalendar
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    DistributionEngine_Clear
    Set DE_Warnings = WarningResults

    CurrentStep = "تحميل مصادر بيانات التوزيع"

    If DR_Employees Is Nothing Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    ElseIf DR_Employees.Count = 0 Then
        ReaderPassed = DistributionReader_LoadAll( _
            ErrorResults, PassedResults)
    Else
        ReaderPassed = True
    End If

    If Not ReaderPassed Then Exit Function

    ValidationPassed = True

    CurrentStep = "تجهيز حالات الموظفين"
    If Not DE_InitializeEmployeeStates(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تحليل مرات تشغيل البيرنقات"
    If Not DE_InitializeRunPlans(ErrorResults, PassedResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "تثبيت ارتباطات الموظفين"
    If Not DE_RegisterEvents( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تثبيت جداول الاستاندباي"
    If Not DE_RegisterStandby(ErrorResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "تثبيت رحلات الإداريين"
    If Not DE_RegisterOfficeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    ' يجب حجز أيام الاحتياط قبل توزيع الرحلات العادية؛
    ' حتى تُبنى الرحلات حول الاحتياط وفترة الراحة التالية له.
    CurrentStep = "توزيع أيام الاحتياط"
    If Not DE_DistributeReserveDays( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    ' الاحتياط تكليف إلزامي. إذا تعذر حجز يوم مناسب له،
    ' لا يبدأ توزيع الرحلات على خطة ناقصة أو غير قابلة للتنفيذ.
    If Not ValidationPassed Then Exit Function

    ' الرحلات المطالب بها من الشهر السابق تُثبت قبل التوزيع
    ' العادي؛ لأنها حق مرحّل للموظف وتُعد رحلة إضافية فوق
    ' مستهدفه الأساسي، مع تطبيق الأهلية والتعارض وراحة 24 ساعة.
    CurrentStep = "توزيع الرحلات المطالب بها"
    If Not DE_DistributeCarryoverFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    If Not ValidationPassed Then Exit Function

    CurrentStep = "توزيع أطقم الرحلات"
    If Not DE_DistributeFlights( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
    End If

    CurrentStep = "مراجعة ترتيب القيادة والأفراد"
    If Not DE_ValidateRolePolicy(ErrorResults) Then
        ValidationPassed = False
    End If

    ' يضاف الملخص سواء نجحت الخطة أم ظهر عجز؛ حتى يوضح
    ' عدد الرحلات الإضافية المستخدمة فعليًا حسب المجموعة.
    DE_AddExtraFlightSummary WarningResults


    CurrentStep = "مراجعة توافق تأهيل الموظفين مع الناقلات"
    If ValidationPassed Then
        If Not DE_ValidateCarrierAssignments(ErrorResults) Then
            ValidationPassed = False
        End If
    End If

    If ValidationPassed Then
        PassedResults.Add _
            "نجح إنشاء خطة توزيع الرحلات في الذاكرة."

        PassedResults.Add _
            "عدد مرات التشغيل المخططة: " & _
            CStr(DE_RunPlans.Count) & _
            "، وعدد التكليفات: " & _
            CStr(DE_Assignments.Count) & "."

        PassedResults.Add _
            "لم يتم تعديل أي خلية في شيت LINE."

        PassedResults.Add _
            "تكليفات الرحلات حسب الناقل: السعودية " & _
            CStr(DE_CarrierAssignmentCount("SAUDIA")) & _
            "، ناس " & CStr(DE_CarrierAssignmentCount("NAS")) & _
            "، أديل " & CStr(DE_CarrierAssignmentCount("ADEAL")) & "."

        PassedResults.Add _
            "تمت تغطية الرحلات المطالب بها: " & _
            CStr(DE_CarryoverAssignedCount()) & _
            " من " & CStr(DR_CarryoverFlights.Count) & "."

        PassedResults.Add _
            "راحة بداية الشهر بسبب آخر رحلة في الشهر السابق: " & _
            "يوم واحد لعدد " & _
            CStr(DE_PreviousMonthRestOneDayCount) & _
            "، يومان لعدد " & _
            CStr(DE_PreviousMonthRestTwoDaysCount) & _
            "، ثلاثة أيام لعدد " & _
            CStr(DE_PreviousMonthRestThreeDaysCount) & _
            "، والإجمالي " & _
            CStr(DE_PreviousMonthRestCount) & " موظفًا."
    End If

    DistributionEngine_BuildPlan = ValidationPassed
    Exit Function

EngineError:
    DE_EnsureCollections ErrorResults, PassedResults, WarningResults
    ErrorResults.Add _
        "تعذر بناء خطة التوزيع أثناء " & CurrentStep & _
        ". تفاصيل الخطأ: " & Err.Description
    DistributionEngine_BuildPlan = False

End Function

'==========================================================
' مسح خطة سابقة من الذاكرة
'==========================================================
Public Sub DistributionEngine_Clear()

    Set DE_Assignments = New Collection
    Set DE_RunPlans = New Collection
    Set DE_EmployeeStates = _
        CreateObject("Scripting.Dictionary")
    DE_EmployeeStates.CompareMode = vbTextCompare
    Set DE_Warnings = New Collection
    Set DE_RTDayLoad = CreateObject("Scripting.Dictionary")
    DE_RTDayLoad.CompareMode = vbTextCompare
    DE_PreviousMonthRestCount = 0
    DE_PreviousMonthRestOneDayCount = 0
    DE_PreviousMonthRestTwoDaysCount = 0
    DE_PreviousMonthRestThreeDaysCount = 0

End Sub

'==========================================================
' اختبار مستقل للمحرك من دون الكتابة في LINE
'==========================================================
Public Sub TestDistributionEngine()

    Dim Errors As Collection
    Dim Passed As Collection
    Dim Warnings As Collection
    Dim ResultPassed As Boolean

    Set Errors = New Collection
    Set Passed = New Collection
    Set Warnings = New Collection

    ResultPassed = DistributionEngine_BuildPlan( _
        Errors, Passed, Warnings)

    DE_WriteDistributionTestReport _
        ResultPassed, Errors, Warnings, Passed

    MsgBox _
        "تم إنشاء تقرير اختبار التوزيع في شيت " & _
        "DISTRIBUTION_REPORT.", _
        IIf(ResultPassed, vbInformation, vbExclamation), _
        "اختبار محرك التوزيع"

End Sub

'==========================================================
' إنشاء تقرير اختبار المحرك داخل شيت مستقل.
'
' لا يكتب هذا الإجراء أي شيء في LINE، ولا يحذف الشيت؛
' بل يمسح محتوى التقرير السابق ثم يكتب التقرير الجديد.
'==========================================================
Private Sub DE_WriteDistributionTestReport( _
    ByVal ResultPassed As Boolean, _
    ByVal ErrorResults As Collection, _
    ByVal WarningResults As Collection, _
    ByVal PassedResults As Collection)

    Const REPORT_SHEET As String = _
        "DISTRIBUTION_REPORT"

    Dim ws As Worksheet
    Dim NextRow As Long
    Dim SequenceNumber As Long
    Dim FinalRow As Long
    Dim ReportRange As Range
    Dim ReportWindow As Window
    Dim PreviousScreenUpdating As Boolean
    Dim ReportErrorNumber As Long
    Dim ReportErrorDescription As String

    PreviousScreenUpdating = Application.ScreenUpdating
    On Error GoTo ReportError

    Set ws = DE_GetOrCreateReportSheet(REPORT_SHEET)

    Application.ScreenUpdating = False

    ws.Visible = xlSheetVisible
    ws.Cells.Clear
    ws.DisplayRightToLeft = True

    ' عنوان التقرير.
    With ws.Range("A1:D1")
        .HorizontalAlignment = xlCenterAcrossSelection
        .VerticalAlignment = xlCenter
        .Font.Bold = True
        .Font.Size = 16
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
    End With

    ws.Range("A1").Value = _
        "تقرير اختبار محرك توزيع الرحلات"
    ws.Rows(1).RowHeight = 28

    ws.Range("A2").Value = "الشهر"
    ws.Range("B2").Value = _
        UCase$(MonthName(DR_ActiveMonth, True))
    ws.Range("C2").Value = "السنة"
    ws.Range("D2").Value = DR_ActiveYear

    ws.Range("A3").Value = "نتيجة الاختبار"
    If ResultPassed Then
        ws.Range("B3").Value = "نجح الاختبار"
        ws.Range("B3").Interior.Color = _
            RGB(198, 239, 206)
        ws.Range("B3").Font.Color = _
            RGB(0, 97, 0)
    Else
        ws.Range("B3").Value = "لم تكتمل خطة التوزيع"
        ws.Range("B3").Interior.Color = _
            RGB(255, 199, 206)
        ws.Range("B3").Font.Color = _
            RGB(156, 0, 6)
    End If

    ws.Range("C3").Value = "تاريخ التقرير"
    ws.Range("D3").NumberFormat = "@"
    ws.Range("D3").Value = _
        "'" & Format$(Now, "dd/mm/yyyy hh:mm")

    With ws.Range("A2:D3")
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlCenter
        .Font.Bold = True
    End With

    ' رؤوس أعمدة النتائج.
    ws.Range("A5").Value = "التسلسل"
    ws.Range("B5").Value = "التصنيف"
    ws.Range("C5").Value = "الحالة"
    ws.Range("D5").Value = "التفاصيل"

    With ws.Range("A5:D5")
        .Font.Bold = True
        .Font.Color = RGB(255, 255, 255)
        .Interior.Color = RGB(0, 78, 76)
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlCenter
        .Borders.LineStyle = xlContinuous
    End With

    NextRow = 6
    SequenceNumber = 0

    DE_WriteReportCollection _
        ws, ErrorResults, "خطأ", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, WarningResults, "تنبيه", _
        NextRow, SequenceNumber

    DE_WriteReportCollection _
        ws, PassedResults, "سليم", _
        NextRow, SequenceNumber

    If SequenceNumber = 0 Then
        SequenceNumber = 1
        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = "معلومة"
        ws.Cells(NextRow, "C").Value = "لا توجد نتائج"
        ws.Cells(NextRow, "D").Value = _
            "لم يُرجع المحرك أي نتائج لعرضها."
        NextRow = NextRow + 1
    End If

    FinalRow = NextRow - 1
    Set ReportRange = ws.Range( _
        "A5:D" & CStr(FinalRow))

    With ReportRange
        .Borders.LineStyle = xlContinuous
        .VerticalAlignment = xlTop
    End With

    ws.Range("A6:C" & CStr(FinalRow)).HorizontalAlignment = _
        xlCenter
    ws.Range("D6:D" & CStr(FinalRow)).HorizontalAlignment = _
        xlRight
    ws.Range("D6:D" & CStr(FinalRow)).WrapText = True

    ws.Columns("A").ColumnWidth = 10
    ws.Columns("B").ColumnWidth = 16
    ws.Columns("C").ColumnWidth = 20
    ws.Columns("D").ColumnWidth = 95

    ws.Rows("2:3").AutoFit
    ws.Rows("5:" & CStr(FinalRow)).AutoFit

    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    ws.Range("A5:D" & CStr(FinalRow)).AutoFilter

    ' إعداد عرض التقرير اختياري ولا يوقف كتابة النتائج.
    On Error Resume Next
    ws.Parent.Activate
    ws.Activate
    Set ReportWindow = Application.ActiveWindow
    If Not ReportWindow Is Nothing Then
        ReportWindow.View = xlNormalView
        ReportWindow.FreezePanes = False
        ReportWindow.SplitRow = 0
        ReportWindow.SplitColumn = 0
        Err.Clear
        ws.Range("A6").Select
        If Err.Number = 0 Then
            ReportWindow.SplitRow = 5
            ReportWindow.FreezePanes = True
        End If
    End If
    Err.Clear
    On Error GoTo ReportError
    Application.ScreenUpdating = PreviousScreenUpdating
    Exit Sub
ReportError:
    ReportErrorNumber = Err.Number
    ReportErrorDescription = Err.Description
    Application.ScreenUpdating = PreviousScreenUpdating
    MsgBox "تعذر إخراج التقرير. رقم الخطأ: " & CStr(ReportErrorNumber) & _
        vbCrLf & ReportErrorDescription, vbExclamation, "تقرير التوزيع"

End Sub

'==========================================================
' إرجاع شيت التقرير أو إنشاؤه إذا لم يكن موجودًا.
'==========================================================
Private Function DE_GetOrCreateReportSheet( _
    ByVal SheetName As String) As Worksheet

    Dim ws As Worksheet

    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(SheetName)
    On Error GoTo 0

    If ws Is Nothing Then
        Set ws = ThisWorkbook.Worksheets.Add( _
            After:=ThisWorkbook.Worksheets( _
                ThisWorkbook.Worksheets.Count))
        ws.NAME = SheetName
    End If

    Set DE_GetOrCreateReportSheet = ws

End Function

'==========================================================
' كتابة مجموعة نتائج في تقرير الاختبار.
'==========================================================
Private Sub DE_WriteReportCollection( _
    ByVal ws As Worksheet, _
    ByVal Results As Collection, _
    ByVal DefaultCategory As String, _
    ByRef NextRow As Long, _
    ByRef SequenceNumber As Long)

    Dim Item As Variant
    Dim ResultText As String
    Dim CategoryText As String
    Dim StatusText As String

    If Results Is Nothing Then Exit Sub

    For Each Item In Results

        ResultText = CStr(Item)
        CategoryText = DefaultCategory
        StatusText = DefaultCategory

        If DE_IsShortageDiagnostic(ResultText) Then
            CategoryText = "تشخيص عجز"
            StatusText = "تفاصيل الموانع"
        ElseIf DefaultCategory = "خطأ" _
            And InStr(1, ResultText, _
                "عجز", vbTextCompare) > 0 Then
            CategoryText = "عجز"
            StatusText = "غير مغطى"
        ElseIf DefaultCategory = "خطأ" Then
            StatusText = "يحتاج معالجة"
        ElseIf DefaultCategory = "تنبيه" Then
            StatusText = "للمراجعة"
        ElseIf DefaultCategory = "سليم" Then
            StatusText = "مكتمل"
        End If

        SequenceNumber = SequenceNumber + 1

        ws.Cells(NextRow, "A").Value = SequenceNumber
        ws.Cells(NextRow, "B").Value = CategoryText
        ws.Cells(NextRow, "C").Value = StatusText
        ws.Cells(NextRow, "D").Value = ResultText

        DE_ApplyReportRowColor _
            ws.Range("A" & CStr(NextRow) & _
                ":D" & CStr(NextRow)), _
            CategoryText

        NextRow = NextRow + 1

    Next Item

End Sub

'==========================================================
' تلوين صف التقرير حسب نوع النتيجة.
'==========================================================
Private Sub DE_ApplyReportRowColor( _
    ByVal TargetRange As Range, _
    ByVal CategoryText As String)

    Select Case CategoryText

        Case "خطأ"
            TargetRange.Interior.Color = _
                RGB(255, 199, 206)
            TargetRange.Font.Color = _
                RGB(156, 0, 6)

        Case "عجز", "تشخيص عجز"
            TargetRange.Interior.Color = _
                RGB(252, 229, 205)
            TargetRange.Font.Color = _
                RGB(156, 87, 0)

        Case "تنبيه"
            TargetRange.Interior.Color = _
                RGB(255, 242, 204)
            TargetRange.Font.Color = _
                RGB(127, 96, 0)

        Case "سليم"
            TargetRange.Interior.Color = _
                RGB(198, 239, 206)
            TargetRange.Font.Color = _
                RGB(0, 97, 0)

        Case Else
            TargetRange.Interior.Color = _
                RGB(217, 217, 217)
            TargetRange.Font.Color = _
                RGB(0, 0, 0)

    End Select

End Sub

'==========================================================
' التحقق من أن النص تقرير تشخيص عجز تفصيلي.
'==========================================================
Private Function DE_IsShortageDiagnostic( _
    ByVal ResultText As String) As Boolean

    DE_IsShortageDiagnostic = _
        (Left$(Trim$(ResultText), _
            Len("[تشخيص]")) = "[تشخيص]" _
        Or Left$(Trim$(ResultText), Len("[أهلية فعلية")) = "[أهلية فعلية")

End Function

'==========================================================
' عرض تقارير تشخيص العجز في رسائل منفصلة.
'==========================================================
Private Sub DE_ShowShortageDiagnostics( _
    ByVal ErrorResults As Collection)

    Dim Item As Variant
    Dim DiagnosticNumber As Long

    If ErrorResults Is Nothing Then Exit Sub

    For Each Item In ErrorResults

        If DE_IsShortageDiagnostic(CStr(Item)) Then
            DiagnosticNumber = DiagnosticNumber + 1

            DE_ShowLongDiagnostic _
                CStr(Item), _
                "تفاصيل عجز التوزيع " & _
                CStr(DiagnosticNumber)
        End If

    Next Item

End Sub

'==========================================================
' تقسيم التقرير الطويل إلى صفحات من دون قطع السطور.
'==========================================================
Private Sub DE_ShowLongDiagnostic( _
    ByVal DiagnosticText As String, _
    ByVal WindowTitle As String)

    Const MAX_PAGE_LENGTH As Long = 650

    Dim Lines As Variant
    Dim LineItem As Variant
    Dim CurrentPage As String
    Dim Pages As Collection
    Dim PageNumber As Long
    Dim CandidateText As String

    Set Pages = New Collection
    Lines = Split(DiagnosticText, vbCrLf)

    For Each LineItem In Lines

        If CurrentPage = vbNullString Then
            CandidateText = CStr(LineItem)
        Else
            CandidateText = CurrentPage & _
                vbCrLf & CStr(LineItem)
        End If

        If Len(CandidateText) > MAX_PAGE_LENGTH _
            And CurrentPage <> vbNullString Then

            Pages.Add CurrentPage
            CurrentPage = CStr(LineItem)
        Else
            CurrentPage = CandidateText
        End If

    Next LineItem

    If CurrentPage <> vbNullString Then
        Pages.Add CurrentPage
    End If

    For PageNumber = 1 To Pages.Count

        MsgBox _
            CStr(Pages(PageNumber)), _
            vbExclamation, _
            WindowTitle & " - الصفحة " & _
            CStr(PageNumber) & " من " & _
            CStr(Pages.Count)

    Next PageNumber

End Sub

'==========================================================
' تجهيز حالة كل موظف ومستهدف مجموعته
'==========================================================
Private Function DE_GroupFlightTarget(ByVal GroupCode As String) As Long
    Select Case UCase$(Trim$(GroupCode))
        Case "SSS", "SST": DE_GroupFlightTarget = 7
        Case "STS": DE_GroupFlightTarget = 8
        Case "STT": DE_GroupFlightTarget = 10
    End Select
End Function
Private Function DE_InitializeEmployeeStates( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim EmployeeRecord As Object
    Dim State As Object
    Dim Targets As Object
    Dim GroupCode As String
    Dim BusyPeriods As Collection
    Dim EmployeeAssignments As Collection
    Dim EventDates As Object
    Dim PreviousLastFlightDate As Date
    Dim PreviousMonthEnd As Date
    Dim NewMonthStart As Date
    Dim PreviousMonthRestEnd As Date
    Dim PreviousMonthRestDays As Long
    Dim PreviousTripType As String

    Set Targets = DE_ReadGroupTargets(ErrorResults)
    If Targets Is Nothing Then Exit Function

    For Each EmployeeKey In DR_Employees.Keys

        Set EmployeeRecord = DR_Employees(EmployeeKey)
        GroupCode = UCase$(CStr(EmployeeRecord("GroupCode")))

        If Not Targets.Exists(GroupCode) Then
            ErrorResults.Add _
                "لا يوجد مستهدف للمجموعة " & GroupCode & _
                " الخاصة بالموظف " & CStr(EmployeeKey) & "."
            Exit Function
        End If

        Set State = DE_NewRecord()
        State.Add "EmployeeNumber", CStr(EmployeeKey)
        State.Add "EmployeeName", _
            CStr(EmployeeRecord("EmployeeName"))
        State.Add "GroupCode", GroupCode
        State.Add "CarrierProfile", _
            UCase$(CStr(EmployeeRecord("CarrierProfile")))
        State.Add "InfoRow", CLng(EmployeeRecord("InfoRow"))
        State.Add "LineRow", CLng(EmployeeRecord("LineRow"))
        State.Add "IsOffice", CBool(EmployeeRecord("IsOffice"))
        State.Add "IsHold", CBool(EmployeeRecord("IsHold"))
        State.Add "IsStandby", _
            DR_SBByEmployee.Exists(CStr(EmployeeKey))
        State.Add "OriginalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "NormalFlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "FlightTarget", _
            DE_GroupFlightTarget(GroupCode)
        State.Add "OriginalLayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LayoverTarget", _
            CLng(Targets(GroupCode)("Layovers"))
        State.Add "LongLayoverTarget", 0&
        State.Add "ShortLayoverTarget", 0&
        State.Add "LongLayoverCount", 0&
        State.Add "ShortLayoverCount", 0&
        State.Add "EventDayCount", 0&
        State.Add "LongestEventDays", 0&
        State.Add "TargetAdjustmentReason", vbNullString
        State.Add "IsNoFlightMonth", False
        State.Add "SaudiaTarget", 0&
        State.Add "SpecialCarrierTarget", 0&
        State.Add "SpecialCarrierCode", vbNullString
        State.Add "SaudiaCount", 0&
        State.Add "SpecialCarrierCount", 0&
        State.Add "FlightCount", 0&
        State.Add "LayoverCount", 0&
        State.Add "CarryoverTarget", _
            DE_EmployeeCarryoverCount(CStr(EmployeeKey))
        State.Add "CarryoverAssigned", 0&
        State.Add "CarryoverLayoverCount", 0&
        State.Add "CarryoverLongLayoverCount", 0&
        State.Add "CarryoverShortLayoverCount", 0&
        State.Add "CarryoverSaudiaTarget", _
            DE_EmployeeCarryoverCarrierCount( _
                CStr(EmployeeKey), "SAUDIA")
        State.Add "CarryoverSpecialTarget", _
            DE_EmployeeCarryoverSpecialCount( _
                CStr(EmployeeKey))
        State.Add "LeaderCount", 0&
        State.Add "ReserveCount", 0&
        State.Add "HasPreviousMonthEndRest", False
        ' عدد الرحلات التي أضيفت فوق مستهدف المجموعة لمعالجة العجز
        State.Add "ExtraFlightCount", 0&
        Set BusyPeriods = New Collection
        Set EmployeeAssignments = New Collection
        Set EventDates = CreateObject("Scripting.Dictionary")
        EventDates.CompareMode = vbTextCompare
        State.Add "BusyPeriods", BusyPeriods
        State.Add "Assignments", EmployeeAssignments
        State.Add "EventDates", EventDates

        ' إذا كانت آخر رحلة للموظف في آخر يوم من الشهر السابق،
        ' تُحجز راحة بداية الشهر حسب تصنيف الرحلة السابقة:
        '   LAY = ثلاثة أيام، LNG أو MED = يومان، SHT = يوم واحد.
        ' وتستمر بقية شروط الراحة والتعارض دون أي تغيير.
        If EmployeeRecord.Exists("HasPreviousLastFlight") Then

            If CBool(EmployeeRecord("HasPreviousLastFlight")) Then

                PreviousLastFlightDate = CDate(CDbl( _
                    EmployeeRecord("PreviousLastFlightDate")))

                PreviousMonthEnd = DateSerial( _
                    DR_ActiveYear, DR_ActiveMonth, 0)

                If Int(CDbl(PreviousLastFlightDate)) = _
                    Int(CDbl(PreviousMonthEnd)) Then

                    NewMonthStart = DateSerial( _
                        DR_ActiveYear, DR_ActiveMonth, 1)

                    PreviousMonthRestDays = _
                        DE_GetPreviousMonthRestDays( _
                            EmployeeRecord, PreviousTripType)

                    PreviousMonthRestEnd = DateAdd( _
                        "d", PreviousMonthRestDays, NewMonthStart)

                    DE_AddBusyPeriod _
                        State, _
                        NewMonthStart, _
                        PreviousMonthRestEnd, _
                        DE_TYPE_PREVIOUS_MONTH_REST, _
                        False, _
                        "راحة بداية الشهر لمدة " & _
                        DE_ArabicDayPhrase(PreviousMonthRestDays) & _
                        " بسبب رحلة " & PreviousTripType & _
                        " في آخر يوم من الشهر السابق"

                    State("HasPreviousMonthEndRest") = True
                    DE_PreviousMonthRestCount = _
                        DE_PreviousMonthRestCount + 1

                    Select Case PreviousMonthRestDays
                        Case 1
                            DE_PreviousMonthRestOneDayCount = _
                                DE_PreviousMonthRestOneDayCount + 1
                        Case 2
                            DE_PreviousMonthRestTwoDaysCount = _
                                DE_PreviousMonthRestTwoDaysCount + 1
                        Case 3
                            DE_PreviousMonthRestThreeDaysCount = _
                                DE_PreviousMonthRestThreeDaysCount + 1
                    End Select

                End If

            End If

        End If

        DE_EmployeeStates.Add CStr(EmployeeKey), State

    Next EmployeeKey

    DE_InitializeEmployeeStates = True

End Function

'==========================================================
' حساب عدد المطالبات المسجلة لموظف
'==========================================================
Private Function DE_EmployeeCarryoverCount( _
    ByVal EmployeeNumber As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCount = _
                DE_EmployeeCarryoverCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' حساب مطالبات موظف لناقل محدد
'==========================================================
Private Function DE_EmployeeCarryoverCarrierCount( _
    ByVal EmployeeNumber As String, _
    ByVal CarrierCode As String) As Long

    Dim CarryoverRecord As Object

    For Each CarryoverRecord In DR_CarryoverFlights
        If StrComp(CStr(CarryoverRecord("EmployeeNumber")), _
            EmployeeNumber, vbTextCompare) = 0 _
            And StrComp(CStr(CarryoverRecord("CarrierCode")), _
                CarrierCode, vbTextCompare) = 0 Then
            DE_EmployeeCarryoverCarrierCount = _
                DE_EmployeeCarryoverCarrierCount + 1
        End If
    Next CarryoverRecord

End Function

'==========================================================
' حساب مطالبات الموظف للناقل المتخصص في ملفه
'==========================================================
Private Function DE_EmployeeCarryoverSpecialCount( _
    ByVal EmployeeNumber As String) As Long

    Dim EmployeeRecord As Object
    Dim SpecialCarrier As String

    If Not DR_Employees.Exists(EmployeeNumber) Then Exit Function
    Set EmployeeRecord = DR_Employees(EmployeeNumber)

    Select Case UCase$(CStr(EmployeeRecord("CarrierProfile")))
        Case "SAUDIA_NAS": SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL": SpecialCarrier = "ADEAL"
        Case Else: Exit Function
    End Select

    DE_EmployeeCarryoverSpecialCount = _
        DE_EmployeeCarryoverCarrierCount( _
            EmployeeNumber, SpecialCarrier)

End Function

'==========================================================
' قراءة مستهدف الرحلات والمبيت من INFO!K:M
'==========================================================
Private Function DE_ReadGroupTargets( _
    ByRef ErrorResults As Collection) As Object

    Dim ws As Worksheet
    Dim Targets As Object
    Dim LastRow As Long
    Dim CurrentRow As Long
    Dim GroupCode As String
    Dim FlightsText As String
    Dim LayoversText As String
    Dim Target As Object

    Set ws = ThisWorkbook.Worksheets(DE_INFO)
    Set Targets = CreateObject("Scripting.Dictionary")
    Targets.CompareMode = vbTextCompare

    LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row

    For CurrentRow = 1 To LastRow

        GroupCode = UCase$(DE_Text( _
            ws.Cells(CurrentRow, "K").Value2))

        If DE_IsGroupCode(GroupCode) Then

            FlightsText = DE_Text( _
                ws.Cells(CurrentRow, "L").Value2)
            LayoversText = DE_Text( _
                ws.Cells(CurrentRow, "M").Value2)

            If Not DE_IsDigits(FlightsText) _
                Or Not DE_IsDigits(LayoversText) Then
                ErrorResults.Add _
                    "مستهدف المجموعة " & GroupCode & _
                    " غير صحيح في INFO!K:M."
                Exit Function
            End If

            Set Target = DE_NewRecord()
            Target.Add "Flights", CLng(FlightsText)
            Target.Add "Layovers", CLng(LayoversText)

            If Targets.Exists(GroupCode) Then
                Targets.Remove GroupCode
            End If
            Targets.Add GroupCode, Target

        End If

    Next CurrentRow

    If Targets.Count <> 4 Then
        ErrorResults.Add _
            "تعذر قراءة مستهدفات المجموعات الأربع من INFO!K:M."
        Exit Function
    End If

    Set DE_ReadGroupTargets = Targets

End Function

'==========================================================
' تحليل كل مرة تشغيل وإضافة تفاصيلها اللازمة للتوزيع
'==========================================================
Private Function DE_InitializeRunPlans( _
    ByRef ErrorResults As Collection, _
    ByRef PassedResults As Collection) As Boolean

    Dim ws As Worksheet
    Dim SourceRun As Object
    Dim RunPlan As Object
    Dim SortedRuns() As Object
    Dim RunCount As Long
    Dim Index As Long
    Dim SwapIndex As Long
    Dim TempRun As Object

    Set ws = ThisWorkbook.Worksheets(DE_PAIRING)
    RunCount = DR_PairingRuns.Count

    If RunCount = 0 Then
        ErrorResults.Add "لا توجد مرات تشغيل جاهزة للتوزيع."
        Exit Function
    End If

    ReDim SortedRuns(1 To RunCount)

    For Index = 1 To RunCount
        Set SourceRun = DR_PairingRuns(Index)
        Set RunPlan = DE_BuildRunPlan( _
            ws, SourceRun, ErrorResults)

        If RunPlan Is Nothing Then Exit Function
        Set SortedRuns(Index) = RunPlan
        PassedResults.Add "[تصنيف البيرنق] " & _
            CStr(RunPlan("PairingNumber")) & " بتاريخ " & _
            Format$(CDate(RunPlan("OperatingDate")), "dd/mm/yyyy") & _
            ": " & CStr(RunPlan("LayoverEvidence"))
    Next Index

    ' ترتيب زمني ثابت؛ وعند تساوي التاريخ يرتب برقم البيرنق.
    For Index = 1 To RunCount - 1
        For SwapIndex = Index + 1 To RunCount
            If DE_RunComesAfter( _
                SortedRuns(Index), SortedRuns(SwapIndex)) Then
                Set TempRun = SortedRuns(Index)
                Set SortedRuns(Index) = SortedRuns(SwapIndex)
                Set SortedRuns(SwapIndex) = TempRun
            End If
        Next SwapIndex
    Next Index

    For Index = 1 To RunCount
        SortedRuns(Index)("RunIndex") = Index
        DE_RunPlans.Add SortedRuns(Index)
    Next Index

    DE_InitializeRunPlans = True

End Function

Private Function DE_BuildRunPlan( _
    ByVal ws As Worksheet, _
    ByVal SourceRun As Object, _
    ByRef ErrorResults As Collection) As Object

    Dim StartRow As Long
    Dim CreditRow As Long
    Dim StartColumn As Long
    Dim EndColumn As Long
    Dim DetailRow As Long
    Dim FirstFlightRow As Long
    Dim LastFlightRow As Long
    Dim LayoverRow As Long
    Dim HotelName As String
    Dim CandidateHotel As String
    Dim HotelColumn As Long
    Dim DurationText As String
    Dim FlightDayCode As String
    Dim FirstDayCode As String
    Dim LastDayCode As String
    Dim StationCode As String
    Dim LayoverStation As String
    Dim LayoverClass As String
    Dim LayoverHours As Double
    Dim TripType As String
    Dim RequiredCrew As Long
    Dim StartClock As Double
    Dim EndClock As Double
    Dim DayOffset As Long
    Dim OperatingDate As Date
    Dim StartDateTime As Date
    Dim EndDateTime As Date
    Dim RunPlan As Object
    Dim AssignedEmployees As Collection

    StartRow = CLng(SourceRun("StartRow"))
    CreditRow = CLng(SourceRun("CreditRow"))
    StartColumn = CLng(SourceRun("StartColumn"))
    EndColumn = CLng(SourceRun("EndColumn"))
    FirstDayCode = UCase$(CStr(SourceRun("DayCode")))
    OperatingDate = CDate(CDbl(SourceRun("OperatingDate")))

    For DetailRow = StartRow + 2 To CreditRow - 1

        FlightDayCode = UCase$(DE_Text( _
            ws.Cells(DetailRow, StartColumn).Value2))

        If DE_IsDayCode(FlightDayCode) Then
            If FirstFlightRow = 0 Then FirstFlightRow = DetailRow
            LastFlightRow = DetailRow
        ElseIf DE_RowHasData( _
            ws, DetailRow, StartColumn, EndColumn) Then

            ' اسم الفندق يقع قبل عمودي محطة المبيت ومدته.
            ' تجاهل الصفوف الفارغة والملاحظات التي لا تحمل بيانات مبيت.
            CandidateHotel = vbNullString
            For HotelColumn = StartColumn To StartColumn + 7
                If Not IsError(ws.Cells(DetailRow, HotelColumn).Value2) Then
                    DurationText = DE_Text( _
                        ws.Cells(DetailRow, HotelColumn).Value2)
                    If DurationText <> vbNullString Then
                        If Not IsNumeric(DurationText) Then
                            If CandidateHotel <> vbNullString Then _
                                CandidateHotel = CandidateHotel & " "
                            CandidateHotel = CandidateHotel & DurationText
                        End If
                    End If
                End If
            Next HotelColumn

            DurationText = DE_Text( _
                ws.Cells(DetailRow, StartColumn + 9).Value2)

            ' لا يكفي اختلاف يوم العودة أو اسم المحطة لإثبات المبيت.
            If CandidateHotel <> vbNullString Or _
                DurationText <> vbNullString Then
                If CandidateHotel = vbNullString Then
                    ErrorResults.Add "مدة مبيت دون اسم فندق في البيرنق " & _
                        CStr(SourceRun("PairingNumber")) & _
                        "، صف " & CStr(DetailRow) & ". راجع بيانات البيرنق."
                    Exit Function
                End If
                If LayoverRow > 0 Then
                    ErrorResults.Add "يوجد أكثر من سطر مبيت في البيرنق " & _
                        CStr(SourceRun("PairingNumber")) & _
                        ". يلزم دعم المبيت المتعدد قبل توزيعه."
                    Exit Function
                End If
                LayoverRow = DetailRow
                HotelName = CandidateHotel
            End If
        End If

    Next DetailRow

    If FirstFlightRow = 0 Or LastFlightRow = 0 Then
        ErrorResults.Add _
            "تعذر قراءة قطاعات البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    StationCode = UCase$(DE_Text( _
        ws.Cells(FirstFlightRow, StartColumn + 6).Value2))

    If StationCode = "JED" Then
        StationCode = UCase$(DE_Text( _
            ws.Cells(LastFlightRow, StartColumn + 3).Value2))
    End If

    If LayoverRow > 0 Then
        LayoverStation = UCase$(DE_Text( _
            ws.Cells(LayoverRow, StartColumn + 8).Value2))
        If LayoverStation <> vbNullString Then
            StationCode = LayoverStation
        End If

        If Not DE_ReadDurationHours( _
            ws.Cells(LayoverRow, StartColumn + 9), _
            LayoverHours) Then
            ErrorResults.Add _
                "تعذر قراءة ساعات المبيت للبيرنق " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours <= 0# Or LayoverStation = vbNullString Then
            ErrorResults.Add _
                "بيانات المبيت ناقصة أو مدته غير موجبة للبيرنق " & _
                CStr(SourceRun("PairingNumber")) & "."
            Exit Function
        End If

        If LayoverHours >= 50# Then
            LayoverClass = "LONG"
        Else
            LayoverClass = "SHORT"
        End If
    End If

    If Not DR_CrewRequirements.Exists(StationCode) Then
        ErrorResults.Add _
            "المحطة " & StationCode & _
            " للبيرنق " & CStr(SourceRun("PairingNumber")) & _
            " غير موجودة ضمن متطلبات INFO!G:I."
        Exit Function
    End If

    RequiredCrew = CLng( _
        DR_CrewRequirements(StationCode)("CrewCount"))
    TripType = CStr( _
        DR_CrewRequirements(StationCode)("TripType"))

    ' عدد أفراد المبيت مستقل عن عدد أفراد الراوندترب للمحطة.
    If LayoverRow > 0 Then RequiredCrew = 2

    ' استثناء عدد الطاقم اختياري ومقيد بالشهر والسنة والبيرنق.
    If Not DE_ResolveCrewOverride( _
        CStr(SourceRun("PairingNumber")), OperatingDate, _
        RequiredCrew, ErrorResults) Then Exit Function


    If Not DE_ReadClock( _
        ws.Cells(FirstFlightRow, StartColumn + 4), StartClock) Then
        ErrorResults.Add _
            "تعذر قراءة وقت بداية البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    If Not DE_ReadClock( _
        ws.Cells(LastFlightRow, StartColumn + 7), EndClock) Then
        ErrorResults.Add _
            "تعذر قراءة وقت نهاية البيرنق " & _
            CStr(SourceRun("PairingNumber")) & "."
        Exit Function
    End If

    LastDayCode = UCase$(DE_Text( _
        ws.Cells(LastFlightRow, StartColumn).Value2))

    DayOffset = DE_DayOffset(FirstDayCode, LastDayCode)

    If DayOffset = 0 Then
        If EndClock < DE_CellClockValue( _
            ws.Cells(LastFlightRow, StartColumn + 4)) Then
            DayOffset = 1
        End If
    End If

    StartDateTime = OperatingDate + StartClock
    EndDateTime = OperatingDate + DayOffset + EndClock

    If EndDateTime <= StartDateTime Then
        EndDateTime = EndDateTime + 1
    End If

    Set RunPlan = DE_NewRecord()
    RunPlan.Add "RunIndex", 0&
    RunPlan.Add "PairingNumber", _
        CStr(SourceRun("PairingNumber"))
    ' رحلات حلب ALP تُغطى حصريًا بواسطة المؤهلين لرحلات ناس،
    ' حتى لو كانت واقعة قبل عنوان FLY NAS في شيت PAIRING.
    If StationCode = "ALP" Then
        RunPlan.Add "CarrierCode", "NAS"
    Else
        RunPlan.Add "CarrierCode", _
            UCase$(CStr(SourceRun("CarrierCode")))
    End If
    RunPlan.Add "OperatingDate", CDbl(OperatingDate)
    RunPlan.Add "StartDateTime", CDbl(StartDateTime)
    RunPlan.Add "EndDateTime", CDbl(EndDateTime)
    RunPlan.Add "ReturnDayOffset", DayOffset
    RunPlan.Add "StationCode", StationCode
    RunPlan.Add "TripType", TripType
    RunPlan.Add "IsLayover", (LayoverRow > 0)
    RunPlan.Add "HotelName", HotelName
    If LayoverRow > 0 Then
        RunPlan.Add "LayoverEvidence", _
            "مبيت؛ الفندق: " & HotelName & _
            "؛ المدة بالساعات: " & Format$(LayoverHours, "0.00") & _
            "؛ صف المصدر: " & CStr(LayoverRow)
    Else
        RunPlan.Add "LayoverEvidence", _
            "راوندترب؛ لا يوجد سطر فندق ومدة مبيت في البيرنق."
    End If
    RunPlan.Add "LayoverHours", LayoverHours
    RunPlan.Add "LayoverClass", LayoverClass
    RunPlan.Add "RequiredCrew", RequiredCrew
    RunPlan.Add "StartRow", StartRow
    RunPlan.Add "CreditRow", CreditRow
    RunPlan.Add "StartColumn", StartColumn
    Set AssignedEmployees = New Collection
    RunPlan.Add "AssignedEmployees", AssignedEmployees
    RunPlan.Add "LeaderEmployee", vbNullString
    RunPlan.Add "OfficeCount", 0&

    Set DE_BuildRunPlan = RunPlan

End Function

'==========================================================
' تثبيت الارتباطات باعتبارها فترات منع
'==========================================================
Private Function DE_RegisterEvents( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EventRecord As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim StartDate As Date
    Dim EndDate As Date
    Dim MonthStart As Date
    Dim MonthEnd As Date
    Dim ActiveStart As Date
    Dim ActiveEnd As Date
    Dim DateCursor As Date
    Dim DateKey As String
    Dim EventDates As Object

    MonthStart = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth, 1)
    MonthEnd = DateSerial( _
        DR_ActiveYear, DR_ActiveMonth + 1, 0)

    For Each EventRecord In DR_Events

        EmployeeNumber = CStr(EventRecord("EmployeeNumber"))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "موظف الارتباط رقم " & EmployeeNumber & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)
        StartDate = CDate(CDbl(EventRecord("StartDate")))
        EndDate = CDate(CDbl(EventRecord("EndDate"))) + 1

        DE_AddBusyPeriod State, StartDate, EndDate, _
            DE_TYPE_EVENT, False, _
            CStr(EventRecord("EventName"))

        ActiveStart = StartDate
        If ActiveStart < MonthStart Then ActiveStart = MonthStart

        ActiveEnd = EndDate - 1
        If ActiveEnd > MonthEnd Then ActiveEnd = MonthEnd

        If ActiveStart <= ActiveEnd Then
            Set EventDates = State("EventDates")

            If CLng(CDbl(ActiveEnd) - _
                CDbl(ActiveStart) + 1) > _
                CLng(State("LongestEventDays")) Then

                State("LongestEventDays") = _
                    CLng(CDbl(ActiveEnd) - _
                    CDbl(ActiveStart) + 1)
            End If

            For DateCursor = ActiveStart To ActiveEnd
                DateKey = CStr(CLng(CDbl(DateCursor)))
                If Not EventDates.Exists(DateKey) Then
                    EventDates.Add DateKey, True
                End If
            Next DateCursor
        End If

    Next EventRecord

    If Not DE_ApplyEventTargetAdjustments( _
        ErrorResults, WarningResults) Then Exit Function

    DE_RegisterEvents = True

End Function

'==========================================================
' تحديد راحة بداية الشهر من تصنيف آخر رحلة في الشهر السابق
'==========================================================
Private Function DE_GetPreviousMonthRestDays( _
    ByVal EmployeeRecord As Object, _
    ByRef PreviousTripType As String) As Long

    Dim StationCode As String
    Dim SeparatorPosition As Long
    Dim Requirement As Object

    PreviousTripType = vbNullString

    If EmployeeRecord.Exists("PreviousLastStation") Then
        StationCode = UCase$(Trim$(CStr( _
            EmployeeRecord("PreviousLastStation"))))
    End If

    ' قد تُحفظ الرحلة السابقة مثل DAC-MED أو DAC MED؛
    ' التصنيف يعتمد على محطة الذهاب الأساسية.
    StationCode = Replace$(StationCode, "/", "-")
    StationCode = Replace$(StationCode, " ", "-")

    Do While InStr(1, StationCode, "--", vbBinaryCompare) > 0
        StationCode = Replace$(StationCode, "--", "-")
    Loop

    SeparatorPosition = InStr(1, StationCode, "-", vbTextCompare)
    If SeparatorPosition > 1 Then
        StationCode = Left$(StationCode, SeparatorPosition - 1)
    ElseIf Len(StationCode) = 6 Then
        StationCode = Left$(StationCode, 3)
    End If

    If Not DR_CrewRequirements Is Nothing Then
        If DR_CrewRequirements.Exists(StationCode) Then
            Set Requirement = DR_CrewRequirements(StationCode)
            PreviousTripType = UCase$(Trim$(CStr( _
                Requirement("TripType"))))
        End If
    End If

    Select Case PreviousTripType
        Case "LAY"
            DE_GetPreviousMonthRestDays = 3
        Case "LNG", "MED"
            DE_GetPreviousMonthRestDays = 2
        Case "SHT"
            DE_GetPreviousMonthRestDays = 1
        Case Else
            ' إذا تعذر التعرف على التصنيف، نستخدم يومين كحل آمن
            ' ونسجل تنبيهًا واضحًا بدل إيقاف خطة التوزيع بالكامل.
            DE_GetPreviousMonthRestDays = 2
            PreviousTripType = "غير مصنفة"

            If Not DE_Warnings Is Nothing Then
                DE_Warnings.Add _
                    "تعذر تحديد تصنيف آخر رحلة للموظف " & _
                    CStr(EmployeeRecord("EmployeeNumber")) & _
                    " من المحطة [" & StationCode & _
                    "]. تم اعتماد يومين راحة بصورة احتياطية."
            End If
    End Select

End Function

'==========================================================
' صياغة كلمة يوم في وصف فترة الراحة
'==========================================================
Private Function DE_ArabicDayPhrase( _
    ByVal DayCount As Long) As String

    Select Case DayCount
        Case 1
            DE_ArabicDayPhrase = "يوم واحد"
        Case 2
            DE_ArabicDayPhrase = "يومين"
        Case Else
            DE_ArabicDayPhrase = CStr(DayCount) & " أيام"
    End Select

End Function

'==========================================================
' تعديل مستهدف الموظف حسب مجموع أيام ارتباطاته في الشهر
'
' المدد المعتمدة:
'   10 أيام: خصم رحلتين.
'   15 يومًا: نصف المستهدف مع التقريب للأعلى.
'   30 يومًا: منع كامل من الرحلات والمبيت والاحتياط.
'==========================================================
Private Function DE_ApplyEventTargetAdjustments( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim EventDayCount As Long
    Dim OriginalFlights As Long
    Dim AdjustedFlights As Long
    Dim OriginalLayovers As Long
    Dim AdjustedLayovers As Long
    Dim DaysInMonth As Long
    Dim CarryoverTarget As Long

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DE_EmployeeStates.Keys

        Set State = DE_EmployeeStates(EmployeeKey)
        EventDayCount = CLng(State("LongestEventDays"))
        OriginalFlights = CLng(State("OriginalFlightTarget"))
        OriginalLayovers = CLng(State("OriginalLayoverTarget"))
        AdjustedFlights = OriginalFlights
        CarryoverTarget = CLng(State("CarryoverTarget"))
        State("EventDayCount") = State("EventDates").Count
        State("TargetAdjustmentReason") = vbNullString
        State("IsNoFlightMonth") = False

        Select Case EventDayCount

            Case 0 To 9
                ' يمنع الارتباط أيامه فقط، ولا يخفض المستهدف.

            Case 10
                AdjustedFlights = OriginalFlights - 2
                If AdjustedFlights < 0 Then AdjustedFlights = 0
                State("TargetAdjustmentReason") = _
                    "ارتباط 10 أيام: خصم رحلتين"

            Case 15
                AdjustedFlights = (OriginalFlights + 1) \ 2
                State("TargetAdjustmentReason") = _
                    "ارتباط 15 يومًا: نصف المستهدف"

            Case Is >= 30
                AdjustedFlights = 0
                State("IsNoFlightMonth") = True
                State("TargetAdjustmentReason") = _
                    "ارتباط 30 يومًا: منع شهري كامل"

            Case Else
                WarningResults.Add _
                    "مدة ارتباط الموظف " & _
                    CStr(State("EmployeeNumber")) & _
                    " داخل الشهر هي " & _
                    CStr(EventDayCount) & _
                    " يومًا، وهي ليست من المدد المعتمدة " & _
                    "10 أو 15 أو 30 يومًا؛ مُنعت أيام " & _
                    "الارتباط دون تخفيض المستهدف."

        End Select

        If OriginalFlights > 0 _
            And OriginalLayovers > 0 _
            And AdjustedFlights > 0 Then

            AdjustedLayovers = _
                ((OriginalLayovers * AdjustedFlights) + _
                OriginalFlights - 1) \ OriginalFlights
        Else
            AdjustedLayovers = 0
        End If

        If AdjustedLayovers > AdjustedFlights Then
            AdjustedLayovers = AdjustedFlights
        End If

        ' الإداري والموقوف وموظف الاستاندباي لا يدخلون في
        ' التوزيع الآلي ولا الاحتياط. رحلات الإداري تُثبت فقط
        ' من OFFICE_DATA، وموظف SB يُسجل له تناوبه الشهري فقط.
        If CBool(State("IsOffice")) _
            Or CBool(State("IsHold")) _
            Or CBool(State("IsStandby")) Then

            AdjustedFlights = 0
            AdjustedLayovers = 0
            CarryoverTarget = 0
        End If

        State("NormalFlightTarget") = AdjustedFlights
        State("FlightTarget") = _
            AdjustedFlights + CarryoverTarget
        State("LayoverTarget") = AdjustedLayovers

        If AdjustedLayovers > 0 Then
            State("LongLayoverTarget") = 1
            State("ShortLayoverTarget") = _
                AdjustedLayovers - 1
        Else
            State("LongLayoverTarget") = 0
            State("ShortLayoverTarget") = 0
        End If

        DE_SetEmployeeCarrierTargets State

    Next EmployeeKey

    DE_ApplyEventTargetAdjustments = True

End Function

'==========================================================
' تقسيم مستهدف الموظف بين السعودية والناقل المتخصص
'==========================================================
Private Sub DE_SetEmployeeCarrierTargets( _
    ByVal State As Object)

    Dim TotalTarget As Long
    Dim SaudiaTarget As Long
    Dim SpecialTarget As Long
    Dim SpecialCarrier As String
    Dim SaudiaDemand As Long
    Dim SpecialDemand As Long

    ' يقسم المستهدف العادي أولًا، ثم تضاف مطالبات الشهر
    ' السابق إلى ناقلها المحدد دون تغيير توازن الرحلات العادية.
    TotalTarget = CLng(State("NormalFlightTarget"))

    Select Case UCase$(CStr(State("CarrierProfile")))
        Case "SAUDIA_NAS"
            SpecialCarrier = "NAS"
        Case "SAUDIA_ADEAL"
            SpecialCarrier = "ADEAL"
        Case Else
            SpecialCarrier = vbNullString
    End Select

    If SpecialCarrier = vbNullString Then
        SaudiaTarget = TotalTarget
        SpecialTarget = 0
    Else
        SaudiaTarget = TotalTarget \ 2
        SpecialTarget = TotalTarget \ 2

        If TotalTarget Mod 2 <> 0 Then
            SaudiaDemand = DE_CarrierRunCount("SAUDIA")
            SpecialDemand = DE_CarrierRunCount(SpecialCarrier)

            If SaudiaDemand > SpecialDemand Then
                SaudiaTarget = SaudiaTarget + 1
            Else
                SpecialTarget = SpecialTarget + 1
            End If
        End If
    End If

    State("SaudiaTarget") = SaudiaTarget
    State("SpecialCarrierTarget") = SpecialTarget
    State("SpecialCarrierCode") = SpecialCarrier

    State("SaudiaTarget") = _
        CLng(State("SaudiaTarget")) + _
        CLng(State("CarryoverSaudiaTarget"))

    State("SpecialCarrierTarget") = _
        CLng(State("SpecialCarrierTarget")) + _
        CLng(State("CarryoverSpecialTarget"))

End Sub

Private Function DE_CarrierRunCount( _
    ByVal CarrierCode As String) As Long

    Dim RunPlan As Object

    For Each RunPlan In DE_RunPlans
        If StrComp(CStr(RunPlan("CarrierCode")), _
            CarrierCode, vbTextCompare) = 0 Then
            DE_CarrierRunCount = _
                DE_CarrierRunCount + 1
        End If
    Next RunPlan

End Function

'==========================================================
' إنشاء تناوب SB-1 / SB-2 / SB-3 طوال الشهر
'==========================================================
Private Function DE_RegisterStandby( _
    ByRef ErrorResults As Collection) As Boolean

    Dim EmployeeKey As Variant
    Dim State As Object
    Dim SBGroup As String
    Dim GroupNumber As Long
    Dim DayNumber As Long
    Dim DaysInMonth As Long
    Dim DutyDate As Date

    DaysInMonth = GregDaysInMonth( _
        DR_ActiveYear, DR_ActiveMonth)

    For Each EmployeeKey In DR_SBByEmployee.Keys

        If Not DE_EmployeeStates.Exists(CStr(EmployeeKey)) Then
            ErrorResults.Add _
                "موظف الاستاندباي رقم " & CStr(EmployeeKey) & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set State = DE_EmployeeStates(CStr(EmployeeKey))

        State("IsStandby") = True
        SBGroup = UCase$(CStr(DR_SBByEmployee(EmployeeKey)))

        Select Case SBGroup
            Case "SB-1": GroupNumber = 1
            Case "SB-2": GroupNumber = 2
            Case "SB-3": GroupNumber = 3
            Case Else
                ErrorResults.Add _
                    "رمز الاستاندباي " & SBGroup & _
                    " غير صحيح للموظف " & CStr(EmployeeKey) & "."
                Exit Function
        End Select

        For DayNumber = GroupNumber To DaysInMonth Step 3
            DutyDate = DateSerial( _
                DR_ActiveYear, DR_ActiveMonth, DayNumber)

            DE_AddAssignment _
                State, Nothing, DE_TYPE_SB, SBGroup, _
                DutyDate, DutyDate + 1, False, False
        Next DayNumber

    Next EmployeeKey

    DE_RegisterStandby = True

End Function

'==========================================================
' تثبيت رحلات الإداريين قبل التوزيع الآلي
'==========================================================
Private Function DE_RegisterOfficeFlights( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim OfficeRecord As Object
    Dim RunPlan As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim PairingNumber As String
    Dim FlightDate As Date
    Dim IsLeader As Boolean
    Dim GroupCode As String
    Dim ExistingLeader As String
    Dim ExistingLeaderState As Object
    Dim ConflictDetails As String

    For Each OfficeRecord In DR_OfficeFlights

        EmployeeNumber = CStr(OfficeRecord("EmployeeNumber"))
        PairingNumber = CStr(OfficeRecord("PairingNumber"))
        FlightDate = CDate(CDbl(OfficeRecord("FlightDate")))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set RunPlan = DE_FindRun(PairingNumber, FlightDate)
        If RunPlan Is Nothing Then
            ErrorResults.Add _
                "تعذر إيجاد تشغيل البيرنق " & PairingNumber & _
                " بتاريخ " & Format$(FlightDate, "dd/mm/yyyy") & _
                " الخاص بالإداري " & EmployeeNumber & "."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)

        If CBool(State("IsNoFlightMonth")) Then
            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " لديه ارتباط مدته 30 يومًا، ولا يجوز " & _
                "تثبيت رحلة له خلال الشهر."
            Exit Function
        End If

        If Not DE_EmployeeCanCoverCarrier( _
            CStr(State("CarrierProfile")), _
            CStr(RunPlan("CarrierCode"))) Then

            ErrorResults.Add _
                "الإداري رقم " & EmployeeNumber & _
                " مؤهل بصفة " & CStr(State("CarrierProfile")) & _
                "، ولا يستطيع تغطية البيرنق " & PairingNumber & _
                " التابع للناقل " & _
                CStr(RunPlan("CarrierCode")) & "."
            Exit Function

        End If

        ConflictDetails = vbNullString

        If Not DE_IsAvailable( _
            State, CDate(RunPlan("StartDateTime")), _
            CDate(RunPlan("EndDateTime")), _
            ConflictDetails) Then

            ErrorResults.Add _
                "رحلة الإداري رقم " & EmployeeNumber & _
                "، البيرنق " & PairingNumber & _
                "، بتاريخ " & _
                Format$(FlightDate, "dd/mm/yyyy") & _
                " تتعارض مع فترة مسجلة مسبقًا." & _
                vbCrLf & _
                "الفترة الجديدة: " & _
                Format$(CDate(RunPlan("StartDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                " إلى " & _
                Format$(CDate(RunPlan("EndDateTime")), _
                    "dd/mm/yyyy hh:mm") & _
                vbCrLf & ConflictDetails

            Exit Function
        End If

        GroupCode = CStr(State("GroupCode"))
        ExistingLeader = CStr(RunPlan("LeaderEmployee"))
        IsLeader = False

        Select Case GroupCode
            Case "SSS", "SST"

                If ExistingLeader <> vbNullString Then
                    Set ExistingLeaderState = _
                        DE_EmployeeStates(ExistingLeader)

                    If CStr(ExistingLeaderState("GroupCode")) = "STS" Then
                        DE_DemoteLeader _
                            ExistingLeaderState, RunPlan
                        ExistingLeader = vbNullString
                    End If
                End If

                IsLeader = True

            Case "STS"
                IsLeader = (ExistingLeader = vbNullString)
        End Select

        If IsLeader Then
            If ExistingLeader <> vbNullString Then
                WarningResults.Add _
                    "البيرنق " & PairingNumber & _
                    " بتاريخ " & Format$(FlightDate, "dd/mm/yyyy") & _
                    " يحتوي أكثر من قائد إداري وفق الاختيار المعتمد."
            Else
                RunPlan("LeaderEmployee") = EmployeeNumber
            End If
        End If

        DE_AddAssignment _
            State, RunPlan, DE_TYPE_OFFICE, PairingNumber, _
            CDate(RunPlan("StartDateTime")), _
            CDate(RunPlan("EndDateTime")), _
            CBool(RunPlan("IsLayover")), IsLeader

        RunPlan("OfficeCount") = _
            CLng(RunPlan("OfficeCount")) + 1

        If RunPlan("AssignedEmployees").Count > _
            CLng(RunPlan("RequiredCrew")) Then
            WarningResults.Add _
                "عدد الإداريين المختارين على البيرنق " & _
                PairingNumber & " تجاوز عدد الطاقم المطلوب."
        End If

    Next OfficeRecord

    DE_RegisterOfficeFlights = True

End Function

Private Sub DE_DemoteLeader( _
    ByVal State As Object, _
    ByVal RunPlan As Object)

    Dim Assignment As Object

    For Each Assignment In State("Assignments")
        If CLng(Assignment("RunIndex")) = _
            CLng(RunPlan("RunIndex")) _
            And CBool(Assignment("IsLeader")) Then
            Assignment("IsLeader") = False
            If CLng(State("LeaderCount")) > 0 Then
                State("LeaderCount") = _
                    CLng(State("LeaderCount")) - 1
            End If
            Exit For
        End If
    Next Assignment

    RunPlan("LeaderEmployee") = vbNullString

End Sub

'==========================================================
' توزيع الرحلات المطالب بها من الشهر السابق
'
' كل سجل في CARRYOVER_FLIGHTS يمثل رحلة إضافية واحدة فوق
' المستهدف العادي. تُختار له رحلة فعلية تطابق الناقل ونوع
' الرحلة، مع المحافظة على الأهلية والتعارض وراحة 24 ساعة.
' لا تدخل رحلة المبيت المطالب بها ضمن حصة المبيت العادية.
'==========================================================
Private Function DE_DistributeCarryoverFlights( _
    ByRef ErrorResults As Collection, _
    ByRef WarningResults As Collection) As Boolean

    Dim CarryoverRecord As Object
    Dim RunPlan As Object
    Dim State As Object
    Dim EmployeeNumber As String
    Dim IsLeader As Boolean

    For Each CarryoverRecord In DR_CarryoverFlights

        EmployeeNumber = CStr( _
            CarryoverRecord("EmployeeNumber"))

        If Not DE_EmployeeStates.Exists(EmployeeNumber) Then
            ErrorResults.Add _
                "موظف الرحلة المطالب بها رقم " & _
                EmployeeNumber & _
                " غير موجود ضمن حالات التوزيع."
            Exit Function
        End If

        Set State = DE_EmployeeStates(EmployeeNumber)

        If CBool(State("IsHold")) _
            Or CBool(State("IsOffice")) _
            Or CBool(State("IsStandby")) _
            Or CBool(State("IsNoFlightMonth")) Then

            ErrorResults.Add _
                "تعذر توزيع الرحلة المطالب بها للموظف " & _
                EmployeeNumber & "؛ حالته الحالية لا تسمح " & _
                "بدخوله في التوزيع الآلي."
            Exit Function
        End If

        Set RunPlan = DE_SelectCarryoverRun( _
            State, CarryoverRecord, IsLeader)

        If RunPlan Is Nothing Then
            ErrorResults.Add _
                "تعذر تغطية الرحلة المطالب بها للموظف " & _
                EmployeeNumber & " - " & _
                CStr(State("EmployeeName")) & _


### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: الجزء الثاني بدأ من الموضع الصحيح، لكنه **انقطع أيضًا** داخل `DE_DistributeReserveDays`. Auto Claude: حدّد آخر سطر كامل وصل الآن، واطلب من المستخدم أن يكمل من عدة أسطر قبله فقط، دون بدء تحليل الكود حتى تصل نهاية الموديول.

### Auto Claude (Claude Sonnet 5.5)

وصل الجزء الثاني من الموضع الصحيح، وهو موصول بالأول: القيمتان `72#` و`48#` ظهرتا كاملتين، وبعدهما بدأت `DistributionEngine_BuildPlan`.

**آخر ما وصل:** انقطع الجزء داخل هذا الشرط:

```vb
    CurrentStep = "توزيع أيام الاحتياط"
    If Not DE_DistributeReserveDays( _
        ErrorResults, WarningResults) Then
        ValidationPassed = False
```

السطر الأخير الكامل هو `ValidationPassed = False`، وقد يكون ما بعده سطر `End If` ناقصًا. وما وصلني يكفي لفهم بداية البناء فقط، أما الإجراءات الفعلية (المبيت، الراوندترب، الإصلاح) فلم تصل بعد، فلن أحلل شيئًا الآن.

**المطلوب:** أرسل الجزء الثالث، وابدأه من نحو 5 أسطر قبل `CurrentStep = "توزيع أيام الاحتياط"` (أي من `If Not DE_RegisterOfficeFlights...`)، وكمّل حتى النهاية.

**نصيحة:** لا تنتظر أن تنقطع الرسائل؛ قسّم الباقي من البداية إلى رسائل أقصر (نحو 150 سطرًا لكل رسالة)، وابدأ كل واحدة بـ`--- جزء 3 ---` وهكذا، واختم الأخيرة بـ`--- نهاية الموديول ---`.

### Participant

اين توقف الكود

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: أجب المستخدم مباشرة وباختصار شديد: اذكر **آخر سطر كامل وصل حرفيًا**، ثم السطر الذي يُفضّل أن يبدأ منه الإرسال التالي حتى نضمن الاتصال.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

آخر سطر كامل وصل هو:

```vb
ValidationPassed = False
```

ولضمان الاتصال، ابدأ الإرسال القادم من هذا السطر:

```vb
CurrentStep = "توزيع أيام الاحتياط"
```

ثم أكمل من بعده.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: ابدأ الجزء القادم من `CurrentStep = "توزيع أيام الاحتياط"` ثم أكمل الكود من هناك.

**Agreed**
- آخر سطر كامل وصل هو `ValidationPassed = False`.
- إعادة بضعة أسطر مكررة لا تسبب مشكلة.
- استمر حتى نهاية الموديول.

### Participant

على الوضع هذا بنتاخر كثير لانه ياخذ جزء جزء وهذا جدأ سيئ ارد طريقه لتحميل الكود كاملا هنا .. كيف يكون ذلك

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Auto Claude: نحتاج حلًا عمليًا نهائيًا بدل التقسيم؛ بما أن `.bas` ظهرت عليه علامة المنع، اشرح للمستخدم **طريقة واحدة مضمونة لتحويل/حفظ الكود في ملف يقبل الرفع هنا** مع الحفاظ على الكود كاملًا، خطوة بخطوة وبأقل تعقيد.

### Auto Claude (Claude Sonnet 5.5)

الطريقة المضمونة: **حوّل الكود إلى ملف `.txt` وارفعه**، لأن علامة المنع ظهرت على `.bas` فقط على الأغلب بسبب نوع الملف، وأنا ما أقدر أؤكد أن واجهة هذه الغرفة تقبل أي مرفق أصلًا.

**الخطوات:**
1. صدّر الموديول: في VBA كليك يمين على الموديول ثم **Export File** واحفظه على سطح المكتب.
2. اضغط على الملف مرة واحدة، ثم **F2** وغيّر الامتداد من `.bas` إلى `.txt`، ووافق على التحذير.
3. اسحبه إلى مربع المحادثة أو استخدم أيقونة المرفق 📎 (إن وُجدت).

**إذا ظهر المنع أيضًا مع `.txt`:** فالغرفة لا تقبل مرفقات، وعندها لا يبقى إلا النسخ واللصق.

**لتقليل التأخير في النسخ واللصق:**
- افتح الملف بالمفكرة، واضغط **Ctrl+A** ثم **Ctrl+C**.
- الصق كل 300–400 سطر في رسالة، وأرسل الرسائل متتالية بدون انتظار ردنا، مع ترقيمها `1/4` و`2/4`.

وبما أنك أرسلت إلى الآن حتى `DE_DistributeReserveDays`، فالباقي هو كل ما بعد هذا الموضع.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: راجع اقتراح تغيير امتداد `.bas` إلى `.txt` من زاوية **الحفاظ على العربية والترميز**؛ هل إعادة التسمية وحدها آمنة، أم الأفضل فتحه وحفظ نسخة TXT بترميز محدد قبل رفعه؟ أعط الطريقة الأقل مخاطرة للمستخدم.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

الأفضل **لا تغيّر امتداد `.bas` فقط**؛ لأننا رأينا بالفعل أن التعليقات العربية وصلت مشوهة. نريد ملفًا نصيًا جديدًا بترميز يحفظ العربية.

اعمل التالي:

1. افتح **المفكرة Notepad**.
2. من VBA افتح الموديول المطلوب واضغط داخل الكود:
   **Ctrl+A** ثم **Ctrl+C**.
3. الصقه في المفكرة بـ **Ctrl+V**.
4. من المفكرة اختر **File → Save As**.
5. سمّه مثلًا:
   `R19.txt`
6. عند **Encoding / الترميز** اختر **UTF-8**.
7. اضغط **Save**.
8. الآن اسحب ملف `R19.txt` نفسه إلى هذه المحادثة وأرسله.

هذه أفضل من مجرد إعادة تسمية `.bas` إلى `.txt`؛ لأنها تعطينا **الكود كاملًا والتعليقات العربية سليمة** في ملف واحد.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: أفضل طريقة هي نسخ الموديول كاملًا من VBA إلى المفكرة وحفظه باسم `R19.txt` بترميز **UTF-8** ثم رفع الملف هنا.

**Agreed**
- استخدم `Ctrl+A` ثم `Ctrl+C` داخل الموديول، والصقه في Notepad.
- احفظه كملف `.txt` واختر **UTF-8**.
- ارفع ملف `R19.txt` كاملًا بدل تقسيم الكود على رسائل.

**Why**
- بهذه الطريقة يصل الكود كاملًا في ملف واحد.
- UTF-8 يحافظ على التعليقات العربية بدل ظهورها مشوهة.

### Participant

اذا رفعته في هذه المحادثه تنفتح لي موقع ويب فيه الكود

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: سحب ملف `.txt` يبدو أنه يُفتح في المتصفح بدل أن يُرفق بالمحادثة. Auto Claude: أعط المستخدم طريقة رفع الملف **من داخل واجهة ChatGPT نفسها** دون السحب والإفلات، بخطوات واضحة ومختصرة.

### Auto Claude (Claude Sonnet 5.5)

الظاهر أن المتصفح فتح الملف بدل إرفاقه، لأن الإسقاط وقع خارج مربع الكتابة، أو لأن هذه الغرفة لا تدعم المرفقات أصلًا. وما أعرف شكل واجهتها بالضبط، فهذا تخمين.

**جرّب بدون سحب وإفلات:**
1. اضغط داخل مربع الكتابة نفسه.
2. ابحث بجانبه عن **+** أو أيقونة **📎**، واضغطها.
3. اختر **Add photos & files** (أو ما يشبهها)، ثم حدد `R19.txt` من سطح المكتب.
4. انتظر ظهور اسم الملف في المربع، ثم أرسل.

**إذا ما وجدت أي أيقونة بجانب المربع:** فالمرفقات غير متاحة في هذه الغرفة، ولا يبقى إلا النسخ واللصق.

**أسرع طريقة للنسخ واللصق (بدون انتظارنا):**
- افتح `R19.txt` في المفكرة، وانسخ **من سطر `CurrentStep = "توزيع أيام الاحتياط"` إلى النهاية** فقط، لأن ما قبله وصلنا.
- الصق كل نحو 150 سطرًا في رسالة، وأرسل الرسائل متتالية، مرقمة `3/N` و`4/N`.
- اختم الأخيرة بـ`--- نهاية الموديول ---`.

وقل لنا وش تشوف بجانب مربع الكتابة، حتى أعطيك الخطوة المناسبة لواجهتك بدل التخمين.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: هناك احتمال أن المشكلة ليست دعم المرفقات بل **طريقة فتح الملف من Windows/المتصفح**؛ أعط المستخدم اختبارًا واحدًا بسيطًا يحدد هل زر `+` في هذه المحادثة يسمح باختيار `R19.txt` وإرفاقه فعلًا.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

خلنا نجرب **اختبارًا واحدًا فقط** بدون سحب الملف:

1. انظر بجانب مربع الكتابة في هذه المحادثة.
2. اضغط علامة **`+`** الموجودة بجانب مربع الرسالة.
3. إذا ظهر لك خيار **إضافة صور وملفات / Add photos & files** اضغط عليه.
4. اختر ملف **`R19.txt`** من جهازك.

إذا ظهر **اسم `R19.txt` داخل مربع الرسالة كمرفق** قبل الإرسال، فالأمور تمام — اضغط إرسال.

إذا لم يظهر خيار إضافة الملفات عند الضغط على `+`، قل لي **ما الخيارات التي ظهرت لك بالضبط** وسأعطيك الخطوة المناسبة مباشرة.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

**Conclusion**: نحتاج نتيجة اختبار زر `+` قبل تجربة أي طريقة أخرى.

**Agreed**
- اضغط `+` بجانب مربع الرسالة واختر **إضافة صور وملفات** إن ظهر.
- اختر `R19.txt` وتأكد هل يظهر اسمه كمرفق داخل الرسالة.

**What would change it**
- إذا لم يظهر خيار الملفات أو فتح الملف كصفحة ويب، أخبرنا بما ظهر تحديدًا لننتقل للطريقة المناسبة.

### Participant

لا يوجد + بجانب مكان ارسال الرسائل

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: واجهة المستخدم لا تعرض زر `+`، لذلك نحتاج معرفة هل توجد **أيقونة مشبك/ملف أو زر آخر للإرفاق** بدل افتراض أن المرفقات غير مدعومة. Auto Claude: أعطه طريقة سريعة لتحديد زر الإرفاق في واجهته الحالية.

### Auto Claude (Claude Sonnet 5.5)

بما أنه ما في زر إرفاق ظاهر، فالأرجح أن هذه الغرفة **ما تدعم المرفقات**، وكون الملف فُتح كصفحة ويب لما أسقطته يدعم هذا. لكنه استنتاج وليس تأكيدًا، فما أعرف واجهتك.

**فحص سريع (نصف دقيقة):**
- مرّر الماوس على الأيقونات بجانب مربع الكتابة (يمين ويسار وتحته) وشوف هل فيها مشبك 📎 أو أيقونة ملف.
- جرّب **Ctrl+V** بعد نسخ الملف نفسه من المجلد (ليس نصه). إذا ظهر كمرفق فالرفع مدعوم.
- إذا ما ظهر شيء، اعتبر الرفع غير متاح.

**الحل العملي بدل الانتظار:** النسخ واللصق، لكن بدون تقسيم بطيء:
1. افتح الموديول في VBA واضغط **Ctrl+A** ثم **Ctrl+C**.
2. الصق في رسالة واحدة. إذا رفضتها الغرفة لطولها، قسّمها إلى 3 أو 4 رسائل كبيرة (نحو 400 سطر).
3. أرسلها **متتالية دون انتظار ردنا**، مرقمة `1/4` إلى `4/4`، واختم بـ`--- نهاية الموديول ---`.

أنت أرسلت لنا فعلًا ما قبل `CurrentStep = "توزيع أيام الاحتياط"`، فابدأ من هذا السطر فقط واكمل للنهاية. هذا يوفر نصف العمل.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

Chatty GPT 5.6: بدل التخمين حول دعم المرفقات، هل توجد طريقة **أخرى داخل ChatGPT على الكمبيوتر** للوصول لاختيار الملفات عندما لا يظهر `+` أو المشبك؟ أعط خيارًا واحدًا فقط يمكن للمستخدم تجربته فورًا.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

نعم، يوجد خيار آخر أسهل ولا يعتمد على زر `+`:

من القائمة الجانبية اليسرى في ChatGPT افتح **Library / المكتبة**، ثم اضغط **Upload / رفع** واختر ملف `R19.txt`. رفع ملفات TXT مدعوم رسميًا، والمكتبة تسمح برفع الملف مباشرة من الكمبيوتر. 

جرّب هذه الخطوة فقط الآن. إذا وجدت **Library / المكتبة** ورفعت الملف بنجاح، أخبرني وسأصل إليه من هنا بدل تقطيع الكود.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: اقتُرح رفع `R19.txt` عبر **Library / المكتبة** بدل مربع المحادثة. Auto Claude: تحقّق من صحة هذا المسار، وهل الملف المرفوع للمكتبة يصبح قابلًا للوصول من هذه المحادثة مباشرة دون خطوة إضافية؟

### Auto Claude (Claude Sonnet 5.5)

لا أستطيع تأكيد هذا المسار، ولا أعتمد عليه. Chatty GPT 5.6 قال إن رفع TXT إلى المكتبة مدعوم رسميًا وإن الملف سيصل إلينا، لكنه لم يذكر مصدرًا، ولم أتحقق منه بنفسي. وأنا ضعيف التحقق هنا لسببين.

1. **رفع الملف إلى مكتبة حسابك لا يعني أنه يصلني.** هذه الغرفة تجمع عدة نماذج، وأنا لا أرى إلا الرسائل التي تصلني في المحادثة. لا أعرف إن كانت مكتبة ChatGPT تظهر لي أصلًا، والأرجح أنها لا تظهر. وإذا كان ذلك صحيحًا فلن أصل إلى الملف حتى لو رفعه المستخدم بنجاح.
2. **قد تكون الرسالة التالية "رفعت الملف" ثم لا يصل شيء**، فيضيع وقت المستخدم مرة أخرى.

لذلك أقترح اختبارًا رخيصًا قبل أي رفع: يرفع المستخدم الملف ثم يكتب رسالة قصيرة، وإذا لم نستطع ذكر أول سطر فيه وآخر سطر فيه، فالمسار غير صالح وينتقل فورًا إلى اللصق.

ومع هذا يبقى اللصق المباشر هو الطريق المضمون. يلصق المستخدم من `CurrentStep = "توزيع أيام الاحتياط"` إلى النهاية في رسائل كبيرة متتالية.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

يوجد تعارض واقعي حول ما إذا كان رفع الملف إلى **Library** يجعله متاحًا لهذه المحادثة. Chatty GPT 5.6: تحقّق عبر الويب من توثيق OpenAI الرسمي فقط، وحدد بدقة هل يمكن رفع `R19.txt` للمكتبة ثم استخدامه في هذه المحادثة، وما الخطوة المطلوبة إن وجدت.

### Chatty GPT 5.6 (ChatGPT GPT-5.6)

تحققت الآن من **توثيق OpenAI الرسمي**، وتصحيح Auto Claude مهم جزئيًا: **رفع الملف إلى Library وحده لا يعني أنه أُضيف تلقائيًا لهذه المحادثة**.

OpenAI توضح أن Library تسمح برفع الملفات وحفظها وإعادة استخدامها، لكن لإضافة ملف محفوظ إلى محادثة توجد خطوة مستقلة: من قائمة الكتابة تختار **Add from library** ثم الملف. 

لكن توجد نقطة مهمة جدًا لحالتنا: توثيق OpenAI الأحدث يقول أيضًا إن ChatGPT يستطيع البحث في ملفات Library تلقائيًا إذا كانت ميزة **Library search** مفعّلة، ويمكن للمستخدم أن يسأل عن ملف محفوظ لديه. 

لذلك أسهل اختبار عندك، خصوصًا لأن زر `+` غير موجود:

**ارفع `R19.txt` إلى Library، ثم ارجع لهذه المحادثة واكتب فقط:**
> رفعت ملف R19.txt في المكتبة، اقرأه.

وأنا سأحاول الوصول إليه من المكتبة مباشرة. إذا ظهر لي، انتهت مشكلة تقطيع الكود نهائيًا. وإذا لم يظهر، نعرف يقينًا أن هذه المحادثة تحتاج ربط الملف بطريقة أخرى.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

النقطة الجديدة: Library قد تتيح الوصول للملف عبر البحث، لكن ذلك يعتمد على توفر **Library search** في هذه الجلسة. Auto Claude: بناءً على هذا التحقق، أعط المستخدم الآن خطوة واحدة فقط لتنفيذها، دون بدائل أو تقسيم للكود.

### Auto Claude (Claude Sonnet 5.5)

الخطوة الوحيدة الآن: **الصق الكود مباشرة في رسالة**، بدل تجربة المكتبة.

لماذا أتراجع عن المكتبة: Chatty GPT 5.6 قال إنه تحقق من توثيق OpenAI، لكنه لم يضع رابطًا أستطيع فحصه. وحتى لو كان الوصف صحيحًا لواجهة ChatGPT نفسها، فهذه الغرفة تجمع أكثر من نموذج. لا أرى أنا إلا الرسائل المكتوبة فيها، ولا شيء يثبت أن مكتبة حسابك تصل إليّ. فقد تضيّع وقتك في رفع ملف لا أراه.

الجزء الأول وصل كاملًا حتى `DE_DistributeReserveDays`. المطلوب الآن:

1. افتح الموديول في VBA، واضغط **Ctrl+A** ثم **Ctrl+C**.
2. الصق في رسالة واحدة ابتداءً من السطر `CurrentStep = "توزيع أيام الاحتياط"` وحتى نهاية الموديول.
3. إذا قُطعت الرسالة، قل لنا فقط آخر كلمة ظهرت لك، ونكمل من هناك.

وإن أمكن أرسل بعدها تقرير آخر تشغيل. التقرير أقصر من الكود، ويفيدنا في فهم سبب بقاء 135 ناقصًا.

### Chatty GPT GPT-5.6 (ChatGPT GPT-5.6) · Moderator

_This AI did not answer_

---

— https://claudetuner.com/c/nUYJAP39cuNx9ure3vJjqN (Claude Tuner)
