Add: Database verification scripts and troubleshooting guide
This commit is contained in:
195
TROUBLESHOOTING.md
Normal file
195
TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
## Error: "Unknown column 't.amount' in 'SELECT'"
|
||||
|
||||
### Kemungkinan Penyebab:
|
||||
1. **OPcache belum di-clear** - PHP masih menggunakan file lama
|
||||
2. **Struktur database berbeda** - Kolom `amount` mungkin tidak ada atau nama berbeda
|
||||
3. **File belum ter-update** - Meskipun sudah `git pull`, file mungkin belum benar-benar ter-update
|
||||
|
||||
---
|
||||
|
||||
## Solusi di Production
|
||||
|
||||
### Step 1: Clear OPcache
|
||||
```bash
|
||||
# Di production server
|
||||
cd /www/wwwroot/api.btekno.cloud/api
|
||||
|
||||
# Clear OPcache via PHP CLI
|
||||
php -r "opcache_reset();"
|
||||
|
||||
# Atau restart PHP-FPM
|
||||
systemctl restart php-fpm
|
||||
# atau
|
||||
/www/server/php/83/bin/php-fpm restart
|
||||
```
|
||||
|
||||
### Step 2: Verify File Sudah Ter-update
|
||||
```bash
|
||||
# Cek apakah file sudah benar-benar ter-update
|
||||
grep -n "COALESCE(t.amount, 0)" src/Modules/Retribusi/Summary/DailySummaryService.php
|
||||
|
||||
# Harus muncul di line 46 dan 61
|
||||
# Line 46: COALESCE(t.amount, 0) as tariff_amount
|
||||
# Line 61: COALESCE(t.amount, 0)
|
||||
```
|
||||
|
||||
### Step 3: Verify Struktur Database
|
||||
```bash
|
||||
# Login ke MySQL
|
||||
mysql -u sql_retribusi -p sql_retribusi
|
||||
|
||||
# Cek struktur tabel tariffs
|
||||
DESCRIBE tariffs;
|
||||
|
||||
# Harus ada kolom:
|
||||
# - location_code
|
||||
# - gate_code
|
||||
# - category
|
||||
# - amount <-- INI HARUS ADA!
|
||||
```
|
||||
|
||||
### Step 4: Test Query Manual
|
||||
```bash
|
||||
# Di MySQL client
|
||||
mysql -u sql_retribusi -p sql_retribusi < bin/verify_query.sql
|
||||
|
||||
# Atau jalankan query ini manual:
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
DATE(e.event_time) as summary_date,
|
||||
e.location_code,
|
||||
e.gate_code,
|
||||
e.category,
|
||||
COUNT(*) as total_count,
|
||||
COALESCE(t.amount, 0) as tariff_amount
|
||||
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) = CURDATE()
|
||||
GROUP BY
|
||||
DATE(e.event_time),
|
||||
e.location_code,
|
||||
e.gate_code,
|
||||
e.category,
|
||||
COALESCE(t.amount, 0)
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
### Step 5: Test Script Lagi
|
||||
```bash
|
||||
cd /www/wwwroot/api.btekno.cloud/api
|
||||
php bin/daily_summary.php 2025-12-16
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cek Database Lokal
|
||||
|
||||
### Step 1: Setup .env (jika belum)
|
||||
```bash
|
||||
# Copy .env.example ke .env
|
||||
cp .env.example .env
|
||||
|
||||
# Edit .env, isi dengan database lokal:
|
||||
DB_HOST=localhost
|
||||
DB_NAME=sql_retribusi
|
||||
DB_USER=sql_retribusi
|
||||
DB_PASS=your_password_here
|
||||
```
|
||||
|
||||
### Step 2: Jalankan Script Check Database
|
||||
```bash
|
||||
php bin/check_database.php
|
||||
```
|
||||
|
||||
### Step 3: Verify Query di Lokal
|
||||
```bash
|
||||
# Via MySQL client
|
||||
mysql -u sql_retribusi -p sql_retribusi < bin/verify_query.sql
|
||||
```
|
||||
|
||||
### Step 4: Test Daily Summary di Lokal
|
||||
```bash
|
||||
php bin/daily_summary.php 2025-12-16
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Apply Migrations
|
||||
|
||||
### Migration yang Tersedia:
|
||||
1. `001_create_audit_logs.sql` - Tabel audit_logs
|
||||
2. `002_create_hourly_summary.sql` - Tabel hourly_summary
|
||||
3. `003_create_realtime_events.sql` - Tabel realtime_events
|
||||
|
||||
### Cara Apply (Lokal):
|
||||
```bash
|
||||
# Via MySQL command line
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/001_create_audit_logs.sql
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/002_create_hourly_summary.sql
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/003_create_realtime_events.sql
|
||||
```
|
||||
|
||||
### Cara Apply (Production):
|
||||
```bash
|
||||
# Di production server
|
||||
cd /www/wwwroot/api.btekno.cloud/api
|
||||
|
||||
# Backup dulu!
|
||||
mysqldump -u sql_retribusi -p sql_retribusi > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# Apply migration
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/001_create_audit_logs.sql
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/002_create_hourly_summary.sql
|
||||
mysql -u sql_retribusi -p sql_retribusi < migrations/003_create_realtime_events.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Jika Masih Error
|
||||
|
||||
### Cek Log Error Detail:
|
||||
```bash
|
||||
# Di production
|
||||
cd /www/wwwroot/api.btekno.cloud/api
|
||||
php bin/daily_summary.php 2025-12-16 2>&1 | tee error.log
|
||||
```
|
||||
|
||||
### Cek Apakah Tabel Tariffs Ada:
|
||||
```sql
|
||||
-- Di MySQL
|
||||
SHOW TABLES LIKE 'tariffs';
|
||||
SELECT COUNT(*) FROM tariffs;
|
||||
```
|
||||
|
||||
### Cek Apakah Ada Data di Entry Events:
|
||||
```sql
|
||||
-- Di MySQL
|
||||
SELECT COUNT(*) FROM entry_events WHERE DATE(event_time) = CURDATE();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Fix (Jika OPcache Issue)
|
||||
|
||||
```bash
|
||||
# Di production, restart PHP-FPM
|
||||
systemctl restart php-fpm
|
||||
|
||||
# Atau via aaPanel
|
||||
# PHP → PHP-FPM → Restart
|
||||
|
||||
# Lalu test lagi
|
||||
cd /www/wwwroot/api.btekno.cloud/api
|
||||
php bin/daily_summary.php 2025-12-16
|
||||
```
|
||||
|
||||
133
bin/check_database.php
Normal file
133
bin/check_database.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Script to check database structure
|
||||
* Usage: php bin/check_database.php
|
||||
*/
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Config\AppConfig;
|
||||
use App\Support\Database;
|
||||
|
||||
// Load environment variables
|
||||
AppConfig::loadEnv(__DIR__ . '/..');
|
||||
|
||||
// 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 not configured in .env\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getConnection($dbHost, $dbName, $dbUser, $dbPass);
|
||||
|
||||
echo "=== Database Structure Check ===\n\n";
|
||||
|
||||
// Check tariffs table structure
|
||||
echo "1. Checking tariffs table structure:\n";
|
||||
$stmt = $db->query("DESCRIBE tariffs");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($columns)) {
|
||||
echo " ❌ Table 'tariffs' does not exist!\n";
|
||||
} else {
|
||||
echo " ✅ Table 'tariffs' exists with columns:\n";
|
||||
foreach ($columns as $col) {
|
||||
echo " - {$col['Field']} ({$col['Type']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n2. Checking locations table structure:\n";
|
||||
$stmt = $db->query("DESCRIBE locations");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($columns)) {
|
||||
echo " ❌ Table 'locations' does not exist!\n";
|
||||
} else {
|
||||
echo " ✅ Table 'locations' exists with columns:\n";
|
||||
foreach ($columns as $col) {
|
||||
echo " - {$col['Field']} ({$col['Type']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n3. Checking gates table structure:\n";
|
||||
$stmt = $db->query("DESCRIBE gates");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($columns)) {
|
||||
echo " ❌ Table 'gates' does not exist!\n";
|
||||
} else {
|
||||
echo " ✅ Table 'gates' exists with columns:\n";
|
||||
foreach ($columns as $col) {
|
||||
echo " - {$col['Field']} ({$col['Type']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n4. Checking entry_events table structure:\n";
|
||||
$stmt = $db->query("DESCRIBE entry_events");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($columns)) {
|
||||
echo " ❌ Table 'entry_events' does not exist!\n";
|
||||
} else {
|
||||
echo " ✅ Table 'entry_events' exists with columns:\n";
|
||||
foreach ($columns as $col) {
|
||||
echo " - {$col['Field']} ({$col['Type']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n5. Testing query (similar to DailySummaryService):\n";
|
||||
$testSql = "
|
||||
SELECT
|
||||
DATE(e.event_time) as summary_date,
|
||||
e.location_code,
|
||||
e.gate_code,
|
||||
e.category,
|
||||
COUNT(*) as total_count,
|
||||
COALESCE(t.amount, 0) as tariff_amount
|
||||
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) = CURDATE()
|
||||
GROUP BY
|
||||
DATE(e.event_time),
|
||||
e.location_code,
|
||||
e.gate_code,
|
||||
e.category,
|
||||
COALESCE(t.amount, 0)
|
||||
LIMIT 1
|
||||
";
|
||||
|
||||
try {
|
||||
$stmt = $db->query($testSql);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
echo " ✅ Query executed successfully!\n";
|
||||
if ($result) {
|
||||
echo " Sample result: " . json_encode($result, JSON_PRETTY_PRINT) . "\n";
|
||||
} else {
|
||||
echo " No data found for today.\n";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo " ❌ Query failed: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "\n=== Check Complete ===\n";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo "Database Error: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user