diff --git a/.gitignore b/.gitignore index af68f56..4cd5923 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,10 @@ composer.phar composer.lock # Backup -*.sql *.bak backup/ +# SQL files (exclude backup, but allow migrations) +*.sql +!migrations/*.sql + diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 04d35ed..3a0a307 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -77,36 +77,37 @@ composer dump-autoload --optimize - Memory limit: 256M (minimum) 3. **Nginx Configuration** (PENTING untuk fix 404): - + Masuk ke: **Website -> api.btekno.cloud -> Settings -> Configuration** - + Ganti isi configuration dengan: + ```nginx server { listen 80; listen 443 ssl http2; server_name api.btekno.cloud; - + # Document Root - HARUS ke folder public/ root /www/wwwroot/api.btekno.cloud/api/public; index index.php index.html; - + # Logs access_log /www/wwwlogs/api.btekno.cloud.log; error_log /www/wwwlogs/api.btekno.cloud.error.log; - + # Disable access to hidden files location ~ /\. { deny all; access_log off; log_not_found off; } - + # Main location block - routing untuk Slim Framework location / { try_files $uri $uri/ /index.php?$query_string; } - + # PHP-FPM configuration location ~ \.php$ { try_files $uri =404; @@ -115,29 +116,30 @@ composer dump-autoload --optimize fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - + # Disable buffering for SSE fastcgi_buffering off; } - + # Disable PHP execution in uploads location ~* /uploads/.*\.php$ { deny all; } } ``` - + **Cek PHP socket path:** + ```bash # Cek PHP version yang digunakan php -v - + # Cek socket path (biasanya di /tmp/php-cgi-XX.sock) ls -la /tmp/php-cgi-*.sock ``` - + Setelah edit, klik **Save** dan **Reload** nginx. - + **Atau copy file `nginx.conf.example` dan sesuaikan path PHP socket.** ## 🔧 Environment Configuration diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 0d3b410..5d10138 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -2,10 +2,14 @@ ## Error: "Unknown column 't.amount' in 'SELECT'" -### Kemungkinan Penyebab: +### Root Cause: +**Kolom di database adalah `price`, bukan `amount`!** + +Database production menggunakan kolom `price` di tabel `tariffs`, tapi code menggunakan `amount`. Ini menyebabkan mismatch. + +### Kemungkinan Penyebab Lain: 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 +2. **File belum ter-update** - Meskipun sudah `git pull`, file mungkin belum benar-benar ter-update --- @@ -65,7 +69,7 @@ SELECT e.gate_code, e.category, COUNT(*) as total_count, - COALESCE(t.amount, 0) as tariff_amount + COALESCE(t.price, 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 @@ -80,7 +84,7 @@ GROUP BY e.location_code, e.gate_code, e.category, - COALESCE(t.amount, 0) + COALESCE(t.price, 0) LIMIT 5; ``` diff --git a/bin/check_database.php b/bin/check_database.php index 62c7eaa..7a4631b 100644 --- a/bin/check_database.php +++ b/bin/check_database.php @@ -92,22 +92,22 @@ try { 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) + COALESCE(t.price, 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.price, 0) LIMIT 1 "; diff --git a/src/Modules/Retribusi/Frontend/RetribusiWriteService.php b/src/Modules/Retribusi/Frontend/RetribusiWriteService.php index 488cdb5..e31dd32 100644 --- a/src/Modules/Retribusi/Frontend/RetribusiWriteService.php +++ b/src/Modules/Retribusi/Frontend/RetribusiWriteService.php @@ -236,7 +236,7 @@ class RetribusiWriteService public function getTariff(string $locationCode, string $gateCode, string $category): ?array { $stmt = $this->db->prepare( - 'SELECT location_code, gate_code, category, amount + 'SELECT location_code, gate_code, category, price FROM tariffs WHERE location_code = ? AND gate_code = ? AND category = ? LIMIT 1' @@ -256,7 +256,7 @@ class RetribusiWriteService public function createTariff(array $data): array { $stmt = $this->db->prepare( - 'INSERT INTO tariffs (location_code, gate_code, category, amount) + 'INSERT INTO tariffs (location_code, gate_code, category, price) VALUES (?, ?, ?, ?)' ); @@ -264,7 +264,7 @@ class RetribusiWriteService $data['location_code'], $data['gate_code'], $data['category'], - (int) $data['amount'] + (int) $data['price'] ]); return $this->getTariff($data['location_code'], $data['gate_code'], $data['category']); @@ -285,9 +285,9 @@ class RetribusiWriteService $updates = []; $params = []; - if (isset($data['amount'])) { - $updates[] = 'amount = ?'; - $params[] = (int) $data['amount']; + if (isset($data['price'])) { + $updates[] = 'price = ?'; + $params[] = (int) $data['price']; } if (empty($updates)) { diff --git a/src/Modules/Retribusi/Summary/DailySummaryService.php b/src/Modules/Retribusi/Summary/DailySummaryService.php index 92e0e20..666e762 100644 --- a/src/Modules/Retribusi/Summary/DailySummaryService.php +++ b/src/Modules/Retribusi/Summary/DailySummaryService.php @@ -43,7 +43,7 @@ class DailySummaryService e.gate_code, e.category, COUNT(*) as total_count, - COALESCE(t.amount, 0) as tariff_amount + COALESCE(t.price, 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 @@ -58,7 +58,7 @@ class DailySummaryService e.location_code, e.gate_code, e.category, - COALESCE(t.amount, 0) + COALESCE(t.price, 0) "; $stmt = $this->db->prepare($sql); diff --git a/src/Modules/Retribusi/Summary/HourlySummaryService.php b/src/Modules/Retribusi/Summary/HourlySummaryService.php index cf1d2ed..829fcc7 100644 --- a/src/Modules/Retribusi/Summary/HourlySummaryService.php +++ b/src/Modules/Retribusi/Summary/HourlySummaryService.php @@ -45,7 +45,7 @@ class HourlySummaryService e.gate_code, e.category, COUNT(*) as total_count, - COALESCE(t.amount, 0) as tariff_amount + COALESCE(t.price, 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 @@ -61,7 +61,7 @@ class HourlySummaryService e.location_code, e.gate_code, e.category, - COALESCE(t.amount, 0) + COALESCE(t.price, 0) "; $stmt = $this->db->prepare($sql); diff --git a/src/Support/Validator.php b/src/Support/Validator.php index 37241a5..23b7f2c 100644 --- a/src/Support/Validator.php +++ b/src/Support/Validator.php @@ -281,15 +281,15 @@ class Validator } } - // Amount: required for POST, optional for update - if (isset($data['amount'])) { - if (!is_int($data['amount']) && !is_numeric($data['amount'])) { - $errors['amount'] = 'Must be an integer'; - } elseif ((int) $data['amount'] < 0) { - $errors['amount'] = 'Must be >= 0'; + // Price: required for POST, optional for update + if (isset($data['price'])) { + if (!is_int($data['price']) && !is_numeric($data['price'])) { + $errors['price'] = 'Must be an integer'; + } elseif ((int) $data['price'] < 0) { + $errors['price'] = 'Must be >= 0'; } } elseif (!$isUpdate) { - $errors['amount'] = 'Field is required'; + $errors['price'] = 'Field is required'; } return $errors;