# خطة العمل الشاملة والتوجيهات المعمارية - لوحة تحكم Apex_Logistics

## 📌 المبادئ الأساسية للمشروع (الدستور)
- **Zero Tolerance for Bugs**: الجودة المعمارية والبرمجية هي الأولوية القصوى.
- **Single Codebase**: بناء اللوحة والـ Backend بالكامل باستخدام `Laravel` و `Filament V3`.
- **Concurrency & Scaling**: تجنب عنق الزجاجة والـ Race Conditions باستخدام قفل السجلات والتخزين المؤقت اللحظي (Redis).

---

## 🛠️ المرحلة الأولى: التأسيس والبنية التحتية (Core & Infrastructure)
- [x] **تجهيز قواعد البيانات**: هيكلة الجداول الأساسية وفقاً لمتطلبات (Zero Tolerance).
- [x] **Filament Setup**: تثبيت وتهيئة اللوحة الأساسية مع دعم اللغة العربية (RTL).
- [x] **Sessions Management**: تم تحويل `SESSION_DRIVER` إلى `database` لضمان إمكانية طرد المستخدمين لحظياً.
- [x] **RBAC**: دمج `Spatie Permission` وإنشاء نظام إدارة الصلاحيات (`Roles` & `Permissions`) وتطبيقه على مستوى كل `Resource` في Filament.
- [x] **Audit Trail**: تفعيل واجهة `Spatie Activitylog` في اللوحة لتتبع النشاطات، مع تأكيد استثناء الحقول سريعة التحديث (تم تأسيسه في الـ Models).
- [x] **Data Integrity**: 
  - التأكد من تفعيل فلاتر الـ `Trashed` عبر الواجهات للبحث في البيانات المحذوفة (SoftDeletes).
  - **🚨 (Edge Case Fix) Restrict Deletion**: منع حذف (Soft Delete) أي كيان مرتبط بعمليات حية (مثل `Zone` تحتوي على مناديب نشطين، أو `Customer` لديه طلبات قيد التوصيل). سيتم بناء `BeforeDelete` Hook لرفض الحذف وإظهار خطأ للمستخدم.

---

## 👥 المرحلة الثانية: إدارة الكيانات (Entities Management)
- [x] **العملاء (Customers)**:
  - [x] إنشاء جدول `customer_locations` بعلاقة `One-to-Many` (يحتوي: `latitude`, `longitude`, `address_type`).
  - [x] بناء `CustomerResource`.
  - [x] بناء `RelationManager` لمواقع العميل داخل صفحة العميل، ودمج `Map Picker Component` لتحديد الموقع.
- [x] **المناطق (Zones)**:
  - [x] بناء `ZoneResource`.
  - [x] دمج `Map Picker` مخصص لرسم حدود التوصيل (`Polygons`) وتحديد المناطق الجغرافية برمجياً.
- [x] **المناديب (Drivers)**:
  - [x] إنشاء العلاقة `BelongsToMany` بين المناديب والمناطق (جدول `driver_zone`).
  - [x] استخدام `Multiple Select` في لوحة تحكم المندوب لتعيين مناطق عمله.
  - [x] بناء **360 View**: قسم مخصص (Custom Section) داخل صفحة المندوب يعرض الـ Current Active Order (الطلب الحالي، المسار على الخريطة، الحالة اللحظية، والعهدة المالية).

---

## 📦 المرحلة الثالثة: الكتالوج والمبيعات (Catalog & Regular Orders)
- [x] **المنتجات (Products)**:
  - [x] بناء `ProductResource` لإدارة المخزون، الأسعار، وحالة التفعيل (`Is Active`).
- [x] **الطلبات (Orders)**:
  - [x] بناء `OrderResource`.
  - [x] تصميم `Status Pipeline` لحالات الطلب.
  - [x] ربط محتويات الطلب (`order_items`) وضمان ظهور وحفظ `product_name` كقيمة ثابتة لا تتأثر بتعديلات الكتالوج مستقبلاً.

---

## 🔥 المرحلة الرابعة: الطلبات الحرة المتعددة المحطات (Multi-Stop Errand Orders)
- [x] **هيكلة قاعدة البيانات (Database Schema)**:
  - [x] إنشاء جدول أقسام الطلبات الحرة (`errand_categories`): `name`, `is_active`.
  - [x] إنشاء جدول محطات المشوار (`errand_stops`): `order_id` FK, `errand_category_id` FK, `description`, `customer_image_url`, `receipt_amount`, `receipt_image_path`, `status` (Enum: `pending`, `purchased`, `not_found`).
  - [x] تحديث جدول الطلبات الموحد (`orders`) بالحقول: `order_type`, `payment_method`, `pricing_method`, `cancellation_reason`, `cancellation_requested_at`, `cancellation_approved_by` FK, `financial_status`.
  - [x] إنشاء أمر `php artisan migrate:free-orders` لترحيل بيانات `free_orders` بأسلوب (Chunking 100 سجل/دفعة) مع نقل الملفات الفيزيائية (`Storage::move`) من Public Disk لـ Private Disk.
  - [x] حذف جدول `free_orders` نهائياً.
- [x] **بناء وتنظيف الـ Models**:
  - [x] إنشاء `ErrandCategory` و `ErrandStop` مع `LogsActivity`.
  - [x] تحديث `Order` Model: إضافة العلاقات `errandStops()`, `cancellationApprovedBy()` + إضافة كل الحقول الجديدة لـ `$fillable` + `$casts`.
  - [x] حذف `FreeOrder.php` بالكامل.
  - [x] تنظيف Models (`Customer`, `Driver`, `Product`): إزالة `freeOrders()` وتعديل `boot()`.
- [x] **واجهات Filament**:
  - [x] إنشاء `ErrandCategoryResource` (CRUD كامل).
  - [x] حذف مجلد `FreeOrders` Resource بالكامل.
  - [x] تحديث شامل لـ `OrderForm.php`: عرض ديناميكي حسب `order_type` (`standard` ← Items Repeater / `errand` ← Stops Repeater). تعطيل `order_type` في Edit Page (`->disabledOn('edit')`).
  - [x] تحديث `OrdersTable.php`: إضافة Badges لـ `order_type`, `payment_method`, `financial_status` + فلاتر.
  - [x] تحديث `CreateOrder.php`: تخطي خصم المخزون للطلبات الحرة (`errand`).
  - [x] تحديث `EditOrder.php`: قفل `Cache-Based` + `mutateFormDataBeforeFill` حسب النوع.
- [x] **توحيد الـ Status Pipeline**:
  - [x] ترتيب الحالات الصحيح (بعد إصلاح ⚡ ثغرة #10):
    - **Standard**: `pending` → `assigned` → `picked_up` → `delivered`.
    - **Errand**: `pending` → `assigned` → `pending_pricing` → `priced` → `picked_up` → `delivered` / `partially_delivered`.
  - [x] الحالات الإضافية: `cancellation_requested`, `cancelled`, `disputed`.
  - [x] إنشاء `OrderObserver`: استرداد المخزون تلقائياً عند إلغاء طلب `standard` بـ `lockForUpdate()` مع ترتيب المنتجات بـ `sortBy('product_id')` لمنع Deadlocks.
- [x] **نظام القفل (Cache-Based Locking)**:
  - [x] `Cache::put("order_lock:{id}", userId, 90)` عند فتح شاشة التعديل.
  - [x] JS Heartbeat (في `AdminPanelProvider` عبر `BODY_END` RenderHook) كل 30 ثانية يجدد TTL.
  - [x] Route `POST /order/heartbeat/{order}` محمي بـ `auth` middleware.
  - [x] الكاش حالياً `file` — سيتسرع أوتوماتيك عند الانتقال لـ Redis في المرحلة السادسة بدون تغيير كود.
- [x] **مراقب الإجمالي (`ErrandStopObserver`)**:
  - [x] يراقب أحداث `created`, `updated`, `deleted` على `ErrandStop`.
  - [x] حساب الإجمالي: `SUM(receipt_amount WHERE status='purchased') + delivery_fee + (delivery_fee × VAT%) - discount`.
  - [x] **⚡ ثغرة #9 (Double Taxation):** الـ VAT تُحسب فقط على رسوم التوصيل (`shipping`). الفواتير الورقية تنزل زي ما هي.
  - [x] نسبة الضريبة تُجلب ديناميكياً من `config('settings.vat_percentage', 15)`.
  - [x] تغيير حالة الطلب أوتوماتيك لـ `priced` بمجرد تسعير كافة المحطات.
- [x] **مراقب التأخر (`CheckPendingDriverPricing` Command)**:
  - [x] أمر `orders:check-pricing` مُجدول كل 15 دقيقة.
  - [x] يكتشف الطلبات في `pending_pricing` لأكثر من 30 دقيقة.
  - [x] يرسل `Filament Notification` لكل مستخدم `super_admin`.
- [x] **سجل الثغرات المُعالجة (Patch Log - 11 ثغرة)**:

  | # | الثغرة | الحل |
  |---|--------|------|
  | 1 | Cache = file مش Redis | نشتغل بـ file حالياً، ننتقل لـ Redis في المرحلة السادسة |
  | 2 | صورة طلب العميل ضاعت | `customer_image_url` في `errand_stops` |
  | 3 | تغيير `order_type` بعد الإنشاء | `->disabledOn('edit')` في OrderForm |
  | 4 | المخزون مش بيرجع عند الإلغاء | `OrderObserver` + Restock بـ `lockForUpdate` + `sortBy('product_id')` |
  | 5 | إمتى الإجمالي بيتحسب؟ | `ErrandStopObserver` عند كل تحديث لمحطة |
  | 6 | `pricing_method=driver` بدون API | readOnly + "بانتظار التسعير" حتى المرحلة الخامسة |
  | 7 | `financial_status` مفقود من `$fillable` | تمت إضافته في Model + Badge في الجدول |
  | 8 | مفيش حالة للمحطة الفردية | `status` Enum (`pending`/`purchased`/`not_found`) في `errand_stops` |
  | 9 | ضريبة مزدوجة (Double Taxation) | VAT على `delivery_fee` فقط، مش على الفواتير الورقية |
  | 10 | تسلسل الحالات خاطئ (Pipeline Paradox) | `assigned` قبل `pending_pricing` |
  | 11 | نقل ملفات وهمي (Storage Disk Disaster) | `Storage::move()` للملفات الفيزيائية من public لـ private |

---

## 🚚 المرحلة الخامسة: الرحلات المجمعة، محرك التسعير، وواجهات المندوب

### 5.1 — إعدادات صاحب المتجر (Global Settings)
- [ ] إنشاء جدول `settings` (أو استخدام `spatie/laravel-settings`) لتخزين الإعدادات ديناميكياً.
- [ ] بناء صفحة إعدادات في Filament (`SettingsPage`).
- [ ] الإعدادات المطلوبة:

  | الإعداد | النوع | الوصف |
  |---------|-------|-------|
  | `require_receipt_image` | Boolean | إلزام المندوب بتصوير الفاتورة الورقية |
  | `driver_can_cancel_directly` | Boolean | صلاحية الإلغاء المباشر (أو يروح `cancellation_requested`) |
  | `available_payment_methods` | Array | طرق الدفع المتاحة (`cash`, `visa`, `transfer`) |
  | `enable_vat` | Boolean | تفعيل/إيقاف الضريبة |
  | `vat_percentage` | Decimal | نسبة الضريبة (مثلاً 15%) |
  | `currency` | String | العملة (`SAR`, `EGP`, ...) |
  | `delivery_fee_strategy` | Enum | استراتيجية التوصيل (`flat_fee` / `base_plus_stop`) |
  | `base_delivery_fee` | Decimal | سعر التوصيل الأساسي (ثابت) |
  | `extra_stop_fee` | Decimal | سعر إضافي لكل محطة زيادة (لاستراتيجية `base_plus_stop`) |
  | `dispatch_mode` | Enum | طريقة التعيين (`manual` / `auto`) |
  | `enable_batched_orders` | Boolean | السماح بتعيين أكثر من طلب للمندوب في نفس الوقت |
  | `max_orders_per_driver` | Integer | الحد الأقصى لعدد الطلبات المتزامنة للمندوب الواحد (مثلاً 3) |
  | `batch_routing_mode` | Enum | نطاق التجميع: `same_zone` (أي مكان في نفس منطقة المندوب) / `strict_nearby` (في نطاق جغرافي قريب فقط — للمستقبل) |

- [ ] ربط `ErrandStopObserver` بقراءة `vat_percentage` و `enable_vat` من الإعدادات بدلاً من `config()` فقط.
- [ ] ربط `OrderForm.php` بقراءة `require_receipt_image` لإظهار/إخفاء حقل صورة الفاتورة ديناميكياً.
- [ ] ربط `OrderForm.php` بقراءة `available_payment_methods` لعرض طرق الدفع المتاحة فقط.

### 5.2 — محرك التسعير الديناميكي (Strategy Pattern)
- [ ] إنشاء Interface: `DeliveryFeeCalculator` بدالة `calculate(Order $order): float`.
- [ ] إنشاء `FlatFeeStrategy`: يرجع `base_delivery_fee` ثابت بغض النظر عن عدد المحطات.
- [ ] إنشاء `BasePlusStopStrategy`: يحسب `base_delivery_fee + (عدد المحطات × extra_stop_fee)`.
- [ ] ربط الاستراتيجية بإعداد `delivery_fee_strategy` من جدول Settings.
- [ ] استدعاء المحرك من `ErrandStopObserver` و `CreateOrder` لحساب `shipping` أوتوماتيك عند إنشاء أو تحديث الطلب الحر.

### 5.3 — جدول الرحلات المجمعة (`delivery_batches`)
- [ ] إنشاء Migration:

  | الحقل | النوع | الوصف |
  |-------|-------|-------|
  | `id` | BigInt PK | |
  | `driver_id` | FK → drivers | المندوب المعين |
  | `status` | Enum(`active`, `completed`, `cancelled`) | حالة الرحلة |
  | `started_at` | Timestamp, nullable | بداية الرحلة |
  | `completed_at` | Timestamp, nullable | نهاية الرحلة |
  | `timestamps` | | |

- [ ] إضافة `batch_id` FK nullable لجدول `orders`.
- [ ] إنشاء `DeliveryBatch` Model مع علاقة `orders()` (HasMany) و `driver()` (BelongsTo).
- [ ] تحديث `Order` Model بعلاقة `batch()` (BelongsTo).
- [ ] بناء `DeliveryBatchResource` في Filament لعرض الرحلات المجمعة.
- [ ] **Validation عند تعيين طلب لمندوب (يدوي أو آلي):**
  - التحقق من إعداد `enable_batched_orders`: لو مقفول، المندوب لازم يخلص طلبه الحالي الأول.
  - التحقق من `max_orders_per_driver`: لو المندوب وصل للحد الأقصى، السيستم يرفض التعيين ويظهر رسالة خطأ.
  - التحقق من `batch_routing_mode`:
    - `same_zone`: الطلب الجديد لازم يكون عنوان العميل جوه إحدى الـ Zones المربوطة بالمندوب.
    - `strict_nearby`: (للمستقبل) الطلب لازم يكون في نطاق جغرافي قريب من موقع المندوب الحالي.
  - لو كل الشروط اتحققت → يتم إضافة الطلب الجديد إلى الـ `DeliveryBatch` النشطة (`active`) للمندوب، أو إنشاء واحدة جديدة لو مفيش.

### 5.4 — واجهات API للمندوب (Driver Mobile API)
- [ ] إنشاء `api.php` Routes محمية بـ `auth:sanctum`.
- [ ] الـ Endpoints المطلوبة:

  | Method | Endpoint | الوصف |
  |--------|----------|-------|
  | `GET` | `/api/driver/orders` | جلب الطلبات المعينة للمندوب الحالي |
  | `GET` | `/api/driver/orders/{id}` | تفاصيل طلب معين مع المحطات |
  | `PATCH` | `/api/driver/stops/{id}/status` | تحديث حالة محطة فردية (`purchased` / `not_found`) |
  | `POST` | `/api/driver/stops/{id}/receipt` | رفع صورة الفاتورة + كتابة `receipt_amount` |
  | `POST` | `/api/driver/orders/{id}/cancel` | طلب إلغاء (يملأ `cancellation_reason`). سلوكه يعتمد على إعداد `driver_can_cancel_directly` |
  | `POST` | `/api/driver/location` | تحديث موقع المندوب اللحظي (`lat`, `lng`) |
  | `PATCH` | `/api/driver/orders/{id}/status` | تغيير حالة الطلب الكلية (`picked_up` → `delivered`) |

- [ ] **تأمين رفع الصور:**
  - فحص `mimetypes` بـ Magic Bytes (أول بايتات الملف) وليس بالامتداد فقط.
  - التخزين في `Private Disk` (غير متاح للعامة).
  - توليد `signed URL` مؤقت لعرض الصور في الداشبورد.
- [ ] **تأمين التسعير:**
  - `lockForUpdate()` على `ErrandStop` عند تحديث `receipt_amount` لمنع Race Conditions.

### 5.5 — أزرار الإدارة في Filament (Custom Actions)
- [ ] زر **"موافقة على الإلغاء"** في `EditOrder`: يظهر فقط إذا كانت حالة الطلب `cancellation_requested`. عند الضغط:
  - يغير الحالة لـ `cancelled`.
  - يملأ `cancellation_approved_by` بمعرف المشرف.
  - يستدعي `OrderObserver` تلقائياً لاسترداد المخزون (للطلبات `standard`).
- [ ] زر **"رفض الإلغاء"**: يعيد حالة الطلب لـ الحالة السابقة (`assigned` أو `pending_pricing`).
- [ ] زر **"حل النزاع"** في `EditOrder`: يظهر فقط إذا كانت الحالة `disputed`. خيارات:
  - إعادة المبلغ (→ `cancelled` + `financial_status = refunded`).
  - تأكيد التسليم (→ `delivered` + `financial_status = settled`).

### 5.6 — القيود المالية الأساسية والمراقب المالي (تم سحبها من المرحلة السابعة)

> **سبب السحب:** تشغيل طلبات حرة ومحطات متعددة وفلوس رايحة جاية بدون قيود محاسبية (Debit/Credit) تتسجل لحظياً = فوضى مالية. لازم الدفاتر تتفتح من أول يوم تشغيل.

- [ ] إنشاء جدول `financial_ledgers`:

  | الحقل | النوع | الوصف |
  |-------|-------|-------|
  | `id` | BigInt PK | |
  | `order_id` | FK → orders | الطلب المرتبط |
  | `driver_id` | FK → drivers, nullable | المندوب المرتبط |
  | `type` | Enum(`credit`, `debit`) | نوع القيد (إضافة / خصم) |
  | `amount` | Decimal(10,2) | المبلغ |
  | `balance_after` | Decimal(10,2) | الرصيد بعد العملية |
  | `description` | String | وصف القيد |
  | `created_by` | FK → users, nullable | من أنشأ القيد (النظام أو مشرف) |
  | `timestamps` | | |

- [ ] إنشاء `FinancialLedger` Model مع علاقات `order()`, `driver()`, `createdBy()`.
- [ ] إنشاء `FinancialObserver` يراقب تغييرات `status` و `financial_status` على Order.
- [ ] **عند `delivered` أو `partially_delivered`:**
  - إنشاء قيد `debit` على المندوب (المبلغ المحصّل من العميل).
  - إنشاء قيد `credit` للمتجر (حصة المتجر من التوصيل).
  - تحديث `financial_status` إلى `settled`.
- [ ] **عند `cancelled` (بعد التسليم أو الاستلام):**
  - إنشاء قيود عكسية (Reverse Entries) لإلغاء القيود السابقة.
  - تحديث `financial_status` إلى `unsettled`.
- [ ] **عند `disputed`:**
  - تجميد القيود (لا يتم عمل `settled` حتى يتم حل النزاع).
  - عند حل النزاع → تطبيق القيود أو عكسها حسب القرار.
- [ ] كل القيود تتم داخل `DB::transaction` + `lockForUpdate` على رصيد المندوب.

---

## 📡 المرحلة السادسة: التتبع الحي والتوزيع الآلي

### 6.1 — تسطيب Redis والانتقال
- [ ] تسطيب Redis Server على بيئة الإنتاج.
- [ ] تغيير `CACHE_STORE=redis` و `QUEUE_CONNECTION=redis` في `.env`.
- [ ] **أثر فوري:** نظام القفل (Cache-Based Locking) من المرحلة الرابعة هيتسرع أوتوماتيك بدون تغيير كود.

### 6.2 — WebSockets والإشعارات اللحظية
- [ ] تسطيب `Laravel Reverb` كخادم WebSocket.
- [ ] إنشاء Channels:
  - `driver.{id}` — إشعارات المندوب (طلب جديد، موافقة إلغاء، تحديثات).
  - `admin.dashboard` — تحديثات لحظية للوحة التحكم (طلب جديد، تغيير حالة).
  - `order.{id}` — تتبع الطلب (للعميل مستقبلاً).
- [ ] بناء Events: `OrderStatusChanged`, `NewOrderCreated`, `DriverLocationUpdated`.
- [ ] تفعيل إشعارات PWA (Push Notifications) للعميل عبر Service Worker.

### 6.3 — خريطة التتبع الحي
- [ ] بناء صفحة `LiveMap` في Filament باستخدام `Leaflet.js`.
- [ ] عرض مواقع المناديب اللحظية (من بيانات `driver/location` API).
- [ ] عرض مسار الرحلة الحالية لكل مندوب.
- [ ] ألوان مختلفة حسب حالة المندوب (متاح / في مشوار / غير متصل).

### 6.4 — التوزيع الآلي (Auto-Dispatch)
- [ ] تفعيل يعتمد على إعداد `dispatch_mode` من Settings.
- [ ] عند `auto`:
  - عند إنشاء طلب جديد، يبحث عن أقرب مندوب متاح في نفس المنطقة.
  - **يراعي إعدادات التجميع:** لو `enable_batched_orders` مفعّل، يقبل تعيين الطلب لمندوب معاه طلبات بالفعل (طالما لم يتجاوز `max_orders_per_driver` والطلب في نفس الـ Zone حسب `batch_routing_mode`).
  - يضع `Redis Lock` على المندوب بـ TTL 60 ثانية (لمنع تعيينه لطلبين في نفس اللحظة).
  - يرسل WebSocket Notification للمندوب.
  - إذا لم يقبل خلال 60 ثانية → يفك القفل ويعيد التوزيع لمندوب آخر.
- [ ] عند `manual`: التعيين اليدوي من الداشبورد (الوضع الحالي). نفس قواعد الـ Validation بتاعة التجميع (من المرحلة الخامسة) بتنطبق عند التعيين اليدوي.

### 6.5 — Widgets وإحصائيات KPIs
- [ ] بناء Widgets في الصفحة الرئيسية للداشبورد:
  - عدد الطلبات اليوم (حسب النوع والحالة).
  - إجمالي الإيرادات اليومية.
  - عدد المناديب النشطين.
  - متوسط وقت التوصيل.
  - طلبات معلقة تحتاج تدخل (`pending_pricing` > 30 دقيقة).
- [ ] Charts (رسوم بيانية) للأداء الأسبوعي والشهري.

---

## 💰 المرحلة السابعة: شاشات التسويات والتقارير المالية (Audit Dashboard & Reports)

> **ملاحظة:** جدول القيود المالية (`financial_ledgers`) والمراقب المالي (`FinancialObserver`) تم سحبهم للمرحلة الخامسة (قسم 5.6). المرحلة السابعة تركز فقط على **شاشات العرض والتقارير**.

- [ ] بناء `FinancialLedgerResource` في Filament:
  - جدول القيود مع فلاتر (حسب المندوب، التاريخ، النوع).
  - عرض الرصيد الحالي لكل مندوب.
  - زر "تسوية يدوية" للمشرفين.
- [ ] بناء صفحة تقارير:
  - تقرير تسويات المناديب (يومي / أسبوعي / شهري).
  - تقرير حركة المخزون (المنتجات المباعة، المرتجعة).
  - تقرير الطلبات الملغية وأسبابها.
  - تقرير النزاعات المفتوحة.
- [ ] تصدير التقارير بصيغة Excel / PDF.

