إصلاح انحدار أدخلتُه: /admin/overview كان يستعلم عن `t.created_at` وهو غير موجود في كيان الرحلة (الاسم `requested_at`)، فترجع النقطة 500 ويُفرَّغ جدول المستأجرين في لوحة السوبر-أدمن. فحص الأنواع لم يمسكه لأن استعلامات QueryBuilder نصوص، والمحكّ الذي كان سيمسكه لم يُشغَّل. قرارات المالك 2026-07-18: - الخرائط: `intaleq_maps` حصراً (SDK انطلق على MapLibre). تصحيح قرار سابق خاطئ: flutter_map + latlong2 طبقة منافسة تعطي نوعَي LatLng متضاربين. السبب ليس عمل جوجل في سوريا — بل قِدَم بياناتها في المنطقة. - docs/24 جديد: فلسفة المال — فصل الإيراد (شحن السائق · رسوم العمليات) عن الأمانة (شحن الراكب)، بمحفظتين لا بحقل حالة، ودفتر مضاف فقط. - P موسّعة (بوابات · تسوية بالرسائل لكليك/شام كاش · رسوم بالدولة · تقارير) و O5 (ترتيب المهام المجدولة)، وتأجيل Android Auto/CarPlay. - الحزم: نسخ سيرو الأحدث + live_activities لشاشة القفل في iOS. تنظيف: إخراج 463 ملف بناء NDK من التتبّع، وإخراج شهادات التوقيع ومفتاح App Store من المستودع (تُولَّد من جديد عند الحاجة) مع تحديث .gitignore. الملفات باقية على القرص — أُزيلت من الفهرس فقط. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
79 lines
7.0 KiB
Markdown
79 lines
7.0 KiB
Markdown
# 24 — محفظة المستأجر وإيرادات المنصة (فلسفة المال)
|
||
|
||
> قرارات المالك 2026-07-18. هذا المستند يحكم **كل** ما يتعلّق بحركة المال، ويسبق أي كود في المجموعة P.
|
||
> ذو صلة: [18](18-driver-credit-commission.md) (الرصيد والعمولة) · [22](22-full-product-roadmap.md) §P · [[siro-reference]].
|
||
|
||
---
|
||
|
||
## 1. القاعدة الحاكمة: مالٌ لنا، ومالٌ نحتفظ به لغيرنا
|
||
|
||
الخطأ الذي يقع فيه أغلب التطبيقات — **وسيرو منها** — هو خلط النوعين في وعاء واحد، فيبدو الرصيد ضخماً بينما أغلبه دَيْن على المنصة لا ملكاً لها.
|
||
|
||
| النوع | مثاله | مِلك مَن؟ | في الميزانية |
|
||
|---|---|---|---|
|
||
| **إيراد** | شحن السائق لرصيده التشغيلي · عمولة كل عملية دفع | **المستأجر** (مالك التطبيق) | دخل مُحقَّق |
|
||
| **أمانة (pending)** | شحن الراكب لمحفظته · أجرة رحلة لم تُسوَّ بعد | **الراكب/السائق** | **التزام**، لا دخل |
|
||
|
||
> **المحصّلة العملية**: نقود الأمانة تمرّ عبر حسابنا لكنها ليست لنا — سنسلّمها للسائق أو نردّها للراكب. عدّها إيراداً يعني تقريراً ماليّاً كاذباً، وقراراً بالإنفاق من مالٍ ليس لنا.
|
||
|
||
## 2. محفظتا المستأجر
|
||
|
||
لكل مستأجر (مثال: `siro`) محفظتان منفصلتان — لا حقل واحد بحالتين:
|
||
|
||
| المحفظة | يدخلها | يخرج منها |
|
||
|---|---|---|
|
||
| **`tenant_wallet`** (محفظة المستأجر — «سيرو والت») | شحن الرصيد التشغيلي للسائقين · عمولة عمليات الدفع · عمولة الرحلات | سحب المالك لأرباحه |
|
||
| **`tenant_pending`** (أمانات) | شحن الراكب · أجرة محصَّلة لم تُسلَّم | تسوية للسائق · ردّ للراكب |
|
||
|
||
**لماذا محفظتان لا حقل `status`**: التقارير والسحب يقرآن `tenant_wallet` وحده. لو كان الفصل بحقلٍ في صفوف واحدة، فاستعلامٌ واحد ينسى شرط الحالة يجعل المالك يسحب من أموال الركّاب — وهذا خطأ يقع مرة واحدة ويكلّف ثقة السوق.
|
||
|
||
**دفتر مضاف فقط (append-only)** لكل محفظة: كل حركة صفٌّ جديد بمرجعها وسببها، لا تحديثٌ لرصيد. الرصيد مجموعٌ مشتق. (سيرو خزّن المال في `varchar(10)` وجمع نصوصاً — لا يُنقل هذا إطلاقاً.)
|
||
|
||
## 3. مسارات المال الثلاثة
|
||
|
||
### 3.1 شحن السائق رصيده التشغيلي ← **إيراد مباشر**
|
||
هذا هو نموذج العمل الأساسي (docs/18). السائق يدفع مقدَّماً ليعمل؛ المبلغ الحقيقي يصل → **`tenant_wallet`** فور نجاح الدفع. لا مرحلة أمانة: المال صار للمستأجر مقابل خدمة تشغيلية.
|
||
|
||
### 3.2 شحن الراكب محفظته ← **أمانة**
|
||
المال يدخل حسابنا لكنه **يبقى ملك الراكب** → **`tenant_pending`** + رصيد الراكب. حين يركب، يُخصم من رصيده وتُسوَّى الأجرة للسائق (ناقص العمولة). الجزء الوحيد الذي يتحوّل إلى إيراد هو **العمولة**.
|
||
|
||
### 3.3 عمولة عملية الدفع ← **إيراد**
|
||
رسم ثابت على **كل** عملية تحويل/دفع، محدَّد بالدولة (`countryPack`):
|
||
|
||
| الدولة | الرسم لكل عملية |
|
||
|---|---|
|
||
| سوريا | **35 ل.س** (بالعملة الجديدة) |
|
||
| مصر | **5 ج.م** |
|
||
| الأردن | **20 قرشاً** (0.20 د.أ) |
|
||
|
||
يُقتطع عند العملية ويُقيَّد في `tenant_wallet`. يُخزَّن **في إعداد المستأجر لا في الكود** — سيرو كتب الرقم مرتين في مكانين فتناقضا وتسرّب المال (docs/21).
|
||
|
||
## 4. جدول لكل وسيلة دفع + تقارير
|
||
|
||
نمط سيرو الصحيح: جدول مستقل لكل وسيلة (`cliq_invoices` · `invoices_shamcash` · `mtn_invoices` · `ecash_transactions` …) لأن لكل مزوّد حقولَه ومرجعَه وسلوكَ تأكيده. فوقها **محوّل موحّد** يعطي الباقي واجهة واحدة.
|
||
|
||
التقارير المطلوبة للوحة المستأجر: الدخل حسب الوسيلة · حسب اليوم · شحن السائقين مقابل الأمانات · العمولات المحصَّلة · المعلّق غير المسوّى.
|
||
|
||
## 5. الأسواق بلا API (كليك · شام كاش) — تسوية بالرسائل
|
||
|
||
كليك وشام كاش **لا توفّران API**. الحل المُثبَت في سيرو ويُنقل:
|
||
|
||
1. تطبيق أندرويد صغير على جهاز مخصّص يلتقط رسائل التأكيد الواردة من المزوّد.
|
||
2. يرفعها **خاماً** إلى webhook عندنا (`raw_sms_log`) — تُحفظ كما وصلت، لا تُحلَّل على الجهاز.
|
||
3. يُستخرج المبلغ والمرجع والمرسل (**Gemini** كما في `process_with_gemini.php`) ثم تُطابَق مع فاتورة معلّقة وتُسوَّى المحفظة.
|
||
|
||
**شروط لا تُتنازل عنها**: الرسالة الخام تُحفظ دائماً (أثر للنزاع) · المطابقة **مرة واحدة** بمفتاح تفرّد على مرجع العملية (وإلا سوّت رسالةٌ مكرّرة الفاتورة مرتين) · وما لا يُطابَق آلياً يذهب لطابور مراجعة بشرية لا يُهمَل بصمت.
|
||
|
||
## 6. ما لا يُنقل من سيرو (مُدقَّق — docs/21)
|
||
|
||
- **IDOR في `request_payout.php`**: هوية السائق من الطلب لا من التوكن → سحبُ رصيد الغير. (Tripz سليم: من التوكن.)
|
||
- **بلا حجز رصيد عند طلب السحب** → طلبات متزامنة تمرّ كلها = صرفٌ مزدوج.
|
||
- **رسم 3500 متناقض**: الفحص يشترط `amount + 3500` والتسوية تخصم `amount − 3500` → تسريب مال.
|
||
- **`finalizePayout` خمس كتابات بلا معاملة** → فشلٌ في المنتصف يترك سحباً نصف مسوّى.
|
||
- **المال في `varchar(10)`**.
|
||
- **جدولان متداخلان** لنفس المفهوم (`payout_requests` و`driver_withdrawal_requests`).
|
||
|
||
## 7. أثره على ما هو مبنيّ الآن
|
||
|
||
`GET /admin/overview` يحسب `revenue` = مجموع عمولة الرحلات فقط. بعد هذا المستند يصير الإيراد الحقيقي = **عمولة الرحلات + شحن السائقين + عمولات عمليات الدفع**، والأمانات تُستثنى صراحةً. النقطة تحتاج تحديثاً ضمن P.
|