Fix daily_summary dan hourly_summary aggregation, tambah fallback logic untuk dashboard, update validator untuk camera dan location type
This commit is contained in:
91
bin/check_daily_summary_issue.php
Normal file
91
bin/check_daily_summary_issue.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Comparing entry_events vs daily_summary ===\n\n";
|
||||
|
||||
// Check dates that have entry_events
|
||||
$stmt = $db->query('SELECT DATE(event_time) as date, COUNT(*) as count FROM entry_events GROUP BY DATE(event_time) ORDER BY date DESC LIMIT 10');
|
||||
$dates = $stmt->fetchAll();
|
||||
|
||||
foreach ($dates as $dateRow) {
|
||||
$date = $dateRow['date'];
|
||||
$entryCount = $dateRow['count'];
|
||||
|
||||
echo "--- Date: $date ---\n";
|
||||
|
||||
// Count from entry_events
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events WHERE DATE(event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$entryEvents = $stmt->fetch()['count'];
|
||||
|
||||
// Count from daily_summary
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as records, SUM(total_count) as total_count, SUM(total_amount) as total_amount FROM daily_summary WHERE summary_date = ?');
|
||||
$stmt->execute([$date]);
|
||||
$summary = $stmt->fetch();
|
||||
$summaryRecords = $summary['records'] ?? 0;
|
||||
$summaryTotal = $summary['total_count'] ?? 0;
|
||||
$summaryAmount = $summary['total_amount'] ?? 0;
|
||||
|
||||
echo " entry_events: $entryEvents events\n";
|
||||
echo " daily_summary: $summaryRecords records, total_count: $summaryTotal, total_amount: $summaryAmount\n";
|
||||
|
||||
// Check if counts match
|
||||
if ($entryEvents > 0 && $summaryTotal == 0) {
|
||||
echo " ❌ PROBLEM: entry_events has data but daily_summary is empty!\n";
|
||||
echo " Run: php bin/daily_summary.php $date\n";
|
||||
} elseif ($entryEvents > 0 && $summaryTotal > 0 && $entryEvents != $summaryTotal) {
|
||||
$diff = $entryEvents - $summaryTotal;
|
||||
echo " ⚠️ WARNING: Count mismatch! entry_events: $entryEvents, daily_summary: $summaryTotal (diff: $diff)\n";
|
||||
|
||||
// Check why there's a difference
|
||||
echo " Checking why...\n";
|
||||
|
||||
// Count active locations/gates/tariffs
|
||||
$stmt = $db->query('SELECT COUNT(*) as count FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
INNER JOIN gates g ON e.location_code = g.location_code AND e.gate_code = g.gate_code AND g.is_active = 1
|
||||
WHERE DATE(e.event_time) = "' . $date . '"');
|
||||
$activeCount = $stmt->fetch()['count'];
|
||||
echo " Events with active locations & gates: $activeCount\n";
|
||||
|
||||
// Count all events
|
||||
echo " All events (including inactive): $entryEvents\n";
|
||||
|
||||
if ($activeCount == $summaryTotal) {
|
||||
echo " ✓ Reason: daily_summary only counts events with active locations/gates\n";
|
||||
} else {
|
||||
echo " ⚠️ Still a mismatch even with active filter\n";
|
||||
}
|
||||
} else {
|
||||
echo " ✓ OK: Counts match\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Check if there are dates in daily_summary that don't have entry_events
|
||||
echo "=== Dates in daily_summary without entry_events ===\n";
|
||||
$stmt = $db->query('SELECT summary_date, SUM(total_count) as total FROM daily_summary
|
||||
WHERE summary_date NOT IN (SELECT DISTINCT DATE(event_time) FROM entry_events)
|
||||
GROUP BY summary_date ORDER BY summary_date DESC LIMIT 10');
|
||||
$orphanDates = $stmt->fetchAll();
|
||||
if (empty($orphanDates)) {
|
||||
echo "None found - OK\n";
|
||||
} else {
|
||||
foreach ($orphanDates as $row) {
|
||||
echo $row['summary_date'] . ' - ' . $row['total'] . ' total (orphaned data)' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
102
bin/check_dashboard_data.php
Normal file
102
bin/check_dashboard_data.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$dbHost = AppConfig::get('DB_HOST', 'localhost');
|
||||
$dbName = AppConfig::get('DB_NAME', '');
|
||||
$dbUser = AppConfig::get('DB_USER', '');
|
||||
$dbPass = AppConfig::get('DB_PASS', '');
|
||||
|
||||
$db = Database::getConnection($dbHost, $dbName, $dbUser, $dbPass);
|
||||
|
||||
echo "=== Cek Data Dashboard ===\n\n";
|
||||
|
||||
// 1. Cek entry_events
|
||||
echo "1. Entry Events:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM entry_events');
|
||||
$result = $stmt->fetch();
|
||||
echo " Total: {$result['total']}\n";
|
||||
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM entry_events WHERE DATE(event_time) = CURDATE()');
|
||||
$result = $stmt->fetch();
|
||||
echo " Hari ini: {$result['total']}\n";
|
||||
|
||||
// 2. Cek daily_summary
|
||||
echo "\n2. Daily Summary:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM daily_summary');
|
||||
$result = $stmt->fetch();
|
||||
echo " Total: {$result['total']}\n";
|
||||
|
||||
$stmt = $db->query('SELECT * FROM daily_summary WHERE summary_date = CURDATE() LIMIT 5');
|
||||
$results = $stmt->fetchAll();
|
||||
echo " Hari ini: " . count($results) . " records\n";
|
||||
if (!empty($results)) {
|
||||
foreach ($results as $row) {
|
||||
echo " - {$row['summary_date']} | {$row['location_code']} | {$row['gate_code']} | {$row['category']} | Count: {$row['total_count']} | Amount: {$row['total_amount']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Cek locations
|
||||
echo "\n3. Locations:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM locations WHERE is_active = 1');
|
||||
$result = $stmt->fetch();
|
||||
echo " Active: {$result['total']}\n";
|
||||
|
||||
// 4. Cek gates
|
||||
echo "\n4. Gates:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM gates WHERE is_active = 1');
|
||||
$result = $stmt->fetch();
|
||||
echo " Active: {$result['total']}\n";
|
||||
|
||||
// 5. Test query dashboard summary
|
||||
echo "\n5. Test Query Dashboard Summary (Hari Ini):\n";
|
||||
$sql = "
|
||||
SELECT
|
||||
SUM(total_count) as total_count,
|
||||
SUM(total_amount) as total_amount
|
||||
FROM daily_summary
|
||||
WHERE summary_date = CURDATE()
|
||||
";
|
||||
$stmt = $db->query($sql);
|
||||
$result = $stmt->fetch();
|
||||
echo " Total Count: " . ($result['total_count'] ?? 0) . "\n";
|
||||
echo " Total Amount: " . ($result['total_amount'] ?? 0) . "\n";
|
||||
|
||||
// 6. Test query by category
|
||||
echo "\n6. Test Query By Category (Hari Ini):\n";
|
||||
$sql = "
|
||||
SELECT
|
||||
category,
|
||||
SUM(total_count) as total_count,
|
||||
SUM(total_amount) as total_amount
|
||||
FROM daily_summary
|
||||
WHERE summary_date = CURDATE()
|
||||
GROUP BY category
|
||||
";
|
||||
$stmt = $db->query($sql);
|
||||
$results = $stmt->fetchAll();
|
||||
if (empty($results)) {
|
||||
echo " Tidak ada data\n";
|
||||
} else {
|
||||
foreach ($results as $row) {
|
||||
echo " - {$row['category']}: Count={$row['total_count']}, Amount={$row['total_amount']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Kesimpulan ===\n";
|
||||
if (($result['total_count'] ?? 0) == 0) {
|
||||
echo "⚠️ Data kosong! Kemungkinan:\n";
|
||||
echo " 1. Belum ada data entry_events\n";
|
||||
echo " 2. Data belum di-aggregate ke daily_summary\n";
|
||||
echo " 3. Perlu jalankan: php bin/daily_summary.php\n";
|
||||
} else {
|
||||
echo "✅ Data ada, tapi mungkin perlu di-aggregate ulang\n";
|
||||
}
|
||||
|
||||
29
bin/check_dates.php
Normal file
29
bin/check_dates.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Dates with entry_events data ===\n";
|
||||
$stmt = $db->query('SELECT DATE(event_time) as date, COUNT(*) as count FROM entry_events GROUP BY DATE(event_time) ORDER BY date DESC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo $row['date'] . ' - ' . $row['count'] . ' events' . "\n";
|
||||
}
|
||||
|
||||
echo "\n=== Dates with daily_summary data ===\n";
|
||||
$stmt = $db->query('SELECT summary_date, SUM(total_count) as total FROM daily_summary GROUP BY summary_date ORDER BY summary_date DESC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo $row['summary_date'] . ' - ' . $row['total'] . ' total' . "\n";
|
||||
}
|
||||
|
||||
72
bin/check_entry_events.php
Normal file
72
bin/check_entry_events.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$dbHost = AppConfig::get('DB_HOST', 'localhost');
|
||||
$dbName = AppConfig::get('DB_NAME', '');
|
||||
$dbUser = AppConfig::get('DB_USER', '');
|
||||
$dbPass = AppConfig::get('DB_PASS', '');
|
||||
|
||||
$db = Database::getConnection($dbHost, $dbName, $dbUser, $dbPass);
|
||||
|
||||
echo "=== Cek Entry Events ===\n\n";
|
||||
|
||||
// Cek tanggal data
|
||||
echo "1. Data per tanggal:\n";
|
||||
$stmt = $db->query('SELECT DATE(event_time) as date, COUNT(*) as total FROM entry_events GROUP BY DATE(event_time) ORDER BY date DESC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo " {$row['date']}: {$row['total']} events\n";
|
||||
}
|
||||
|
||||
// Cek data hari ini
|
||||
echo "\n2. Data hari ini:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM entry_events WHERE DATE(event_time) = CURDATE()');
|
||||
$result = $stmt->fetch();
|
||||
echo " Total: {$result['total']}\n";
|
||||
|
||||
// Cek sample data
|
||||
echo "\n3. Sample data (5 terakhir):\n";
|
||||
$stmt = $db->query('SELECT id, location_code, gate_code, category, event_time FROM entry_events ORDER BY id DESC LIMIT 5');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo " ID: {$row['id']} | {$row['location_code']} | {$row['gate_code']} | {$row['category']} | {$row['event_time']}\n";
|
||||
}
|
||||
|
||||
// Cek apakah data punya location/gate yang valid
|
||||
echo "\n4. Validasi data:\n";
|
||||
$stmt = $db->query("
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
COUNT(CASE WHEN location_code IS NULL OR location_code = '' THEN 1 END) as null_location,
|
||||
COUNT(CASE WHEN gate_code IS NULL OR gate_code = '' THEN 1 END) as null_gate,
|
||||
COUNT(CASE WHEN category IS NULL OR category = '' THEN 1 END) as null_category
|
||||
FROM entry_events
|
||||
WHERE DATE(event_time) = CURDATE()
|
||||
");
|
||||
$result = $stmt->fetch();
|
||||
echo " Total hari ini: {$result['total']}\n";
|
||||
echo " Null location_code: {$result['null_location']}\n";
|
||||
echo " Null gate_code: {$result['null_gate']}\n";
|
||||
echo " Null category: {$result['null_category']}\n";
|
||||
|
||||
// Cek apakah location/gate ada di master
|
||||
echo "\n5. Validasi dengan master data:\n";
|
||||
$stmt = $db->query("
|
||||
SELECT
|
||||
COUNT(*) as total_valid
|
||||
FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
INNER JOIN gates g ON e.location_code = g.location_code AND e.gate_code = g.gate_code AND g.is_active = 1
|
||||
WHERE DATE(e.event_time) = CURDATE()
|
||||
");
|
||||
$result = $stmt->fetch();
|
||||
echo " Data valid (ada di master): {$result['total_valid']}\n";
|
||||
|
||||
34
bin/check_gates.php
Normal file
34
bin/check_gates.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Gates in database ===\n\n";
|
||||
|
||||
$stmt = $db->query('SELECT location_code, gate_code, name, direction, camera, is_active FROM gates ORDER BY location_code, gate_code');
|
||||
$gates = $stmt->fetchAll();
|
||||
|
||||
if (empty($gates)) {
|
||||
echo "No gates found in database!\n";
|
||||
} else {
|
||||
echo "Total gates: " . count($gates) . "\n\n";
|
||||
foreach ($gates as $gate) {
|
||||
echo "Location: {$gate['location_code']}\n";
|
||||
echo " Gate Code: {$gate['gate_code']}\n";
|
||||
echo " Name: {$gate['name']}\n";
|
||||
echo " Direction: {$gate['direction']}\n";
|
||||
echo " Camera: " . ($gate['camera'] ?: 'NULL') . "\n";
|
||||
echo " Active: " . ($gate['is_active'] ? 'Yes' : 'No') . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
66
bin/check_hourly_summary.php
Normal file
66
bin/check_hourly_summary.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$dates = ['2025-12-14', '2025-12-15', '2025-12-16', '2025-12-17', '2025-12-18'];
|
||||
|
||||
echo "=== Checking hourly_summary ===\n\n";
|
||||
|
||||
foreach ($dates as $date) {
|
||||
echo "--- Date: $date ---\n";
|
||||
|
||||
// Check entry_events per hour
|
||||
$stmt = $db->prepare('SELECT HOUR(event_time) as hour, COUNT(*) as count FROM entry_events WHERE DATE(event_time) = ? GROUP BY HOUR(event_time) ORDER BY hour');
|
||||
$stmt->execute([$date]);
|
||||
$entryHours = $stmt->fetchAll();
|
||||
|
||||
$entryTotal = 0;
|
||||
foreach ($entryHours as $row) {
|
||||
$entryTotal += $row['count'];
|
||||
}
|
||||
|
||||
echo " entry_events: $entryTotal total events\n";
|
||||
if (!empty($entryHours)) {
|
||||
echo " Hours with data: " . count($entryHours) . "\n";
|
||||
}
|
||||
|
||||
// Check hourly_summary
|
||||
$stmt = $db->prepare('SELECT summary_hour, SUM(total_count) as total_count, SUM(total_amount) as total_amount FROM hourly_summary WHERE summary_date = ? GROUP BY summary_hour ORDER BY summary_hour');
|
||||
$stmt->execute([$date]);
|
||||
$summaryHours = $stmt->fetchAll();
|
||||
|
||||
$summaryTotal = 0;
|
||||
foreach ($summaryHours as $row) {
|
||||
$summaryTotal += $row['total_count'];
|
||||
}
|
||||
|
||||
echo " hourly_summary: $summaryTotal total\n";
|
||||
if (!empty($summaryHours)) {
|
||||
echo " Hours with data: " . count($summaryHours) . "\n";
|
||||
} else {
|
||||
echo " ⚠️ No hourly_summary data!\n";
|
||||
echo " Run: php bin/hourly_summary.php $date\n";
|
||||
}
|
||||
|
||||
if ($entryTotal > 0 && $summaryTotal == 0) {
|
||||
echo " ❌ PROBLEM: entry_events has data but hourly_summary is empty!\n";
|
||||
} elseif ($entryTotal > 0 && $summaryTotal > 0 && $entryTotal != $summaryTotal) {
|
||||
echo " ⚠️ WARNING: Count mismatch! entry_events: $entryTotal, hourly_summary: $summaryTotal\n";
|
||||
} elseif ($entryTotal > 0 && $summaryTotal > 0) {
|
||||
echo " ✓ OK: Counts match\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
29
bin/check_locations_table.php
Normal file
29
bin/check_locations_table.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Columns in locations table ===\n\n";
|
||||
|
||||
$stmt = $db->query('DESCRIBE locations');
|
||||
$columns = $stmt->fetchAll();
|
||||
|
||||
foreach ($columns as $col) {
|
||||
echo "Field: {$col['Field']}\n";
|
||||
echo " Type: {$col['Type']}\n";
|
||||
echo " Null: {$col['Null']}\n";
|
||||
echo " Key: {$col['Key']}\n";
|
||||
echo " Default: " . ($col['Default'] ?? 'NULL') . "\n";
|
||||
echo " Extra: {$col['Extra']}\n\n";
|
||||
}
|
||||
|
||||
39
bin/check_specific_dates.php
Normal file
39
bin/check_specific_dates.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$dates = ['2025-12-16', '2025-12-17', '2025-12-18'];
|
||||
|
||||
echo "=== Checking specific dates ===\n";
|
||||
foreach ($dates as $date) {
|
||||
// Check entry_events
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events WHERE DATE(event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$entryCount = $stmt->fetch()['count'];
|
||||
|
||||
// Check daily_summary
|
||||
$stmt = $db->prepare('SELECT SUM(total_count) as total FROM daily_summary WHERE summary_date = ?');
|
||||
$stmt->execute([$date]);
|
||||
$summaryTotal = $stmt->fetch()['total'] ?? 0;
|
||||
|
||||
echo "\nDate: $date\n";
|
||||
echo " entry_events: $entryCount events\n";
|
||||
echo " daily_summary: $summaryTotal total\n";
|
||||
|
||||
if ($entryCount > 0 && $summaryTotal == 0) {
|
||||
echo " ⚠️ Data exists in entry_events but not aggregated to daily_summary!\n";
|
||||
echo " Run: php bin/daily_summary.php $date\n";
|
||||
}
|
||||
}
|
||||
|
||||
55
bin/check_tariffs.php
Normal file
55
bin/check_tariffs.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Tariffs in database ===\n\n";
|
||||
|
||||
$stmt = $db->query('SELECT location_code, gate_code, category, price FROM tariffs ORDER BY location_code, gate_code, category');
|
||||
$results = $stmt->fetchAll();
|
||||
|
||||
if (empty($results)) {
|
||||
echo "No tariffs found in database!\n";
|
||||
} else {
|
||||
foreach ($results as $row) {
|
||||
$key = $row['location_code'] . '|' . $row['gate_code'] . '|' . $row['category'];
|
||||
echo "Key: $key\n";
|
||||
echo " Location: {$row['location_code']}\n";
|
||||
echo " Gate: {$row['gate_code']}\n";
|
||||
echo " Category: {$row['category']}\n";
|
||||
echo " Price: Rp {$row['price']}\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check sample events
|
||||
echo "=== Sample events ===\n\n";
|
||||
$stmt = $db->query('SELECT location_code, gate_code, category FROM entry_events ORDER BY event_time DESC LIMIT 5');
|
||||
$events = $stmt->fetchAll();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$key = $event['location_code'] . '|' . $event['gate_code'] . '|' . $event['category'];
|
||||
echo "Event key: $key\n";
|
||||
|
||||
// Check if tariff exists
|
||||
$tariffStmt = $db->prepare('SELECT price FROM tariffs WHERE location_code = ? AND gate_code = ? AND category = ?');
|
||||
$tariffStmt->execute([$event['location_code'], $event['gate_code'], $event['category']]);
|
||||
$tariff = $tariffStmt->fetch();
|
||||
|
||||
if ($tariff) {
|
||||
echo " Tariff found: Rp {$tariff['price']}\n";
|
||||
} else {
|
||||
echo " ⚠️ Tariff NOT found!\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
86
bin/check_today_data.php
Normal file
86
bin/check_today_data.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
// Get today's date
|
||||
$today = date('Y-m-d');
|
||||
$yesterday = date('Y-m-d', strtotime('-1 day'));
|
||||
|
||||
echo "=== Checking Today's Data ===\n";
|
||||
echo "Today: $today\n";
|
||||
echo "Yesterday: $yesterday\n\n";
|
||||
|
||||
$dates = [$today, $yesterday, '2025-12-17', '2025-12-18'];
|
||||
|
||||
foreach ($dates as $date) {
|
||||
echo "--- Date: $date ---\n";
|
||||
|
||||
// Check entry_events
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count, MIN(event_time) as first_event, MAX(event_time) as last_event FROM entry_events WHERE DATE(event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$entryResult = $stmt->fetch();
|
||||
$entryCount = $entryResult['count'];
|
||||
|
||||
echo " entry_events:\n";
|
||||
echo " Count: $entryCount events\n";
|
||||
if ($entryCount > 0) {
|
||||
echo " First event: " . ($entryResult['first_event'] ?? 'N/A') . "\n";
|
||||
echo " Last event: " . ($entryResult['last_event'] ?? 'N/A') . "\n";
|
||||
|
||||
// Get sample data
|
||||
$stmt = $db->prepare('SELECT event_time, location_code, gate_code, category FROM entry_events WHERE DATE(event_time) = ? ORDER BY event_time DESC LIMIT 5');
|
||||
$stmt->execute([$date]);
|
||||
$samples = $stmt->fetchAll();
|
||||
echo " Sample events (last 5):\n";
|
||||
foreach ($samples as $sample) {
|
||||
echo " - " . $sample['event_time'] . " | " . $sample['location_code'] . " | " . $sample['gate_code'] . " | " . $sample['category'] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check daily_summary
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count, SUM(total_count) as total_count, SUM(total_amount) as total_amount FROM daily_summary WHERE summary_date = ?');
|
||||
$stmt->execute([$date]);
|
||||
$summaryResult = $stmt->fetch();
|
||||
$summaryCount = $summaryResult['count'];
|
||||
$summaryTotal = $summaryResult['total_count'] ?? 0;
|
||||
$summaryAmount = $summaryResult['total_amount'] ?? 0;
|
||||
|
||||
echo " daily_summary:\n";
|
||||
echo " Records: $summaryCount\n";
|
||||
echo " Total count: $summaryTotal\n";
|
||||
echo " Total amount: $summaryAmount\n";
|
||||
|
||||
if ($entryCount > 0 && $summaryCount == 0) {
|
||||
echo " ⚠️ WARNING: Data exists in entry_events but NOT aggregated to daily_summary!\n";
|
||||
echo " Run: php bin/daily_summary.php $date\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Check all dates with data
|
||||
echo "=== All dates with entry_events (last 10) ===\n";
|
||||
$stmt = $db->query('SELECT DATE(event_time) as date, COUNT(*) as count FROM entry_events GROUP BY DATE(event_time) ORDER BY date DESC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo $row['date'] . ' - ' . $row['count'] . ' events' . "\n";
|
||||
}
|
||||
|
||||
echo "\n=== All dates with daily_summary (last 10) ===\n";
|
||||
$stmt = $db->query('SELECT summary_date, SUM(total_count) as total FROM daily_summary GROUP BY summary_date ORDER BY summary_date DESC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as $row) {
|
||||
echo $row['summary_date'] . ' - ' . $row['total'] . ' total' . "\n";
|
||||
}
|
||||
|
||||
100
bin/debug_daily_summary.php
Normal file
100
bin/debug_daily_summary.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$date = '2025-12-15'; // Date with mismatch
|
||||
|
||||
echo "=== Debugging daily_summary for $date ===\n\n";
|
||||
|
||||
// Total events
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events WHERE DATE(event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$totalEvents = $stmt->fetch()['count'];
|
||||
echo "Total entry_events: $totalEvents\n\n";
|
||||
|
||||
// Events with active locations
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
WHERE DATE(e.event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$withActiveLocation = $stmt->fetch()['count'];
|
||||
echo "Events with active location: $withActiveLocation\n";
|
||||
|
||||
// Events with active gates
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
INNER JOIN gates g ON e.location_code = g.location_code AND e.gate_code = g.gate_code AND g.is_active = 1
|
||||
WHERE DATE(e.event_time) = ?');
|
||||
$stmt->execute([$date]);
|
||||
$withActiveGate = $stmt->fetch()['count'];
|
||||
echo "Events with active location + gate: $withActiveGate\n\n";
|
||||
|
||||
// Events without active location
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events e
|
||||
LEFT JOIN locations l ON e.location_code = l.code
|
||||
WHERE DATE(e.event_time) = ? AND (l.code IS NULL OR l.is_active = 0)');
|
||||
$stmt->execute([$date]);
|
||||
$withoutActiveLocation = $stmt->fetch()['count'];
|
||||
echo "Events WITHOUT active location: $withoutActiveLocation\n";
|
||||
|
||||
// Events without active gate
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
LEFT JOIN gates g ON e.location_code = g.location_code AND e.gate_code = g.gate_code
|
||||
WHERE DATE(e.event_time) = ? AND (g.gate_code IS NULL OR g.is_active = 0)');
|
||||
$stmt->execute([$date]);
|
||||
$withoutActiveGate = $stmt->fetch()['count'];
|
||||
echo "Events with active location but WITHOUT active gate: $withoutActiveGate\n\n";
|
||||
|
||||
// Check daily_summary
|
||||
$stmt = $db->prepare('SELECT SUM(total_count) as total FROM daily_summary WHERE summary_date = ?');
|
||||
$stmt->execute([$date]);
|
||||
$summaryTotal = $stmt->fetch()['total'] ?? 0;
|
||||
echo "daily_summary total_count: $summaryTotal\n\n";
|
||||
|
||||
// Check what's in daily_summary
|
||||
$stmt = $db->prepare('SELECT location_code, gate_code, category, total_count, total_amount FROM daily_summary WHERE summary_date = ? ORDER BY location_code, gate_code, category');
|
||||
$stmt->execute([$date]);
|
||||
$summaryRows = $stmt->fetchAll();
|
||||
echo "daily_summary records:\n";
|
||||
foreach ($summaryRows as $row) {
|
||||
echo " - " . $row['location_code'] . " | " . $row['gate_code'] . " | " . $row['category'] . " | count: " . $row['total_count'] . " | amount: " . $row['total_amount'] . "\n";
|
||||
}
|
||||
|
||||
// Check events that should be aggregated
|
||||
$stmt = $db->prepare('SELECT
|
||||
e.location_code,
|
||||
e.gate_code,
|
||||
e.category,
|
||||
COUNT(*) as count,
|
||||
COALESCE(t.price, 0) as price
|
||||
FROM entry_events e
|
||||
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1
|
||||
INNER JOIN gates g ON e.location_code = g.location_code AND e.gate_code = g.gate_code AND g.is_active = 1
|
||||
LEFT JOIN tariffs t ON e.location_code = t.location_code AND e.gate_code = t.gate_code AND e.category = t.category
|
||||
WHERE DATE(e.event_time) = ?
|
||||
GROUP BY e.location_code, e.gate_code, e.category, COALESCE(t.price, 0)
|
||||
ORDER BY e.location_code, e.gate_code, e.category');
|
||||
$stmt->execute([$date]);
|
||||
$shouldBeAggregated = $stmt->fetchAll();
|
||||
echo "\nEvents that SHOULD be aggregated:\n";
|
||||
$totalShouldBe = 0;
|
||||
foreach ($shouldBeAggregated as $row) {
|
||||
$totalShouldBe += $row['count'];
|
||||
echo " - " . $row['location_code'] . " | " . $row['gate_code'] . " | " . $row['category'] . " | count: " . $row['count'] . " | price: " . $row['price'] . "\n";
|
||||
}
|
||||
echo "\nTotal that should be aggregated: $totalShouldBe\n";
|
||||
echo "Total in daily_summary: $summaryTotal\n";
|
||||
echo "Difference: " . ($totalShouldBe - $summaryTotal) . "\n";
|
||||
|
||||
64
bin/run_all_daily_summary.php
Normal file
64
bin/run_all_daily_summary.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
// Get all dates with entry_events
|
||||
$stmt = $db->query('SELECT DISTINCT DATE(event_time) as date FROM entry_events ORDER BY date DESC');
|
||||
$dates = $stmt->fetchAll();
|
||||
|
||||
echo "=== Running daily_summary for all dates with entry_events ===\n\n";
|
||||
|
||||
foreach ($dates as $dateRow) {
|
||||
$date = $dateRow['date'];
|
||||
|
||||
// Skip old/invalid dates
|
||||
if ($date < '2020-01-01') {
|
||||
echo "Skipping old date: $date\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Processing: $date\n";
|
||||
|
||||
// Run daily_summary for this date
|
||||
$command = sprintf(
|
||||
'php %s/bin/daily_summary.php %s',
|
||||
escapeshellarg(__DIR__ . '/..'),
|
||||
escapeshellarg($date)
|
||||
);
|
||||
|
||||
$output = [];
|
||||
$returnCode = 0;
|
||||
exec($command, $output, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
echo " ✓ Success\n";
|
||||
if (!empty($output)) {
|
||||
foreach ($output as $line) {
|
||||
echo " $line\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo " ✗ Failed (return code: $returnCode)\n";
|
||||
if (!empty($output)) {
|
||||
foreach ($output as $line) {
|
||||
echo " $line\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "=== Done ===\n";
|
||||
|
||||
150
bin/run_migrations.php
Normal file
150
bin/run_migrations.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Script untuk menjalankan migration yang belum dijalankan
|
||||
* Usage: php bin/run_migrations.php
|
||||
*/
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
// Load environment variables
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
echo "=== Menjalankan Database Migrations ===\n\n";
|
||||
|
||||
// Get database connection
|
||||
$dbHost = AppConfig::get('DB_HOST', 'localhost');
|
||||
$dbName = AppConfig::get('DB_NAME', '');
|
||||
$dbUser = AppConfig::get('DB_USER', '');
|
||||
$dbPass = AppConfig::get('DB_PASS', '');
|
||||
|
||||
if (empty($dbName) || empty($dbUser)) {
|
||||
echo "❌ Error: Database credentials tidak lengkap di .env\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getConnection($dbHost, $dbName, $dbUser, $dbPass);
|
||||
|
||||
$migrationsDir = __DIR__ . '/../migrations';
|
||||
$migrationFiles = [
|
||||
'001_create_audit_logs.sql',
|
||||
'002_create_hourly_summary.sql',
|
||||
'003_create_realtime_events.sql',
|
||||
'004_add_camera_to_gates.sql'
|
||||
];
|
||||
|
||||
foreach ($migrationFiles as $migrationFile) {
|
||||
$migrationPath = $migrationsDir . '/' . $migrationFile;
|
||||
|
||||
if (!file_exists($migrationPath)) {
|
||||
echo "⚠️ File migration tidak ditemukan: {$migrationFile}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Menjalankan: {$migrationFile}...\n";
|
||||
|
||||
$sql = file_get_contents($migrationPath);
|
||||
|
||||
// Remove comments and clean up SQL
|
||||
$sql = preg_replace('/--.*$/m', '', $sql);
|
||||
$sql = preg_replace('/\/\*.*?\*\//s', '', $sql);
|
||||
|
||||
// Split by semicolon, but keep multi-line statements intact
|
||||
$statements = [];
|
||||
$currentStatement = '';
|
||||
$lines = explode("\n", $sql);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$currentStatement .= $line . "\n";
|
||||
|
||||
// If line ends with semicolon, it's a complete statement
|
||||
if (substr(rtrim($line), -1) === ';') {
|
||||
$stmt = trim($currentStatement);
|
||||
if (!empty($stmt) && strlen($stmt) > 5) {
|
||||
$statements[] = $stmt;
|
||||
}
|
||||
$currentStatement = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Add any remaining statement
|
||||
if (!empty(trim($currentStatement))) {
|
||||
$statements[] = trim($currentStatement);
|
||||
}
|
||||
|
||||
foreach ($statements as $statement) {
|
||||
if (empty(trim($statement))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$db->exec($statement);
|
||||
} catch (\PDOException $e) {
|
||||
// Skip jika error karena table/column sudah ada
|
||||
$errorMsg = $e->getMessage();
|
||||
if (strpos($errorMsg, 'already exists') !== false ||
|
||||
strpos($errorMsg, 'Duplicate column') !== false ||
|
||||
strpos($errorMsg, 'Duplicate key name') !== false) {
|
||||
echo " ⚠️ Sudah ada: " . substr($errorMsg, 0, 100) . "\n";
|
||||
} else {
|
||||
echo " ❌ Error: " . $errorMsg . "\n";
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo " ✅ Selesai: {$migrationFile}\n\n";
|
||||
}
|
||||
|
||||
echo "=== Migration Selesai ===\n";
|
||||
|
||||
// Verifikasi tabel
|
||||
echo "\n=== Verifikasi Tabel ===\n";
|
||||
$requiredTables = ['audit_logs', 'hourly_summary', 'realtime_events'];
|
||||
|
||||
foreach ($requiredTables as $table) {
|
||||
try {
|
||||
$stmt = $db->query("SHOW TABLES LIKE '{$table}'");
|
||||
$exists = $stmt->fetch() !== false;
|
||||
|
||||
if ($exists) {
|
||||
echo " ✅ Tabel '{$table}' ada\n";
|
||||
} else {
|
||||
echo " ❌ Tabel '{$table}' tidak ada\n";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo " ❌ Error cek tabel '{$table}': " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Cek field camera di gates
|
||||
echo "\n=== Verifikasi Field Camera di Gates ===\n";
|
||||
try {
|
||||
$stmt = $db->query("SHOW COLUMNS FROM gates LIKE 'camera'");
|
||||
$exists = $stmt->fetch() !== false;
|
||||
|
||||
if ($exists) {
|
||||
echo " ✅ Field 'camera' ada di tabel gates\n";
|
||||
} else {
|
||||
echo " ❌ Field 'camera' tidak ada di tabel gates\n";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo " ❌ Error cek field camera: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
110
bin/test_api_local.php
Normal file
110
bin/test_api_local.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Script untuk test API lokal
|
||||
* Usage: php bin/test_api_local.php [base_url]
|
||||
* Default: http://localhost:8000
|
||||
*/
|
||||
|
||||
$baseUrl = $argv[1] ?? 'http://localhost:8000';
|
||||
|
||||
echo "=== Test API Lokal ===\n";
|
||||
echo "Base URL: {$baseUrl}\n\n";
|
||||
|
||||
$tests = [
|
||||
[
|
||||
'name' => 'Health Check',
|
||||
'method' => 'GET',
|
||||
'url' => "{$baseUrl}/health",
|
||||
'headers' => [],
|
||||
'body' => null
|
||||
],
|
||||
[
|
||||
'name' => 'Login (Invalid - untuk test)',
|
||||
'method' => 'POST',
|
||||
'url' => "{$baseUrl}/auth/v1/login",
|
||||
'headers' => ['Content-Type: application/json'],
|
||||
'body' => json_encode(['username' => 'test', 'password' => 'test'])
|
||||
],
|
||||
[
|
||||
'name' => 'Get Locations (No Auth - akan 401)',
|
||||
'method' => 'GET',
|
||||
'url' => "{$baseUrl}/retribusi/v1/frontend/locations",
|
||||
'headers' => [],
|
||||
'body' => null
|
||||
],
|
||||
[
|
||||
'name' => 'Dashboard Summary (No Auth - akan 401)',
|
||||
'method' => 'GET',
|
||||
'url' => "{$baseUrl}/retribusi/v1/dashboard/summary",
|
||||
'headers' => [],
|
||||
'body' => null
|
||||
],
|
||||
[
|
||||
'name' => 'Realtime Snapshot (No Auth - akan 401)',
|
||||
'method' => 'GET',
|
||||
'url' => "{$baseUrl}/retribusi/v1/realtime/snapshot",
|
||||
'headers' => [],
|
||||
'body' => null
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tests as $test) {
|
||||
echo "Test: {$test['name']}\n";
|
||||
echo " URL: {$test['url']}\n";
|
||||
|
||||
$ch = curl_init($test['url']);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $test['method']);
|
||||
|
||||
if (!empty($test['headers'])) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $test['headers']);
|
||||
}
|
||||
|
||||
if ($test['body'] !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $test['body']);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
echo " ❌ Error: {$error}\n";
|
||||
} else {
|
||||
echo " Status: {$httpCode}\n";
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
echo " ✅ Success\n";
|
||||
$data = json_decode($response, true);
|
||||
if ($data) {
|
||||
echo " Response: " . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
|
||||
} else {
|
||||
echo " Response: {$response}\n";
|
||||
}
|
||||
} elseif ($httpCode === 401) {
|
||||
echo " ⚠️ Unauthorized (Expected - butuh JWT token)\n";
|
||||
} elseif ($httpCode === 422) {
|
||||
echo " ⚠️ Validation Error (Expected - parameter tidak valid)\n";
|
||||
$data = json_decode($response, true);
|
||||
if ($data) {
|
||||
echo " Response: " . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " ❌ Failed\n";
|
||||
echo " Response: " . substr($response, 0, 200) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "=== Test Selesai ===\n";
|
||||
echo "\nCatatan:\n";
|
||||
echo "- Status 401 = Normal (butuh JWT token untuk endpoint protected)\n";
|
||||
echo "- Status 422 = Normal (validation error, parameter tidak valid)\n";
|
||||
echo "- Status 200 = Endpoint berfungsi dengan baik\n";
|
||||
|
||||
47
bin/test_api_response.php
Normal file
47
bin/test_api_response.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
use App\Modules\Retribusi\Dashboard\DashboardService;
|
||||
use App\Modules\Retribusi\Dashboard\DashboardController;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$service = new DashboardService($db);
|
||||
$controller = new DashboardController($service);
|
||||
|
||||
// Simulate request
|
||||
$request = new \Slim\Psr7\Factory\ServerRequestFactory();
|
||||
$response = new \Slim\Psr7\Factory\ResponseFactory();
|
||||
|
||||
// Test summary endpoint
|
||||
echo "=== Test Dashboard Summary API Response ===\n\n";
|
||||
|
||||
// Test dengan tanggal yang ada data
|
||||
$request = $request->createServerRequest('GET', '/retribusi/v1/dashboard/summary')
|
||||
->withQueryParams(['date' => '2025-12-16']);
|
||||
|
||||
$response = $controller->getSummary($request, $response->createResponse());
|
||||
|
||||
echo "Status: " . $response->getStatusCode() . "\n";
|
||||
echo "Body:\n";
|
||||
echo $response->getBody() . "\n\n";
|
||||
|
||||
// Test by category
|
||||
$request = $request->withQueryParams(['date' => '2025-12-16']);
|
||||
$response = $controller->getByCategoryChart($request, $response->createResponse());
|
||||
|
||||
echo "=== Test By Category API Response ===\n";
|
||||
echo "Status: " . $response->getStatusCode() . "\n";
|
||||
echo "Body:\n";
|
||||
echo $response->getBody() . "\n";
|
||||
|
||||
45
bin/test_dashboard_fallback.php
Normal file
45
bin/test_dashboard_fallback.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
use App\Modules\Retribusi\Dashboard\DashboardService;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$service = new DashboardService($db);
|
||||
|
||||
echo "=== Test Dashboard Fallback ===\n\n";
|
||||
|
||||
// Test hari ini (tidak ada data)
|
||||
echo "1. Summary hari ini (2025-12-18):\n";
|
||||
$result = $service->getSummary('2025-12-18');
|
||||
echo " Total Count: {$result['total_count']}\n";
|
||||
echo " Total Amount: {$result['total_amount']}\n\n";
|
||||
|
||||
// Test kemarin (ada data)
|
||||
echo "2. Summary kemarin (2025-12-16):\n";
|
||||
$result = $service->getSummary('2025-12-16');
|
||||
echo " Total Count: {$result['total_count']}\n";
|
||||
echo " Total Amount: {$result['total_amount']}\n\n";
|
||||
|
||||
// Test by category hari ini
|
||||
echo "3. By Category hari ini (2025-12-18):\n";
|
||||
$result = $service->getByCategoryChart('2025-12-18');
|
||||
echo " Labels: " . implode(', ', $result['labels']) . "\n";
|
||||
echo " Counts: " . implode(', ', $result['series']['total_count']) . "\n\n";
|
||||
|
||||
// Test by category kemarin
|
||||
echo "4. By Category kemarin (2025-12-16):\n";
|
||||
$result = $service->getByCategoryChart('2025-12-16');
|
||||
echo " Labels: " . implode(', ', $result['labels']) . "\n";
|
||||
echo " Counts: " . implode(', ', $result['series']['total_count']) . "\n";
|
||||
|
||||
80
bin/test_db_connection.php
Normal file
80
bin/test_db_connection.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Script untuk test koneksi database
|
||||
* Usage: php bin/test_db_connection.php
|
||||
*/
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
// Load environment variables
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
echo "=== Test Koneksi Database ===\n\n";
|
||||
|
||||
// Get database connection
|
||||
$dbHost = AppConfig::get('DB_HOST', 'localhost');
|
||||
$dbName = AppConfig::get('DB_NAME', '');
|
||||
$dbUser = AppConfig::get('DB_USER', '');
|
||||
$dbPass = AppConfig::get('DB_PASS', '');
|
||||
|
||||
echo "Konfigurasi Database:\n";
|
||||
echo " Host: {$dbHost}\n";
|
||||
echo " Database: {$dbName}\n";
|
||||
echo " User: {$dbUser}\n";
|
||||
echo " Password: " . (empty($dbPass) ? '(kosong)' : str_repeat('*', strlen($dbPass))) . "\n\n";
|
||||
|
||||
if (empty($dbName) || empty($dbUser)) {
|
||||
echo "❌ Error: Database credentials tidak lengkap di .env\n";
|
||||
echo " Pastikan DB_NAME dan DB_USER sudah di-set\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
echo "Mencoba koneksi ke database...\n";
|
||||
$db = Database::getConnection($dbHost, $dbName, $dbUser, $dbPass);
|
||||
|
||||
echo "✅ Koneksi database BERHASIL!\n\n";
|
||||
|
||||
// Test query sederhana
|
||||
echo "Test query sederhana...\n";
|
||||
$stmt = $db->query("SELECT VERSION() as version, DATABASE() as current_db, NOW() as server_time");
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo " MySQL Version: {$result['version']}\n";
|
||||
echo " Current Database: {$result['current_db']}\n";
|
||||
echo " Server Time: {$result['server_time']}\n\n";
|
||||
|
||||
// Cek tabel yang ada
|
||||
echo "Mengecek tabel yang ada...\n";
|
||||
$stmt = $db->query("SHOW TABLES");
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (empty($tables)) {
|
||||
echo " ⚠️ Tidak ada tabel di database ini\n";
|
||||
} else {
|
||||
echo " ✅ Ditemukan " . count($tables) . " tabel:\n";
|
||||
foreach ($tables as $table) {
|
||||
echo " - {$table}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Test Selesai ===\n";
|
||||
|
||||
} catch (\PDOException $e) {
|
||||
echo "❌ Error: Koneksi database GAGAL!\n";
|
||||
echo " Pesan Error: " . $e->getMessage() . "\n";
|
||||
echo "\nKemungkinan penyebab:\n";
|
||||
echo " 1. Database server tidak berjalan\n";
|
||||
echo " 2. Host/Port salah\n";
|
||||
echo " 3. Username/Password salah\n";
|
||||
echo " 4. Database tidak ada\n";
|
||||
echo " 5. User tidak punya akses ke database\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
50
bin/test_entry_events_api.php
Normal file
50
bin/test_entry_events_api.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
use App\Modules\Retribusi\Realtime\RealtimeService;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
$service = new RealtimeService($db);
|
||||
|
||||
echo "=== Testing Entry Events API ===\n\n";
|
||||
|
||||
// Test getEntryEvents
|
||||
$page = 1;
|
||||
$limit = 20;
|
||||
$data = $service->getEntryEvents($page, $limit, null, null, null, null, null);
|
||||
$total = $service->getEntryEventsTotal(null, null, null, null, null);
|
||||
|
||||
echo "Total events: $total\n";
|
||||
echo "Events returned: " . count($data) . "\n\n";
|
||||
|
||||
if (!empty($data)) {
|
||||
echo "Sample events:\n";
|
||||
foreach (array_slice($data, 0, 5) as $event) {
|
||||
echo " - ID: {$event['id']}\n";
|
||||
echo " Time: {$event['event_time']}\n";
|
||||
echo " Location: {$event['location_code']}\n";
|
||||
echo " Gate: {$event['gate_code']}\n";
|
||||
echo " Category: {$event['category']}\n";
|
||||
echo "\n";
|
||||
}
|
||||
} else {
|
||||
echo "No events found!\n";
|
||||
}
|
||||
|
||||
// Test dengan date filter
|
||||
echo "\n=== Test dengan date filter (2025-12-18) ===\n";
|
||||
$data = $service->getEntryEvents($page, $limit, null, null, null, '2025-12-18', '2025-12-18');
|
||||
$total = $service->getEntryEventsTotal(null, null, null, '2025-12-18', '2025-12-18');
|
||||
echo "Total events for 2025-12-18: $total\n";
|
||||
echo "Events returned: " . count($data) . "\n";
|
||||
|
||||
63
bin/test_routes.php
Normal file
63
bin/test_routes.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Bootstrap\AppBootstrap;
|
||||
use App\Config\AppConfig;
|
||||
use App\Modules\Health\HealthRoutes;
|
||||
use App\Modules\Auth\AuthRoutes;
|
||||
use App\Modules\Retribusi\RetribusiRoutes;
|
||||
use App\Modules\Retribusi\Summary\SummaryRoutes;
|
||||
use App\Modules\Retribusi\Dashboard\DashboardRoutes;
|
||||
use App\Modules\Retribusi\Realtime\RealtimeRoutes;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
$app = AppBootstrap::create();
|
||||
|
||||
// Register routes
|
||||
HealthRoutes::register($app);
|
||||
AuthRoutes::register($app);
|
||||
RetribusiRoutes::register($app);
|
||||
SummaryRoutes::register($app);
|
||||
DashboardRoutes::register($app);
|
||||
RealtimeRoutes::register($app);
|
||||
|
||||
// Get all routes
|
||||
$routes = [];
|
||||
foreach ($app->getRouteCollector()->getRoutes() as $route) {
|
||||
foreach ($route->getMethods() as $method) {
|
||||
$routes[] = [
|
||||
'method' => $method,
|
||||
'pattern' => $route->getPattern()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== Registered Routes ===\n\n";
|
||||
foreach ($routes as $route) {
|
||||
echo "{$route['method']} {$route['pattern']}\n";
|
||||
}
|
||||
|
||||
echo "\n=== Testing Specific Routes ===\n";
|
||||
$testRoutes = [
|
||||
'/health',
|
||||
'/auth/v1/login',
|
||||
'/retribusi/v1/frontend/locations',
|
||||
'/retribusi/v1/dashboard/summary',
|
||||
'/retribusi/v1/dashboard/daily',
|
||||
'/retribusi/v1/realtime/snapshot',
|
||||
];
|
||||
|
||||
foreach ($testRoutes as $testRoute) {
|
||||
$found = false;
|
||||
foreach ($routes as $route) {
|
||||
// Simple pattern matching
|
||||
$pattern = str_replace(['{', '}'], ['', ''], $route['pattern']);
|
||||
if (strpos($testRoute, $pattern) === 0 || $route['pattern'] === $testRoute) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo ($found ? '✅' : '❌') . " {$testRoute}\n";
|
||||
}
|
||||
|
||||
48
bin/test_summary_api.php
Normal file
48
bin/test_summary_api.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
$db = Database::getConnection(
|
||||
AppConfig::get('DB_HOST'),
|
||||
AppConfig::get('DB_NAME'),
|
||||
AppConfig::get('DB_USER'),
|
||||
AppConfig::get('DB_PASS')
|
||||
);
|
||||
|
||||
echo "=== Testing Summary API Endpoints ===\n\n";
|
||||
|
||||
// Test daily_summary
|
||||
echo "1. Daily Summary:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM daily_summary');
|
||||
$result = $stmt->fetch();
|
||||
echo " Total rows: {$result['total']}\n";
|
||||
|
||||
$stmt = $db->query('SELECT summary_date, SUM(total_count) as total_count, SUM(total_amount) as total_amount FROM daily_summary GROUP BY summary_date ORDER BY summary_date DESC LIMIT 5');
|
||||
$results = $stmt->fetchAll();
|
||||
echo " Sample data:\n";
|
||||
foreach ($results as $row) {
|
||||
echo " - {$row['summary_date']}: {$row['total_count']} events, Rp " . number_format($row['total_amount']) . "\n";
|
||||
}
|
||||
|
||||
echo "\n2. Hourly Summary:\n";
|
||||
$stmt = $db->query('SELECT COUNT(*) as total FROM hourly_summary');
|
||||
$result = $stmt->fetch();
|
||||
echo " Total rows: {$result['total']}\n";
|
||||
|
||||
$stmt = $db->query('SELECT summary_date, summary_hour, SUM(total_count) as total_count, SUM(total_amount) as total_amount FROM hourly_summary GROUP BY summary_date, summary_hour ORDER BY summary_date DESC, summary_hour ASC LIMIT 10');
|
||||
$results = $stmt->fetchAll();
|
||||
echo " Sample data:\n";
|
||||
foreach ($results as $row) {
|
||||
echo " - {$row['summary_date']} {$row['summary_hour']}:00: {$row['total_count']} events, Rp " . number_format($row['total_amount']) . "\n";
|
||||
}
|
||||
|
||||
echo "\n3. Checking API response format:\n";
|
||||
echo " GET /retribusi/v1/summary/daily?date=2025-12-16\n";
|
||||
echo " Expected: { success: true, data: { summary_date, total_count, total_amount, ... }, timestamp }\n";
|
||||
echo "\n GET /retribusi/v1/summary/hourly?date=2025-12-16\n";
|
||||
echo " Expected: { success: true, data: { labels: [...], series: { total_count: [...], total_amount: [...] } }, timestamp }\n";
|
||||
|
||||
Reference in New Issue
Block a user