# 04 — محرك التعرفة ## المبدأ محرك قواعد يقرأ تعريف التعرفة كـ **وثيقة JSON** لكل مجموعة `(مستأجر × مدينة × فئة خدمة)`. يغطي كل قدرات Onde الموثقة ويضيف ما يحتاجه سوقنا (العداد المنظّم الأردني، تقريب العملة). ## الأوضاع المدعومة | الوضع | الوصف | مثال سوق | |-------|-------|---------| | `time_and_distance` | زمن + مسافة معاً | عام | | `time_or_distance` | يبدّل حسب عتبة سرعة (تحت العتبة = دقيقة/زحمة، فوقها = كم) | العداد المنظّم — عمّان | | `fixed_quote` | سعر مقفول لحظة تحديد الوجهة | يلا غو / كريم | | `zone_matrix` | مصفوفة منطقة ← منطقة (PostGIS) | مطار ← وسط البلد | ## المكوّنات - **فتحة عداد** (flag down) + **سعر/كم** + **سعر/دقيقة** + **رسوم خدمة/حجز**. - **نوافذ زمنية:** نهار/ليل/جمعة/أعياد بجدولة صريحة. - **Surge** بسقف معلن (مضاعف حسب العرض/الطلب في خلايا H3، قابل للتعطيل حيث يمنعه المنظّم). - **رسوم إضافية مسماة:** بدل تطبيق، مطار، أمتعة. - **بدل انتظار:** بالدقيقة أو بزيادات ثوانٍ. - **رسوم إلغاء متدرجة** بمرحلة الرحلة + **حد أدنى للأجرة**. - **قواعد تقريب لكل عملة** (أقرب 0.05 دينار؛ أقرب 500 ل.س...). - **سياسة إعادة الاحتساب:** متى يُكسر السعر المقفول (انحراف مسار > نسبة محددة). ## واجهة المحرك (الباك إند) ``` tariff.quote(input) → يحسب سعراً تقديرياً/مقفولاً قبل الطلب tariff.finalize(trip) → يحسب السعر النهائي عند الإنهاء tariff.cancelFee(trip) → رسم الإلغاء حسب المرحلة ``` كلها نقية، مختبَرة بوحدات (نفس المدخل = نفس المخرج). ## مثال — عمّان على العداد المنظّم (أرقام رسمية) ```json { "tenant": "amman-operator-x", "service_class": "taxi-yellow", "currency": "JOD", "rounding": { "increment": 0.05, "mode": "nearest" }, "mode": "time_or_distance", "speed_threshold_kmh": 18, "windows": [ { "name": "day", "from": "06:00", "to": "22:00", "flag": 0.39, "per_km": 0.28, "per_min_waiting": 0.48 }, { "name": "night", "from": "22:00", "to": "06:00", "flag": 0.40, "per_km": 0.33, "per_min_waiting": 0.55 } ], "booking_fee": 0.25, "min_fare": 1.00, "surge": { "enabled": false }, "cancellation": [ { "stage": "after_assign", "after_sec": 120, "fee": 0.50 }, { "stage": "driver_arrived", "fee": 1.00 } ], "recalc_policy": { "fixed_quote": false } } ``` > قيم الانتظار بالدقيقة تقديرية للتوضيح وتُضبط من الزيادات الرسمية (كل 35 ثانية) عند التفعيل؛ فتحة العداد وسعر الكيلومتر هما الرقمان الرسميان المعتمدان. ## مخطط التخزين - الجدول `tariffs`: `id, tenant_id, city, service_class, definition (jsonb), version, active_from`. - إصدارات (versioning) — التعرفة النافذة وقت الرحلة تُثبَّت في `trip.tariff_version` للمراجعة. - مناطق `zone_matrix` تُخزَّن كـ PostGIS polygons في [pricing-zones](02-backend-plan.md). ## قواعد - **لا سعر بلا تعرفة نافذة مطابقة** — خطأ صريح لا افتراض صامت. - **كل رحلة تحفظ نسخة من التعرفة المستخدمة** (audit trail + مطلب المنظّم). - **التقريب آخر خطوة دائماً** بعد جمع كل المكوّنات. ← السابق: [03-mobile-plan](03-mobile-plan.md) · التالي: [05-pricing-billing](05-pricing-billing.md)