'comfortPrice', 'Speed' => 'speedPrice', 'Lady' => 'ladyPrice', 'Electric' => 'electricPrice', 'Van' => 'vanPrice', 'Delivery' => 'deliveryPrice', 'Mishwar Vip' => 'mishwarVipPrice', 'Fixed Price' => 'fixedPrice', 'Awfar Car' => 'awfarPrice', ]; $column = $rateColumns[$carType] ?? 'speedPrice'; // دعم التوافق مع الإصدارات القديمة (backward compatibility) $rate = floatval($kazanRow[$column] ?? 0); if ($rate <= 0) { $oldColumnMap = [ 'Lady' => 'familyPrice', 'Mishwar Vip' => 'freePrice', 'Electric' => 'naturePrice', 'Van' => 'heavyPrice', ]; $oldColumn = $oldColumnMap[$carType] ?? null; if ($oldColumn && isset($kazanRow[$oldColumn])) { $rate = floatval($kazanRow[$oldColumn]); } } // Fallback أخير if ($rate <= 0) { $rate = floatval($kazanRow['speedPrice'] ?? 36); } return $rate; } /** * الحصول على سعر الدقيقة حسب وقت اليوم من جدول kazan * * @param array $countryPricing صف من جدول kazan * @return float */ function getPerMinRate(array $countryPricing): float { $hour = (int)date('H'); // قراءة الأسعار من الأعمدة الجديدة للدقيقة $normalMinPrice = floatval($countryPricing['normalMinPrice'] ?? 0); $peakMinPrice = floatval($countryPricing['peakMinPrice'] ?? 0); $lateMinPrice = floatval($countryPricing['lateMinPrice'] ?? 0); // دعم التوافق مع الإصدارات القديمة (latePrice, naturePrice) if ($lateMinPrice <= 0) $lateMinPrice = floatval($countryPricing['latePrice'] ?? 0); if ($normalMinPrice <= 0) $normalMinPrice = floatval($countryPricing['naturePrice'] ?? 0); // Fallback: حساب سعر الدقيقة من speedPrice إذا كانت الأعمدة الجديدة فارغة if ($normalMinPrice <= 0) { $speedPrice = floatval($countryPricing['speedPrice'] ?? 36); $normalMinPrice = $speedPrice / 4; } if ($peakMinPrice <= 0) $peakMinPrice = $normalMinPrice * 1.15; // 15% زيادة if ($lateMinPrice <= 0) $lateMinPrice = $normalMinPrice * 1.25; // 25% زيادة if ($hour >= 21 || $hour < 1) { return round($lateMinPrice, 2); // Late Night } if ($hour >= 14 && $hour <= 17) { return round($peakMinPrice, 2); // Peak } return round($normalMinPrice, 2); // Normal } /** * تحديد رمز العملة حسب الدولة * * @param string $countryCode رمز الدولة (Syria, Egypt, Jordan, ...) * @return string رمز العملة (SYP, EGP, JOD, ...) */ function getCurrencyByCountry(string $countryCode): string { $currencies = [ 'Syria' => 'SYP', 'Egypt' => 'EGP', 'Jordan' => 'JOD', 'Iraq' => 'IQD', 'UAE' => 'AED', 'Saudi Arabia' => 'SAR', 'Qatar' => 'QAR', 'Kuwait' => 'KWD', 'Bahrain' => 'BHD', 'Oman' => 'OMR', 'Turkey' => 'TRY', 'Lebanon' => 'LBP', 'Palestine' => 'ILS', 'Yemen' => 'YER', 'Libya' => 'LYD', 'Tunisia' => 'TND', 'Algeria' => 'DZD', 'Morocco' => 'MAD', 'Sudan' => 'SDG', ]; return $currencies[$countryCode] ?? 'SYP'; // افتراضي: ليرة سورية } /** * يقدّر مقدار زيادة أجرة السائق عندنا مقارنة بأقرب منافس لنفس الرحلة. * يعتمد على معاملات المنافس المخزّنة بـ Redis (يحدّثها cron_ai_engine.php * كل 3 ساعات على مفتاح pricing:driver_comparison:{country_code}) — لا يوجد * أي استعلام مباشر لقاعدة البيانات هنا لتفادي أي تأخير وقت إنشاء الرحلة. * * @return array|null ['extra' => float, 'currency' => string, 'competitor_name' => string] * أو null إذا ما في بيانات كافية أو الفرق مش مجدي يُعرض */ function estimateDriverEarningsAdvantage( string $countryCode, float $distanceKm, float $durationMin, float $ourDriverPrice, $redis ): ?array { if (!$redis || $ourDriverPrice <= 0) return null; try { $json = $redis->get("pricing:driver_comparison:{$countryCode}"); } catch (Exception $e) { return null; } if (!$json) return null; $data = json_decode($json, true); if (!is_array($data) || empty($data['competitor_name'])) return null; $competitorFare = (float)($data['base_fare'] ?? 0) + (float)($data['km_rate'] ?? 0) * $distanceKm + (float)($data['min_rate'] ?? 0) * $durationMin; if ($competitorFare <= 0) return null; $commissionPct = (float)($data['commission_pct'] ?? 20.0); $competitorDriverPayout = $competitorFare * (1 - $commissionPct / 100); $extra = round($ourDriverPrice - $competitorDriverPayout, 2); // حد أدنى نسبي (3% من أجرة سائق المنافس) حتى ما نعرض فرق تافه أو غير موثوق $minMeaningful = $competitorDriverPayout * 0.03; if ($extra <= $minMeaningful) return null; return [ 'extra' => $extra, 'currency' => $data['currency'] ?? 'SYP', '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); }