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:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -31,7 +31,10 @@ composer.phar
|
||||
composer.lock
|
||||
|
||||
# Backup
|
||||
*.sql
|
||||
*.bak
|
||||
backup/
|
||||
|
||||
# SQL files (exclude backup, but allow migrations)
|
||||
*.sql
|
||||
!migrations/*.sql
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
";
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user