Update: 2026-08-07 05:03:39
This commit is contained in:
@@ -55,88 +55,9 @@ $driverId = $ride['driver_id'];
|
||||
|
||||
// --- دوال مساعدة لجلب البيانات (تم افتراض أن الدوال تصل إلى $con و $encryptionHelper عبر النطاق global أو تمريرها كـ arguments) ---
|
||||
|
||||
/**
|
||||
* جلب بيانات ومعلومات تقييم السائق
|
||||
*/
|
||||
function getDriverFullProfile($con, $encryptionHelper, $driverId) {
|
||||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||||
|
||||
// جلب معلومات السائق الأساسية
|
||||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM driver WHERE id = ?");
|
||||
$stmt->execute([$driverId]);
|
||||
$driverInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// فك تشفير البيانات الحساسة
|
||||
if ($driverInfo) {
|
||||
// التحقق من وجود ودوال فك التشفير قبل الاستخدام
|
||||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||||
$decryptedFirstName = $encryptionHelper->decryptData($driverInfo['first_name']);
|
||||
$decryptedLastName = $encryptionHelper->decryptData($driverInfo['last_name']);
|
||||
$driverInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||||
} else {
|
||||
$driverInfo['full_name'] = 'Decryption Failed';
|
||||
}
|
||||
|
||||
unset($driverInfo['first_name'], $driverInfo['last_name']); // إزالة الحقول المشفرة
|
||||
$profile['info'] = $driverInfo;
|
||||
}
|
||||
|
||||
// جلب ملخص التقييمات والتعليقات
|
||||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingDriver WHERE driver_id = ?");
|
||||
$stmt->execute([$driverId]);
|
||||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $con->prepare("SELECT comment FROM ratingDriver WHERE driver_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||||
$stmt->execute([$driverId]);
|
||||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* جلب بيانات ومعلومات تقييم الراكب
|
||||
*/
|
||||
function getPassengerFullProfile($con, $encryptionHelper, $passengerId) {
|
||||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||||
|
||||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM passengers WHERE id = ?");
|
||||
$stmt->execute([$passengerId]);
|
||||
$passengerInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// فك تشفير البيانات الحساسة
|
||||
if ($passengerInfo) {
|
||||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||||
$decryptedFirstName = $encryptionHelper->decryptData($passengerInfo['first_name']);
|
||||
$decryptedLastName = $encryptionHelper->decryptData($passengerInfo['last_name']);
|
||||
$passengerInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||||
} else {
|
||||
$passengerInfo['full_name'] = 'Decryption Failed';
|
||||
}
|
||||
|
||||
unset($passengerInfo['first_name'], $passengerInfo['last_name']);
|
||||
$profile['info'] = $passengerInfo;
|
||||
}
|
||||
|
||||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingPassenger WHERE passenger_id = ?");
|
||||
$stmt->execute([$passengerId]);
|
||||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $con->prepare("SELECT comment FROM ratingPassenger WHERE passenger_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||||
$stmt->execute([$passengerId]);
|
||||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* جلب بيانات سلوك السائق في الرحلة المحددة
|
||||
*/
|
||||
function getDriverBehavior($con, $rideId, $driverId) {
|
||||
$stmt = $con->prepare("SELECT max_speed, avg_speed, hard_brakes, behavior_score FROM driver_behavior WHERE trip_id = ? AND driver_id = ?");
|
||||
$stmt->execute([$rideId, $driverId]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
|
||||
// الدوال الثلاث انتقلت إلى complaint_context.php ليشاركها مستشار
|
||||
// خدمة العملاء (serviceapp/complaint_ai_suggest.php) بلا تكرار.
|
||||
require_once __DIR__ . '/complaint_context.php';
|
||||
// استدعاء الدوال لجلب البيانات
|
||||
$passengerProfile = getPassengerFullProfile($con, $encryptionHelper, $passengerId);
|
||||
$driverProfile = getDriverFullProfile($con, $encryptionHelper, $driverId);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// complaint_context.php — جمع سياق الشكوى الكامل
|
||||
//
|
||||
// استُخرجت من add_solve_all.php ليشاركها مستشار خدمة العملاء.
|
||||
// كانت هذه الدوال محبوسة في نقطة تقديم الشكوى، فبنيتُ نسخة أضعف منها
|
||||
// لمستشار الدعم قبل أن أنتبه — وهذا الاستخراج يمنع تباعد النسختين.
|
||||
//
|
||||
// ما تجمعه: ملف السائق (تقييماته وتعليقات الركّاب عليه)، ملف الراكب
|
||||
// (تقييماته وتعليقات السائقين عليه)، وسلوك القيادة في الرحلة نفسها.
|
||||
// أي أن النموذج يرى "من هؤلاء" لا الشكوى وحدها.
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* جلب بيانات ومعلومات تقييم السائق
|
||||
*/
|
||||
function getDriverFullProfile($con, $encryptionHelper, $driverId) {
|
||||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||||
|
||||
// جلب معلومات السائق الأساسية
|
||||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM driver WHERE id = ?");
|
||||
$stmt->execute([$driverId]);
|
||||
$driverInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// فك تشفير البيانات الحساسة
|
||||
if ($driverInfo) {
|
||||
// التحقق من وجود ودوال فك التشفير قبل الاستخدام
|
||||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||||
$decryptedFirstName = $encryptionHelper->decryptData($driverInfo['first_name']);
|
||||
$decryptedLastName = $encryptionHelper->decryptData($driverInfo['last_name']);
|
||||
$driverInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||||
} else {
|
||||
$driverInfo['full_name'] = 'Decryption Failed';
|
||||
}
|
||||
|
||||
unset($driverInfo['first_name'], $driverInfo['last_name']); // إزالة الحقول المشفرة
|
||||
$profile['info'] = $driverInfo;
|
||||
}
|
||||
|
||||
// جلب ملخص التقييمات والتعليقات
|
||||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingDriver WHERE driver_id = ?");
|
||||
$stmt->execute([$driverId]);
|
||||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $con->prepare("SELECT comment FROM ratingDriver WHERE driver_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||||
$stmt->execute([$driverId]);
|
||||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* جلب بيانات ومعلومات تقييم الراكب
|
||||
*/
|
||||
function getPassengerFullProfile($con, $encryptionHelper, $passengerId) {
|
||||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||||
|
||||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM passengers WHERE id = ?");
|
||||
$stmt->execute([$passengerId]);
|
||||
$passengerInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// فك تشفير البيانات الحساسة
|
||||
if ($passengerInfo) {
|
||||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||||
$decryptedFirstName = $encryptionHelper->decryptData($passengerInfo['first_name']);
|
||||
$decryptedLastName = $encryptionHelper->decryptData($passengerInfo['last_name']);
|
||||
$passengerInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||||
} else {
|
||||
$passengerInfo['full_name'] = 'Decryption Failed';
|
||||
}
|
||||
|
||||
unset($passengerInfo['first_name'], $passengerInfo['last_name']);
|
||||
$profile['info'] = $passengerInfo;
|
||||
}
|
||||
|
||||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingPassenger WHERE passenger_id = ?");
|
||||
$stmt->execute([$passengerId]);
|
||||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $con->prepare("SELECT comment FROM ratingPassenger WHERE passenger_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||||
$stmt->execute([$passengerId]);
|
||||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* جلب بيانات سلوك السائق في الرحلة المحددة
|
||||
*/
|
||||
function getDriverBehavior($con, $rideId, $driverId) {
|
||||
$stmt = $con->prepare("SELECT max_speed, avg_speed, hard_brakes, behavior_score FROM driver_behavior WHERE trip_id = ? AND driver_id = ?");
|
||||
$stmt->execute([$rideId, $driverId]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,6 @@ function estimateDriverEarningsAdvantage(
|
||||
'competitor_name' => $data['competitor_name'],
|
||||
];
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* صف التسعير الصحيح لدولة هذه الرحلة.
|
||||
@@ -230,3 +229,35 @@ function getKazanForRide(PDO $con, $rideId, string $fallbackCountry = 'Syria', $
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
// • الأسعار ← جدول kazan عبر pricing_helper.php
|
||||
// ============================================================
|
||||
|
||||
// سقف صلب فوق أي قيمة في القاعدة — يمنع خطأً إدارياً من خصم كارثي.
|
||||
const PASSENGER_CANCEL_ABSOLUTE_CAP = 50000.0;
|
||||
|
||||
// حد أقصى للدقائق المحتسبة: سائق قَبِل ونسي الرحلة ساعة لا يتحول
|
||||
// نسيانه إلى فاتورة على الراكب.
|
||||
const PASSENGER_CANCEL_MAX_BILLABLE_MINUTES = 20;
|
||||
@@ -112,7 +109,8 @@ function computePassengerCancelFee(
|
||||
$fee = ($approachKm * getPerKmRate($carType, $kazan))
|
||||
+ ($billableMinutes * getPerMinRate($kazan));
|
||||
|
||||
$fee = round(min($fee, $cap, PASSENGER_CANCEL_ABSOLUTE_CAP), 2);
|
||||
// سقف صلب بعملة الدولة — حزام أمان أخير ضد خطأ في صف kazan.
|
||||
$fee = round(min($fee, $cap, getMoneyHardCap($currency)), 2);
|
||||
|
||||
error_log("[cancel_fee] ride=$rideId رسم إلغاء: {$approachKm}كم"
|
||||
. " + {$billableMinutes}د = $fee $currency (سقف $cap)");
|
||||
|
||||
@@ -30,10 +30,6 @@ if (filterRequest("penalty_fee") !== null) {
|
||||
. " (ride=$rideId driver=$driverId) — التعويض يُحسب على الخادم");
|
||||
}
|
||||
|
||||
// سقف صلب فوق أي قيمة في قاعدة البيانات — يمنع خطأ إدارياً أو صفاً تالفاً
|
||||
// من تحويل التعويض إلى خصم كارثي.
|
||||
const CANCEL_COMPENSATION_ABSOLUTE_CAP = 50000.0;
|
||||
|
||||
// حد أقصى لدقائق الانتظار المحتسبة: سائق نسي الرحلة مفتوحة ساعتين لا
|
||||
// يتحول انتظاره إلى فاتورة على الراكب.
|
||||
const CANCEL_MAX_BILLABLE_WAIT_MINUTES = 20;
|
||||
@@ -261,8 +257,11 @@ try {
|
||||
$perMin = getPerMinRate($kazan);
|
||||
|
||||
$compensation = ($approachKm * $perKm) + ($billableMinutes * $perMin);
|
||||
// سقف صلب بعملة الدولة لا رقماً مجرّداً: 50000 كانت
|
||||
// بلا أثر على الليرة بعد حذف صفرين، وكارثية على الدينار.
|
||||
$currencyCode = (string) ($kazan['currency'] ?? 'SYP');
|
||||
$compensation = round(min($compensation, $compensationCap,
|
||||
CANCEL_COMPENSATION_ABSOLUTE_CAP), 2);
|
||||
getMoneyHardCap($currencyCode)), 2);
|
||||
|
||||
error_log("[cancel_ride_by_driver] تعويض ride=$rideId:"
|
||||
. " {$approachKm}كم×$perKm + {$billableMinutes}د×$perMin"
|
||||
|
||||
Reference in New Issue
Block a user