FIP Tadbeer API

منصة قياس وتحليل الأداء المالي للمنشآت — توثيق مفصّل
Postman Collection 65 endpoints 9 groups

البدء السريع

الـ Base URL: https://financial.tadbeer.sa/api/v1

  1. اطلب OTP: POST /auth/request-otp ببعت { "email": "..." }
  2. تحقّق: POST /auth/verify-otp ببعت الـ email + code، تستلم Bearer token
  3. استخدم الـ token: في كل request: Authorization: Bearer <token>
  4. لا تنسى: Header Accept: application/json في كل request
دليل الرموز
HTTP Methods:
GET قراءة
POST إنشاء / إجراء
PUT تحديث كامل
PATCH تحديث جزئي
DELETE حذف
مستويات الـ Auth:
🔓 Public بدون auth
🔑 Token يحتاج Bearer token
⭐ Plan يحتاج خطة معينة
🛡️ Admin role=admin فقط
أقسام كل endpoint:
  • الفايدة (Purpose): ليه الـ endpoint ده موجود وإيه القيمة اللي بيقدّمها.
  • إمتى تستخدمه: السيناريو العملي في الـ UI/Flow.
  • الحقول (Fields): شرح كل حقل في الـ request body — إلزامي/اختياري ومعناه.
  • ملاحظات (Notes): تفاصيل مهمة (rate limits, edge cases, gotchas).

تدفّق الـ Authentication كامل

دي السلسلة الكاملة من تسجيل الدخول لاستخدام الـ API:

# 1. Request OTP — يبعت OTP للإيميل
curl -X POST https://financial.tadbeer.sa/api/v1/auth/request-otp \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"email": "your@email.com"}'

# الـ response:
# { "message": "تم إرسال رمز التحقق...", "email": "your@email.com" }

# 2. Check email — افتح storage/logs/laravel.log في dev mode

# 3. Verify OTP — يرجّع Bearer token
curl -X POST https://financial.tadbeer.sa/api/v1/auth/verify-otp \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"email": "your@email.com", "code": "12345678", "device_name": "my-app"}'

# الـ response:
# { "token": "1|abc...xyz", "token_type": "Bearer", "user": {...} }

# 4. استخدم الـ token في كل request
curl https://financial.tadbeer.sa/api/v1/auth/me \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|abc...xyz"

# 5. Logout لما تخلّص
curl -X POST https://financial.tadbeer.sa/api/v1/auth/logout \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|abc...xyz"

1. Authentication

تسجيل دخول بـ OTP عبر الإيميل — يرجّع Sanctum Bearer token

المنصة بتستخدم **OTP عبر الإيميل** كطريقة تسجيل دخول (بدون كلمة مرور). السبب: أبسط للمستخدم وأكثر أماناً من كلمات المرور المسرّبة. كل عملية تسجيل دخول بترجع **Bearer token** لازم تتبعت مع كل request في الـ Header: `Authorization: Bearer <token>`.
POST /api/v1/auth/request-otp 🔓 Public

الوصف: يرسل رمز تحقق (OTP) من 8 أرقام إلى البريد الإلكتروني للمستخدم.

الفايدة (Purpose)

الخطوة الأولى في تسجيل الدخول. لو المستخدم جديد (إيميل غير مسجّل) يتم إنشاء account له تلقائياً.

إمتى تستخدمه

لما المستخدم يدخل إيميله في شاشة الـ Login. اطلب الـ OTP، خبّيه من المستخدم، انتظر يدخله في الخطوة التالية.

{
    "email": "admin@tadbeer.sa"
}
شرح الحقول (1)
email إلزامي — البريد الإلكتروني الصحيح للمستخدم. لو غير مسجّل، يتم إنشاء حساب جديد.
{
    "message": "تم إرسال رمز التحقق إلى بريدك الإلكتروني.",
    "email": "admin@tadbeer.sa",
    "dev_otp": "33029284",
    "dev_note": "الـ OTP ظاهر هنا فقط في بيئة التطوير — في production لازم تيجي من الإيميل.",
    "expires_at": "2026-05-19T06:20:45+00:00"
}
ملاحظات مهمة
  • ⏱️ الـ OTP صالح لمدة **10 دقائق فقط**.
  • 🚫 Rate limit: **10 محاولات في الدقيقة** لكل IP/email. لو تجاوزت ترجع `429 Too Many Requests`.
  • 🛠️ **في بيئة التطوير (APP_ENV != production)** الـ OTP بيتبعت في الـ response فيه field اسمه `dev_otp` عشان تختبر بدون ما تفتح الإيميل.
  • 🚀 **في الإنتاج** الـ `dev_otp` و `dev_note` و `expires_at` بيتشالوا — الـ OTP بييجي بس عبر الإيميل.
  • 📧 محلياً، الإيميل بيتسجّل في `storage/logs/laravel.log` بدل ما يتبعت فعلاً (MAIL_MAILER=log).
POST /api/v1/auth/verify-otp 🔓 Public

الوصف: يتحقق من صحة الـ OTP ويرجّع Bearer token لاستخدامه في كل الـ requests القادمة.

الفايدة (Purpose)

إصدار Sanctum token مرتبط بالمستخدم. الـ token هو الـ credential الوحيد بعد كده — احفظه في secure storage (Keychain/iOS, EncryptedSharedPreferences/Android, HttpOnly cookie/Web).

إمتى تستخدمه

بعد ما المستخدم يستلم الـ OTP في إيميله ويدخله في شاشة التحقق.

{
    "email": "admin@tadbeer.sa",
    "code": "12345678",
    "device_name": "mobile-app"
}
شرح الحقول (3)
email إلزامي — نفس الإيميل اللي طلبت له OTP.
code إلزامي — الـ 8 أرقام اللي وصلت في الإيميل.
device_name اختياري — اسم الجهاز/التطبيق (مثل `iPhone 14 Pro`, `Web Browser`, `postman`). مفيد عشان تشوف كل الـ tokens النشطة وتلغي token معيّن.
{
    "token": "1|abc...xyz",
    "token_type": "Bearer",
    "user": {
        "id": 1,
        "email": "admin@tadbeer.sa",
        "roles": [
            "admin"
        ],
        "current_plan": "premium"
    }
}
ملاحظات مهمة
  • ✅ بعد التحقق الناجح، يتم **مسح الـ OTP** من قاعدة البيانات (مرة استخدام واحدة).
  • ✅ يتم تأكيد الإيميل تلقائياً (`email_verified_at`).
  • 🔐 الـ token format: `<id>|<random_string>` — الجزء قبل `|` هو الـ id والجزء بعده هو الـ secret.
GET /api/v1/auth/me 🔑 Token

الوصف: يرجّع بيانات المستخدم الحالي المرتبط بالـ token.

الفايدة (Purpose)

تأكد إن الـ token لسه شغّال + احصل على معلومات المستخدم (roles, current_plan) عشان تبني الـ UI بناءً عليها.

إمتى تستخدمه

في أول لما الـ frontend يبدأ — للتأكد إن الـ user مسجّل دخول + لمعرفة صلاحياته. ممكن كمان بعد update لخطة الاشتراك.

{
    "user": {
        "id": 1,
        "email": "admin@tadbeer.sa",
        "roles": [
            "admin"
        ],
        "current_plan": "premium"
    }
}
POST /api/v1/auth/logout 🔑 Token

الوصف: يلغي **الـ token الحالي فقط** (الـ token اللي بعتته في الـ request).

الفايدة (Purpose)

تسجيل خروج آمن من جهاز واحد بدون التأثير على باقي الأجهزة.

إمتى تستخدمه

لما المستخدم يضغط زرار "Logout" في التطبيق.

ملاحظات مهمة
  • الجلسات على الأجهزة الأخرى **تفضل شغّالة**.
POST /api/v1/auth/logout-all 🔑 Token

الوصف: يلغي **كل الـ tokens** للمستخدم (تسجيل خروج من كل الأجهزة).

الفايدة (Purpose)

للحماية لو المستخدم شك إن حد دخل على حسابه — يلغي كل الـ sessions النشطة.

إمتى تستخدمه

في إعدادات الحساب: "تسجيل الخروج من كل الأجهزة"، أو بعد تغيير بيانات حساسة.

2. Lookups (Dropdowns)

البيانات الثابتة (dropdowns) المستخدمة في النماذج — متاحة بدون auth

الـ Lookups هي البيانات الثابتة اللي بتظهر في الـ dropdowns (الأنشطة، المدن، أحجام المنشآت، إلخ). هتحتاجها لبناء شاشة تسجيل المنشأة وشاشة البحث في تقرير السوق. **مفيش auth مطلوب** — أي حد يقدر يجيبها.
GET /api/v1/lookups 🔓 Public

الوصف: كل الـ dropdowns في request واحد.

الفايدة (Purpose)

تحميل كل البيانات اللازمة لشاشة تسجيل المنشأة دفعة واحدة — يقلل عدد الـ API calls من 7+ لواحد.

إمتى تستخدمه

في بداية شاشة "تسجيل منشأة جديدة" — استدعيه مرة واحدة واحفظ النتيجة في memory/state عشان تستخدمها في كل الـ dropdowns.

{
    "data_entries": "[4 items] — صفة مدخل البيانات (صاحب المنشأة، مدير مالي، محاسب، أخرى)",
    "main_activities": "[8 items] — الأنشطة الرئيسية (المطاعم، العقاري، الطبي، إلخ)",
    "sub_activities": "[33 items] — كل الأنشطة الفرعية (parent_id يربطها بالنشاط الرئيسي)",
    "cities": "[16 items] — المدن السعودية + خيار \"أخرى\"",
    "facility_sizes": "[6 items] — أحجام المنشأة حسب عدد الموظفين",
    "facility_ages": "[5 items] — عمر المنشأة",
    "filter_by_outlets": "[3 items] — نوع منفذ البيع (أرضي\/إلكتروني\/كلاهما)",
    "products": "[26 items] — قائمة المنتجات (للأنشطة الصناعية والتجارية)"
}
ملاحظات مهمة
  • 💡 كل entry فيه `id` + `titles: {ar, en}` + `title` (باللغة الحالية حسب Accept-Language).
  • 🌐 تقدر تستخدم header `Accept-Language: en` لو عايز الإنجليزي.
GET /api/v1/lookups/sub-activities 🔓 Public

الوصف: الأنشطة الفرعية حسب النشاط الرئيسي المختار.

الفايدة (Purpose)

تنفيذ **Cascading dropdown** — لما المستخدم يختار النشاط الرئيسي تظهر الأنشطة الفرعية المرتبطة فقط.

إمتى تستخدمه

في حدث `change` على dropdown الـ main_activity_id. اعمل request وحدّث الـ sub_activity_id options.

main_activity_id 1 — id النشاط الرئيسي
{
    "data": "[N items] — الأنشطة الفرعية اللي parent_id بتاعتها = main_activity_id"
}
GET /api/v1/lookups/products 🔓 Public

الوصف: قائمة الـ 26 منتج كاملة.

الفايدة (Purpose)

استدعاء قائمة المنتجات بمفردها لما النشاط المختار صناعي أو تجاري.

إمتى تستخدمه

لو محتاج المنتجات بس بدون باقي الـ lookups — مفيد لشاشة منفصلة لإدارة منتجات المنشأة.

ملاحظات مهمة
  • تظهر للمستخدم فقط لو اختار نشاط من نوع "الصناعة" أو "التجارية".

3. Plans (الخطط)

عرض الخطط المتاحة للمستخدم

المنصة فيها **4 خطط**: مجاني (0 مؤشر — تقرير السوق فقط)، أساسي (13 مؤشر، 99 ر.س)، Premium (21 مؤشر، 199 ر.س)، محفظة استثمارية (مع مزايا المحفظة، 299 ر.س). كل مستخدم له خطة واحدة نشطة في كل وقت.
GET /api/v1/plans 🔓 Public

الوصف: كل الخطط النشطة مرتبة بالـ order.

الفايدة (Purpose)

عرض الخطط في صفحة "الاشتراكات" أو "الترقية".

إمتى تستخدمه

صفحة pricing/upgrade — قبل ما المستخدم يقرر يشترك.

{
    "data": "[4 items] — كل خطة فيها: slug, ar_title, en_title, indicators_count, features {portfolio, year_changes, financing}, price"
}
GET /api/v1/plans/{plan} 🔓 Public

الوصف: تفاصيل خطة واحدة بالـ id.

الفايدة (Purpose)

صفحة تفاصيل الخطة قبل الاشتراك.

إمتى تستخدمه

لما المستخدم يضغط على خطة معينة لمعرفة تفاصيلها كاملة.

4. Company (المنشأة)

إدارة منشأة المستخدم — واحدة فقط لكل user

كل مستخدم له **منشأة واحدة فقط**. المنشأة بتربط المستخدم بنشاط معيّن + مدينة + حجم + عمر + (اختياري) قائمة منتجات. لازم تتسجّل قبل ما يقدر يضيف بيانات مالية أو يشوف تقارير.
GET /api/v1/companies 🔑 Token

الوصف: منشأة المستخدم الحالي مع كل الـ relations + كل الـ accounts (السنوات المالية).

الفايدة (Purpose)

تحميل البيانات الكاملة للمنشأة في صفحة "ملفي" أو لما تبدأ التطبيق.

إمتى تستخدمه

بعد تسجيل الدخول مباشرة، للتأكد إن المستخدم له منشأة. لو ترجع `404` معناه لازم تعرض شاشة "سجّل منشأتك".

{
    "data": "Company object مع: data_entry, main_activity, sub_activity, facility_size, facility_age, city, filter_by_outlet, products[], accounts[]"
}
POST /api/v1/companies 🔑 Token

الوصف: تسجيل منشأة جديدة (مرة واحدة فقط).

الفايدة (Purpose)

الخطوة الأولى لكل مستخدم بعد تسجيل الدخول. لو حاول يعمل create مرتين يرجّع `409 Conflict`.

إمتى تستخدمه

في شاشة "تسجيل منشأة جديدة" أول مرة المستخدم يدخل التطبيق.

{
    "name": "شركتي التجارية",
    "data_entry_id": 1,
    "main_activity_id": 9,
    "sub_activity_id": 10,
    "facility_size_id": null,
    "city_id": null,
    "other_city": null,
    "facility_age_id": null,
    "filter_by_outlet_id": null,
    "portfolio_code": null,
    "excerpt": "نبذة عن المنشأة",
    "agreement": true,
    "product_ids": [
        1,
        2,
        3
    ]
}
شرح الحقول (13)
name اختياري — اسم تجاري للمنشأة. لو فاضي يتعرض كـ "منشأة #ID".
data_entry_id إلزامي — id من lookups.data_entries — صفة من يدخل البيانات.
main_activity_id إلزامي — id من lookups.main_activities — النشاط الرئيسي.
sub_activity_id إلزامي — id من lookups.sub_activities (parent_id لازم = main_activity_id).
facility_size_id اختياري — حجم المنشأة. مهم للمقارنة في التقارير.
city_id اختياري — المدينة. لو اخترت "أخرى" استخدم `other_city` لاسم نصي.
other_city اختياري — اسم مدينة نصياً لو غير موجودة في القائمة.
facility_age_id اختياري — عمر المنشأة. مهم للمقارنة.
filter_by_outlet_id اختياري — نوع منفذ البيع.
portfolio_code اختياري — كود محفظة خاصة (لـ Premium plan فقط) — لربط عدة منشآت بمحفظة.
excerpt اختياري — وصف مختصر.
agreement إلزامي — لازم `true` (موافقة على شروط الاستخدام).
product_ids اختياري — array بـ ids المنتجات (للأنشطة الصناعية/التجارية فقط).
ملاحظات مهمة
  • ⚠️ كل مستخدم له منشأة واحدة فقط. لو حاولت تعمل تانية يرجّع `409`.
  • 🛒 `product_ids` مفيد للأنشطة الصناعية أو التجارية لتحديد المنتجات اللي بتتعامل فيها.
PUT /api/v1/companies 🔑 Token

الوصف: تحديث جزئي لبيانات المنشأة.

الفايدة (Purpose)

تعديل بيانات المنشأة بعد التسجيل (مثلاً تغيير الاسم، إضافة كود محفظة، تحديث المدينة).

إمتى تستخدمه

في صفحة "إعدادات المنشأة".

{
    "name": "الاسم الجديد",
    "excerpt": "نبذة محدّثة"
}
ملاحظات مهمة
  • كل الحقول `sometimes` — ابعت اللي عايز تعدّله فقط.

5. Accounts (البيانات المالية السنوية)

إدخالات Excel Sheets 5 و 6 — 24 حقل لكل سنة مالية

دي **القلب الأساسي للنظام** — البيانات المالية اللي يدخلها المستخدم لكل سنة (revenue، تكاليف، مصروفات، أرصدة، إلخ). من البيانات دي يتم حساب الـ 13 مؤشر اللي بتظهر في التقارير.

كل entry لسنة واحدة، ولكل منشأة. المستخدم يقدر يدخل بيانات سنوات متعددة (سنة سابقة لمقارنتها بالحالية).
GET /api/v1/accounts 🔑 Token

الوصف: كل البيانات المالية لكل السنوات، مرتبة بالأحدث.

الفايدة (Purpose)

عرض جدول بكل السنوات اللي المستخدم سجّلها + المؤشرات المحسوبة لكل سنة.

إمتى تستخدمه

في صفحة "بياناتي المالية" — جدول للسنوات والمؤشرات لكل سنة.

{
    "data": "[N items] — كل entry فيه: الـ 24 حقل + `computed` بـ 10 مؤشرات محسوبة"
}
ملاحظات مهمة
  • ✨ كل entry فيه `computed` object فيه: `net_profit`, `profit_margin`, `expenses_ratio`, `marketing_ratio`, `customers_turnover`, `inventory_turnover`, `suppliers_turnover`, `return_on_investment`, `operating_cash_surplus`, `year_end_cash_surplus`
GET /api/v1/accounts/{year} 🔑 Token

الوصف: بيانات سنة محددة.

الفايدة (Purpose)

تحميل بيانات سنة واحدة لتعديلها أو لعرض تفاصيلها.

إمتى تستخدمه

في شاشة "تعديل بيانات سنة" — استدعي البيانات الحالية قبل عرض الـ form.

{
    "data": "Account object كامل مع `computed`"
}
POST /api/v1/accounts 🔑 Token

الوصف: إنشاء أو تحديث بيانات سنة مالية (24 حقل من Excel Sheet 5).

الفايدة (Purpose)

الـ endpoint الأكثر استخداماً — لإدخال البيانات المالية. لو السنة موجودة بالفعل يتم **تحديث** بدل إنشاء (upsert).

إمتى تستخدمه

في شاشة "إدخال بيانات سنة" — بعد ما المستخدم يملا كل الحقول الإلزامية يضغط حفظ.

{
    "fiscal_year": 2024,
    "revenue": 1000000,
    "purchases": 600000,
    "cost": 500000,
    "cost_goods_sold": 300000,
    "cost_direct_salaries": 150000,
    "cost_other_direct": 50000,
    "general_expenses": 200000,
    "gen_emp_expenses": 100000,
    "gen_rent_expenses": 50000,
    "gen_admin_expenses": 30000,
    "gen_training_expenses": 20000,
    "marketing_expenses": 100000,
    "mkt_salaries_commissions": 60000,
    "mkt_ads_campaigns": 40000,
    "fixed_assets": 300000,
    "customers_balance": 100000,
    "inventory_balance": 200000,
    "suppliers_balance": 150000,
    "accrued_expenses": 50000,
    "loans_balance": 200000,
    "property_rights": 500000,
    "loans_increase": 50000,
    "capital_increase": 100000
}
شرح الحقول (24)
fiscal_year إلزامي — السنة المالية (مثلاً 2024). فريدة لكل منشأة.
revenue إلزامي — إجمالي الإيرادات/المبيعات خلال السنة (بدون الضريبة).
purchases اختياري — إجمالي المشتريات من الموردين. مهم! بيدخل في حساب دوران المخزون والموردين.
cost إلزامي (إجمالي) — إجمالي التكلفة (المصروفات المباشرة).
cost_goods_sold جزئي — تكلفة البضاعة المباعة (من البرنامج المحاسبي).
cost_direct_salaries جزئي — رواتب مهنيين/فنيين/عمالة مباشرة.
cost_other_direct جزئي — تكاليف مباشرة أخرى (استشاريين، عقود خدمات).
general_expenses إلزامي (إجمالي) — المصروفات العمومية والإدارية.
gen_emp_expenses جزئي — مصاريف الموظفين الإداريين.
gen_rent_expenses جزئي — مصاريف الإيجارات.
gen_admin_expenses جزئي — مصاريف إدارية متنوعة (رسوم، استشارات، صيانة، نقل، مطبوعات، ماء، كهرباء).
gen_training_expenses جزئي — التطوير والتدريب.
marketing_expenses إلزامي (إجمالي) — مصروفات التسويق.
mkt_salaries_commissions جزئي — رواتب وعمولات التسويق والمبيعات.
mkt_ads_campaigns جزئي — مصاريف المطبوعات والإعلانات والحملات.
fixed_assets إلزامي — الأصول الثابتة (مباني، سيارات، أثاث، أجهزة — بدون الأراضي). يدخل في معادلة الفائض النقدي.
customers_balance إلزامي — رصيد العملاء آخر السنة (مديونيات المبيعات الآجلة).
inventory_balance إلزامي — رصيد المخزون آخر السنة.
suppliers_balance إلزامي — رصيد الموردين آخر السنة (المشتريات الآجلة).
accrued_expenses إلزامي — المصروفات المستحقة (رواتب/إيجارات لم تدفع). تطرح في الفائض النقدي.
loans_balance إلزامي — رصيد القروض آخر السنة.
property_rights إلزامي — رأس المال / حقوق الملكية (رأس المال + أرباح مبقاة).
loans_increase إلزامي — الزيادة في القروض خلال السنة (قروض جديدة).
capital_increase إلزامي — الزيادة في رأس المال خلال السنة.
ملاحظات مهمة
  • 🔄 **Upsert** — لو السنة موجودة يتم update، غير كده create.
  • 📊 الحقول "الجزئي" اختيارية لكن مفيدة للـ Premium plan (المؤشرات التحليلية 14-21).
  • 💡 الإجمالي ≥ مجموع الجزئيات (مفروض، لكن مفيش validation لذلك).
  • 📎 لرفع ملف PDF (القوائم المالية) لازم تستخدم `multipart/form-data` بدل JSON، مع field اسمه `financial_statements_file`.
DELETE /api/v1/accounts/{year} 🔑 Token

الوصف: حذف بيانات سنة (soft delete — يمكن استرجاعها).

الفايدة (Purpose)

لو المستخدم أدخل بيانات سنة بالغلط، يقدر يحذفها. الحذف soft delete عشان نقدر نسترجعها من الأدمن لو احتجنا.

إمتى تستخدمه

في زرار "حذف" بجانب كل سنة في جدول البيانات المالية. اعرض confirmation قبل ما تستدعي الـ endpoint.

6. Reports (التقارير)

التقارير الـ 3 من Excel (Sheets 7, 8, 9) — 13 مؤشر لكل تقرير

**أهم جزء في المنصة** — التقارير اللي بتقارن أداء المستخدم بالسوق والمنافسين. كل تقرير بيرجع نفس الـ 13 مؤشر لكن بـ scope مختلف:

- **Facility Report**: المنشأة الواحدة vs متوسط القطاع vs الأفضل (Benchmark)
- **Portfolio Report**: المحفظة (مجموعة منشآت) vs متوسط السوق vs الأفضل
- **Market Report**: متوسط السوق vs الأفضل (للجميع، بدون auth)
GET /api/v1/reports/facility ⭐ Plan: basic+

الوصف: تقرير المنشأة (Excel Sheet 7) — يقارن منشأتك بمتوسط القطاع وأفضل منشأة.

الفايدة (Purpose)

الـ endpoint الأهم للمستخدم — يحلل أداء منشأته. التقرير يرجّع 3 صفوف (my-company / avg / benchmark) × 13 مؤشر.

إمتى تستخدمه

في الصفحة الرئيسية للمستخدم بعد إدخال بيانات سنة. ممكن يفلتر بسنة معينة.

year 2024 — *اختياري*، لو ما اتبعت يجيب آخر سنة
{
    "indicators": "[13 definitions] — تعريفات المؤشرات (slug, ar, en, unit, direction)",
    "report": {
        "my-company": "{revenue, profit_margin, expenses_ratio, ... year_end_cash_surplus}",
        "avg": "{...متوسط القطاع لنفس النشاط\/المدينة\/الحجم}",
        "benchmark": "{...أعلى قيمة في القطاع لكل مؤشر}"
    },
    "meta": "{company_id, company_name, fiscal_year}"
}
ملاحظات مهمة
  • ⭐ يحتاج **plan: basic أو أعلى** — لو الخطة free يرجّع `403` مع `required_feature: basic`.
  • 🎯 الفلترة في الـ avg/benchmark بتكون تلقائية حسب نفس النشاط/المدينة/الحجم/العمر بتاع المنشأة.
GET /api/v1/reports/portfolio/{portfolio} ⭐ Plan: portfolio+

الوصف: تقرير المحفظة (Excel Sheet 8) — يحلل أداء مجموعة منشآت كمحفظة استثمارية.

الفايدة (Purpose)

للمستثمر اللي عنده عدة منشآت — يشوف أداء المحفظة ككل ومقارنتها بالسوق.

إمتى تستخدمه

في صفحة "تقرير المحفظة" — بعد ما المستخدم ينشئ محفظة ويربطها بمنشآت.

year 2024 — *اختياري*
ملاحظات مهمة
  • ⭐ يحتاج **plan: portfolio** — أعلى خطة في المنصة.
  • 🔒 المستخدم يقدر يشوف محافظه فقط — لو حاول يشوف محفظة تانية يرجّع `403`.
GET /api/v1/reports/market 🔓 Public

الوصف: تقرير السوق (Excel Sheet 9) — متوسط السوق vs الأفضل، للجميع.

الفايدة (Purpose)

تقرير عام للجميع (بدون auth) — يخلي زوار الموقع يشوفوا قوة المنصة قبل ما يسجّلوا. كمان مفيد للمسجّلين عشان يبحثوا في قطاع معين.

إمتى تستخدمه

في الصفحة الرئيسية للموقع كـ teaser، أو في شاشة "ابحث في السوق" بفلاتر مختلفة.

main_activity_id *اختياري*
sub_activity_id *اختياري*
facility_size_id *اختياري*
city_id *اختياري*
facility_age_id *اختياري*
filter_by_outlet_id *اختياري*
fiscal_year *اختياري*
ملاحظات مهمة
  • 🌍 متاح للجميع — مفيد لـ SEO والـ teaser content.

7. Portfolios (المحافظ الاستثمارية)

مجموعة منشآت يديرها مستخدم واحد — يحتاج plan: portfolio

المحفظة الاستثمارية = مجموعة منشآت لها مستثمر واحد. مفيد للأشخاص اللي عندهم عدة شركات وعايزين يحللوا أداءها مجتمعة. **كل الـ endpoints دي محصورة على المشتركين في خطة portfolio (أعلى خطة).**
GET /api/v1/portfolios ⭐ Plan: portfolio+

الوصف: كل المحافظ بتاعة المستخدم.

الفايدة (Purpose)

عرض قائمة المحافظ في صفحة "محافظي".

{
    "data": "[N items] — كل محفظة فيها: name, code (PF-XXXX), companies_count"
}
POST /api/v1/portfolios ⭐ Plan: portfolio+

الوصف: إنشاء محفظة جديدة.

الفايدة (Purpose)

إضافة محفظة جديدة. الـ code يتم إنشاؤه تلقائياً بصيغة `PF-XXXXXXXX`.

إمتى تستخدمه

لما المستخدم يضغط "محفظة جديدة" في صفحة المحافظ.

{
    "name": "محفظة الشرقية"
}
شرح الحقول (1)
name إلزامي — اسم المحفظة.
GET /api/v1/portfolios/{portfolio} ⭐ Plan: portfolio+

الوصف: تفاصيل المحفظة + كل الشركات المرتبطة بها.

الفايدة (Purpose)

صفحة عرض المحفظة — تشوف الشركات وتحلل الأداء.

POST /api/v1/portfolios/{portfolio}/companies ⭐ Plan: portfolio+

الوصف: إضافة شركة للمحفظة.

الفايدة (Purpose)

ربط شركة موجودة بمحفظة. الشركة لازم تكون بتاعة نفس المستخدم.

إمتى تستخدمه

في صفحة المحفظة، زرار "إضافة شركة".

{
    "company_id": 1
}
DELETE /api/v1/portfolios/{portfolio}/companies/{company} ⭐ Plan: portfolio+

الوصف: إزالة شركة من المحفظة (مش حذف الشركة).

الفايدة (Purpose)

فك الربط بدون حذف الشركة نفسها.

DELETE /api/v1/portfolios/{portfolio} ⭐ Plan: portfolio+

الوصف: حذف المحفظة (الشركات تفضل موجودة).

الفايدة (Purpose)

لما المستخدم ميحتاجش المحفظة. الشركات بتاعتها تتفك تلقائياً.

8. Subscriptions (الاشتراكات)

إدارة اشتراك المستخدم

المستخدم يقدر يطلب اشتراك في خطة. **الخطط المدفوعة تنتظر تفعيل أدمن يدوياً** (مفيش بوابة دفع حالياً). الخطة المجانية تتفعّل تلقائياً.
GET /api/v1/subscriptions 🔑 Token

الوصف: تاريخ اشتراكات المستخدم (الحالية والسابقة).

الفايدة (Purpose)

عرض تاريخ الاشتراكات في صفحة "حسابي".

{
    "data": "[N items] — كل اشتراك فيه: plan, starts_at, ends_at, is_active, is_currently_active"
}
POST /api/v1/subscriptions 🔑 Token

الوصف: طلب اشتراك في خطة.

الفايدة (Purpose)

إنشاء طلب اشتراك. الـ workflow:
1. لو الخطة `price = 0` (مجاني) → يتفعّل تلقائياً (`is_active = true`)
2. لو الخطة مدفوعة → يتسجّل كـ pending وينتظر أدمن يفعّله

إمتى تستخدمه

لما المستخدم يضغط "اشترك في هذه الخطة" في صفحة Plans.

{
    "plan_id": 3,
    "notes": "محتاج Premium لاستخدام محاسبي"
}
شرح الحقول (2)
plan_id إلزامي — id الخطة المطلوبة.
notes اختياري — ملاحظة للأدمن (مثلاً رقم العملية بعد الدفع).
ملاحظات مهمة
  • ⏳ بعد الإنشاء، الـ response.message هتقول "تم الإرسال" أو "ينتظر تفعيل" حسب نوع الخطة.
  • 💳 المستخدم لازم يدفع خارج المنصة (تحويل بنكي مثلاً) ويبعت ملاحظة، ثم الأدمن يفعّل يدوياً.

9. Admin (لوحة الإدارة)

كل الـ endpoints بتاع الإدارة — يحتاج role: admin

لوحة الإدارة لإدارة المنصة: الخطط، الاشتراكات (تفعيل يدوي)، المنتجات، الـ Lookups، الشركات. **كل الـ endpoints دي محصورة على المستخدمين اللي عندهم role = admin** (يتعيّن من قاعدة البيانات أو عبر Spatie Permission).
GET /api/v1/admin/dashboard 🛡️ Admin only

الوصف: إحصائيات عامة عن المنصة.

الفايدة (Purpose)

صفحة الـ overview في لوحة الأدمن.

{
    "counts": "{users, companies, accounts, portfolios, products, plans, active_subscriptions, pending_subscriptions}"
}
GET /api/v1/admin/subscriptions 🛡️ Admin only

الوصف: كل الاشتراكات (paginated).

الفايدة (Purpose)

صفحة إدارة الاشتراكات — اللي فيها الأدمن يشوف الاشتراكات المعلّقة ويفعّلها بعد تأكيد الدفع.

status *اختياري* — `active` أو `pending` للفلترة
POST /api/v1/admin/subscriptions 🛡️ Admin only

الوصف: إنشاء اشتراك يدوي لمستخدم.

الفايدة (Purpose)

الأدمن يقدر يضيف اشتراك لمستخدم بدون ما يكون طلبه (مثلاً عميل خاص).

{
    "user_id": 1,
    "plan_id": 3,
    "starts_at": "2026-05-19",
    "ends_at": null,
    "is_active": true,
    "notes": "تفعيل من أدمن"
}
PATCH /api/v1/admin/subscriptions/{subscription}/toggle 🛡️ Admin only

الوصف: تفعيل أو إلغاء تفعيل اشتراك (toggle).

الفايدة (Purpose)

**أهم action في لوحة الأدمن** — تفعيل الاشتراكات المدفوعة المعلّقة بعد تأكيد الدفع.

إمتى تستخدمه

الأدمن يستقبل تأكيد الدفع → يدخل صفحة الاشتراكات → يضغط "تفعيل" بجانب الاشتراك.

DELETE /api/v1/admin/subscriptions/{subscription} 🛡️ Admin only

الوصف: حذف اشتراك.

GET /api/v1/admin/plans 🛡️ Admin only

الوصف: كل الخطط (بما في ذلك الـ inactive).

الفايدة (Purpose)

صفحة إدارة الخطط — تعديل الأسعار، إضافة خطط جديدة، تعطيل خطة قديمة.

POST /api/v1/admin/plans 🛡️ Admin only

الوصف: إنشاء خطة جديدة.

الفايدة (Purpose)

لإضافة خطة مخصصة (مثلاً Enterprise، أو Trial 7 أيام).

{
    "slug": "enterprise",
    "en_title": "Enterprise",
    "ar_title": "مؤسسي",
    "description": "خطة للشركات الكبيرة",
    "indicators_count": 21,
    "has_portfolio": true,
    "has_year_changes": true,
    "has_financing": true,
    "price": 999,
    "is_active": true,
    "order": 5
}
PUT /api/v1/admin/plans/{plan} 🛡️ Admin only

الوصف: تحديث خطة موجودة.

الفايدة (Purpose)

مثلاً تغيير سعر، أو تعطيل خطة قديمة، أو تعديل المميزات.

DELETE /api/v1/admin/plans/{plan} 🛡️ Admin only

الوصف: حذف خطة.

ملاحظات مهمة
  • ⚠️ احذر — لو فيه اشتراكات نشطة على الخطة دي.
GET /api/v1/admin/products 🛡️ Admin only

الوصف: كل المنتجات (paginated).

الفايدة (Purpose)

إدارة قائمة المنتجات الـ 26 اللي تظهر للأنشطة الصناعية والتجارية.

POST /api/v1/admin/products 🛡️ Admin only

الوصف: إضافة منتج جديد.

{
    "ar_title": "منتج جديد",
    "en_title": "New Product",
    "order": 27
}
PUT /api/v1/admin/products/{product} 🛡️ Admin only

الوصف: تحديث منتج.

DELETE /api/v1/admin/products/{product} 🛡️ Admin only

الوصف: حذف منتج.

GET /api/v1/admin/constants 🛡️ Admin only

الوصف: كل الـ lookups (الـ dropdowns).

الفايدة (Purpose)

إدارة كل الـ dropdowns: الأنشطة، المدن، أحجام المنشآت، إلخ.

post_type *اختياري* — مثلاً `cities`, `main_activities`, `sub_activities`
parent_id *اختياري* — للفلترة على الـ children (مثلاً sub_activities of activity X)
POST /api/v1/admin/constants 🛡️ Admin only

الوصف: إضافة قيمة lookup جديدة.

الفايدة (Purpose)

مثلاً إضافة مدينة جديدة، أو نشاط فرعي جديد.

{
    "ar_title": "نشاط جديد",
    "en_title": "New Activity",
    "post_type": "sub_activities",
    "parent_id": 5,
    "order": 99
}
PUT /api/v1/admin/constants/{constant} 🛡️ Admin only

الوصف: تحديث lookup.

DELETE /api/v1/admin/constants/{constant} 🛡️ Admin only

الوصف: حذف lookup (soft delete).

GET /api/v1/admin/companies 🛡️ Admin only

الوصف: كل الشركات (paginated).

الفايدة (Purpose)

الأدمن يشوف كل الشركات المسجّلة في المنصة + يقدر يبحث.

search *اختياري* — بحث في اسم الشركة أو الـ code
GET /api/v1/admin/companies/{company} 🛡️ Admin only

الوصف: تفاصيل شركة محددة + كل بياناتها المالية.

DELETE /api/v1/admin/companies/{company} 🛡️ Admin only

الوصف: حذف شركة (soft delete).

GET /api/v1/admin/users 🛡️ Admin only

الوصف: كل المستخدمين (paginated).

الفايدة (Purpose)

إدارة المستخدمين — عرض، بحث، فلتر بالـ role.

search *اختياري* — بحث في email أو name
role *اختياري* — admin أو user
{
    "data": "[{ id, name, email, roles, current_plan, company, active_subscription }]"
}
POST /api/v1/admin/users 🛡️ Admin only

الوصف: إنشاء مستخدم جديد.

الفايدة (Purpose)

إضافة مستخدم يدوياً (مثلاً موظف جديد، أو مستخدم خاص).

{
    "name": "اسم المستخدم",
    "email": "new@tadbeer.sa",
    "password": "secret-password",
    "roles": [
        "user"
    ]
}
شرح الحقول (4)
name إلزامي — اسم المستخدم.
email إلزامي — لازم يكون فريد.
password اختياري — لو فاضي يتم توليده عشوائياً (المستخدم يستخدم OTP).
roles اختياري — array من admin/user.
GET /api/v1/admin/users/{user} 🛡️ Admin only

الوصف: تفاصيل كاملة عن مستخدم.

الفايدة (Purpose)

صفحة تفاصيل المستخدم: roles + company + subscriptions + portfolios + active tokens.

{
    "data": "{ id, name, email, email_verified_at, roles, current_plan, has_company, company, subscriptions_count, subscriptions, active_subscription, portfolios_count, tokens_count, created_at }"
}
PUT /api/v1/admin/users/{user} 🛡️ Admin only

الوصف: تحديث بيانات المستخدم.

الفايدة (Purpose)

تعديل الاسم/الإيميل/كلمة المرور/الأدوار — كل الحقول `sometimes`.

{
    "name": "اسم محدّث",
    "email": "updated@tadbeer.sa",
    "roles": [
        "admin",
        "user"
    ]
}
DELETE /api/v1/admin/users/{user} 🛡️ Admin only

الوصف: حذف مستخدم + إلغاء كل tokens.

ملاحظات مهمة
  • ⚠️ الأدمن مش قادر يحذف حسابه الخاص (يرجع 422).
POST /api/v1/admin/users/{user}/roles 🛡️ Admin only

الوصف: إضافة دور للمستخدم.

الفايدة (Purpose)

ترقية مستخدم لأدمن مثلاً.

{
    "role": "admin"
}
DELETE /api/v1/admin/users/{user}/roles/{role} 🛡️ Admin only

الوصف: إزالة دور من المستخدم.

الفايدة (Purpose)

إلغاء صلاحية الأدمن مثلاً.

POST /api/v1/admin/users/{user}/revoke-tokens 🛡️ Admin only

الوصف: إلغاء كل الـ tokens النشطة للمستخدم (force logout).

الفايدة (Purpose)

لو فيه شك في اختراق حساب — يجبر المستخدم على تسجيل دخول من جديد.

{
    "message": "...",
    "revoked_tokens": 3
}
GET /api/v1/admin/companies/{company}/accounts 🛡️ Admin only

الوصف: كل السنوات المالية لشركة محددة.

الفايدة (Purpose)

الأدمن يشوف كل البيانات المالية لأي شركة (مش بس بتاعته). كل entry فيه `computed` بالمؤشرات المحسوبة.

إمتى تستخدمه

في صفحة "تفاصيل شركة" في لوحة الأدمن — لمراجعة بيانات الشركة قبل تفعيل اشتراك أو حل مشكلة.

{
    "company": {
        "id": 1,
        "name": "Test Co"
    },
    "data": "[N items] — كل سنة مع computed indicators"
}
GET /api/v1/admin/companies/{company}/accounts/{year} 🛡️ Admin only

الوصف: بيانات سنة مالية واحدة لشركة محددة.

الفايدة (Purpose)

استعراض تفاصيل سنة معينة لأي شركة.

DELETE /api/v1/admin/companies/{company}/accounts/{year} 🛡️ Admin only

الوصف: حذف بيانات سنة (soft delete).

الفايدة (Purpose)

لو الشركة دخلت بيانات غلط، الأدمن يقدر يحذفها.

GET /api/v1/admin/companies/{company}/report 🛡️ Admin only

الوصف: تقرير المنشأة لأي شركة (Sheet 7 — 13 مؤشر).

الفايدة (Purpose)

الأدمن يقدر يشوف تقرير أي شركة بدون قيود الـ plan. response.meta فيه `owner_email` لمعرفة المالك.

إمتى تستخدمه

لـ debugging أو دعم عملاء — تشوف نفس التقرير اللي العميل بيشوفه.

year 2024 — *اختياري*
{
    "indicators": "[13 definitions]",
    "report": {
        "my-company": "...",
        "avg": "...",
        "benchmark": "..."
    },
    "meta": {
        "company_id": 1,
        "company_name": "...",
        "owner_email": "...",
        "fiscal_year": 2024
    }
}
GET /api/v1/admin/portfolios/{portfolio}/report 🛡️ Admin only

الوصف: تقرير المحفظة لأي محفظة (Sheet 8).

الفايدة (Purpose)

الأدمن يشوف تقرير المحفظة لأي مستخدم بدون قيود.

year 2024 — *اختياري*
GET /api/v1/admin/users/{user}/report 🛡️ Admin only

الوصف: اختصار: تقرير المنشأة من user_id مباشرة.

الفايدة (Purpose)

بدلاً من جلب company_id الأول، يقدر تجيب التقرير من user_id مباشرة. مفيد لما تكون شغّال في صفحة "تفاصيل مستخدم".

year 2024 — *اختياري*
ملاحظات مهمة
  • لو المستخدم ما عندوش منشأة → 404 مع رسالة واضحة.
GET /api/v1/admin/portfolios 🛡️ Admin only

الوصف: كل المحافظ عبر كل المستخدمين (paginated).

الفايدة (Purpose)

الأدمن يشوف كل المحافظ الموجودة في المنصة + إحصائيات.

search *اختياري* — بحث في name أو code
user_id *اختياري* — فلتر بمستخدم معيّن
GET /api/v1/admin/portfolios/{portfolio} 🛡️ Admin only

الوصف: تفاصيل محفظة + المالك + الشركات بداخلها.

الفايدة (Purpose)

استعراض كامل لمحفظة + بيانات مالكها.

DELETE /api/v1/admin/portfolios/{portfolio} 🛡️ Admin only

الوصف: حذف محفظة (الشركات بداخلها تتفك بدون حذف).

أكواد الأخطاء (HTTP Status Codes)

كل الـ errors بترجع بـ JSON بنفس الشكل الموحّد: {"message": "...", "errors": {...}}

Codeالمعنىالسبب الشائعمثال response
200 نجاح الـ request اشتغل تمام {"data": {...}}
201 تم الإنشاء POST لإنشاء resource جديد نجح {"data": {...}}
401 Unauthenticated الـ token مفقود أو منتهي أو غلط {"message": "Unauthenticated"}
403 Forbidden الخطة الحالية لا تسمح بهذا الـ endpoint {"message": "...", "required_feature": "portfolio", "current_plan": "basic"}
404 Not Found الـ resource غير موجود (مثلاً user مفيش عنده منشأة) {"message": "..."}
409 Conflict محاولة إنشاء resource موجود بالفعل (مثلاً منشأة تانية) {"message": "Company already registered"}
422 Validation Failed الـ request body فيه أخطاء (missing fields, wrong types) {"message": "...", "errors": {"email": ["..."]}}
429 Rate Limited تجاوزت العدد المسموح من الـ requests {"message": "...", "retry_after": 60}
500 Server Error خطأ في السيرفر — راجع logs

المؤشرات الـ 13 (من Excel Sheet 7)

دي المؤشرات اللي بتطلع في كل التقارير. كل مؤشر له unit (amount/ratio/percent) و direction (higher_better/lower_better).

#المؤشرالـ Slugالوحدةالاتجاه الأفضل
1 الإيرادات (المبيعات)
Revenue
revenue amount ⬆️ أعلى أفضل
2 هامش ربح المبيعات
Profit Margin
profit_margin percent ⬆️ أعلى أفضل
3 نسبة المصروفات / الإيرادات
Expenses / Revenue
expenses_ratio percent ⬇️ أقل أفضل
4 مصروفات التسويق
Marketing Expenses
marketing_ratio percent ⬇️ أقل أفضل
5 صافي الربح / الخسارة
Net Profit / Loss
net_profit amount ⬆️ أعلى أفضل
6 معدل دوران مديونيات العملاء
Customer Receivables Turn
customers_turnover ratio ⬆️ أعلى أفضل
7 معدل دوران المخزون
Inventory Turnover
inventory_turnover ratio ⬆️ أعلى أفضل
8 معدل دوران مديونيات الموردين
Suppliers Payables Turn
suppliers_turnover ratio ⬆️ أعلى أفضل
9 رصيد القروض
Loans Balance
loans_balance amount ⬆️ أعلى أفضل
10 رأس المال (حقوق الملكية)
Capital / Equity
property_rights amount ⬇️ أقل أفضل
11 العائد / الاستثمار
Return on Investment
roi percent ⬆️ أعلى أفضل
12 الفائض النقدي من التشغيل
Operating Cash Surplus
operating_cash_surplus amount ⬆️ أعلى أفضل
13 الفائض النقدي آخر السنة
Year-end Cash Surplus
year_end_cash_surplus amount ⬆️ أعلى أفضل
⚠️ تغييرات مهمة من Excel:
  • دوران المخزون = purchases / inventory_balance (كان revenue/inventory في النسخة القديمة)
  • دوران الموردين = purchases / suppliers_balance (كان revenue/suppliers)
  • 🆕 الفائض النقدي من التشغيل = net_profit + (fixed_assets × 0.10) − accrued_expenses
  • 🆕 الفائض النقدي آخر السنة = operating_cash + loans_increase + capital_increase

المعادلات الحسابية بالتفصيل

كل العمليات الحسابية المستخدمة في حساب المؤشرات والمقارنات

1. الحقول المُدخَلة من المستخدم (Inputs)

كل الحسابات بتعتمد على 24 حقل بيدخلهم المستخدم لكل سنة مالية:

المتغير المعنى
revenueإجمالي الإيرادات (المبيعات) خلال السنة — إلزامي
purchasesإجمالي المشتريات من الموردين خلال السنة
costالتكلفة (المصروفات المباشرة) — إلزامي
general_expensesالمصروفات العمومية والإدارية — إلزامي
marketing_expensesمصروفات التسويق — إلزامي
fixed_assetsالأصول الثابتة (بدون الأراضي) — إلزامي
customers_balanceرصيد العملاء آخر السنة
inventory_balanceرصيد المخزون آخر السنة
suppliers_balanceرصيد الموردين آخر السنة
accrued_expensesالمصروفات المستحقة (لم تدفع)
loans_balanceرصيد القروض آخر السنة
property_rightsرأس المال / حقوق الملكية
loans_increaseالزيادة في القروض خلال السنة
capital_increaseالزيادة في رأس المال خلال السنة
2. معادلات المؤشرات الـ 13

كل المؤشرات بتتحسب في App\Helpers\CompanyHelper::indicators():

1️⃣ الإيرادات (Revenue)
revenue = revenue
قيمة مباشرة من الإدخال — مقدار ر.س.
2️⃣ هامش ربح المبيعات (Profit Margin)
profit_margin = (revenue - cost) / revenue
نسبة (0–1) → تعرض كنسبة مئوية. مثال: 0.5 = 50%. الأعلى أفضل.

📌 لو revenue = 0 → النتيجة 0 (حماية من القسمة على صفر).

3️⃣ نسبة المصروفات / الإيرادات (Expenses Ratio)
expenses_ratio = (cost + general_expenses + marketing_expenses) / revenue
نسبة من الإيرادات تذهب للمصروفات. الأقل أفضل.
4️⃣ نسبة مصروفات التسويق (Marketing Ratio)
marketing_ratio = marketing_expenses / revenue
نسبة مصاريف التسويق من الإيرادات. الأقل أفضل.
5️⃣ صافي الربح/الخسارة (Net Profit) ⭐
net_profit = revenue - cost - general_expenses - marketing_expenses
المبلغ بالـ ر.س. أساسي لحساب ROI و Operating Cash Surplus.
6️⃣ معدل دوران مديونيات العملاء (Customers Turnover)
customers_turnover = revenue / customers_balance
عدد مرات تحصيل مديونيات العملاء سنوياً. الأعلى أفضل.
7️⃣ معدل دوران المخزون (Inventory Turnover) — ⚠️ تغيّر
inventory_turnover = purchases / inventory_balance
تغيير مهم: في النسخة القديمة كانت revenue / inventory_balance، الآن استبدلناها بـ purchases لأنها الأدق محاسبياً.
8️⃣ معدل دوران مديونيات الموردين (Suppliers Turnover) — ⚠️ تغيّر
suppliers_turnover = purchases / suppliers_balance
تغيير مهم: كانت revenue / suppliers_balance، الآن أصبحت تستخدم المشتريات.
9️⃣ رصيد القروض (Loans Balance)
loans_balance = loans_balance
قيمة مباشرة من الإدخال.
🔟 رأس المال (Property Rights / Capital)
property_rights = property_rights
قيمة مباشرة من الإدخال — رأس المال + الأرباح المبقاة.
1️⃣1️⃣ العائد على الاستثمار (ROI)
roi = net_profit / property_rights
   = (revenue - cost - general_expenses - marketing_expenses) / property_rights
نسبة (0–1) — تعرض كنسبة مئوية. الأعلى أفضل.
1️⃣2️⃣ الفائض النقدي من التشغيل (Operating Cash Surplus) 🆕
operating_cash_surplus = net_profit + (fixed_assets × 0.10) - accrued_expenses
المبلغ النقدي المتاح من العمليات التشغيلية.

التحليل المنطقي:

  • net_profit: الربح الصافي
  • + (fixed_assets × 0.10): إضافة استهلاك تقديري (10% من الأصول الثابتة) باعتباره مصروف غير نقدي
  • - accrued_expenses: طرح المصروفات المستحقة (لم تُدفع نقداً)
1️⃣3️⃣ الفائض النقدي آخر السنة (Year-end Cash Surplus) 🆕
year_end_cash_surplus = operating_cash_surplus + loans_increase + capital_increase
                      = [net_profit + (fixed_assets × 0.10) - accrued_expenses]
                      + loans_increase + capital_increase
إجمالي السيولة آخر السنة بعد إضافة التمويلات الجديدة.
3. عمليات التجميع (Aggregations)

كل تقرير بيقارن منشأة المستخدم بـ متوسط القطاع وأفضل منشأة. الـ aggregations بتتم في SQL:

متوسط القطاع (avg)
SELECT
    AVG(revenue) as revenue,
    AVG(cost) as cost,
    AVG(purchases) as purchases,
    AVG(general_expenses) as general_expenses,
    ...
FROM accounts
INNER JOIN companies ON companies.id = accounts.company_id
WHERE companies.deleted_at IS NULL
  AND accounts.deleted_at IS NULL
  AND companies.main_activity_id = ?
  AND companies.sub_activity_id = ?
  AND companies.facility_size_id = ?
  AND companies.city_id = ?
  AND companies.facility_age_id = ?
يحسب متوسط كل حقل لمنشآت نفس القطاع (نفس النشاط/المدينة/الحجم/العمر).
أفضل منشأة (benchmark / pioneer)
SELECT
    MAX(revenue) as revenue,
    MAX(cost) as cost,
    ...
FROM accounts INNER JOIN companies ...
WHERE [same filters as avg]
يحسب القيمة القصوى لكل حقل في نفس القطاع — تمثيل لـ "الرائد".
⚠️ ملاحظة هامة: الـ MAX على المؤشرات المعكوسة (مثل expenses_ratio حيث الأقل أفضل) ممكن يكون misleading. الـ benchmark بيرجع أعلى قيمة لكل عمود مدخل، والمؤشرات بتتحسب من القيم دي بعد كده. النظام يحدد direction (higher/lower_better) لكل مؤشر لما يعرض المقارنة.
4. منطق المقارنة (Comparison Logic)

في الـ views/reports، كل قيمة بتتقارن بمتوسط السوق لتحديد إن كانت أعلى/مقاربة/أقل:

// نسبة الفرق
diff_percent = ((my_value - avg_value) / avg_value) × 100

// التصنيف
if (abs(diff_percent) <= 10):
    analysis = "مقارب للمتوسط (± 10%)"
    color    = neutral (gray)

else if (diff_percent > 0):
    // قيمتي أعلى من المتوسط
    if (indicator.direction === "higher_better"):
        analysis = "أعلى من المتوسط ✓"
        color    = positive (green)
    else:  // lower_better
        analysis = "أعلى من المتوسط ✗"
        color    = negative (red)

else:  // diff_percent < 0
    // قيمتي أقل من المتوسط
    if (indicator.direction === "lower_better"):
        analysis = "أقل من المتوسط ✓"
        color    = positive (green)
    else:  // higher_better
        analysis = "أقل من المتوسط ✗"
        color    = negative (red)
أمثلة عملية
مؤشرمنشأتيالمتوسطالفرقالاتجاهالتحليل
هامش الربح50%35%+42.9%higher_betterأعلى ✓
نسبة المصروفات80%85%-5.9%lower_betterمقارب
دوران المخزون3x5x-40%higher_betterأقل ✗
رأس المال500K600K-16.6%lower_betterأقل ✓
💡 رأس المال "الأقل أفضل" لأنه يدل على كفاءة استخدام رأس المال — حقّق ربح أعلى بـ رأس مال أقل.
5. اتجاه كل مؤشر (Direction)

في المؤشرات الـ 13، بعضها "الأعلى أفضل" وبعضها "الأقل أفضل":

الاتجاهالمؤشراتالسبب
⬆️ Higher Better (10) revenue, profit_margin, net_profit, customers_turnover, inventory_turnover, suppliers_turnover, loans_balance, roi, operating_cash_surplus, year_end_cash_surplus كلها مؤشرات على نمو/كفاءة/ربحية أعلى
⬇️ Lower Better (3) expenses_ratio, marketing_ratio, property_rights الأقل = تحكّم أفضل في المصروفات / كفاءة أعلى في استخدام رأس المال
💭 ملاحظة عن loans_balance: حالياً مُصنف "higher_better" — قد يحتاج مراجعة (القروض الأقل عادة أفضل، لكن الأكثر يعني وصول أعلى لتمويل).
6. تنسيق الأرقام (Number Formatting)

في CompanyHelper::formatValue():

switch (unit):
    case 'percent':
        // المؤشرات: profit_margin, expenses_ratio, marketing_ratio, roi
        return number_format(value × 100, 2) + '%'
        // مثال: 0.4 → "40.00%"

    case 'ratio':
        // المؤشرات: customers_turnover, inventory_turnover, suppliers_turnover
        return number_format(value, 2)
        // مثال: 3.5 → "3.50"

    case 'amount':
        // المؤشرات: revenue, net_profit, loans_balance, property_rights,
        //          operating_cash_surplus, year_end_cash_surplus
        return number_format(value, 2)
        // مثال: 1000000 → "1,000,000.00"
7. أين تجد الكود
الملفالوظيفة
app/Helpers/CompanyHelper.phpكل المعادلات الـ 13 + الـ aggregations في indicators() و buildAggregateQuery()
app/Models/Account.phpنفس المعادلات كـ accessors على Eloquent model (مثلاً $account->net_profit)
app/Helpers/CompanyHelper::facilityComparison()تقرير المنشأة: my-company vs avg vs benchmark
app/Helpers/CompanyHelper::portfolioComparison()تقرير المحفظة
app/Helpers/CompanyHelper::marketComparison()تقرير السوق
tests/Unit/CompanyHelperTest.php12 unit test يثبت صحة كل المعادلات
8. مثال عملي كامل

منشأة بيانات السنة المالية 2024:

الحقلالقيمة
revenue1,000,000
purchases600,000
cost500,000
general_expenses200,000
marketing_expenses100,000
fixed_assets300,000
customers_balance100,000
inventory_balance200,000
suppliers_balance150,000
accrued_expenses50,000
property_rights500,000
loans_increase50,000
capital_increase100,000

المؤشرات المحسوبة:

المؤشرالمعادلةالناتج
profit_margin(1,000,000 - 500,000) / 1,000,0000.50 → 50%
expenses_ratio(500K + 200K + 100K) / 1,000,0000.80 → 80%
marketing_ratio100,000 / 1,000,0000.10 → 10%
net_profit1M - 500K - 200K - 100K200,000 ر.س
customers_turnover1,000,000 / 100,00010x
inventory_turnover600,000 / 200,0003x
suppliers_turnover600,000 / 150,0004x
roi200,000 / 500,0000.40 → 40%
operating_cash_surplus200,000 + (300,000 × 0.10) - 50,000
= 200,000 + 30,000 - 50,000
180,000 ر.س
year_end_cash_surplus180,000 + 50,000 + 100,000330,000 ر.س