########################################
# SALES ORDERS & APPOINTMENTS ENHANCEMENTS
########################################

We want to enhance both:

- Salon Orders module (طلبات الصالون)
- Appointments module (الحجوزات)

The same business logic applies to both, with minor differences.

Key features to add:

1) Inline customer creation on order/appointment form
2) Show customer loyalty points + search by name/phone
3) Auto-fill order creator employee
4) Coupon selection (from existing coupons only)
5) Add packages (البكجات) to order/appointment
6) After completion:
   - Create proper accounting journal entries:
     - salon revenue
     - collected VAT
     - cost of goods sold (COGS)
   - Create journal entries for payments
7) Scheduling orders / appointments
8) Cancel orders/appointments with reversal journal entry
9) Views for:
   - today's orders
   - app orders
   - cancelled orders
   - scheduled orders
   - evaluations from app
10) Apply same logic for appointments.
11) Support gift orders (طلبات هدايا) with sender/receiver and gift expiry date.


========================================
# A) INLINE CUSTOMER CREATION + LOYALTY
========================================

### A.1 Inline customer creation on Order/Appointment creation page

On the "Create Order" and "Create Appointment" screens:

- Allow creating a NEW customer directly from the same page,
  without leaving the form.

API:

- When POST /orders or POST /appointments:
  - Accept either:
    - `customer_id` (existing customer), OR
    - `customer` object to create a new one.

Example:

{
  "customer": {
    "id": null,
    "name_ar": "عميلة جديدة",
    "phone": "0550000000",
    "email": null,
    "city_ar": "جدة"
  },
  ...
}

Behavior:

- If `customer.id` is null:
  - Create a new customer in customers table.
  - Use returned id as customer_id for the order/appointment.
- If `customer_id` is provided and not null:
  - Use existing customer as usual.
- Validation:
  - At least one of (customer_id OR customer object) must be provided.
  - If both missing → return: "يجب اختيار عميل أو إنشاء عميل جديد".

### A.2 Search customers by name or phone

Add an endpoint for searching customers:

GET /customers/search?q=...

- The query `q` can be:
  - part of the name
  - full or partial phone number

Return:
- id
- name_ar
- phone
- email
- current loyalty points balance (see next section)

Make sure the search is case-insensitive and supports partial matches.

### A.3 Show customer loyalty points

On order/appointment creation page:

- When a customer is selected (existing or newly created),
  show:

  - current_points_balance
  - points_expiring_soon (optional)

Use existing loyalty logic (if there is a summary endpoint).
If not, create:

GET /customers/:id/loyalty-summary

Return:
- total_points_balance
- points_expiring_soon


========================================
# B) EMPLOYEE AUTO-ASSIGNMENT (منشئ الطلب)
========================================

When creating an order or appointment from the admin panel:

- The "order creator employee" field should be automatically
  set to the currently logged-in employee.

### B.1 Update orders table

Add/confirm:

- created_by_employee_id (integer, nullable, FK employees.id)

On POST /orders:

- If there is an authenticated employee context:
  - Set created_by_employee_id = current employee id.
- Do NOT require it in the request body from frontend;
  it should come from auth/session.

Similarly for appointments:

- Add created_by_employee_id to `appointments` table
  and set it the same way.


========================================
# C) COUPONS & PACKAGES ON ORDERS/APPOINTMENTS
========================================

We already have:

- packages (الحزم)
- coupons (الكوبونات)
- loyalty settings
- basic support for package_id & coupon_id at DB level (from previous prompts).

Now we refine behavior.

### C.1 Coupon selection at top of form

On the order creation screen:

- At the top, show a field to apply a coupon:

  - Dropdown or search input for existing coupons only.
  - No "create new" from here.

Behavior:

- Frontend sends either:
  - coupon_id
  - or coupon_code (string)

Server:

- Validate coupon:
  - is_active = true
  - within date range (start_date <= today <= end_date)
- If invalid:
  - return "الكوبون غير صالح أو منتهي".

Apply logic described previously:

- Determine which lines are affected (services/products).
- Calculate discount amount.
- Store:
  - coupon_id
  - coupon_discount_amount
- Reduce total_amount accordingly.

### C.2 Packages (الحزم) in orders/appointments

On order creation page:

- Allow selecting a package (optional).

Behavior:

- Frontend sends:
  - package_id (nullable)

Server:

- Validate package:
  - is_active
  - within start/end date.
- If invalid:
  - "الحزمة غير صالحة أو منتهية".

When package selected:

- Auto-add lines (services/products) according to:
  - package_services
  - package_products
- Use package_price as line price.
- Still allow adding extra lines manually.

Apply same logic in appointments:

- For appointments, only service lines are added from package_services.
- If needed, you can ignore package_products in appointments or treat them separately.


========================================
# D) ORDER STATUSES, SCHEDULING, CANCELLATION
========================================

We need consistent statuses and views.

### D.1 Order fields

Ensure `orders` table has:

- status (string, not null)
  - allowed values:
    - "draft"
    - "pending"
    - "confirmed"
    - "in_progress"
    - "completed"
    - "cancelled"
    - "scheduled"

- order_date (date/datetime, not null)
- scheduled_at (datetime, nullable)    // for scheduled orders
- source (string, not null, default "pos")   // "pos" or "app"
- cancellation_reason (string, nullable)
- rating_value (integer, nullable)          // from app
- rating_comment (string, nullable)

### D.2 Scheduling orders

- If the user marks the order as scheduled:

  - Set:
    - status = "scheduled"
    - scheduled_at = chosen date/time

- When the scheduled order is served/completed:

  - Update:
    - status = "completed"
    - (and create financial journal entry as described later)

### D.3 Cancellation logic

Endpoint: POST /orders/:id/cancel

Request JSON example:

{
  "reason": "العميلة ألغت الموعد"
}

Behavior:

- Only allow cancel if status is NOT already "cancelled" or "completed" (depending on business rules).
- Set:
  - status = "cancelled"
  - cancellation_reason = reason

Financially:

- If the order was already posted to accounting (journal entry created):
  - Create a reversal journal entry (credit/debit swap) to cancel the revenue, VAT, and COGS.
  - Mark the original journal entry as reversed (add a flag or link).

Return error in Arabic if invalid:
- "لا يمكن إلغاء الطلب في حالته الحالية".


========================================
# E) FILTERED VIEWS (طلبات اليوم، طلبات التطبيق، …)
========================================

We need API endpoints or filters to support the following views:

1) طلبات اليوم (Today’s Orders)
2) طلبات التطبيق (App Orders)
3) الطلبات الملغية (Cancelled Orders)
4) الطلبات المجدولة (Scheduled Orders)
5) تقييمات (Orders with ratings)

Either:

- Use one endpoint GET /orders with query params:

  - ?date=today                        → طلبات اليوم
  - ?source=app                        → طلبات التطبيق
  - ?status=cancelled                 → إلغاء الطلب
  - ?status=scheduled                 → الطلبات المجدولة
  - ?has_rating=true                  → تقييمات

Or define separate endpoints if you prefer, but the query approach is fine.

Implementation:

- date filter compares order_date with today.
- source filter -> "app" vs "pos".
- has_rating -> rating_value IS NOT NULL.


========================================
# F) ACCOUNTING ENTRIES FOR ORDERS (SALES)
========================================

We already have accounting tables:

- journal_entries
- journal_lines

And config for accounts (for purchases):

- inventory_account_code = "1201"
- cash_account_code = "1110"
- bank_account_code = "1120"
- card_account_code = "1130" (if needed)

We now add sales accounts:

In a config object:

- salon_revenue_account_code = "4100"    // إيرادات الصالون
- vat_output_account_code    = "2411"    // ضريبة محصلة
- cogs_account_code          = "5100"    // تكلفة البضاعة المباعة
- inventory_account_code     = "1201"    // المخزون (already used)
- customer_control_account_code = "1100" // العملاء (if credit sales are used)

### F.1 When an order is COMPLETED (status = "completed")

We need to:

1) Calculate revenue and VAT from the order lines.
2) Calculate COGS based on product average_unit_cost and quantities sold.
3) Create a journal entry:

Example: order total_amount = 1150 (1000 + 150 VAT).

- For a CASH sale (full payment):

  Journal Entry 1 (Sales & COGS) on completion:

  - Debit: cash/bank/card account     1150
  - Credit: salon_revenue_account_code  (net revenue, e.g. 1000)
  - Credit: vat_output_account_code     (VAT collected, e.g. 150)

  And COGS part (if using separate entry or same entry):

  - Debit: cogs_account_code          (e.g. 600)
  - Credit: inventory_account_code    (600)

Alternatively, you can split it into 2 journal entries:

- JV1: Revenue & VAT
- JV2: COGS & Inventory

For simplicity, you can put all lines in one journal_entry as long as:
- sum(debit) = sum(credit).

### F.2 Payments for the order ( partial or full )

If we allow partial payments for orders, similar to suppliers:

- Table: customer_payments (if not already present).
- Or reuse a generic payments table.

For each payment:

- Determine payment method type:
  - CASH -> cash_account_code
  - BANK -> bank_account_code
  - CARD -> card_account_code (or bank account)

Journal for each payment:

- For CASH payment:

  - Debit: cash_account_code           (amount)
  - Credit: customer_control_account_code (if credit sale)
  - Or, if the sale is fully cash and no receivable is used:
    - This was already recorded by debiting cash in main sales entry.

You can choose one of two models:

1) **Pure cash sales**: No receivable account.
   - On completion:
     - Debit cash/bank directly for full amount.
   - No extra payment entry needed (unless for tracking methods separately).

2) **Sales on account (credit)**:
   - On completion:
     - Debit customer_control_account_code (total_amount).
     - Credit revenue + VAT.
   - When payment is received:
     - Debit cash/bank.
     - Credit customer_control_account_code.

Implement whichever fits current design, but document in config/comments.


### F.3 Cancellation journal entry

When cancelling an order that already has a journal entry:

- Create a reversal journal_entry:

  - Same accounts and amounts, but swap debit and credit.

Example:

Original:
- Dr Cash 1150
- Cr Revenue 1000
- Cr VAT Output 150

Reversal:
- Dr Revenue 1000
- Dr VAT Output 150
- Cr Cash 1150

Also reverse COGS entry:

Original:
- Dr COGS 600
- Cr Inventory 600

Reversal:
- Dr Inventory 600
- Cr COGS 600

Mark the original entry as reversed if you have a flag, or store link to reversal entry.


========================================
# G) APPLY SAME LOGIC TO APPOINTMENTS
========================================

For Appointments (الحجوزات):

- Same inline customer creation.
- Same customer search and loyalty points display.
- Same coupon & package support.
- Same scheduling & cancellation logic.

Differences:

- Revenue is recognized when the appointment is actually served/completed
  (status changes to "completed" or when converted to an order).

Two options:

1) If appointments are converted into orders:
   - Perform the accounting when the order is created/completed.
   - Appointment itself has no journal entries (only bookings).

2) If appointments can be completed directly without converting to orders:
   - Apply the same accounting logic as orders directly on completion.

Choose one model and document it in comments.

Filtered views for appointments:

- "حجوزات اليوم"     → scheduled_at = today
- "حجوزات التطبيق"   → source = "app"
- "الحجوزات المعلقة"  → status = "pending" or "waiting"
- "الحجوزات الملغاة"  → status = "cancelled"


========================================
# H) EVALUATIONS (التقييمات)
========================================

From the mobile app, customers can send:

- rating_value (1–5)
- rating_comment

For orders and/or appointments, we already added fields:

- rating_value
- rating_comment

API endpoint to submit rating from app:

POST /orders/:id/rate

{
  "rating_value": 5,
  "rating_comment": "خدمة ممتازة"
}

Behavior:

- Validate rating_value between 1 and 5.
- Save it on the order.
- Optionally, create a separate `order_ratings` table if needed.

The "تقييمات" view in sidebar should list:

- Orders with rating_value IS NOT NULL,
- With filters by branch, employee, date, etc. (optional).

Same concept can be mirrored for appointments if required.


========================================
# I) GIFT ORDERS (طلبات الهدايا)
========================================

This section applies to **Salon Orders (طلبات الصالون)**.

We want to support orders where the type is "gift":  
The order is a gift from someone (customer or supervisor) to a specific customer, with an expiry date for the gift.

### I.1 Database fields

Update `orders` table with the following fields:

- order_type (string, not null, default "normal")
  - Allowed values:
    - "normal"   → طلب عادي
    - "gift"     → طلب هدية

- gift_from_type (string, nullable)
  - Allowed values:
    - "customer"    → الهدية من عميلة
    - "supervisor"  → الهدية من المشرف / الإدارة

- gift_from_customer_id (integer, nullable, FK customers.id)
  // المستخدم إذا كان المُهدي عميل

- gift_from_supervisor_id (integer, nullable, FK employees.id)
  // المستخدم إذا كان المُهدي موظف/مشرف

- gift_to_customer_id (integer, nullable, FK customers.id)
  // العميل المُهدى له (المستفيد من الطلب)

- gift_expiry_date (date, nullable)
  // تاريخ انتهاء صلاحية الهدية (آخر يوم يمكن استخدام/احتساب هذه الهدية فيه)

Validation rules:

- If order_type = "gift":
  - gift_from_type is REQUIRED.
  - gift_to_customer_id is REQUIRED.
  - gift_expiry_date is REQUIRED.
  - If gift_from_type = "customer":
      - gift_from_customer_id is REQUIRED and must exist in customers.
  - If gift_from_type = "supervisor":
      - gift_from_supervisor_id is REQUIRED and must exist in employees with a supervisor/admin role.
  - Also:
      - order.customer_id should be set to gift_to_customer_id (the receiver).

- If order_type = "normal":
  - All gift_* fields must be NULL.


### I.2 Order creation API changes

On POST /orders:

Extend the request body to accept:

{
  "order_type": "gift",            // "normal" or "gift"
  "customer_id": 987,              // or customer object
  "gift_from_type": "customer",    // or "supervisor"
  "gift_from_customer_id": 123,    // if from customer
  "gift_from_supervisor_id": null, // if from supervisor, fill this instead
  "gift_to_customer_id": 987,      // receiver customer id
  "gift_expiry_date": "2025-12-31",

  "coupon_id": ...,
  "package_id": ...,
  "services": [...],
  "products": [...],
  ...
}

Behavior:

- If order_type = "gift":
  - Validate all gift_* fields as per rules.
  - Set order.customer_id = gift_to_customer_id.
- If order_type = "normal":
  - Ignore any gift_* fields if sent.

Arabic error messages:

- "يجب تحديد نوع الطلب (عادي أو هدية)"
- "يجب تحديد العميل المُهدى له"
- "يجب تحديد من قام بإهداء الطلب"
- "العميل المرسل للهدية غير موجود"
- "المشرف المرسل للهدية غير موجود"
- "تاريخ انتهاء الهدية مطلوب"


### I.3 UI behavior

On the Order creation form:

- Add a field:
  - نوع الطلب:
    - عادي
    - هدية

- If نوع الطلب = عادي:
  - Hide all gift fields.

- If نوع الطلب = هدية:
  - Show:
    - من المُهدي؟ (اختيار نوع المٌهدي):
      - عميلة
      - مشرف
    - إذا كانت "عميلة":
      - حقل اختيار عميلة (بحث من العملاء بالاسم/الجوال).
    - إذا كانت "مشرف":
      - حقل اختيار مشرف (بحث من الموظفين بدور مشرف).
    - حقل اختيار "العميل المُهدى له" (أيضًا من قائمة العملاء).
    - حقل "تاريخ انتهاء الهدية".

In the order details view:

- If order_type = "gift":
  - Display:
    - "نوع الطلب: هدية"
    - "من: [اسم العميل المُهدي أو المشرف]"
    - "إلى: [اسم العميل المُهدى له]"
    - "تاريخ انتهاء الهدية: [gift_expiry_date]"


### I.4 Accounting (high-level note)

At this stage, we only store who gifted and to whom, plus expiry date.  
The accounting treatment (whether the gift creates revenue, discount, or internal cost) can be decided later and implemented as a separate step, based on business rules.

For now:
- Apply the same sales journal logic as normal orders,
  unless additional config is added later to treat gifts as discounts or marketing expenses.


========================================
# GENERAL NOTES
========================================

- Use Arabic validation messages such as:
  - "العميل غير موجود"
  - "الكوبون غير صالح أو منتهي"
  - "الحزمة غير صالحة أو منتهية"
  - "لا يمكن إلغاء الطلب في حالته الحالية"
  - "يجب اختيار عميل أو إنشاء عميل جديد"
- Keep the code modular:
  - orders logic in services/controllers for sales
  - accounting logic in a dedicated accounting service (to avoid duplication between orders and appointments).
- Do NOT break existing APIs; extend them carefully and update frontend accordingly.
