feat: المجموعة B (شريحة 1) — فصل السعر + الانتظار + مشوار الوصول + الطوابع

B6 فصل السعر (الأهم — أساس كل عمل مالي لاحق):
- price_for_passenger / price_for_driver / commission_amount / commission_rate
- العمولة تُعرَّف في التعرفة: commission { percent, flat, min }، ولا تتجاوز
  الأجرة أبداً (دخل السائق لا يصير سالباً)
- **إصلاح**: تسوية المحفظة كانت تُضيف للسائق كامل أجرة الراكب — أي أن
  العمولة كانت تضيع. صار الراكب يُخصم passenger والسائق يُضاف له driver
- العرض يحمل earnings (ما يقبضه السائق) لا أجرة الراكب

B2 الانتظار: يبدأ عند driver_arrived ويُحتسب عند in_progress. أول 5 دقائق
مجانية (free_waiting_min قابل للضبط لكل تعرفة)، والزائد بسعر النافذة.

B3 مشوار الوصول: يُقاس من موقع السائق الحيّ لحظة القبول حتى نقطة الالتقاط.
لا يُحتسب على الراكب ما لم يُفعّله المالك (pickup_leg.charge) — تفعيله يرفع
كل أجرة فهو قرار لا افتراض صامت. فشل القياس لا يُفشل القبول.

B7 الطوابع: driver_going_at · arrived_at · started_at (+ completed_at).
كشف «الإنهاء السريع» صار يقيس من بدء الرحلة لا إسنادها — المشوار والانتظار
ليسا جزءاً من زمنها.

B1: كان منفَّذاً أصلاً — driver_arrived و in_progress حالتان منفصلتان منذ P1.

معلَّق على I2: العمولة محسوبة ومسجّلة لكنها لا تُقيَّد في محفظة المنصة
(لم تُبنَ بعد).

هجرة: TripPricingAndTimestamps (تملأ الرحلات القائمة: دفع = قبض).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-17 03:11:36 +03:00
co-authored by Claude Opus 4.8
parent 7e24c63dd8
commit f3fdc1bc20
9 changed files with 468 additions and 34 deletions
+18 -15
View File
@@ -22,22 +22,25 @@
---
## المجموعة B — اكتمال نموذج الرحلة
## المجموعة B — اكتمال نموذج الرحلة — 🔵 الشريحة الأولى منفَّذة (المال والزمن)
> مقارنة بجدول `ride` في سيرو + قواعد التشغيل.
| # | البند | التفصيل |
|---|-------|---------|
| B1 | **حالة `started` وفصل الوصول عن البدء** | السائق وصل ≠ الرحلة بدأت. الراكب قد لا يرد/يلغي. نحتاج تمييزاً صريحاً. |
| B2 | **عدّاد انتظار 5 دقائق (قانون)** | يبدأ عند وصول السائق (بعد **تأكيد المسافة** من فلاتر). عند انتهائه: خيار للراكب — يبقى السائق منتظراً، أو **تعويض** فترة الانتظار. |
| B3 | **احتساب مشوار الوصول للراكب** | المسافة/الزمن من موقع السائق حتى الراكب تُحتسب بالتعرفة (دقائق + مسافة). |
| B4 | **نقاط توقف (stops)** | نقطتا توقف ضمن الرحلة (كما في سيرو/شير). |
| B5 | **حجز مسبق/جدولة** | جداول الرحلة (`date`/`time`/`endtime` في سيرو). |
| B6 | **فصل السعر** | `price_for_driver` مقابل `price_for_passenger` (العمولة) — **ناقص عندنا**. |
| B7 | **طوابع زمنية دقيقة** | `DriverIsGoingToPassenger` · `rideTimeStart` · `rideTimeFinish` (سيرو) — عندنا `assigned_at`/`completed_at` فقط. |
| B8 | **مطابقة الوجهة** | `is_destination_match` + خصم الراكب. |
| B9 | **تعديل التعرفة من لوحة الأدمن** | جداول تعرفة قابلة للتحرير (موجودة كـ jsonb — نحتاج واجهة/نقاط CRUD). |
| B10 | **كتالوج أنواع الرحلات العالمي** | `ride_types` فكرته سليمة (الأدمن/المستأجر يضيف أنواعه). المطلوب: كتالوج جاهز بما هو شائع عالمياً كخيارات جاهزة للاختيار (عندنا 6 فقط الآن). |
| B11 | **تعرفة بالوزن** | بُعد تسعير إضافي بالوزن (للشحن/التوصيل) بجانب المسافة والزمن. الاستعلام عن التعرفة من Redis خط أول (G5). |
| # | البند | التفصيل | الحالة |
|---|-------|---------|--------|
| B1 | **حالة `started` وفصل الوصول عن البدء** | — | ✅ **كان منفَّذاً أصلاً**: `driver_arrived` و`in_progress` حالتان منفصلتان في آلة الحالة منذ P1. الناقص كان الطوابع والانتظار (B2/B7) لا الحالة نفسها. |
| B2 | **عدّاد انتظار 5 دقائق (قانون)** | يبدأ عند `driver_arrived`، ويُحتسب عند `in_progress`. الدقائق المجانية `free_waiting_min` (5 افتراضاً) قابلة للضبط لكل تعرفة، والزائد بسعر `per_min_waiting` للنافذة الفعّالة. | ✅ `waiting_min` + `waiting_charge` |
| B3 | **احتساب مشوار الوصول للراكب** | يُقاس من موقع السائق **الحيّ لحظة القبول** حتى نقطة الالتقاط. **لا يُحتسب على الراكب ما لم يُفعِّله المالك** (`pickup_leg.charge`) — تفعيله يرفع كل أجرة، فهو قرار لا افتراض صامت. | ✅ `pickup_distance_km`/`_duration_min`/`_charge` |
| B6 | **فصل السعر** | `price_for_passenger` · `price_for_driver` · `commission_amount` · `commission_rate`. العمولة تُعرَّف في التعرفة: `commission: { percent, flat, min }`، ولا تتجاوز الأجرة أبداً. | ✅ + **تسوية المحفظة صُحّحت**: كانت تُضيف للسائق كامل أجرة الراكب — أي أن العمولة كانت تضيع |
| B7 | **طوابع زمنية دقيقة** | `driver_going_at` (`DriverIsGoingToPassenger`) · `arrived_at` · `started_at` (`rideTimeStart`) · `completed_at` (`rideTimeFinish`). | ✅ + كشف «الإنهاء السريع» صار يقيس من **بدء** الرحلة لا إسنادها |
| B4 | **نقاط توقف (stops)** | نقطتا توقف ضمن الرحلة (كما في سيرو/شير). | ⏳ الشريحة التالية |
| B5 | **حجز مسبق/جدولة** | جداول الرحلة (`date`/`time`/`endtime` في سيرو). | ⏳ الشريحة التالية |
| B8 | **مطابقة الوجهة** | `is_destination_match` + خصم الراكب. | ⏳ الشريحة التالية |
| B9 | **تعديل التعرفة من لوحة الأدمن** | جداول تعرفة قابلة للتحرير (موجودة كـ jsonb — نحتاج واجهة/نقاط CRUD). | ⏳ |
| B10 | **كتالوج أنواع الرحلات العالمي** | `ride_types` فكرته سليمة (الأدمن/المستأجر يضيف أنواعه). المطلوب: كتالوج جاهز بما هو شائع عالمياً كخيارات جاهزة للاختيار (عندنا 6 فقط الآن). | ⏳ |
| B11 | **تعرفة بالوزن** | بُعد تسعير إضافي بالوزن (للشحن/التوصيل) بجانب المسافة والزمن. | ⏳ |
**الأجرة النهائية الآن** = السعر المقفول + رسم الانتظار الزائد + مشوار الوصول (إن فُعِّل)، ثم تُفصل: الراكب يدفع `price_for_passenger`، والسائق يقبض `price_for_driver`، والفرق عمولة.
**معلَّق على I2**: العمولة محسوبة ومسجّلة على الرحلة لكنها **لا تُقيَّد في محفظة المنصة** — لأن محفظة المنصة (مكافئ `siroWallet`) لم تُبنَ بعد.
---
@@ -187,7 +190,7 @@
2. ~~**I1** (سباق المحفظة)~~ ✅ **منفَّذة ومُثبَتة بتزامن حقيقي**.
3. ~~**G** (Redis خط أول: لغة/توكنات/tenant/تعرفة/تقييم/كاش الخرائط)~~ ✅ **منفَّذة**.
4. ~~**H** (المواقع: إيقاف الكتابة لكل نبضة + batching + tracks)~~ ✅ **منفَّذة** (عدا H6/H7 — تحليلات مؤجَّلة).
5. **B** (نموذج الرحلة: started/انتظار/فصل السعر/الطوابع/stops).
5. **B** — 🔵 الشريحة الأولى ✅ (الانتظار B2 · مشوار الوصول B3 · فصل السعر B6 · الطوابع B7). الباقي: B4 stops · B5 جدولة · B8 مطابقة الوجهة · B9 · B10 · B11.
6. **I** الباقي (المدفوعات: جداول لكل طريقة + OTP/بصمة/HMAC للسحب).
7. **C** (بيانات المركبة والسائق + ai_data).
8. **D** (تطبيع الهاتف + بصمة الجهاز + HMAC).