########################################
# MARKETING MODULE (التسويق: الحزم + الكوبونات)
########################################

We want to add a new "Marketing" module with:

- Packages (الحزم / البكجات)
- Coupons (الكوبونات)
- Ability to use packages and coupons in:
  - Salon Orders (طلبات الصالون)
  - Appointments (الحجوزات)

We also want to update the sidebar navigation:
- Add "التسويق" after "المبيعات"
- Move loyalty settings under "التسويق"

========================================
# 1) PACKAGES (الحزم / البكجات)
========================================

A package (bundle) is a marketing offer that includes:
- One or more services, each with:
  - quantity
  - special package price
- One or more products, each with:
  - quantity
  - special package price

The package has:
- image
- name
- date range
- description
- active flag


### Table: packages
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)                 // اسم البكج
- image_url (string, nullable)               // صورة البكج (uploaded file URL or path)
- start_date (date, not null)                // تاريخ بداية الحزمة
- end_date (date, not null)                  // تاريخ نهاية الحزمة
- description_ar (string, nullable)          // وصف
- is_active (boolean, default true)          // لتفعيل/إلغاء تفعيل الحزمة
- created_at (datetime)
- updated_at (datetime)

Notes:
- A package is considered "valid" if:
  - is_active = true
  - current_date between start_date and end_date (inclusive)


### Table: package_services
Services included in a package.

Fields:
- id (integer, primary key, auto increment)
- package_id (integer, foreign key to packages.id)
- service_id (integer, foreign key to services.id)
- quantity (real, not null, default 1)           // عدد المرات لهذه الخدمة داخل البكج
- package_price (real, not null)                 // السعر الخاص لهذه الخدمة ضمن البكج (للخدمة الواحدة)

Notes:
- base service price comes from services table, but package_price can override it for this package.


### Table: package_products
Products included in a package.

Fields:
- id (integer, primary key, auto increment)
- package_id (integer, foreign key to packages.id)
- product_id (integer, foreign key to products.id)
- quantity (real, not null, default 1)           // عدد الوحدات في البكج
- package_price (real, not null)                 // السعر الخاص للمنتج ضمن البكج (لوحدة واحدة)


### PACKAGES APIs

1) GET /marketing/packages
   - List all packages with optional filters:
     - ?is_active=true/false
     - ?valid_today=true (between start_date and end_date)
   - Return:
     - id, name_ar, image_url, start_date, end_date, description_ar, is_active.

2) POST /marketing/packages
   - Create a new package with services and products.

   Request JSON example:
   {
     "name_ar": "بكج عروس VIP",
     "image_url": null,
     "start_date": "2025-10-01",
     "end_date": "2025-12-31",
     "description_ar": "بكج متكامل للعروس",
     "is_active": true,
     "services": [
       {
         "service_id": 10,
         "quantity": 1,
         "package_price": 300
       },
       {
         "service_id": 12,
         "quantity": 1,
         "package_price": 250
       }
     ],
     "products": [
       {
         "product_id": 5,
         "quantity": 1,
         "package_price": 80
       }
     ]
   }

   Behavior:
   - Validate that all service_id exist and active.
   - Validate that all product_id exist and active.
   - Insert into packages.
   - Insert rows into package_services and package_products.

3) GET /marketing/packages/:id
   - Return:
     - package header
     - services list with service name and package_price
     - products list with product name and package_price

4) PUT /marketing/packages/:id
   - Update package fields.
   - Optionally replace services/products lists:
     - easiest: delete all old package_services/package_products for this package, then insert new ones from request.

5) DELETE /marketing/packages/:id
   - Soft delete: set is_active = false (do not physically delete).


========================================
# 2) COUPONS (الكوبونات)
========================================

Coupons can be applied to:
- Services (optionally: specific services or all services)
- Products (optionally: specific products or all products)
- With either:
  - percentage discount (%)
  - fixed amount discount

They have:
- name
- code
- date range
- active flag


### Table: coupons
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)              // اسم الكوبون (للعرض)
- code (string, not null, unique)         // رمز الكوبون (يدخله العميل أو الموظف)
- description_ar (string, nullable)
- discount_type (string, not null)        // "percent" or "amount"
- discount_value (real, not null)         // if percent: e.g. 10 = 10%, if amount: 50 = 50 SAR
- applies_to_services (boolean, not null, default true)  // هل يطبق على الخدمات
- applies_to_products (boolean, not null, default false) // هل يطبق على المنتجات
- start_date (date, not null)
- end_date (date, not null)
- is_active (boolean, default true)
- created_at (datetime)
- updated_at (datetime)

Notes:
- A coupon is valid if:
  - is_active = true
  - current_date between start_date and end_date.

### Table: coupon_services
Optional restriction: coupon applies only to specific services.

Fields:
- id (integer, primary key, auto increment)
- coupon_id (integer, foreign key to coupons.id)
- service_id (integer, foreign key to services.id)

If there are NO rows in coupon_services for a coupon AND applies_to_services = true:
- Then coupon applies to ALL services.

If there ARE rows:
- Coupon applies only to those service_id.

### Table: coupon_products
Optional restriction: coupon applies only to specific products.

Fields:
- id (integer, primary key, auto increment)
- coupon_id (integer, foreign key to coupons.id)
- product_id (integer, foreign key to products.id)

Same rule:
- If applies_to_products = true and coupon_products is empty → applies to all products.
- If coupon_products has rows → applies only to those products.


### COUPONS APIs

1) GET /marketing/coupons
   - List coupons with filters:
     - ?is_active=true/false
     - ?valid_today=true
   - Return basic info.

2) POST /marketing/coupons
   - Create a new coupon.

   Request JSON example:
   {
     "name_ar": "كوبون خصم افتتاح الفرع",
     "code": "OPEN10",
     "description_ar": "خصم 10٪ على جميع الخدمات",
     "discount_type": "percent",          // "percent" or "amount"
     "discount_value": 10,
     "applies_to_services": true,
     "applies_to_products": false,
     "start_date": "2025-10-01",
     "end_date": "2025-10-31",
     "service_ids": [],   // empty = all services
     "product_ids": []    // ignored because applies_to_products = false
   }

   Or example restricted to some products:

   {
     "name_ar": "خصم على منتجات العناية بالشعر",
     "code": "HAIR20",
     "discount_type": "percent",
     "discount_value": 20,
     "applies_to_services": false,
     "applies_to_products": true,
     "start_date": "2025-10-01",
     "end_date": "2025-11-01",
     "service_ids": [],
     "product_ids": [5, 6, 7]
   }

   Behavior:
   - Validate uniqueness of code.
   - Validate dates.
   - Insert into coupons.
   - If service_ids not empty, insert rows into coupon_services.
   - If product_ids not empty, insert rows into coupon_products.

3) GET /marketing/coupons/:id
   - Return:
     - coupon header
     - allowed services (if any)
     - allowed products (if any)

4) PUT /marketing/coupons/:id
   - Update coupon header.
   - Replace coupon_services and coupon_products with new lists.

5) DELETE /marketing/coupons/:id
   - Soft delete: is_active = false.


========================================
# 3) USING PACKAGES & COUPONS IN ORDERS AND APPOINTMENTS
========================================

We already have:

- Salon Orders (orders + order_services + order_products)
- Appointments (appointments + appointment_services)

We now want to:

- Allow selecting ONE package per order/appointment (optional).
- Allow applying ONE coupon per order/appointment (optional).
- Use package data to auto-fill services/products.
- Use coupon to calculate discount amount.

### 3.1 Update Orders Table

Update `orders` table to add:

- package_id (integer, nullable, foreign key to packages.id)
- coupon_id (integer, nullable, foreign key to coupons.id)
- coupon_discount_amount (real, not null, default 0)   // already present in some designs; if exists, reuse it
- total_amount should already exist.

Behavior when creating an order:

- If package_id is provided:
  - Load valid package (is_active = true and within date range).
  - Auto-add services:
    - For each package_services row:
      - Create order_services line:
        - service_id
        - quantity = package_services.quantity
        - base_price = package_services.package_price
        - vat_type, vat_rate from services table
        - calculate line_subtotal, vat_amount, line_total.
  - Auto-add products:
    - For each package_products row:
      - Create order_products line:
        - product_id
        - quantity = package_products.quantity
        - unit_price = package_products.package_price
        - vat_type, vat_rate (from product or a default config)
        - calculate line_subtotal, vat_amount, line_total.
- The user can still add extra services/products manually on top of the package if needed.

### 3.2 Using Coupons in Orders

When creating or updating an order, allow passing a `coupon_code` (string) OR `coupon_id`.

Behavior:

1) Validate coupon:
   - must be active and within date range.
2) Determine which lines are affected:
   - If coupon.applies_to_services:
     - Filter order_services lines:
       - If coupon_services has entries → only those service_ids.
       - Else → all services.
   - If coupon.applies_to_products:
     - Filter order_products lines similarly using coupon_products.
3) Calculate discount amount:
   - If discount_type = "percent":
     - discount_base = sum(line_total of affected lines)
     - coupon_discount_amount = discount_base * (discount_value / 100)
   - If discount_type = "amount":
     - coupon_discount_amount = discount_value
4) Apply to order totals:
   - total_before_discount = subtotal_amount + vat_amount
   - total_amount = total_before_discount - coupon_discount_amount (not less than 0).

Store:
- coupon_id
- coupon_discount_amount
- coupon_code (if there is a separate field)

If coupon invalid or expired:
- return error in Arabic: "الكوبون غير صالح أو منتهي".


### 3.3 Using Packages & Coupons in Appointments

Update `appointments` table to add:

- package_id (integer, nullable, foreign key to packages.id)
- coupon_id (integer, nullable, foreign key to coupons.id)
- coupon_discount_amount (real, not null, default 0)

Behavior for appointments is similar to orders, but we only have services (appointment_services):

- If package_id provided:
  - Auto-add appointment_services lines based on package_services with same VAT rules.
- If coupon applied:
  - Affect only appointment_services (services side).
  - Apply discount_type/discount_value on the sum of line_totals.
  - total_amount = subtotal_amount + vat_amount + visit_fee - coupon_discount_amount.

Errors:
- "الحزمة غير صالحة أو منتهية" if package not active/expired.
- "الكوبون غير صالح أو منتهي" if coupon invalid.


========================================
# 4) SIDEBAR / NAVIGATION: MARKETING MODULE
========================================

Update the sidebar navigation to add a "التسويق" section immediately AFTER "المبيعات":

Menu order (top to bottom):

1) لوحة التحكم
2) المبيعات
3) التسويق
4) الكافتريا (if present)
5) الخدمات
6) المشتريات
7) المخزون
8) الموارد البشرية
9) المالية

Under "التسويق" add:

- "الحزم"
- "كوبونات"

If there is an existing "نقاط الولاء" (loyalty settings or loyalty module) currently under المبيعات:
- Move that item under "التسويق" as well.
  - So under التسويق we will have:
    - الحزم
    - كوبونات
    - نقاط الولاء   (if implemented)

Keep RTL layout and Arabic labels.

Do NOT break existing routes; just reorganize the navigation.


========================================
# 5) VALIDATION & ARABIC MESSAGES
========================================

Please add basic Arabic error messages, such as:

- "الحزمة غير صالحة أو منتهية"
- "الكوبون غير صالح أو منتهي"
- "اسم الحزمة مطلوب"
- "اسم الكوبون مطلوب"
- "رمز الكوبون مستخدم من قبل"
- "نوع الخصم غير صحيح"
- "تاريخ البداية يجب أن يكون قبل تاريخ النهاية"
