# 📋 خطة تطوير ERP مصغر — منصة Turista (النسخة النهائية)

## 📌 ملخص القرارات المعتمدة

| السؤال | القرار |
|---|---|
| **هيكل المحفظة** | محفظة رئيسية للمالك + محافظ فرعية لكل عقار |
| **منصة الاستخدام** | API موحد — يشتغل للموبايل والويب |
| **الحجوزات النقدية** | مربوطة بجدول `tbl_booking` ومعلّمة كـ `cash` |
| **الاشتراك** | مجاني حالياً — معمارية جاهزة للتحويل لاحقاً |
| **الموظفون** | نفس `staff` table الموجودة + نظام تسوية محاسبية |

---

## 🏗️ بنية المحافظ المعتمدة

```
TblUser (المالك)
│
├── wallet [slug: 'default']              → محفظة Turista (العمولات، التسويات)
│
├── wallet [slug: 'internal']             → المحفظة الداخلية الرئيسية للمالك
│   └── meta: { type: 'internal', level: 'owner' }
│
├── wallet [slug: 'property_12']          → محفظة عقار رقم 12
│   └── meta: { type: 'internal', level: 'property', property_id: 12 }
│
└── wallet [slug: 'property_37']          → محفظة عقار رقم 37
    └── meta: { type: 'internal', level: 'property', property_id: 37 }
```

**القاعدة:** كل عقار له محفظة مستقلة. التقارير تجمع عبر الـ slug.  
**لا جداول إضافية للمحافظ أو المعاملات** — كل شيء في bavix/wallet الموجود.

---

## 🔵 المرحلة الأولى — المحفظة الداخلية + تسجيل الكاش

### 1.1 — إنشاء/جلب المحفظة

```php
// helper في TblUser.php
public function getOrCreateInternalWallet(): Wallet
{
    return $this->getWallet('internal')
        ?? $this->createWallet([
            'name' => 'المحفظة الداخلية',
            'slug' => 'internal',
            'meta' => ['type' => 'internal', 'level' => 'owner'],
        ]);
}

public function getOrCreatePropertyWallet(int $propertyId): Wallet
{
    $slug = "property_{$propertyId}";
    return $this->getWallet($slug)
        ?? $this->createWallet([
            'name' => "محفظة عقار #{$propertyId}",
            'slug' => $slug,
            'meta' => ['type' => 'internal', 'level' => 'property', 'property_id' => $propertyId],
        ]);
}

// accessor: رصيد عقار محدد
public function getPropertyWalletBalance(int $propertyId): float
{
    return $this->getWallet("property_{$propertyId}")?->balanceFloat ?? 0.0;
}
```

### 1.2 — تسجيل معاملة دخل (income)

```php
// دخل على عقار محدد
$wallet = $owner->getOrCreatePropertyWallet($propertyId);

$wallet->depositFloat($amount, [
    'type'             => 'income',
    'category'         => 'cash_booking',    // من owner_cash_categories
    'description'      => 'حجز نقدي - عميل أحمد',
    'property_id'      => $propertyId,
    'booking_id'       => $bookingId,        // ربط بالحجز في tbl_booking
    'created_by_id'    => $staffOrOwnerId,
    'created_by_type'  => 'staff',           // 'owner' أو 'staff'
    'transaction_date' => '2026-05-23',
]);
```

### 1.3 — تسجيل معاملة مصروف (expense)

```php
$wallet = $owner->getOrCreatePropertyWallet($propertyId);

$wallet->withdrawFloat($amount, [
    'type'             => 'expense',
    'category'         => 'maintenance',
    'description'      => 'صيانة مكيف الغرفة 3',
    'property_id'      => $propertyId,
    'created_by_id'    => $staffOrOwnerId,
    'created_by_type'  => 'staff',
    'transaction_date' => '2026-05-23',
]);
```

### 1.4 — ربط الحجز النقدي بجدول tbl_booking

عند تسجيل حجز كاش، يُسجَّل في `tbl_booking` بقيمة:
```
booking_source = 'cash'   ← إضافة قيمة جديدة (حالياً: 'mobile', 'web')
payment_type   = 'cash'
state_id       = STATE_COMPLETED  ← مباشرة مكتمل
```

ثم يُربط `booking_id` في meta المعاملة كما في 1.2.

### 1.5 — الجدول الوحيد الجديد المطلوب

#### `owner_cash_categories`
```sql
id          BIGINT PK AUTO_INCREMENT
owner_id    BIGINT NULL FK → tbl_user.id   -- NULL = تصنيف نظام
name        VARCHAR(100)                    -- 'صيانة'
name_en     VARCHAR(100)                    -- 'Maintenance'
type        ENUM('income','expense')
icon        VARCHAR(50)                     -- اسم الأيقونة
is_system   TINYINT(1) DEFAULT 0           -- 1 = لا يُحذف
is_active   TINYINT(1) DEFAULT 1
created_at  TIMESTAMP
updated_at  TIMESTAMP
```

**Seeder — تصنيفات الدخل (is_system=1):**

| slug | name_ar | name_en |
|---|---|---|
| `cash_booking` | حجز نقدي | Cash Booking |
| `deposit_received` | عربون مستلم | Deposit Received |
| `extra_services` | خدمات إضافية | Extra Services |
| `equipment_rental` | إيجار معدات | Equipment Rental |
| `other_income` | دخل آخر | Other Income |

**Seeder — تصنيفات المصاريف (is_system=1):**

| slug | name_ar | name_en |
|---|---|---|
| `maintenance` | صيانة | Maintenance |
| `salaries` | رواتب | Salaries |
| `utilities` | مرافق (كهرباء/ماء) | Utilities |
| `cleaning` | تنظيف | Cleaning |
| `marketing` | تسويق | Marketing |
| `other_expense` | مصروف آخر | Other Expense |

### 1.6 — API Endpoints (موحد للموبايل والويب)

#### [NEW] `InternalWalletController`
```
# ── المحفظة ──────────────────────────────────────────
GET    /api/owner/wallets                            → قائمة كل المحافظ (رئيسية + عقارات)
POST   /api/owner/wallets/setup                      → إنشاء المحفظة الرئيسية (أول مرة)
POST   /api/owner/wallets/property/{propertyId}      → إنشاء/جلب محفظة عقار

# ── المعاملات ─────────────────────────────────────────
POST   /api/owner/wallets/{slug}/income              → تسجيل دخل
POST   /api/owner/wallets/{slug}/expense             → تسجيل مصروف
GET    /api/owner/wallets/{slug}/transactions        → سجل المعاملات (فلاتر: نوع، تصنيف، تاريخ)
DELETE /api/owner/wallets/{slug}/transactions/{id}   → حذف/عكس معاملة (reverse)

# ── الملخص ────────────────────────────────────────────
GET    /api/owner/wallets/{slug}/summary             → رصيد + إجمالي دخل/مصاريف/صافي
GET    /api/owner/wallets/{slug}/summary/monthly     → ملخص شهري
GET    /api/owner/wallets/all/summary                → ملخص شامل لكل المحافظ
```

#### [NEW] `CashCategoryController`
```
GET    /api/owner/cash-categories                    → كل التصنيفات (نظام + مخصصة)
GET    /api/owner/cash-categories?type=income        → تصفية حسب النوع
POST   /api/owner/cash-categories                    → إضافة تصنيف مخصص
PUT    /api/owner/cash-categories/{id}               → تعديل (المخصصة فقط)
DELETE /api/owner/cash-categories/{id}               → حذف (المخصصة فقط)
```

---

## 🟠 المرحلة الثانية — تسوية الموظفين (Staff Cash Reconciliation)

### 2.1 — المشكلة التي نحلها

الموظف يستلم كاش من العملاء → المحاسب يحتاج أن يعرف:
- كم استلم كل موظف؟
- هل تم تسليم المبالغ للمحاسب؟
- ما رصيد كل موظف الحالي غير المسوّى؟

### 2.2 — التعديل على جدول `staff` (حقول جديدة)

```sql
ALTER TABLE staff ADD COLUMN cash_received DECIMAL(12,2) DEFAULT 0 
    COMMENT 'إجمالي الكاش المستلم غير المسوّى';
ALTER TABLE staff ADD COLUMN last_reconciled_at TIMESTAMP NULL
    COMMENT 'آخر تاريخ تسوية محاسبية';
```

### 2.3 — جدول التسويات المحاسبية (جديد)

#### `staff_cash_reconciliations`
```sql
id                  BIGINT PK AUTO_INCREMENT
property_id         BIGINT FK → tbl_property_detail.id
staff_id            BIGINT FK → staff.id              -- الموظف المسوَّى معه
reconciled_by_id    BIGINT FK → staff.id              -- المحاسب الذي أجرى التسوية
period_from         DATE                               -- بداية الفترة
period_to           DATE                               -- نهاية الفترة
total_cash_received DECIMAL(12,2)                     -- إجمالي الكاش المستلم
total_expenses      DECIMAL(12,2)                     -- المصاريف المعتمدة
net_amount          DECIMAL(12,2)                     -- الصافي المسلّم
notes               TEXT NULL
status              ENUM('open','closed') DEFAULT 'open'
closed_at           TIMESTAMP NULL
created_at          TIMESTAMP
updated_at          TIMESTAMP
```

### 2.4 — آلية العمل

```
الموظف يسجل حجز كاش (income) 
    ↓
تتراكم المعاملات في wallet العقار (meta.created_by_id = staff.id)
    ↓
المحاسب يفتح صفحة "تسوية الموظفين"
    ↓
يرى: كل موظف + إجمالي الكاش الذي سجّله في الفترة
    ↓
يضغط "إغلاق الحساب" → يُنشئ سجل في staff_cash_reconciliations
    ↓
cash_received للموظف يُعاد إلى 0 بعد التسوية
```

### 2.5 — API Endpoints للتسوية

#### [NEW] `StaffReconciliationController`
```
# ── عرض أرصدة الموظفين ────────────────────────────────
GET  /api/owner/reconciliation/staff-balances           → أرصدة كل الموظفين (كاش غير مسوّى)
GET  /api/owner/reconciliation/staff/{staffId}/details  → تفاصيل معاملات موظف محدد

# ── إجراء التسوية ─────────────────────────────────────
POST /api/owner/reconciliation/close                    → إغلاق حساب موظف (تسوية)
     Body: { staff_id, property_id, period_from, period_to, notes }

# ── سجل التسويات ──────────────────────────────────────
GET  /api/owner/reconciliation/history                  → تاريخ التسويات
GET  /api/owner/reconciliation/{id}                     → تفاصيل تسوية محددة
```

### 2.6 — منطق حساب رصيد الموظف

```php
// ما سجّله موظف محدد من دخل كاش في عقار
$staffIncome = $wallet->transactions()
    ->where('type', 'deposit')                          // income في bavix = deposit
    ->whereJsonContains('meta->created_by_id', $staffId)
    ->whereJsonContains('meta->created_by_type', 'staff')
    ->whereBetween('created_at', [$from, $to])
    ->sum('amount') / 100;                              // bavix يخزن بالسنت
```

---

## 🟢 المرحلة الثالثة — تقارير ERP الشاملة

### 3.1 — لوحة التحكم (Dashboard)

```
GET /api/owner/erp/dashboard
```

**تُجمع من:**
- ✅ دخل Turista → `tbl_booking` + `tbl_transaction` (booking_source = mobile/web)
- ✅ دخل كاش → bavix wallet transactions (type=income, wallet slug=property_*)
- ✅ مصاريف → bavix wallet transactions (type=expense)

**Response:**
```json
{
  "period": "2026-05",
  "turista_income": 12500,
  "cash_income": 8200,
  "total_income": 20700,
  "total_expenses": 4300,
  "net_profit": 16400,
  "by_property": [
    { "property_id": 12, "name": "استراحة النخيل", "income": 9000, "expenses": 2100 },
    { "property_id": 37, "name": "فيلا السلام",    "income": 11700, "expenses": 2200 }
  ]
}
```

### 3.2 — التقارير

```
GET /api/owner/erp/report/monthly?year=2026&month=5     → تقرير شهري
GET /api/owner/erp/report/by-property?from=&to=         → مقارنة بين العقارات
GET /api/owner/erp/report/by-category?property_id=      → مصاريف حسب التصنيف
GET /api/owner/erp/report/export?format=pdf             → تصدير PDF/Excel
```

### 3.3 — جدول الملخصات الشهرية (لتسريع التقارير)

#### `owner_monthly_summaries` (اختياري — للأداء)
```sql
id               BIGINT PK
owner_id         BIGINT FK
property_id      BIGINT FK NULL
year             SMALLINT
month            TINYINT
turista_income   DECIMAL(12,2)
cash_income      DECIMAL(12,2)
total_expenses   DECIMAL(12,2)
net_profit       DECIMAL(12,2)
calculated_at    TIMESTAMP
```

يُحدَّث بـ **Scheduled Job** يومياً.

---

## 🗺️ خريطة التطوير الكاملة

### المرحلة 1 — الأساس (4-5 أسابيع)

| # | المهمة | الأولوية |
|---|---|---|
| 1 | Migration: `owner_cash_categories` | 🔴 عالي |
| 2 | Seeder: التصنيفات الافتراضية | 🔴 عالي |
| 3 | إضافة `booking_source = 'cash'` في `TblBooking` | 🔴 عالي |
| 4 | Helper methods في `TblUser` | 🔴 عالي |
| 5 | `InternalWalletController` (income/expense/summary) | 🔴 عالي |
| 6 | `CashCategoryController` | 🟡 متوسط |
| 7 | API Routes + Swagger docs | 🟡 متوسط |

### المرحلة 2 — تسوية الموظفين (3-4 أسابيع)

| # | المهمة | الأولوية |
|---|---|---|
| 1 | Migration: `staff_cash_reconciliations` + حقول `staff` | 🔴 عالي |
| 2 | `StaffReconciliationController` | 🔴 عالي |
| 3 | منطق حساب رصيد الموظف من meta | 🔴 عالي |
| 4 | صفحة ويب: تسوية الموظفين (للمحاسب) | 🟡 متوسط |

### المرحلة 3 — تقارير ERP (3-4 أسابيع)

| # | المهمة | الأولوية |
|---|---|---|
| 1 | `ERPDashboardController` | 🔴 عالي |
| 2 | تقارير شهرية + حسب عقار + حسب تصنيف | 🟡 متوسط |
| 3 | Scheduled Job للملخصات الشهرية | 🟡 متوسط |
| 4 | تصدير PDF/Excel | 🟢 منخفض |

---

## ⚙️ التعديلات التقنية على الكود الموجود

### [MODIFY] `app/Models/TblBooking.php`
```php
// إضافة قيمة جديدة لـ booking_source
protected $attributes = [
    'booking_source' => 'mobile',  // الحالي
];
// القيم المسموحة: 'mobile', 'web', 'cash'  ← جديدة

// Helper
public function isCashBooking(): bool
{
    return $this->booking_source === 'cash';
}
```

### [MODIFY] `app/Models/TblUser.php`
```php
// إضافة helper methods للمحافظ الداخلية (كما في 1.1)
public function getOrCreateInternalWallet(): Wallet { ... }
public function getOrCreatePropertyWallet(int $propertyId): Wallet { ... }
public function getPropertyWalletBalance(int $propertyId): float { ... }

// علاقة مع التصنيفات المخصصة
public function cashCategories()
{
    return $this->hasMany(OwnerCashCategory::class, 'owner_id');
}
```

### [NEW] `app/Models/OwnerCashCategory.php`
```php
class OwnerCashCategory extends Model
{
    protected $table = 'owner_cash_categories';

    public function scopeForOwner($query, $ownerId)
    {
        return $query->where(function($q) use ($ownerId) {
            $q->where('owner_id', $ownerId)
              ->orWhereNull('owner_id'); // التصنيفات الافتراضية
        })->where('is_active', true);
    }

    public function scopeIncome($query) { return $query->where('type', 'income'); }
    public function scopeExpense($query) { return $query->where('type', 'expense'); }
}
```

### [NEW] `app/Models/StaffCashReconciliation.php`
```php
class StaffCashReconciliation extends Model
{
    protected $table = 'staff_cash_reconciliations';

    public function staff()       { return $this->belongsTo(Staff::class, 'staff_id'); }
    public function reconciledBy(){ return $this->belongsTo(Staff::class, 'reconciled_by_id'); }
    public function property()    { return $this->belongsTo(TblPropertyDetail::class, 'property_id'); }

    public function scopeOpen($query)   { return $query->where('status', 'open'); }
    public function scopeClosed($query) { return $query->where('status', 'closed'); }
}
```

---

## 🔐 الصلاحيات (بسيطة — لا Spatie جديد)

نستخدم `role_id` الموجود في `staff`:

| Role | يسجّل دخل/مصروف | يرى تقارير | يُسوّي الموظفين | يُغلق الحساب |
|---|:---:|:---:|:---:|:---:|
| Co-Admin (1) | ✅ | ✅ | ✅ | ✅ |
| Booking (2) | ✅ | ❌ | ❌ | ❌ |
| Finance/محاسب (3) | ✅ | ✅ | ✅ | ✅ |

> التحقق في كل Controller: `if ($staff->role_id !== 3 && $staff->role_id !== 1) abort(403);`

---

## 📱 تجربة المستخدم المقترحة

### صاحب الاستراحة (Mobile):
```
الشاشة الرئيسية لعقاري:
┌────────────────────────────────┐
│ 🏠 استراحة النخيل              │
│ 💰 الرصيد: 4,500 ريال          │
│                                │
│ [+ دخل نقدي] [+ مصروف]        │
│                                │
│ آخر المعاملات                  │
│ ✅ حجز نقدي   +1,200  أمس      │
│ 🔧 صيانة      -350   السبت     │
│ 💡 كهرباء     -280   الجمعة    │
└────────────────────────────────┘
```

### صاحب المنتجع (Web — المحاسب):
```
تسوية الموظفين:
┌──────────┬──────────┬──────────┬──────────┐
│ الموظف   │ استلم   │ مصاريف  │ الصافي  │
├──────────┼──────────┼──────────┼──────────┤
│ أحمد     │ 3,200   │ 200      │ 3,000   │
│ محمد     │ 1,800   │ 0        │ 1,800   │
└──────────┴──────────┴──────────┴──────────┘
[إغلاق حساب أحمد] [إغلاق حساب محمد]
```

---

## 📦 ملخص الجداول الجديدة

| الجدول | الغرض | المرحلة |
|---|---|---|
| `owner_cash_categories` | تصنيفات الدخل والمصاريف | 1 |
| `staff_cash_reconciliations` | تسوية الموظفين | 2 |
| `owner_monthly_summaries` | تسريع التقارير (اختياري) | 3 |

**المحافظ والمعاملات:** لا جداول إضافية — تخزَّن في `wallets` و`transactions` الخاصة بـ bavix/wallet.
