Update codebase
This commit is contained in:
@@ -166,4 +166,128 @@ function estimateDriverEarningsAdvantage(
|
||||
'competitor_name' => $data['competitor_name'],
|
||||
];
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* صف التسعير الصحيح لدولة هذه الرحلة.
|
||||
*
|
||||
* ⚠️ لا تستخدم "SELECT * FROM kazan LIMIT 1" أبداً. جدول kazan يحمل صفاً
|
||||
* لكل دولة بمقاييس متباعدة جداً:
|
||||
* Syria 40 SYP/كم · 9 SYP/دقيقة
|
||||
* Egypt 3.5 EGP/كم · 0.8 EGP/دقيقة
|
||||
* Jordan 0.284 JOD/كم· 0.05 JOD/دقيقة
|
||||
* فـ LIMIT 1 يعيد أول صف (سوريا) ويطبّق أسعارها على رحلة أردنية — أي
|
||||
* مئات الأضعاف. حدث هذا فعلاً في مسارَي الإلغاء.
|
||||
*
|
||||
* مصدر الدولة: driver.site عبر الرحلة — نفس ما يفعله finish_ride_updates.php.
|
||||
* وإن تعذّر (رحلة بلا سائق مثلاً) نقع على $fallbackCountry.
|
||||
*
|
||||
* @return array صف kazan، أو مصفوفة فارغة إن لم يُوجد شيء إطلاقاً.
|
||||
*/
|
||||
function getKazanForRide(PDO $con, $rideId, string $fallbackCountry = 'Syria', $driverId = null): array
|
||||
{
|
||||
$country = '';
|
||||
|
||||
try {
|
||||
if (!empty($driverId) && (string) $driverId !== '0') {
|
||||
// مسار الإلغاء يصفّر ride.driver_id قبل الوصول إلى هنا، فالانضمام
|
||||
// عبر الرحلة يرجع فارغاً. المستدعي يمرّر السائق الذي يعرفه.
|
||||
$stmt = $con->prepare("SELECT site FROM driver WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$driverId]);
|
||||
} else {
|
||||
$stmt = $con->prepare("
|
||||
SELECT d.site AS country_code
|
||||
FROM ride r
|
||||
LEFT JOIN driver d ON d.id = r.driver_id
|
||||
WHERE r.id = ? LIMIT 1
|
||||
");
|
||||
$stmt->execute([$rideId]);
|
||||
}
|
||||
$country = (string) ($stmt->fetchColumn() ?: '');
|
||||
} catch (PDOException $e) {
|
||||
error_log("[pricing] تعذّر تحديد دولة الرحلة $rideId: " . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($country === '') {
|
||||
$country = $fallbackCountry;
|
||||
error_log("[pricing] الرحلة $rideId بلا دولة معروفة — السقوط على $country");
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $con->prepare("SELECT * FROM kazan WHERE country = ? LIMIT 1");
|
||||
$stmt->execute([$country]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
error_log("[pricing] لا صف kazan للدولة $country — السقوط على $fallbackCountry");
|
||||
$stmt->execute([$fallbackCountry]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
return $row ?: [];
|
||||
} catch (PDOException $e) {
|
||||
error_log("[pricing] تعذّر جلب أسعار $country: " . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* السقف الصلب الأعلى لأي حركة مالية واحدة، بعملة الدولة.
|
||||
*
|
||||
* ⚠️ رقم مجرّد لا معنى له: كان الكود يستخدم 50000 في ثلاثة مواضع، وهو
|
||||
* سقف بلا أثر على الليرة السورية بعد حذف صفرين منها (50000 القديمة =
|
||||
* 500 اليوم)، وكارثي على الدينار الأردني (50000 دينار!).
|
||||
*
|
||||
* هذا ليس سعراً ولا سياسة — هو حزام أمان أخير ضد خطأ مطبعي (صفر زائد)
|
||||
* أو حساب موظف مخترَق. السياسة الحقيقية تُضبط في أعمدة kazan.
|
||||
*/
|
||||
function getMoneyHardCap(string $currency): float
|
||||
{
|
||||
// سقوف حددها المالك: أقصى تعويض واحد ≈ دينار أردني واحد وما يعادله.
|
||||
// رقم صغير عمداً — التعويض استرضاء لا تسوية نزاع، والحالات الكبيرة
|
||||
// تُرفع للإدارة لا تُصرف من شاشة موظف.
|
||||
$caps = [
|
||||
'JOD' => 1.0,
|
||||
'EGP' => 50.0,
|
||||
'SYP' => 100.0, // بعد حذف الصفرين
|
||||
'IQD' => 2000.0,
|
||||
'USD' => 1.5,
|
||||
'TRY' => 50.0,
|
||||
'SAR' => 5.0,
|
||||
'AED' => 5.0,
|
||||
'LBP' => 130000.0,
|
||||
];
|
||||
|
||||
// عملة غير معروفة: نأخذ أدنى سقف في القائمة. الرفض الخاطئ يُراجَع،
|
||||
// والصرف الخاطئ لا يُسترد.
|
||||
return $caps[strtoupper($currency)] ?? min($caps);
|
||||
}
|
||||
|
||||
/**
|
||||
* تسعير داخلي للرحلات التي تُنشأ خادمياً (الحجز المسبق).
|
||||
*
|
||||
* ⚠️ ليس بديلاً عن ride/pricing/get.php — ذاك محرك كامل فيه الذروة
|
||||
* وإضافات المطار والخصومات المناطقية، ويُستدعى من التطبيق مع رمز سعر
|
||||
* موقَّع. هذه دالة أبسط لمسار واحد لا يملك رمزاً ولا جلسة راكب.
|
||||
*
|
||||
* المكوّنات هي نفسها التي تُسوَّى بها الرحلة في finish_ride_updates.php:
|
||||
* فتحة العداد + المسافة × سعر الكيلومتر + الزمن × سعر الدقيقة
|
||||
*
|
||||
* فالنتيجة متسقة مع ما سيُحاسَب عليه الراكب فعلاً عند الإنهاء، وأي فرق
|
||||
* يبقى في صالحه لا ضده (لا تُحتسب إضافات الذروة هنا).
|
||||
*/
|
||||
function computeInternalRidePrice(array $kazan, string $carType,
|
||||
float $distanceKm, int $durationSeconds): float
|
||||
{
|
||||
$startPrice = (float) ($kazan['startPrice'] ?? 0);
|
||||
$perKm = getPerKmRate($carType, $kazan);
|
||||
$perMin = getPerMinRate($kazan);
|
||||
|
||||
// حد أدنى للمسافة المحتسبة — يمنع رحلة بمسافة صفر من إنتاج سعر صفر
|
||||
// إن وصلت بيانات ناقصة.
|
||||
$km = max(0.2, $distanceKm);
|
||||
$minutes = max(1.0, $durationSeconds / 60.0);
|
||||
|
||||
$price = $startPrice + ($km * $perKm) + ($minutes * $perMin);
|
||||
|
||||
return round(max(0.0, $price), 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user