# دورة العروض والخصومات — الدليل الكامل

## ملخص سريع

النظام فيه 3 طبقات منفصلة للخصومات:

| الطبقة | الغرض | من يستفيد | تطبيق |
|--------|--------|-----------|-------|
| عروض مجلس الإدارة | شروط دفع (خصم كاش + أقساط مخصصة) | كل الأعضاء خلال الفترة | تلقائي |
| الخصومات الخاصة | خصم على قيمة العضوية | عضو محدد أو بشرط | يدوي أو تلقائي بشرط |
| تعديل الاشتراك السنوي | نسبة على الاشتراك | كل المشتركين في سنة معينة | تلقائي عند التوليد |

---

## 1. عروض مجلس الإدارة (Board Offers)

### الفكرة

قرار مجلس إدارة بشروط دفع خاصة لفترة محدودة. مثلاً: "عرض الصيف — خصم 10% كاش أو تقسيط على 36 شهر بفائدة 18%".

### المسار: التسعير ← عروض مجلس الإدارة

`/pricing/board-offers`

### ماذا يحتوي العرض

1. **مسار الكاش** (اختياري) — خصم يُطبَّق لو العضو دفع كامل نقداً:
   - نوع: نسبة % أو مبلغ ثابت
   - مثال: خصم 10% = عضوية 150,000 تصبح 135,000

2. **مسار التقسيط** (اختياري) — يتجاوز الإعدادات الافتراضية:
   - نسبة المقدم (الافتراضي 25%)
   - عدد الأشهر (الافتراضي 30)
   - نسبة الفائدة (الافتراضي 22%)
   - فترة سماح:
     - `first_n_months`: أول N شهر بدون فوائد (بعدها فائدة عادية أو مخصصة)
     - `full_free_under_n`: إعفاء كامل من الفوائد لو عدد الأقساط ≤ N

3. **بيانات القرار**: رقم القرار، تاريخه، الفرع (أو كل الفروع)

4. **فترة السريان**: effective_from → effective_to (مطلوبين)

### كيف يُطبَّق تلقائياً

1. العضو يفتح صفحته `/members/{id}`
2. الكنترولر يستدعي `BoardOfferService::getBestOffer($branchId)`
3. لو فيه عرض ساري:
   - يظهر بانر أصفر "عرض مجلس الإدارة: {title}"
   - الكاش يظهر بالمبلغ بعد الخصم + "وفّرت X"
   - التقسيط يظهر بالشروط الجديدة (مقدم، أشهر، فائدة، سماح)
4. عند الضغط "إرسال للخزينة":
   - يُخزن `board_offer_id` + `offer_snapshot_json` في `payment_requests`
   - الـ snapshot يحفظ شروط العرض لحظة الطلب — تغيير العرض بعدها لا يؤثر

### الأولوية

- عرض بـ `branch_id` محدد يتقدم على عرض بدون فرع (كل الفروع)
- لو فيه أكثر من عرض ساري → الأكثر تحديداً يفوز (أحدث effective_from)
- عرض واحد فقط يُطبَّق في المرة الواحدة

### Edge Cases

- **العرض انتهى أثناء ما الطلب في الخزينة**: الـ snapshot محفوظ — الخصم يُطبَّق
- **العرض اتعطّل (is_active=0)**: لا يظهر للأعضاء الجدد، لكن الطلبات القديمة ما تتأثر
- **عرض مستقبلي (effective_from > today)**: لا يظهر — ينشط تلقائياً عند الموعد
- **عرض بدون مسار كاش ولا تقسيط**: لا يُطبَّق (الفاليديشن يمنع حفظه أصلاً)
- **عضو واحد يدفع كاش والتاني تقسيط**: كل واحد يأخذ المسار اللي اختاره

---

## 2. الخصومات الخاصة (Special Discounts)

### الفكرة

خصومات على **قيمة العضوية نفسها** — إما يدوية (تُعيَّن لعضو) أو تلقائية (بشرط).

### المسار: التسعير ← الخصومات الخاصة

`/pricing/special-discounts`

### أنواع الخصم

| النوع | التأثير |
|-------|---------|
| `percentage` | يخصم X% من قيمة العضوية |
| `fixed_amount` | يخصم مبلغ ثابت |
| `free_subscription` | لا يخصم من العضوية — يمنح اشتراكات مجانية فقط |

### شروط التطبيق

| الشرط | المعنى |
|-------|--------|
| `none` | بدون شرط — يُطبَّق يدوياً عند تعيينه لعضو |
| `full_payment` | يُطبَّق تلقائياً لو العضو سدّد كامل قيمة العضوية |
| `min_payment` | يُطبَّق تلقائياً لو العضو سدّد مبلغ ≥ الحد الأدنى |

### كيف يُعيَّن لعضو (يدوي)

1. صفحة العضو `/members/{id}`
2. قسم "خصم خاص" (يظهر فقط قبل التفعيل)
3. اختر الخصم من القائمة → احفظ
4. يُخزن `special_discount_id` في جدول `members`
5. `BillingService::getMemberBill()` يحسب الخصم ويُنقصه من الفاتورة

### كيف يُطبَّق تلقائياً (بشرط)

1. `SpecialDiscountService::evaluateForMember($memberId, $membershipValue, $totalPaid)` يُستدعى أثناء حساب الفاتورة
2. يفحص: هل العضو معيّن له خصم؟ → يستخدمه
3. لو لا → يبحث في الخصومات الشرطية (`condition_type != 'none'`)
4. يطابق الشرط (سداد كامل / حد أدنى) → يُطبَّق أعلى خصم مطابق

### المكافأة: سنوات اشتراك مجانية

- حقل `bonus_free_subscription_years` (0-5)
- بعد تفعيل العضوية (event: `member.activated`):
  - `SpecialDiscountService::getBonusFreeYears($memberId)` يحسب
  - `applyFreeSubscriptionBonus()` يحول اشتراكات pending إلى paid (بـ receipt = 'FREE-BONUS')

### Edge Cases

- **خصم بدون فترة (effective_from/to = null)**: ساري دائماً
- **خصم percentage = 100%**: العضوية مجاناً — ممكن لأبناء شهداء مثلاً
- **خصم condition=full_payment + bonus=1 year**: الأكثر شيوعاً — ادفع كاش واحصل على اشتراك سنة مجاني
- **عضو بخصم يدوي + خصم شرطي**: اليدوي يفوز (يُفحص أولاً)
- **خصم معطّل (is_active=0) ومعيّن لعضو**: لا يُطبَّق — يُتجاهل

---

## 3. تعديل الاشتراك السنوي (Subscription Year Adjustment)

### الفكرة

خصم/زيادة على الاشتراكات السنوية لكل المشتركين في سنة مالية معينة.

### المسار: التسعير ← لوحة التسعير

`/pricing`

### كيف يعمل

- Rule Engine key: `SUBSCRIPTION_YEAR_ADJUSTMENT_{year}` (مثل `SUBSCRIPTION_YEAR_ADJUSTMENT_2026`)
- يحتوي `discount_percentage` (مثل 50 = خصم 50% على الاشتراك)
- `SubscriptionSyncService` و `SubscriptionGenerator` يطبقان الخصم عند إنشاء صف الاشتراك

### متى يُستخدم

- سنة كورونا: خصم 100% (اشتراك مجاني)
- سنة تأخير افتتاح: خصم 50%
- لا يؤثر على اشتراكات سبق إنشاؤها — فقط الجديدة

---

## 4. الفرق بين Board Offer و Special Discount

| | Board Offer | Special Discount |
|---|---|---|
| **يخصم من** | المبلغ المدفوع (كاش) أو يغيّر شروط التقسيط | قيمة العضوية في الفاتورة |
| **يُطبَّق على** | كل من يدفع خلال الفترة | عضو معيّن أو بشرط |
| **مصدره** | قرار مجلس إدارة | إدارة النادي |
| **يتراكم مع الآخر** | نعم — الاتنين ممكن ينطبقوا على نفس العضو | نعم |
| **أين يُحفظ** | `payment_requests.board_offer_id` + snapshot | `members.special_discount_id` |
| **تأثير على الفاتورة** | لا (معلوماتي فقط حتى الدفع) | نعم (ينقّص total_pending) |

### مثال تراكم

عضو عضويته 150,000:
1. عليه خصم خاص 10% → الفاتورة تصبح 135,000
2. عرض مجلس إدارة كاش 10% → يدفع 121,500
3. مكافأة سنة اشتراك مجانية → الاشتراك السنوي (492 + 35) = 0

---

## 5. دورة حياة العرض الكاملة (Board Offer Lifecycle)

```
إنشاء العرض (/pricing/board-offers/create)
    ↓
[is_active=1, effective_from ≤ today ≤ effective_to]
    ↓ يظهر تلقائياً
صفحة العضو ← بانر أصفر + أزرار كاش/تقسيط محدّثة
    ↓ العضو يختار مسار الدفع
إرسال طلب دفع ← board_offer_id + snapshot يُحفظ
    ↓
الخزينة تستلم الطلب ← تعالجه ← يُنشئ payment
    ↓
payment.completed event ← يقرأ snapshot ← يطبّق الشروط
    ↓
installment_plan يُنشأ بشروط العرض (لو تقسيط)
```

---

## 6. دورة حياة الخصم الخاص (Special Discount Lifecycle)

```
إنشاء الخصم (/pricing/special-discounts/create)
    ↓
[حالة 1: بدون شرط]
    الموظف يعيّنه يدوياً لعضو ← members.special_discount_id = X
    ↓
    BillingService.getMemberBill() يحسب الخصم ← total_pending ينقص

[حالة 2: بشرط full_payment]
    العضو يسدّد كامل ← evaluateForMember() يكتشف الشرط متحقق
    ↓
    الخصم يُطبَّق تلقائياً في الفاتورة
    ↓
    member.activated event ← getBonusFreeYears() ← applyFreeSubscriptionBonus()
```

---

## 7. الملفات المسؤولة

| الملف | الدور |
|-------|-------|
| `Pricing/Controllers/BoardOfferController.php` | CRUD عروض مجلس الإدارة |
| `Pricing/Controllers/SpecialDiscountController.php` | CRUD الخصومات الخاصة |
| `Members/Services/BoardOfferService.php` | جلب العرض الأفضل + حساب خصم كاش + شروط تقسيط |
| `Pricing/Services/SpecialDiscountService.php` | تقييم خصم عضو + تطبيق مكافأة سنوات |
| `Members/Services/BillingService.php` (line 649) | يعرض العرض كبند في الفاتورة |
| `Members/Controllers/MemberController.php` (line 514) | يحفظ snapshot العرض عند إرسال طلب الدفع |
| `Pricing/Services/PricingEngine.php` | حساب أسعار العضوية + رسوم الأبناء + الأقساط |
| `Pricing/bootstrap.php` (line 35) | event listener: يطبّق مكافأة الاشتراك بعد التفعيل |

---

## 8. جداول قاعدة البيانات

### `board_offers`
```
id, title_ar, title_en, description,
cash_discount_type (percentage|fixed_amount|NULL),
cash_discount_value,
inst_down_payment_pct, inst_months, inst_interest_rate,
inst_grace_type (first_n_months|full_free_under_n|NULL),
inst_grace_months, inst_post_grace_rate,
branch_id (FK|NULL), applies_to (membership_fee|all),
effective_from, effective_to, is_active,
board_decision_number, board_decision_date, notes,
created_by, updated_by, created_at, updated_at
```

### `special_discounts`
```
id, name_ar, name_en,
discount_type (percentage|fixed_amount|free_subscription),
discount_percentage, fixed_amount,
applies_to (membership_fee|subscription|all),
effective_from (nullable), effective_to (nullable),
condition_type (none|full_payment|min_payment),
condition_min_amount,
bonus_free_subscription_years (0-5),
requires_document (0|1),
description, is_active,
created_at, updated_at, created_by, updated_by
```

### `payment_requests` (الحقول المتعلقة)
```
board_offer_id (FK|NULL),
offer_snapshot_json (JSON string of offer at request time)
```

### `members` (الحقول المتعلقة)
```
special_discount_id (FK|NULL)
```

---

## 9. سيناريوهات شائعة

### "عايزين عرض كاش 10% لمدة 3 شهور"
1. `/pricing/board-offers/create`
2. العنوان: "عرض الصيف — خصم 10% كاش"
3. فعّل مسار الكاش → percentage → 10
4. التواريخ: اليوم → بعد 3 شهور
5. احفظ ← كل عضو يدفع كاش خلال الفترة يحصل على 10% خصم تلقائي

### "عايزين تقسيط على 48 شهر بفائدة 15% بدل 30 شهر بـ 22%"
1. `/pricing/board-offers/create`
2. فعّل مسار التقسيط → 48 شهر → 15% فائدة
3. ممكن تفعّل الكاش أيضاً (العرض يقدم المسارين)

### "عايزين إعفاء من الفوائد لو دفع في 6 شهور أو أقل"
1. مسار تقسيط → grace_type = `full_free_under_n` → grace_months = 6
2. لو العضو اختار 6 أقساط أو أقل → فائدة = 0%
3. لو اختار 7+ → الفائدة العادية تُطبَّق

### "عايزين خصم 10% لأي عضو يدفع كامل"
1. `/pricing/special-discounts/create`
2. نوع: percentage → 10%
3. شرط: full_payment
4. مجال: membership_fee
5. ← أي عضو يسدّد كامل العضوية نقداً يحصل على 10% تلقائياً

### "عايزين سنة اشتراك مجاناً لأي عضو يدفع كاش"
1. نفس الخصم أعلاه + bonus_free_subscription_years = 1
2. بعد تفعيل العضوية → اشتراك السنة الحالية يتحول لـ "paid" تلقائياً

---

## 10. ما لا يجب فعله

- **لا تنشئ board offer + special discount بنفس النسبة** — هيتراكموا والعضو يأخذ خصم مضاعف
- **لا تعدّل عرض ساري والطلبات في الخزينة** — الطلبات القديمة محمية بالـ snapshot، بس ممكن يسبب لخبطة في التقارير
- **لا تحذف خصم معيّن لعضو** — عطّله (is_active=0) بدل ما تحذفه، عشان السجل
- **لا تضع effective_to = null في board offer** — مطلوب؛ العروض لازم لها نهاية
- **لا تنسى إن الفائدة سنوية مش شهرية** — 22% سنوي × (شهور/12) هي المعادلة
