Fix: Change 'amount' to 'price' to match database schema - Database uses 'price' column, not 'amount' - Update all queries in DailySummaryService, HourlySummaryService - Update RetribusiWriteService (SELECT, INSERT, UPDATE) - Update Validator to use 'price' field - Update check_database.php and TROUBLESHOOTING.md

This commit is contained in:
mwpn
2025-12-17 13:36:29 +07:00
parent 56403469ef
commit ca3b1bd0d7
8 changed files with 61 additions and 52 deletions

5
.gitignore vendored
View File

@@ -31,7 +31,10 @@ composer.phar
composer.lock composer.lock
# Backup # Backup
*.sql
*.bak *.bak
backup/ backup/
# SQL files (exclude backup, but allow migrations)
*.sql
!migrations/*.sql

View File

@@ -77,36 +77,37 @@ composer dump-autoload --optimize
- Memory limit: 256M (minimum) - Memory limit: 256M (minimum)
3. **Nginx Configuration** (PENTING untuk fix 404): 3. **Nginx Configuration** (PENTING untuk fix 404):
Masuk ke: **Website -> api.btekno.cloud -> Settings -> Configuration** Masuk ke: **Website -> api.btekno.cloud -> Settings -> Configuration**
Ganti isi configuration dengan: Ganti isi configuration dengan:
```nginx ```nginx
server { server {
listen 80; listen 80;
listen 443 ssl http2; listen 443 ssl http2;
server_name api.btekno.cloud; server_name api.btekno.cloud;
# Document Root - HARUS ke folder public/ # Document Root - HARUS ke folder public/
root /www/wwwroot/api.btekno.cloud/api/public; root /www/wwwroot/api.btekno.cloud/api/public;
index index.php index.html; index index.php index.html;
# Logs # Logs
access_log /www/wwwlogs/api.btekno.cloud.log; access_log /www/wwwlogs/api.btekno.cloud.log;
error_log /www/wwwlogs/api.btekno.cloud.error.log; error_log /www/wwwlogs/api.btekno.cloud.error.log;
# Disable access to hidden files # Disable access to hidden files
location ~ /\. { location ~ /\. {
deny all; deny all;
access_log off; access_log off;
log_not_found off; log_not_found off;
} }
# Main location block - routing untuk Slim Framework # Main location block - routing untuk Slim Framework
location / { location / {
try_files $uri $uri/ /index.php?$query_string; try_files $uri $uri/ /index.php?$query_string;
} }
# PHP-FPM configuration # PHP-FPM configuration
location ~ \.php$ { location ~ \.php$ {
try_files $uri =404; try_files $uri =404;
@@ -115,29 +116,30 @@ composer dump-autoload --optimize
fastcgi_index index.php; fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params; include fastcgi_params;
# Disable buffering for SSE # Disable buffering for SSE
fastcgi_buffering off; fastcgi_buffering off;
} }
# Disable PHP execution in uploads # Disable PHP execution in uploads
location ~* /uploads/.*\.php$ { location ~* /uploads/.*\.php$ {
deny all; deny all;
} }
} }
``` ```
**Cek PHP socket path:** **Cek PHP socket path:**
```bash ```bash
# Cek PHP version yang digunakan # Cek PHP version yang digunakan
php -v php -v
# Cek socket path (biasanya di /tmp/php-cgi-XX.sock) # Cek socket path (biasanya di /tmp/php-cgi-XX.sock)
ls -la /tmp/php-cgi-*.sock ls -la /tmp/php-cgi-*.sock
``` ```
Setelah edit, klik **Save** dan **Reload** nginx. Setelah edit, klik **Save** dan **Reload** nginx.
**Atau copy file `nginx.conf.example` dan sesuaikan path PHP socket.** **Atau copy file `nginx.conf.example` dan sesuaikan path PHP socket.**
## 🔧 Environment Configuration ## 🔧 Environment Configuration

View File

@@ -2,10 +2,14 @@
## Error: "Unknown column 't.amount' in 'SELECT'" ## 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 1. **OPcache belum di-clear** - PHP masih menggunakan file lama
2. **Struktur database berbeda** - Kolom `amount` mungkin tidak ada atau nama berbeda 2. **File belum ter-update** - Meskipun sudah `git pull`, file mungkin belum benar-benar ter-update
3. **File belum ter-update** - Meskipun sudah `git pull`, file mungkin belum benar-benar ter-update
--- ---
@@ -65,7 +69,7 @@ SELECT
e.gate_code, e.gate_code,
e.category, e.category,
COUNT(*) as total_count, COUNT(*) as total_count,
COALESCE(t.amount, 0) as tariff_amount COALESCE(t.price, 0) as tariff_amount
FROM entry_events e FROM entry_events e
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1 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 INNER JOIN gates g ON e.location_code = g.location_code
@@ -80,7 +84,7 @@ GROUP BY
e.location_code, e.location_code,
e.gate_code, e.gate_code,
e.category, e.category,
COALESCE(t.amount, 0) COALESCE(t.price, 0)
LIMIT 5; LIMIT 5;
``` ```

View File

@@ -92,22 +92,22 @@ try {
e.gate_code, e.gate_code,
e.category, e.category,
COUNT(*) as total_count, COUNT(*) as total_count,
COALESCE(t.amount, 0) as tariff_amount COALESCE(t.price, 0) as tariff_amount
FROM entry_events e FROM entry_events e
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1 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 INNER JOIN gates g ON e.location_code = g.location_code
AND e.gate_code = g.gate_code AND e.gate_code = g.gate_code
AND g.is_active = 1 AND g.is_active = 1
LEFT JOIN tariffs t ON e.location_code = t.location_code LEFT JOIN tariffs t ON e.location_code = t.location_code
AND e.gate_code = t.gate_code AND e.gate_code = t.gate_code
AND e.category = t.category AND e.category = t.category
WHERE DATE(e.event_time) = CURDATE() WHERE DATE(e.event_time) = CURDATE()
GROUP BY GROUP BY
DATE(e.event_time), DATE(e.event_time),
e.location_code, e.location_code,
e.gate_code, e.gate_code,
e.category, e.category,
COALESCE(t.amount, 0) COALESCE(t.price, 0)
LIMIT 1 LIMIT 1
"; ";

View File

@@ -236,7 +236,7 @@ class RetribusiWriteService
public function getTariff(string $locationCode, string $gateCode, string $category): ?array public function getTariff(string $locationCode, string $gateCode, string $category): ?array
{ {
$stmt = $this->db->prepare( $stmt = $this->db->prepare(
'SELECT location_code, gate_code, category, amount 'SELECT location_code, gate_code, category, price
FROM tariffs FROM tariffs
WHERE location_code = ? AND gate_code = ? AND category = ? WHERE location_code = ? AND gate_code = ? AND category = ?
LIMIT 1' LIMIT 1'
@@ -256,7 +256,7 @@ class RetribusiWriteService
public function createTariff(array $data): array public function createTariff(array $data): array
{ {
$stmt = $this->db->prepare( $stmt = $this->db->prepare(
'INSERT INTO tariffs (location_code, gate_code, category, amount) 'INSERT INTO tariffs (location_code, gate_code, category, price)
VALUES (?, ?, ?, ?)' VALUES (?, ?, ?, ?)'
); );
@@ -264,7 +264,7 @@ class RetribusiWriteService
$data['location_code'], $data['location_code'],
$data['gate_code'], $data['gate_code'],
$data['category'], $data['category'],
(int) $data['amount'] (int) $data['price']
]); ]);
return $this->getTariff($data['location_code'], $data['gate_code'], $data['category']); return $this->getTariff($data['location_code'], $data['gate_code'], $data['category']);
@@ -285,9 +285,9 @@ class RetribusiWriteService
$updates = []; $updates = [];
$params = []; $params = [];
if (isset($data['amount'])) { if (isset($data['price'])) {
$updates[] = 'amount = ?'; $updates[] = 'price = ?';
$params[] = (int) $data['amount']; $params[] = (int) $data['price'];
} }
if (empty($updates)) { if (empty($updates)) {

View File

@@ -43,7 +43,7 @@ class DailySummaryService
e.gate_code, e.gate_code,
e.category, e.category,
COUNT(*) as total_count, COUNT(*) as total_count,
COALESCE(t.amount, 0) as tariff_amount COALESCE(t.price, 0) as tariff_amount
FROM entry_events e FROM entry_events e
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1 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 INNER JOIN gates g ON e.location_code = g.location_code
@@ -58,7 +58,7 @@ class DailySummaryService
e.location_code, e.location_code,
e.gate_code, e.gate_code,
e.category, e.category,
COALESCE(t.amount, 0) COALESCE(t.price, 0)
"; ";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);

View File

@@ -45,7 +45,7 @@ class HourlySummaryService
e.gate_code, e.gate_code,
e.category, e.category,
COUNT(*) as total_count, COUNT(*) as total_count,
COALESCE(t.amount, 0) as tariff_amount COALESCE(t.price, 0) as tariff_amount
FROM entry_events e FROM entry_events e
INNER JOIN locations l ON e.location_code = l.code AND l.is_active = 1 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 INNER JOIN gates g ON e.location_code = g.location_code
@@ -61,7 +61,7 @@ class HourlySummaryService
e.location_code, e.location_code,
e.gate_code, e.gate_code,
e.category, e.category,
COALESCE(t.amount, 0) COALESCE(t.price, 0)
"; ";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);

View File

@@ -281,15 +281,15 @@ class Validator
} }
} }
// Amount: required for POST, optional for update // Price: required for POST, optional for update
if (isset($data['amount'])) { if (isset($data['price'])) {
if (!is_int($data['amount']) && !is_numeric($data['amount'])) { if (!is_int($data['price']) && !is_numeric($data['price'])) {
$errors['amount'] = 'Must be an integer'; $errors['price'] = 'Must be an integer';
} elseif ((int) $data['amount'] < 0) { } elseif ((int) $data['price'] < 0) {
$errors['amount'] = 'Must be >= 0'; $errors['price'] = 'Must be >= 0';
} }
} elseif (!$isUpdate) { } elseif (!$isUpdate) {
$errors['amount'] = 'Field is required'; $errors['price'] = 'Field is required';
} }
return $errors; return $errors;