Files
Siro/backend/Admin/v2/quality/driver_scorecard.php
T
Hamza-AyedandClaude Opus 5 bc1b0129e8 Fix v2 analytics status matching; give Growth and Analytics real charts
Eight queries across the v2 modules counted only status = 'Finished' and so
reported zero on live data, where the current ride pipeline writes
'completed': realtime revenue for today and yesterday, financial stats,
settlements, driver scorecard, driver ranking, and both revenue queries. All
now match either spelling.

Growth and Advanced Analytics rendered through the generic shape-detecting
renderer, which produced raw tables that said little. Both now have purpose-
built views:

- Growth: totals, 30-day joins, and a two-series daily chart. growth.php only
  returns days that had signups, so the series is expanded to a continuous
  30-day axis with explicit zeros — plotting the returned rows directly would
  hide the gaps and make a quiet month look like steady growth. A caption
  states how many days actually had a signup.
- Analytics: revenue summary tiles, a daily revenue trend, and the captain
  ranking, with a note explaining that platform share is what remains after
  the captain's cut.

Null aggregates render as "—" rather than 0.00, and markers are drawn only on
days with a value so a flat zero line stays readable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 16:55:58 +03:00

107 lines
4.3 KiB
PHP

<?php
// Admin/v2/quality/driver_scorecard.php
require_once __DIR__ . '/../../../connect.php';
// require_once __DIR__ . '/../../../encrypt_decrypt.php';
// التحقق من الصلاحيات
if ($role !== 'admin' && $role !== 'super_admin') {
jsonError("Unauthorized", 403);
}
$driver_id = filterRequest('driver_id');
if (!$driver_id) {
jsonError("Missing driver_id", 400);
}
try {
$scorecard = [];
// 1. البيانات الأساسية للسائق
$stmt = $con->prepare("
SELECT id, first_name, last_name, phone, status, created_at, expiry_date
FROM driver
WHERE id = ?
");
$stmt->execute([$driver_id]);
$driver = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$driver) {
jsonError("Driver not found", 404);
}
// فك التشفير للبيانات الأساسية
if (!empty($driver['first_name'])) $driver['first_name'] = $encryptionHelper->decryptData($driver['first_name']) ?: $driver['first_name'];
if (!empty($driver['last_name'])) $driver['last_name'] = $encryptionHelper->decryptData($driver['last_name']) ?: $driver['last_name'];
if (!empty($driver['phone'])) $driver['phone'] = $encryptionHelper->decryptData($driver['phone']) ?: $driver['phone'];
$scorecard['basic_info'] = $driver;
// 2. إحصائيات الرحلات (نسبة الإنجاز والإلغاء)
$stmt = $con->prepare("
SELECT
COUNT(*) as total_rides,
SUM(CASE WHEN LOWER(status) IN ('finished','completed') THEN 1 ELSE 0 END) as completed_rides,
SUM(CASE WHEN status = 'cancel' AND cancel_by = 'driver' THEN 1 ELSE 0 END) as driver_cancellations,
SUM(CASE WHEN status = 'cancel' AND cancel_by = 'passenger' THEN 1 ELSE 0 END) as passenger_cancellations
FROM ride
WHERE driver_id = ?
");
$stmt->execute([$driver_id]);
$rides = $stmt->fetch(PDO::FETCH_ASSOC);
// حساب نسبة الإنجاز
$total = (int)$rides['total_rides'];
$completed = (int)$rides['completed_rides'];
$rides['completion_rate'] = $total > 0 ? round(($completed / $total) * 100, 2) : 0;
$scorecard['rides_stats'] = $rides;
// 3. التقييمات
$stmt = $con->prepare("SELECT IFNULL(AVG(rating_driver), 0) as avg_rating FROM ride WHERE driver_id = ? AND rating_driver > 0");
$stmt->execute([$driver_id]);
$scorecard['rating'] = round($stmt->fetchColumn(), 2);
// 4. تحليل السلوك (Behavior)
// نستخدم جدول driver_behavior لجمع المتوسطات
$stmt = $con->prepare("
SELECT
IFNULL(AVG(behavior_score), 100) as avg_behavior_score,
IFNULL(AVG(max_speed), 0) as avg_max_speed,
IFNULL(SUM(hard_brakes), 0) as total_hard_brakes,
IFNULL(SUM(rapid_accelerations), 0) as total_rapid_accel
FROM driver_behavior
WHERE driver_id = ?
");
$stmt->execute([$driver_id]);
$scorecard['behavior'] = $stmt->fetch(PDO::FETCH_ASSOC);
// 5. الشكاوى (Complaints)
$stmt = $con->prepare("
SELECT
COUNT(*) as total_complaints,
SUM(CASE WHEN statusComplaint = 'Open' THEN 1 ELSE 0 END) as open_complaints,
SUM(CASE WHEN statusComplaint = 'Resolved' THEN 1 ELSE 0 END) as resolved_complaints
FROM complaint
WHERE driver_id = ?
");
$stmt->execute([$driver_id]);
$scorecard['complaints'] = $stmt->fetch(PDO::FETCH_ASSOC);
// 6. تقييم شامل (Overall Score) من 100
// وزن التقييم: 40% إنجاز رحلات، 30% تقييم ركاب (محول لـ 100)، 30% سلوك قيادة، وخصم للشكاوى
$completion_score = $rides['completion_rate'] * 0.4;
$rating_score = ($scorecard['rating'] / 5) * 100 * 0.3;
$behavior_score = $scorecard['behavior']['avg_behavior_score'] * 0.3;
$complaint_penalty = $scorecard['complaints']['total_complaints'] * 5; // خصم 5 نقاط عن كل شكوى
$overall = $completion_score + $rating_score + $behavior_score - $complaint_penalty;
$scorecard['overall_score'] = max(0, min(100, round($overall, 1)));
jsonSuccess($scorecard);
} catch (Exception $e) {
error_log("[driver_scorecard.php] " . $e->getMessage());
jsonError("Failed to fetch scorecard. Please try again later.", 500);
}
?>