# Deposit Policy Audit

## Status

**الحالة العامة: 🟡 موجودة جزئيًا**

سياسة الدفع/العربون موجودة في المشروع على مستوى **الفوج (cohort / course_sessions)**، وموجودة لها **واجهة إدارة** و**بعض خدمات الـ billing / payment plans**، لكنها **غير مطبقة بالكامل داخل مسار التسجيل الرئيسي (Enrollment flow)**. الموجود اليوم أقرب إلى:

- **سياسة billing على الفوج**: موجودة
- **حساب العربون في pre-enrollment**: موجود جزئيًا
- **تطبيق العربون في التسجيل الرئيسي العام**: غير موجود
- **تخزين snapshot واضح للعربون/الباقي على enrollment/registration**: غير موجود
- **تمييز payment purpose = deposit/balance/full**: غير موجود

---

## Executive Summary

### ما الذي يعمل الآن

1. يمكن للإدارة ضبط سياسة billing على الفوج:
   - نوع السياسة
   - نسبة العربون
   - مدة التقسيط
   - يوم الاستحقاق
   - فترة السماح
   - التذكيرات

2. يوجد subsystem للدفع المجدول:
   - `payment_plans`
   - `payment_schedule_items`
   - خدمات لإنشاء خطط دفع وأقساط

3. يوجد مسار **pre-enrollment** يحسب العربون ويعرضه للمستخدم، بل ويسجل دفعة عربون مبدئية.

4. يوجد دعم عام لـ:
   - `amount_expected`
   - `amount_paid`
   - `remaining_amount`
   - `partial` payment status

### أين الفجوة

1. **التسجيل الرئيسي** (`RegistrationStepper / Choose Payment`) ما يزال ينشئ الدفع على **المبلغ الكامل** وليس العربون.
2. لا يوجد على `enrollments` أو `registrations` حقول snapshot مثل:
   - `total_amount`
   - `deposit_required`
   - `balance_due`
   - `billing_policy_snapshot`
3. لا يوجد على `payments` حقل semantic واضح مثل:
   - `purpose = deposit | balance | full`
4. لا يوجد ربط واضح يجعل:
   - دفع العربون = تأكيد المقعد
   - ثم إنشاء الباقي تلقائيًا داخل **Enrollment flow**
5. الباقي (`remaining / solde`) موجود أكثر داخل **pre-enrollment + payment plans** وليس في التسجيل الرئيسي.

---

## Evidence

## 1. DB / Migrations

### 1.1 السياسة موجودة على مستوى cohort/session

في [`database/migrations/2026_02_28_005929_add_billing_policy_to_course_sessions_table.php`](../database/migrations/2026_02_28_005929_add_billing_policy_to_course_sessions_table.php):

- السطور `14-27` تضيف الحقول التالية إلى `course_sessions`:
  - `billing_policy_type`
  - `deposit_percent`
  - `duration_months`
  - `due_day_of_month`
  - `grace_period_days`
  - `reminders_json`
  - `start_trigger`

وهذا دليل قوي أن سياسة billing/deposit موجودة **في قاعدة البيانات** على مستوى الفوج.

### 1.2 ما هو غير موجود في الـ DB

لم أجد في `app/` أو `database/` أي أثر لحقول مثل:

- `deposit_fixed`
- `down_payment`
- `payment_policy_type`
- `balance_due`
- `deposit_required`

وهذا يعني أن السياسة الحالية تعتمد على:

- `billing_policy_type`
- `deposit_percent`

ولا تعتمد على:

- عربون ثابت fixed deposit
- snapshot صريح على registration/enrollment

### 1.3 طبقة payment plans موجودة

في [`database/migrations/2026_02_14_100637_create_payment_plans_table.php`](../database/migrations/2026_02_14_100637_create_payment_plans_table.php) يوجد جدول `payment_plans`.

وفي [`database/migrations/2026_02_14_111500_create_payment_schedule_items_table.php`](../database/migrations/2026_02_14_111500_create_payment_schedule_items_table.php) يوجد جدول `payment_schedule_items`.

هذا يثبت أن النظام فيه **بنية billing/instalments**، لكنه لا يثبت وحده أن التسجيل العام يستعملها.

---

## 2. Models

### 2.1 Cohort

في [`app/Models/Cohort.php`](../app/Models/Cohort.php):

- السطور `23-40` تُظهر casts لحقول:
  - `deposit_percent`
  - `duration_months`
  - `due_day_of_month`
  - `grace_period_days`
  - `reminders_json`
- السطر `67` يثبت علاقة `paymentPlans()`

### 2.2 Enrollment

في [`app/Models/Enrollment.php`](../app/Models/Enrollment.php):

- السطور `12-26` لا تحتوي أي حقول مثل:
  - `total_amount`
  - `deposit_required`
  - `balance_due`
  - `billing_policy_snapshot`

الموجود فقط:

- status
- reserved_until
- enrolled_at
- attendance fields

### 2.3 Registration

في [`app/Models/Registration.php`](../app/Models/Registration.php):

- السطور `20-26` لا تحتوي snapshot مالي واضح
- السطور `53-68` و`81-101` تثبت وجود علاقات:
  - `paymentPlans`
  - `activePaymentPlan`
  - `payment`
  - `payments`

إذًا `registration` **يمكن ربطه** بخطط دفع، لكن هذا لا يعني أن مسار التسجيل العام يفعل ذلك حاليًا.

### 2.4 Payment

في [`app/Models/Payment.php`](../app/Models/Payment.php):

- السطور `18-29` تحتوي statuses بينها:
  - `partial`
  - `confirmed`
  - `verified`
- السطور `39-61` تحتوي:
  - `payment_plan_id`
  - `amount_expected`
  - `amount_paid`
  - `remaining_amount`

لكن لا يوجد:

- `purpose`
- `payment_purpose`
- `deposit/full/balance`

**الخلاصة**: النظام يدعم partial payments بشكل عام، لكنه لا يميزها كعربون رسمي.

### 2.5 PaymentPlan

في [`app/Models/PaymentPlan.php`](../app/Models/PaymentPlan.php):

- السطور `20-36` تُظهر:
  - `registration_id`
  - `pre_enrollment_id`
  - `cohort_id`
  - `total_amount`
  - `plan_type`
  - `rules_json`

هذه طبقة قابلة جدًا لإعادة الاستخدام إذا أردنا إكمال سياسة العربون في التسجيل فقط.

---

## 3. Admin UI

### 3.1 UI موجودة على الفوج فقط

في [`app/Livewire/Admin/Cohorts/BillingPolicyEditor.php`](../app/Livewire/Admin/Cohorts/BillingPolicyEditor.php):

- السطور `15-20`: props الخاصة بالسياسة
- السطور `84-100`: validation + save
- السطور `123-164`: preview لحساب:
  - total
  - deposit
  - remaining
  - installments

وفي [`resources/views/livewire/admin/cohorts/admin-cohort-form.blade.php`](../resources/views/livewire/admin/cohorts/admin-cohort-form.blade.php):

- يوجد tab باسم `billing_policy`

وفي [`resources/views/livewire/admin/cohorts/billing-policy-editor.blade.php`](../resources/views/livewire/admin/cohorts/billing-policy-editor.blade.php):

- توجد الحقول الفعلية للسياسة
- توجد معاينة مباشرة لـ:
  - Acompte
  - Solde
  - Echeancier

### 3.2 UI على مستوى course

لم أجد UI موازية على مستوى `Course` نفسه.  
كل الأدلة الحالية تشير إلى أن السياسة **مضبوطة على الفوج**، لا على الدورة.

---

## 4. Logic / Services

## 4.1 منطق عربون موجود في pre-enrollment

في [`app/Services/Workflow/PreEnrollmentService.php`](../app/Services/Workflow/PreEnrollmentService.php):

- السطور `67-80`:
  - تحدد هل الفوج يحتاج عربون
  - وتعيد `[requiresDeposit, percent]`

وهنا نرى أن:

- `presentiel_weekly_installments`
- `presentiel_intensif_deposit_then_full`
- `distance_deposit_then_full`

كلها تعتبر `requiresDeposit = true`

و`distance_full_upfront` يعيد `100%`.

## 4.2 يوجد BillingPlanFactory فعلي

في [`app/Services/Billing/BillingPlanFactory.php`](../app/Services/Billing/BillingPlanFactory.php):

- السطور `17-44`: `createDepositPlan(...)`
  - يحسب:
    - `depositAmount = totalAmount * deposit_percent / 100`
  - وينشئ `PaymentPlan` للعربون فقط
- السطور `50-105`: `createRemainingPlanForStart(...)`
  - يحسب الباقي
  - وينشئ plan للباقي
  - ويختار نوع الخطة حسب `billing_policy_type`

**لكن**:

- لم أجد أي استدعاء لـ `createDepositPlan(...)` في runtime الحالي
- الاستدعاء الوحيد الموجود هو `createRemainingPlanForStart(...)` داخل `CohortStartService`

## 4.3 الباقي يُنشأ عند انطلاق الفوج في pre-enrollment flow

في [`app/Services/Workflow/CohortStartService.php`](../app/Services/Workflow/CohortStartService.php):

- السطور `32-34`: يختار فقط `preEnrollments` بحالة `reserved`
- السطر `46`: يحسب `depositPaid`
- السطور `63-69`: ينشئ خطة الباقي عبر `createRemainingPlanForStart(...)`

إذًا “الباقي” موجود كفكرة تشغيلية، لكن في مسار:

- `pre-enrollment -> cohort start`

وليس في:

- `registration -> choose payment`

---

## Workflow As-Is

## 1. Main Registration / Enrollment Flow

### 1.1 أين يُحسب المبلغ المطلوب للدفع؟

في [`app/Application/UseCases/Payments/SelectPaymentMethod.php`](../app/Application/UseCases/Payments/SelectPaymentMethod.php):

- السطر `55`:

```php
$amount = $session->price_override ?? $course?->price ?? 0;
```

- السطور `58-65`:
  - تنشئ/تحدّث `Payment`
  - بقيمة `amount_expected = full amount`

هذا يعني أن **المبلغ الأول المطلوب في التسجيل الرئيسي = المبلغ الكامل**، وليس العربون.

يوجد use case أقدم/legacy مشابه في:

- [`app/Application/UseCases/Registration/SelectPaymentMethodStep.php`](../app/Application/UseCases/Registration/SelectPaymentMethodStep.php)

والسطران `20-29` فيه يفعلان الشيء نفسه: `amount_expected = full amount`.

### 1.2 عند اختيار طريقة الدفع هل يتم إنشاء Payment بالعربون أم بالمبلغ الكامل؟

**الجواب: بالمبلغ الكامل**

الدليل:

- [`SelectPaymentMethod.php`](../app/Application/UseCases/Payments/SelectPaymentMethod.php): السطر `62`
- [`SelectPaymentMethodStep.php`](../app/Application/UseCases/Registration/SelectPaymentMethodStep.php): السطر `28`

### 1.3 هل يوجد snapshot على enrollment؟

**لا**

في [`app/Models/Enrollment.php`](../app/Models/Enrollment.php) لا توجد حقول:

- `total_amount`
- `deposit_required`
- `balance_due`

### 1.4 هل يوجد purpose على payments؟

**لا**

في [`app/Models/Payment.php`](../app/Models/Payment.php) لا يوجد حقل مثل:

- `purpose`
- `deposit/full/balance`

### 1.5 متى يتأكد المقعد؟

المسار الحالي هو:

1. payment status يصبح `verified` أو `confirmed`
2. `PaymentObserver` يطلق `PaymentConfirmed`
3. `SyncEnrollmentAfterPaymentConfirmed` يغيّر `enrollment.status` إلى `confirmed`

الأدلة:

- [`app/Observers/PaymentObserver.php`](../app/Observers/PaymentObserver.php)
  - السطور `24-34`
- [`app/Listeners/Registrations/SyncEnrollmentAfterPaymentConfirmed.php`](../app/Listeners/Registrations/SyncEnrollmentAfterPaymentConfirmed.php)
  - السطور `17-28`

### 1.6 هل هذا التأكيد مرتبط بعربون أم بدفع كامل؟

في **التسجيل الرئيسي الحالي** هو فعليًا مرتبط بدفع **المبلغ الكامل المتوقع**، لأن `amount_expected` نفسه يحفظ كامل السعر.

بالتالي:

- لا يوجد منطق يقول: “إذا دفع العربون فقط فالمقعد confirmed”
- بل النظام يتعامل مع `expected = full amount`

---

## 2. Pre-Enrollment Flow

هذا المسار أقرب لسياسة العربون، لكنه ليس نفس التسجيل الرئيسي.

في [`app/Livewire/Public/PreEnrollmentWizard.php`](../app/Livewire/Public/PreEnrollmentWizard.php):

- السطور `164-182`:
  - تحسب `totalAmount`
  - ثم `depositPercent`
  - ثم `depositAmount`
  - ثم `remainingAmount`

- السطور `231-239`:
  - تنشئ `Payment`
  - وتضع:
    - `amount_paid = depositAmount`
    - `status = pending_review`

لكن توجد فجوة مهمة:

- لا يتم حفظ `amount_expected`
- لا يتم ربط payment بـ `payment_plan_id`
- لا يتم حفظ `purpose = deposit`

إذًا حتى هنا التطبيق **جزئي** وليس كاملًا.

---

## How Remaining Balance Is Handled Today

### الموجود فعليًا

يوجد handling للباقي داخل:

- `Payment.remaining_amount`
- `PaymentPlan.remaining()`
- `BillingPlanFactory::createRemainingPlanForStart()`

### لكن أين يعمل فعليًا؟

الباقي يعمل فعليًا أكثر في:

- الإدارة اليدوية
- payment plans
- pre-enrollment عند انطلاق الفوج

### ما الذي لا يعمل في التسجيل الرئيسي؟

لا يوجد اليوم في التسجيل الرئيسي:

1. إنشاء دفعة أولى كعربون
2. إنشاء plan للباقي تلقائيًا
3. إنشاء payment ثانية أو due item للباقي
4. UI واضحة في choose payment تقول:
   - المطلوب الآن
   - الباقي لاحقًا
   - تاريخ/شرط الاستحقاق

---

## Reports / Admin Visibility As-Is

النظام الحالي يعرض ويتابع:

- `amount_expected`
- `amount_paid`
- `remaining_amount`

في عدة طبقات إدارية وتقارير، مثل:

- [`app/Application/DTOs/Admin/Payments/PaymentReviewRowDTO.php`](../app/Application/DTOs/Admin/Payments/PaymentReviewRowDTO.php)
- [`app/Application/Mappers/Payments/PaymentStatusMapper.php`](../app/Application/Mappers/Payments/PaymentStatusMapper.php)
- [`app/Livewire/Admin/Registrations/AdminRegistrationCreatePage.php`](../app/Livewire/Admin/Registrations/AdminRegistrationCreatePage.php)

لكن العرض الحالي هو:

- due / partial / confirmed

وليس:

- deposit / balance / full

أي أن التقارير **تعرف partial balance**، لكنها لا تعرف semantic purpose.

---

## Gap Analysis

## 1. ما هو موجود

- billing policy fields على cohort
- admin cohort billing UI
- preview للحساب
- payment plans subsystem
- partial payments
- pre-enrollment deposit calculation
- remaining-plan generation عند `CohortStartService`

## 2. ما هو ناقص

### ناقص في الـ data model

- `payments.purpose`
- snapshot مالي على `registration` أو `enrollment`
- fixed deposit amount

### ناقص في enrollment flow

- استخدام `billing_policy_type` و`deposit_percent` عند `choose payment`
- إنشاء `amount_expected = depositAmount` بدل full amount
- إنشاء balance record / plan للباقي
- جعل seat confirmation مبنيًا على deposit policy بدل full amount فقط

### ناقص في UI العام

- choose-payment summary لا تعرض:
  - العربون
  - الباقي
  - متى يستحق الباقي

### ناقص في التقارير

- لا يمكن التفريق بين:
  - partial عشوائي
  - deposit policy payment

---

## Conclusion

**الحكم النهائي: 🟡 موجودة جزئيًا**

### لماذا ليست ✅ كاملة؟

لأن السياسة موجودة في:

- الـ DB
- Admin UI
- بعض خدمات billing
- pre-enrollment flow

لكنها **غير مطبقة في مسار التسجيل الرئيسي** الذي ينشئ `Enrollment` و`Payment` العامة.

### لماذا ليست ❌ غير موجودة؟

لأن هناك فعلًا:

- billing policy fields
- deposit calculations
- payment plans
- remaining-plan logic

لكنها مبعثرة وغير موصولة بالكامل مع enrollment.

---

## Smallest Possible Change (Enrollment Only)

إذا أردنا تفعيل العربون **في التسجيل فقط** دون إعادة بناء النظام كله، فأصغر تعديل منطقي هو:

### 1. إعادة استخدام الحقول الحالية بدل اختراع نظام جديد

استخدام:

- `course_sessions.billing_policy_type`
- `course_sessions.deposit_percent`
- `payments.amount_expected`
- `payments.amount_paid`
- `payments.remaining_amount`
- `payment_plans` فقط إذا احتجنا للباقي فعليًا

### 2. إضافة حقل semantic صغير فقط

أقترح إضافة:

- `payments.purpose` enum:
  - `deposit`
  - `balance`
  - `full`

هذا هو أقل تغيير يمنحنا وضوحًا تشغيليًا وتقاريريًا.

### 3. إضافة Calculator صغيرة بدل إعادة بناء billing كله

خدمة مقترحة:

- `app/Services/Billing/EnrollmentDepositCalculator.php`

وظيفتها:

1. تقرأ `billing_policy_type` و`deposit_percent` من الفوج
2. تعيد:
   - `total_amount`
   - `first_payment_amount`
   - `remaining_amount`
   - `purpose`
   - `requires_balance_plan`

### 4. تعديل التسجيل فقط

في `SelectPaymentMethod`:

- بدل:
  - `amount_expected = full amount`
- يصبح:
  - `amount_expected = first_payment_amount`
  - `remaining_amount = remaining after first payment`
  - `purpose = deposit|full`

### 5. إبقاء تأكيد المقعد متسقًا مع السياسة

عند `PaymentConfirmed`:

- إذا `purpose = deposit` وكان policy يسمح بالحجز بالعربون:
  - confirm enrollment
- وإذا `purpose = full`:
  - confirm enrollment

### 6. إنشاء الباقي بأقل تكلفة

خيار minimal:

- عند اعتماد الدفعة الأولى إن كانت `deposit`
  - ينشأ `PaymentPlan` أو `Payment` due ثانية للباقي

الأفضل reuse الموجود:

- `PaymentPlan`
- وربطه بـ `registration_id`

---

## Files / Classes To Modify If We Implement Deposit In Registration Only

### Core

1. `app/Application/UseCases/Payments/SelectPaymentMethod.php`
2. `app/Application/UseCases/Registration/SelectPaymentMethodStep.php`
3. `app/Observers/PaymentObserver.php`
4. `app/Listeners/Registrations/SyncEnrollmentAfterPaymentConfirmed.php`

### New minimal service

5. `app/Services/Billing/EnrollmentDepositCalculator.php` (new)

### Models / DB

6. `app/Models/Payment.php`
7. `database/migrations/*_add_purpose_to_payments_table.php` (new)

### Optional but recommended

8. `app/Models/Registration.php`  
   فقط إذا أردنا `billing_snapshot_json` أو reuse metadata

### UI

9. `resources/views/livewire/registration/choose-payment-page.blade.php`
10. `resources/views/livewire/registration/registration-stepper.blade.php`
11. أي Livewire class تغذي choose-payment page لعرض:
    - total
    - deposit now
    - remaining later

### Tests

12. tests for:
    - deposit policy cohort => first payment amount
    - full upfront cohort => full amount
    - deposit approval => enrollment confirmed
    - balance plan created

---

## Optional PR Plan

1. Add `payments.purpose` migration + model cast/constants
2. Add `EnrollmentDepositCalculator`
3. Refactor `SelectPaymentMethod` to use calculator
4. Refactor legacy `SelectPaymentMethodStep` to same calculator
5. Update choose-payment UI to show deposit/balance summary
6. Update payment-confirmation listener to confirm seat on valid deposit/full payment
7. Create remaining balance record/plan on first payment approval
8. Add tests for deposit/full/balance workflow

---

## Final Audit Verdict

**Deposit Policy exists in the codebase, but not as a complete enrollment workflow.**

- **DB / Admin / Billing foundations**: yes
- **Main public enrollment application**: no
- **Pre-enrollment partial support**: yes
- **Full deposit-to-balance operational flow in enrollment**: no

Therefore the correct label is:

**🟡 موجودة جزئيًا**
