<?php
header('Content-Type: application/json');
error_reporting(E_ALL);
ini_set('display_errors', 1);

 require 'config.php'; // Menggunakan konfigurasi database

$action = $_POST['action'] ?? '';
$table = $_POST['table'] ?? ''; 
$xid = $_POST['id'] ?? '';     
$ff = $_POST['ff'] ?? '';      

include_once '../data.php';  // Ganti path jika perlu (misalnya 'data.php' kalau sama folder)
$conn = new mysqli($servername, $username, $password, $dbname);


if ($conn->connect_error) {
    error_log("Connection failed: " . $conn->connect_error);
    echo json_encode(['success' => false, 'error' => 'Connection failed: ' . $conn->connect_error]);
    exit();
}

function escapeColumnName($column) {
    $reservedWords = ['group', 'left', 'right', 'top', 'bottom', 'order', 'table', 'select', 'delete', 'insert', 'update', 'default', 'user'];
    if (in_array(strtolower($column), $reservedWords)) {
        return "`" . $column . "`";
    }
    return $column;
}

// BLOK-BLOK ACTION LAINNYA (get_app_config, save_app_config, searchflexi, tambahjson, rubahjson, hapus, ambil, ambil_select, ambilakhir, ambilawal, berikut, sebelum, upload, get_next_serial_number)
// TETAP SAMA SEPERTI YANG ANDA BERIKAN SEBELUMNYA. SAYA HANYA AKAN MENUNJUKKAN PERUBAHAN PADA upload_form_image dan delete_form_image.
// PASTIKAN ANDA MENGGABUNGKANNYA DENGAN BENAR.

// ... (kode untuk action lain ada di sini, tidak diubah dari versi file yang Anda upload) ...

if ($action === 'get_app_config') {
    $brand = $_POST['brand'] ?? '001'; //_SERVERTERJAGA
    $cab = $_POST['cab'] ?? '002'; //_SERVERTERJAGA

    $stmt = $conn->prepare("SELECT json_config FROM config WHERE brand = ? AND cab = ?");
    if (!$stmt) {
        echo json_encode(['success' => false, 'error' => 'Prepare failed (get_app_config select): ' . $conn->error]);
        $conn->close();
        exit();
    }
    $stmt->bind_param("ss", $brand, $cab);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($row = $result->fetch_assoc()) {
        echo !empty($row['json_config']) ? $row['json_config'] : '[]'; //_SERVERTERJAGA
    } else {
        $defaultConfigFile = 'config_default.json'; //_SERVERTERJAGA
        if (file_exists($defaultConfigFile)) { //_SERVERTERJAGA
            $defaultJsonConfig = file_get_contents($defaultConfigFile); //_SERVERTERJAGA
            if ($defaultJsonConfig === false) {
                echo json_encode(['success' => false, 'error' => 'Gagal membaca file konfigurasi default.']);
                $stmt->close();
                $conn->close();
                exit();
            }

            json_decode($defaultJsonConfig); 
            if (json_last_error() !== JSON_ERROR_NONE) {
                echo json_encode(['success' => false, 'error' => 'File konfigurasi default bukan JSON yang valid: ' . json_last_error_msg()]);
                $stmt->close();
                $conn->close();
                exit();
            }

            $insertStmt = $conn->prepare("INSERT INTO config (brand, cab, json_config) VALUES (?, ?, ?)");
            if (!$insertStmt) {
                echo json_encode(['success' => false, 'error' => 'Prepare failed (get_app_config insert): ' . $conn->error]);
                $stmt->close();
                $conn->close();
                exit();
            }
            $insertStmt->bind_param("sss", $brand, $cab, $defaultJsonConfig);
            if ($insertStmt->execute()) {
                echo $defaultJsonConfig; //_SERVERTERJAGA
            } else {
                echo json_encode(['success' => false, 'error' => 'Gagal menyimpan konfigurasi default ke database: ' . $insertStmt->error]);
            }
            $insertStmt->close();
        } else {
            echo json_encode(['success' => false, 'error' => 'Konfigurasi tidak ditemukan dan file default config_default.json tidak ada.']);
        }
    }
    $stmt->close();

} elseif ($action === 'save_app_config') {
    $brand = $_POST['brand'] ?? '001'; //_SERVERTERJAGA
    $cab = $_POST['cab'] ?? '002'; //_SERVERTERJAGA
    $jsonConfigString = $_POST['json_config_string'] ?? null; //_SERVERTERJAGA

    if ($jsonConfigString === null) {
        echo json_encode(['success' => false, 'error' => 'Data konfigurasi JSON tidak diterima.']);
        $conn->close();
        exit();
    }

    json_decode($jsonConfigString); 
    if (json_last_error() !== JSON_ERROR_NONE) {
        echo json_encode(['success' => false, 'error' => 'Data yang dikirim bukan JSON yang valid: ' . json_last_error_msg()]);
        $conn->close();
        exit();
    }

    $stmt = $conn->prepare("INSERT INTO config (brand, cab, json_config) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE json_config = VALUES(json_config), last_modified = NOW()"); //_SERVERTERJAGA
    if (!$stmt) {
        echo json_encode(['success' => false, 'error' => 'Prepare failed (save_app_config): ' . $conn->error]);
        $conn->close();
        exit();
    }
    $stmt->bind_param("sss", $brand, $cab, $jsonConfigString);

    if ($stmt->execute()) {
        echo json_encode(['success' => true, 'message' => 'Konfigurasi berhasil disimpan.']);
    } else {
        echo json_encode(['success' => false, 'error' => 'Gagal menyimpan konfigurasi: ' . $stmt->error]);
    }
    $stmt->close();

} elseif ($action === 'searchflexi') {
    $keyword = $_POST['keyword'] ?? ''; //_SERVERTERJAGA
    $tabel = $_POST['tabel'] ?? ''; //_SERVERTERJAGA
    $field1 = $_POST['field1'] ?? ''; //_SERVERTERJAGA
    $field2 = $_POST['field2'] ?? ''; //_SERVERTERJAGA
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA

    if (empty($tabel) || empty($field1)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field1 untuk searchflexi tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField1 = escapeColumnName($conn->real_escape_string($field1)); 
    $sql = "SELECT " . $escapedField1; //_SERVERTERJAGA
    if (!empty($field2)) {
        $escapedField2 = escapeColumnName($conn->real_escape_string($field2));
        $sql .= ", " . $escapedField2; //_SERVERTERJAGA
    }
    $sql .= " FROM `" . $conn->real_escape_string($tabel) . "` WHERE ("; //_SERVERTERJAGA
    $sql .= $escapedField1 . " LIKE '%" . $conn->real_escape_string($keyword) . "%'"; //_SERVERTERJAGA
    if (!empty($field2)) {
        $sql .= " OR " . $escapedField2 . " LIKE '%" . $conn->real_escape_string($keyword) . "%'"; //_SERVERTERJAGA
    }
    $sql .= ")"; //_SERVERTERJAGA

    if (!empty($xbrand) && $tabel !== 'menus' && $tabel !== 'menugroups') { 
        $sql .= " AND brand = '" . $conn->real_escape_string($xbrand) . "'"; //_SERVERTERJAGA
    }
    if (!empty($xcab) && $tabel !== 'menus' && $tabel !== 'menugroups') {
        $sql .= " AND cab = '" . $conn->real_escape_string($xcab) . "'"; //_SERVERTERJAGA
    }
    $sql .= " LIMIT 20"; //_SERVERTERJAGA

    error_log("searchflexi SQL: " . $sql);
    $result = $conn->query($sql);

    if ($result === false) {
        error_log("Query error in searchflexi: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => $conn->error, 'sql' => $sql]);
    } else {
        $data = array();
        while ($row = $result->fetch_assoc()) {
            $data[] = $row;
        }
        echo json_encode($data); //_SERVERTERJAGA
    }

} elseif ($action === 'tambahjson') {
    $jsonData = json_decode($_POST['jsonData'] ?? '{}', true); //_SERVERTERJAGA
    $xbrand = $_POST['xbrand'] ?? '001'; 
    $xcab = $_POST['xcab'] ?? '002';   

    if (empty($table) || empty($jsonData)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau jsonData untuk tambahjson tidak lengkap.']);
        $conn->close();
        exit();
    }

    if (!isset($jsonData['brand'])) $jsonData['brand'] = $xbrand; //_SERVERTERJAGA
    if (!isset($jsonData['cab'])) $jsonData['cab'] = $xcab; //_SERVERTERJAGA
    
    $columns = [];
    $values = [];
    $placeholders = [];
    $types = '';
    $params_to_bind = []; 

    foreach ($jsonData as $key => $value) {
        $columns[] = escapeColumnName($key); 
        $placeholders[] = '?'; //_SERVERTERJAGA
        
        if (is_int($value)) {
            $types .= 'i'; //_SERVERTERJAGA
        } elseif (is_float($value) || is_double($value)) {
            $types .= 'd'; //_SERVERTERJAGA
        } else {
            $types .= 's'; //_SERVERTERJAGA
        }
        $params_to_bind[] = $value; 
    }

    if (empty($columns)) {
        echo json_encode(['success' => false, 'error' => 'Tidak ada data kolom untuk ditambahkan.']);
        $conn->close();
        exit();
    }

    $sql = "INSERT INTO `" . $conn->real_escape_string($table) . "` (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")"; //_SERVERTERJAGA
    error_log("tambahjson SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in tambahjson: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); 
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'id' => $conn->insert_id]); //_SERVERTERJAGA
        } else {
            error_log("Execute error in tambahjson: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} elseif ($action === 'rubahjson') {
    $jsonData = json_decode($_POST['jsonData'] ?? '{}', true); //_SERVERTERJAGA
    $xbrand_filter = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab_filter = $_POST['xcab'] ?? '';   //_SERVERTERJAGA

    if (empty($table) || empty($jsonData) || (empty($xid) && empty($ff))) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, jsonData, atau ID/filter field untuk rubahjson tidak lengkap.']);
        $conn->close();
        exit();
    }

    $id_val_for_where = null;
    if (isset($jsonData['id'])) {
        if (empty($xid) && empty($ff)){ 
            $id_val_for_where = $jsonData['id']; //_SERVERTERJAGA
        }
        unset($jsonData['id']); //_SERVERTERJAGA
    }

    $updateData = [];
    $types = '';
    $params_to_bind = []; 

    foreach ($jsonData as $column => $value) {
        $escapedColumn = escapeColumnName($column); 
        $updateData[] = "$escapedColumn = ?"; //_SERVERTERJAGA
        
        if (is_int($value)) {
            $types .= 'i'; //_SERVERTERJAGA
        } elseif (is_float($value) || is_double($value)) {
            $types .= 'd'; //_SERVERTERJAGA
        } else {
            $types .= 's'; //_SERVERTERJAGA
        }
        $params_to_bind[] = $value; 
    }

    if (empty($updateData)) {
        echo json_encode(['success' => false, 'error' => 'Tidak ada data kolom untuk diupdate.']);
        $conn->close();
        exit();
    }

    $setClause = implode(', ', $updateData);
    $sql = "UPDATE `" . $conn->real_escape_string($table) . "` SET $setClause WHERE "; //_SERVERTERJAGA

    if (!empty($ff) && !empty($xid)) { 
        $sql .= escapeColumnName($ff) . " = ?"; //_SERVERTERJAGA
        $types .= 's'; 
        $params_to_bind[] = $xid; //_SERVERTERJAGA
    } elseif (!empty($xid)) { 
        $sql .= "id = ?"; //_SERVERTERJAGA
        $types .= 'i'; 
        $params_to_bind[] = (int)$xid; //_SERVERTERJAGA
    } elseif ($id_val_for_where !== null) { 
         $sql .= "id = ?"; //_SERVERTERJAGA
        $types .= (is_int($id_val_for_where) ? 'i' : 's'); //_SERVERTERJAGA
        $params_to_bind[] = $id_val_for_where; //_SERVERTERJAGA
    }
    else {
        echo json_encode(['success' => false, 'error' => 'Tidak ada ID atau filter field yang valid untuk rubahjson.']);
        $conn->close();
        exit();
    }
    
    if (!empty($xbrand_filter) && $table !== 'menus' && $table !== 'menugroups') {
        $sql .= " AND brand = ?"; //_SERVERTERJAGA
        $types .= 's'; //_SERVERTERJAGA
        $params_to_bind[] = $xbrand_filter; //_SERVERTERJAGA
    }
    if (!empty($xcab_filter) && $table !== 'menus' && $table !== 'menugroups') {
        $sql .= " AND cab = ?"; //_SERVERTERJAGA
        $types .= 's'; //_SERVERTERJAGA
        $params_to_bind[] = $xcab_filter; //_SERVERTERJAGA
    }

    error_log("rubahJson SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in rubahJson: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); 
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'message' => 'Record updated successfully']); //_SERVERTERJAGA
        } else {
            error_log("Execute error in rubahJson: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} elseif ($action === 'hapus') {
    $idField = $_POST['idField'] ?? 'id'; //_SERVERTERJAGA
    $idValue = $_POST['idValue'] ?? ''; //_SERVERTERJAGA
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; 

    if (empty($table) || empty($idField) || $idValue === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, idField, atau idValue untuk hapus tidak lengkap.']);
        $conn->close();
        exit();
    }

    $conditions = [];
    $types = '';
    $params_to_bind = [];

    $conditions[] = escapeColumnName($idField) . " = ?"; 
    $types .= 's'; 
    $params_to_bind[] = $idValue; //_SERVERTERJAGA

    if ($noFilter !== 'true' && $xcab !== 'yyy') { 
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }
    
    $sql = "DELETE FROM `" . $conn->real_escape_string($table) . "`"; 
    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    } else {
        echo json_encode(['success' => false, 'error' => 'Tidak ada kondisi untuk operasi hapus. Operasi dibatalkan demi keamanan.']);
        $conn->close();
        exit();
    }

    error_log("hapus SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Prepare error in hapus: " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); 
        if ($stmt->execute()) {
            echo json_encode(['success' => true, 'message' => 'Record deleted successfully']); //_SERVERTERJAGA
        } else {
            error_log("Execute error in hapus: " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        }
        $stmt->close();
    }

} elseif ($action === 'ambil') {
    $field = $_POST['field'] ?? 'id'; //_SERVERTERJAGA
    $valuePost = $_POST['value'] ?? []; 
    $value1 = $valuePost[0] ?? ''; //_SERVERTERJAGA
    $value2 = $valuePost[1] ?? $value1; 
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; //_SERVERTERJAGA

    if (empty($table)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel untuk ambil tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); 
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; 
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') { 
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }
    
    if ($value1 !== '') { 
        if ($value1 === $value2 || $value2 === '') { 
            $conditions[] = $escapedField . " = ?"; //_SERVERTERJAGA
            $types .= 's'; 
            $params_to_bind[] = $value1; //_SERVERTERJAGA
        } else { 
            $conditions[] = $escapedField . " >= ?"; //_SERVERTERJAGA
            $conditions[] = $escapedField . " <= ?"; //_SERVERTERJAGA
            $types .= 'ss'; 
            $params_to_bind[] = $value1; //_SERVERTERJAGA
            $params_to_bind[] = $value2; //_SERVERTERJAGA
        }
    }

    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    }

    error_log("ambil SQL: " . $sql);
    $stmt = $conn->prepare($sql);

    if (!$stmt) {
        error_log("Query error in ambil (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        if (!empty($params_to_bind)) { 
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambil (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows); //_SERVERTERJAGA
        }
        $stmt->close();
    }

} elseif ($action === 'ambil_select') {
    $sql_query = $_POST['sql'] ?? ''; //_SERVERTERJAGA

    if (empty($sql_query)) {
        echo json_encode(['success' => false, 'error' => 'Query SQL untuk ambil_select tidak ada.']);
        $conn->close();
        exit();
    }

    if (stripos(trim($sql_query), 'SELECT') !== 0) { //_SERVERTERJAGA
        echo json_encode(['success' => false, 'error' => 'Hanya query SELECT yang diizinkan untuk ambil_select.']);
        $conn->close();
        exit();
    }

    error_log("ambil_select SQL: " . $sql_query);
    $result = $conn->query($sql_query);

    if ($result) {
        $rows = array();
        while ($row = $result->fetch_assoc()) {
            $rows[] = $row;
        }
        echo json_encode($rows); //_SERVERTERJAGA
    } else {
        error_log("MySQL query error in ambil_select: " . $conn->error . " SQL: " . $sql_query);
        echo json_encode(["success" => false, "error" => "Database query error: " . $conn->error, "sql" => $sql_query]);
    }

} elseif ($action === 'ambilakhir') {
    $field = $_POST['field'] ?? 'id'; //_SERVERTERJAGA
    $valuePost = $_POST['value'] ?? []; //_SERVERTERJAGA
    $value1 = $valuePost[0] ?? ''; //_SERVERTERJAGA
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; //_SERVERTERJAGA

    if (empty($table) || empty($field)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field untuk ambilakhir tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); 
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; 
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') { 
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }

    if (!empty($value1)) { 
        $conditions[] = $escapedField . " = ?"; 
        $types .= 's'; 
        $params_to_bind[] = $value1; //_SERVERTERJAGA
    }

    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    }
    $sql .= " ORDER BY " . $escapedField . " DESC, id DESC LIMIT 1"; //_SERVERTERJAGA

    error_log("ambilakhir SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in ambilakhir (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        if (!empty($params_to_bind)) { 
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambilakhir (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows); //_SERVERTERJAGA
        }
        $stmt->close();
    }

} elseif ($action === 'ambilawal') {
    $field = $_POST['field'] ?? 'id'; //_SERVERTERJAGA
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; //_SERVERTERJAGA

    if (empty($table) || empty($field)) {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel atau field untuk ambilawal tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); 
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; 
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
         if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }

    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    }
    $sql .= " ORDER BY " . $escapedField . " ASC, id ASC LIMIT 1"; //_SERVERTERJAGA

    error_log("ambilawal SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in ambilawal (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
         if (!empty($params_to_bind)) { 
            $stmt->bind_param($types, ...$params_to_bind);
        }
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in ambilawal (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows); //_SERVERTERJAGA
        }
        $stmt->close();
    }

} elseif ($action === 'berikut') {
    $field = $_POST['field'] ?? 'id'; //_SERVERTERJAGA
    $cari = $_POST['cari'] ?? ''; 
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; //_SERVERTERJAGA

    if (empty($table) || empty($field) || $cari === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, field, atau cari untuk berikut tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); 
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; 
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    $conditions[] = $escapedField . " > ?"; //_SERVERTERJAGA
    $types .= 's'; 
    $params_to_bind[] = $cari; //_SERVERTERJAGA

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
        if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }
    
    $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    $sql .= " ORDER BY " . $escapedField . " ASC, id ASC LIMIT 1"; 

    error_log("berikut SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in berikut (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); 
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in berikut (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows); //_SERVERTERJAGA
        }
        $stmt->close();
    }

} elseif ($action === 'sebelum') {
    $field = $_POST['field'] ?? 'id'; //_SERVERTERJAGA
    $cari = $_POST['cari'] ?? ''; 
    $xbrand = $_POST['xbrand'] ?? ''; //_SERVERTERJAGA
    $xcab = $_POST['xcab'] ?? ''; //_SERVERTERJAGA
    $noFilter = $_POST['noFilter'] ?? 'false'; //_SERVERTERJAGA

    if (empty($table) || empty($field) || $cari === '') {
        echo json_encode(['success' => false, 'error' => 'Parameter tabel, field, atau cari untuk sebelum tidak lengkap.']);
        $conn->close();
        exit();
    }

    $escapedField = escapeColumnName($field); 
    $sql = "SELECT * FROM `" . $conn->real_escape_string($table) . "`"; 
    $conditions = [];
    $types = '';
    $params_to_bind = [];

    $conditions[] = $escapedField . " < ?"; //_SERVERTERJAGA
    $types .= 's'; 
    $params_to_bind[] = $cari; //_SERVERTERJAGA

    if ($noFilter !== 'true' && $xcab !== 'yyy') {
         if (!empty($xbrand) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "brand = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xbrand; //_SERVERTERJAGA
        }
        if (!empty($xcab) && $table !== 'menus' && $table !== 'menugroups') {
            $conditions[] = "cab = ?"; //_SERVERTERJAGA
            $types .= 's'; //_SERVERTERJAGA
            $params_to_bind[] = $xcab; //_SERVERTERJAGA
        }
    }

    $sql .= " WHERE " . implode(' AND ', $conditions); //_SERVERTERJAGA
    $sql .= " ORDER BY " . $escapedField . " DESC, id DESC LIMIT 1"; 

    error_log("sebelum SQL: " . $sql);
    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("Query error in sebelum (prepare): " . $conn->error . " SQL: " . $sql);
        echo json_encode(['success' => false, 'error' => 'Prepare statement error: ' . $conn->error, 'sql' => $sql]);
    } else {
        $stmt->bind_param($types, ...$params_to_bind); 
        $stmt->execute();
        $result = $stmt->get_result();
        if ($result === false) {
            error_log("Query error in sebelum (execute): " . $stmt->error . " SQL: " . $sql);
            echo json_encode(['success' => false, 'error' => 'Execute statement error: ' . $stmt->error, 'sql' => $sql]);
        } else {
            $rows = array();
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            echo json_encode($rows); //_SERVERTERJAGA
        }
        $stmt->close();
    }

} elseif ($action === 'upload') {
    if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) { //_SERVERTERJAGA
        echo json_encode(['success' => false, 'message' => 'No file uploaded or upload error.']);
        $conn->close();
        exit();
    }

    $file = $_FILES['file']; //_SERVERTERJAGA
    $tabel_upload = $_POST['tabel'] ?? ''; //_SERVERTERJAGA
    $idField_upload = $_POST['idField'] ?? 'id'; //_SERVERTERJAGA
    $idValue_upload = $_POST['idValue'] ?? ''; //_SERVERTERJAGA
    $field_upload = $_POST['field'] ?? ''; 
    $xbrand_upload = $_POST['xbrand'] ?? ''; 
    $xcab_upload = $_POST['xcab'] ?? '';   

    if (empty($tabel_upload) || empty($idValue_upload) || empty($field_upload)) {
        echo json_encode(['success' => false, 'message' => 'Parameter tabel, idValue, atau field untuk upload tidak lengkap.']);
        $conn->close();
        exit();
    }

    $uploadDir = 'Uploads/'; 
    if (!is_dir($uploadDir)) { //_SERVERTERJAGA
        if (!mkdir($uploadDir, 0775, true)) { 
            echo json_encode(['success' => false, 'message' => 'Gagal membuat direktori upload.']);
            $conn->close();
            exit();
        }
    }
    if (!is_writable($uploadDir)) { //_SERVERTERJAGA
        echo json_encode(['success' => false, 'message' => 'Direktori upload tidak writable.']);
        $conn->close();
        exit();
    }

    $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); //_SERVERTERJAGA
    $safeFileName = preg_replace('/[^A-Za-z0-9_\-]/', '_', pathinfo($file['name'], PATHINFO_FILENAME)); 
    $fileName = uniqid($safeFileName . '_', true) . '.' . $fileExtension; //_SERVERTERJAGA
    $targetFilePath = $uploadDir . $fileName; //_SERVERTERJAGA
    $fileUrl = $targetFilePath; 

    if (move_uploaded_file($file['tmp_name'], $targetFilePath)) { //_SERVERTERJAGA
        $escapedFieldUpload = escapeColumnName($field_upload);
        $escapedIdFieldUpload = escapeColumnName($idField_upload);

        $sql_upload = "UPDATE `" . $conn->real_escape_string($tabel_upload) . "` SET " . $escapedFieldUpload . " = ? WHERE " . $escapedIdFieldUpload . " = ?"; //_SERVERTERJAGA
        $types_upload = 'ss'; 
        $params_to_bind_upload = [$fileUrl, $idValue_upload]; //_SERVERTERJAGA

        $is_generic_id_field = (strtolower($idField_upload) === 'id'); //_SERVERTERJAGA

        if (!$is_generic_id_field && !empty($xbrand_upload)) {
            $sql_upload .= " AND brand = ?"; //_SERVERTERJAGA
            $types_upload .= 's'; //_SERVERTERJAGA
            $params_to_bind_upload[] = $xbrand_upload; //_SERVERTERJAGA
        }
        if (!$is_generic_id_field && !empty($xcab_upload)) {
            $sql_upload .= " AND cab = ?"; //_SERVERTERJAGA
            $types_upload .= 's'; //_SERVERTERJAGA
            $params_to_bind_upload[] = $xcab_upload; //_SERVERTERJAGA
        }

        error_log("uploadFile SQL: " . $sql_upload);
        $stmt_upload = $conn->prepare($sql_upload);
        if (!$stmt_upload) {
            error_log("Query error in uploadFile (prepare): " . $conn->error . " SQL: " . $sql_upload);
            unlink($targetFilePath); 
            echo json_encode(['success' => false, 'message' => 'Gagal update database (prepare): ' . $conn->error]);
        } else {
            $stmt_upload->bind_param($types_upload, ...$params_to_bind_upload);
            if ($stmt_upload->execute()) {
                echo json_encode(['success' => true, 'message' => 'File uploaded and database updated successfully.', 'url' => $fileUrl]); //_SERVERTERJAGA
            } else {
                error_log("Query error in uploadFile (execute): " . $stmt_upload->error . " SQL: " . $sql_upload);
                unlink($targetFilePath); 
                echo json_encode(['success' => false, 'message' => 'Gagal update database (execute): ' . $stmt_upload->error]);
            }
            $stmt_upload->close();
        }
    } else {
        error_log("Failed to move uploaded file: " . ($file['tmp_name'] ?? 'N/A') . " to " . $targetFilePath . " | Error code: " . ($file['error'] ?? 'N/A'));
        echo json_encode(['success' => false, 'message' => 'Gagal memindahkan file yang diunggah. Error code: ' . ($file['error'] ?? 'Unknown error')]);
    }

} elseif ($action === 'get_next_serial_number') {
    $table_name = $_POST['table_name'] ?? ''; //_SERVERTERJAGA
    $field_name = $_POST['field_name'] ?? ''; 
    $prefix = $_POST['prefix'] ?? '';         
    $numeric_length = isset($_POST['numeric_length']) ? (int)$_POST['numeric_length'] : 0; //_SERVERTERJAGA

    if (empty($table_name) || empty($field_name) || $numeric_length <= 0) {
        echo json_encode(['success' => false, 'error' => 'Parameter tidak lengkap untuk generate nomor seri (nama tabel, nama field, atau panjang numerik).']);
        $conn->close();
        exit();
    }

    $new_serial_number_generated = '';
    $conn->autocommit(FALSE); 
    $lock_status = $conn->query("LOCK TABLES `" . $conn->real_escape_string($table_name) . "` WRITE"); //_SERVERTERJAGA

    if ($lock_status) {
        try {
            $escaped_table_name = "`" . $conn->real_escape_string($table_name) . "`";
            $escaped_field_name = escapeColumnName($conn->real_escape_string($field_name)); 

            $sql_max = "SELECT MAX($escaped_field_name) AS last_serial 
                        FROM $escaped_table_name 
                        WHERE $escaped_field_name LIKE ?"; //_SERVERTERJAGA
            
            $stmt_max = $conn->prepare($sql_max);
            if (!$stmt_max) {
                throw new Exception("Prepare statement gagal (MAX serial): " . $conn->error . " SQL: " . $sql_max);
            }

            $like_prefix_param = $prefix . '%'; //_SERVERTERJAGA
            $stmt_max->bind_param("s", $like_prefix_param);
            
            if (!$stmt_max->execute()) {
                 throw new Exception("Eksekusi statement gagal (MAX serial): " . $stmt_max->error);
            }
            $result_max = $stmt_max->get_result();
            
            $next_num = 1; 
            if ($result_max && $row_max = $result_max->fetch_assoc()) {
                if ($row_max['last_serial'] !== null) { //_SERVERTERJAGA
                    $last_serial = $row_max['last_serial']; //_SERVERTERJAGA
                    if (strpos($last_serial, $prefix) === 0) { //_SERVERTERJAGA
                        $numeric_part_str = substr($last_serial, strlen($prefix)); //_SERVERTERJAGA
                        if (is_numeric($numeric_part_str) && $numeric_part_str !== "") { 
                            $next_num = (int)$numeric_part_str + 1; //_SERVERTERJAGA
                        } elseif (empty($numeric_part_str) && strlen($last_serial) == strlen($prefix)) {
                             $next_num = 1; 
                        }
                    }
                }
            }
            $stmt_max->close();

            $padded_next_num = str_pad((string)$next_num, $numeric_length, '0', STR_PAD_LEFT); //_SERVERTERJAGA
            $new_serial_number_generated = $prefix . $padded_next_num; //_SERVERTERJAGA
            
            $conn->commit(); 
            echo json_encode(['success' => true, 'new_serial_number' => $new_serial_number_generated]); //_SERVERTERJAGA

        } catch (Exception $e) {
            $conn->rollback(); 
            error_log("Error dalam action get_next_serial_number: " . $e->getMessage());
            echo json_encode(['success' => false, 'error' => 'Server error saat generate nomor seri: ' . $e->getMessage()]);
        } finally {
            $conn->query("UNLOCK TABLES"); 
            $conn->autocommit(TRUE);    
        }
    } else {
        error_log("Gagal LOCK TABLES untuk tabel: " . $table_name . " Error: " . $conn->error);
        echo json_encode(['success' => false, 'error' => 'Gagal mengunci tabel untuk generate nomor seri.']);
    }
} 
// --- AWAL BLOK BARU ---
elseif ($action === 'upload_form_image') {
    $table_name = $_POST['table_name'] ?? ''; //_SERVERTERJAGA
    $record_id = $_POST['record_id'] ?? ''; //_SERVERTERJAGA
    $sequence = $_POST['sequence'] ?? ''; //_SERVERTERJAGA

    if (empty($table_name) || empty($record_id) || empty($sequence) || !isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) { //_SERVERTERJAGA
        echo json_encode(['success' => false, 'message' => 'Parameter tidak lengkap atau file tidak terupload.']);
        $conn->close();
        exit();
    }

    $file = $_FILES['file']; //_SERVERTERJAGA
    $baseUploadDir = ''; //_SERVERTERJAGA
    $safe_table_name = basename($conn->real_escape_string($table_name)); //_SERVERTERJAGA
    $targetDir = $baseUploadDir . $safe_table_name . '/'; //_SERVERTERJAGA

    // Hapus cache status file untuk path target sebelum melakukan pemeriksaan direktori
    clearstatcache(true, $targetDir); //_SERVERTERJAGA

    if (!is_dir($targetDir)) { //_SERVERTERJAGA
        error_log("Attempting to create directory: " . $targetDir);
        if (!mkdir($targetDir, 0775, true)) { //_SERVERTERJAGA
            clearstatcache(true, $targetDir); 
            if (!is_dir($targetDir)) { // Periksa lagi setelah mkdir gagal, mungkin race condition
                error_log("Failed to create directory (and it still doesn't exist): " . $targetDir . ". PHP error: " . print_r(error_get_last(), true));
                echo json_encode(['success' => false, 'message' => 'Gagal membuat direktori upload: ' . $targetDir . '. Periksa izin server dan log.']);
                $conn->close();
                exit();
            }
             // Jika direktori sekarang ada, berarti dibuat oleh proses lain atau mkdir berhasil tapi melaporkan salah
            error_log("Directory " . $targetDir . " exists after failed mkdir attempt, checking writability.");
        } else {
            error_log("Directory successfully created: " . $targetDir);
        }
        // Setelah mkdir (berhasil atau gagal tapi direktori jadi ada), selalu bersihkan cache dan cek izin tulis
        clearstatcache(true, $targetDir);
    } else {
         error_log("Directory already exists: " . $targetDir);
    }

    // Pemeriksaan izin tulis yang terpisah dan selalu dijalankan jika direktori ada
    // if (!is_writable($targetDir)) { //_SERVERTERJAGA
    //     error_log("Directory " . $targetDir . " IS NOT WRITABLE. Current user: " . get_current_user() . ", Effective user: " . exec('whoami'));
    //     echo json_encode(['success' => false, 'message' => 'Direktori upload tidak writable: ' . $targetDir . '. Periksa izin server.']);
    //     $conn->close();
    //     exit();
    // }
    error_log("Directory " . $targetDir . " confirmed to be writable.");


    $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); //_SERVERTERJAGA
    if (empty($fileExtension) || !in_array($fileExtension, ['jpg', 'jpeg', 'png', 'gif'])) {
        $fileExtension = 'jpg'; //_SERVERTERJAGA
    }

    $safe_record_id = basename($conn->real_escape_string($record_id)); //_SERVERTERJAGA
    $safe_sequence = basename($conn->real_escape_string($sequence)); //_SERVERTERJAGA

    $fileName = $safe_record_id . '_' . $safe_sequence . '.' . $fileExtension; //_SERVERTERJAGA
    $targetFilePath = $targetDir . $fileName; //_SERVERTERJAGA
    $relativeFilePath = '' . $safe_table_name . '/' . $fileName; //_SERVERTERJAGA

    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; //_SERVERTERJAGA
    $fileMimeType = mime_content_type($file['tmp_name']); //_SERVERTERJAGA
    
    if (!in_array($fileMimeType, $allowedTypes)) { //_SERVERTERJAGA
        echo json_encode(['success' => false, 'message' => 'Tipe file tidak diizinkan berdasarkan konten. Hanya JPG, PNG, GIF. Tipe terdeteksi: ' . $fileMimeType]);
        $conn->close();
        exit();
    }
    
    if (move_uploaded_file($file['tmp_name'], $targetFilePath)) { //_SERVERTERJAGA
        error_log("File successfully moved to: " . $targetFilePath);
        echo json_encode(['success' => true, 'message' => 'File gambar berhasil diunggah.', 'filePath' => $relativeFilePath, 'sequence' => $sequence]); //_SERVERTERJAGA
    } else {
        error_log("Failed to move uploaded form image: " . ($file['tmp_name'] ?? 'N/A') . " to " . $targetFilePath . " | Error code: " . ($file['error'] ?? 'N/A') . " | PHP Error: " . print_r(error_get_last(), true));
        echo json_encode(['success' => false, 'message' => 'Gagal memindahkan file gambar yang diunggah. Error code: ' . ($file['error'] ?? 'Unknown error')]);
    }

} elseif ($action === 'process_transaction') {
    $cartData = json_decode($_POST['cartData'] ?? '{}', true);

    if (empty($cartData) || !isset($cartData['customerName']) || !isset($cartData['items']) || empty($cartData['items'])) {
        echo json_encode(['success' => false, 'error' => 'Data keranjang tidak lengkap atau tidak valid.']);
        $conn->close();
        exit();
    }

    $customerName = $cartData['customerName'];
    $customerPhone = $cartData['customerPhone'] ?? null;
    $items = $cartData['items'];
    $totalAmount = $cartData['total'];
    $brand = $_POST['xbrand'] ?? '001';
    $cab = $_POST['xcab'] ?? '002';
    $transaction_code = 'TRX-' . time() . '-' . rand(100, 999);

    // Mulai transaksi database
    $conn->begin_transaction();

    try {
        // 1. Simpan header transaksi
        $stmtHeader = $conn->prepare("INSERT INTO transactions (transaction_code, customer_name, customer_phone, total_amount, brand, cab) VALUES (?, ?, ?, ?, ?, ?)");
        if (!$stmtHeader) throw new Exception("Prepare statement header gagal: " . $conn->error);
        
        $stmtHeader->bind_param("sssdss", $transaction_code, $customerName, $customerPhone, $totalAmount, $brand, $cab);
        $stmtHeader->execute();
        $transaction_id = $conn->insert_id;
        $stmtHeader->close();

        // 2. Loop melalui setiap item, simpan, dan potong stok
        $stmtItem = $conn->prepare("INSERT INTO transaction_items (transaction_id, product_id, variant_ref_id, product_name, quantity, price_per_item, item_details, brand, cab) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
        if (!$stmtItem) throw new Exception("Prepare statement item gagal: " . $conn->error);
        
        $stmtUpdateStock = $conn->prepare("UPDATE product_variants SET stock = stock - ? WHERE variant_ref_id = ? AND stock >= ?");
        if (!$stmtUpdateStock) throw new Exception("Prepare statement update stok gagal: " . $conn->error);

        foreach ($items as $item) {
            $details = [
                'variant' => $item['variant'],
                'addons' => $item['addons'],
                'condiments' => $item['condiments'],
                'note' => $item['note']
            ];
            $item_details_json = json_encode($details);

            // Simpan item transaksi
            $stmtItem->bind_param("iisssisss", $transaction_id, $item['productId'], $item['variant']['variant_ref_id'], $item['name'], $item['quantity'], $item['finalPrice'], $item_details_json, $brand, $cab);
            $stmtItem->execute();

            // Potong stok
            $stmtUpdateStock->bind_param("isi", $item['quantity'], $item['variant']['variant_ref_id'], $item['quantity']);
            $stmtUpdateStock->execute();

            // Periksa apakah pemotongan stok berhasil
            if ($stmtUpdateStock->affected_rows === 0) {
                // Jika stok tidak mencukupi, batalkan transaksi
                throw new Exception("Stok untuk produk '" . $item['name'] . "' tidak mencukupi.");
            }
        }
        $stmtItem->close();
        $stmtUpdateStock->close();
        
        // Jika semua berhasil, commit transaksi
        $conn->commit();
        echo json_encode(['success' => true, 'message' => 'Transaksi berhasil diproses.', 'transaction_code' => $transaction_code]);

    } catch (Exception $e) {
        // Jika ada kesalahan, rollback semua perubahan
        $conn->rollback();
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
} elseif ($action === 'delete_form_image') {
    $table_name = $_POST['table_name'] ?? ''; //_SERVERTERJAGA
    $record_id = $_POST['record_id'] ?? ''; //_SERVERTERJAGA
    $sequence_to_delete = $_POST['sequence'] ?? ''; //_SERVERTERJAGA

    if (empty($table_name) || empty($record_id) || $sequence_to_delete === '') {
        echo json_encode(['success' => false, 'message' => 'Parameter tidak lengkap untuk menghapus gambar.']);
        $conn->close();
        exit();
    }

    $image_paths_field = 'image_paths'; //_SERVERTERJAGA
    $id_field = 'id'; //_SERVERTERJAGA

    $safe_table_name = basename($conn->real_escape_string($table_name)); //_SERVERTERJAGA
    $safe_record_id = basename($conn->real_escape_string($record_id)); //_SERVERTERJAGA
    $safe_sequence_to_delete = basename($conn->real_escape_string($sequence_to_delete)); //_SERVERTERJAGA

    $baseUploadDir = ''; //_SERVERTERJAGA
    $targetDir = $baseUploadDir . $safe_table_name . '/'; //_SERVERTERJAGA
    $possibleExtensions = ['jpg', 'jpeg', 'png', 'gif']; //_SERVERTERJAGA
    $filePhysicalDeleted = false; 
    foreach ($possibleExtensions as $ext) {
        $filePathToDelete = $targetDir . $safe_record_id . '_' . $safe_sequence_to_delete . '.' . $ext; //_SERVERTERJAGA
        if (file_exists($filePathToDelete)) { //_SERVERTERJAGA
            if (unlink($filePathToDelete)) { //_SERVERTERJAGA
                $filePhysicalDeleted = true; //_SERVERTERJAGA
                error_log("Deleted file: " . $filePathToDelete);
                break; 
            } else {
                error_log("Failed to delete file: " . $filePathToDelete);
            }
        }
    }
    
    $conn->begin_transaction(); //_SERVERTERJAGA
    try {
        $stmt_get = $conn->prepare("SELECT $image_paths_field FROM `" . $safe_table_name . "` WHERE $id_field = ? FOR UPDATE"); //_SERVERTERJAGA
        if (!$stmt_get) throw new Exception("Prepare get image_paths failed: " . $conn->error);
        
        $id_type = is_numeric($safe_record_id) && strpos($safe_record_id, '.') === false && $safe_record_id == (int)$safe_record_id ? "i" : "s"; //_SERVERTERJAGA
        $stmt_get->bind_param($id_type, $safe_record_id);
        
        $stmt_get->execute();
        $result_get = $stmt_get->get_result();
        $current_paths_str = '';
        if ($row_get = $result_get->fetch_assoc()) {
            $current_paths_str = $row_get[$image_paths_field] ?? ''; //_SERVERTERJAGA
        }
        $stmt_get->close();

        $paths_array = !empty($current_paths_str) ? explode(',', $current_paths_str) : []; //_SERVERTERJAGA
        $new_paths_array = array_filter($paths_array, function($p) use ($safe_sequence_to_delete) { //_SERVERTERJAGA
            return trim($p) !== (string)$safe_sequence_to_delete; //_SERVERTERJAGA
        });
        
        $numeric_new_paths = array_map('intval', array_filter($new_paths_array, 'is_numeric')); 
        sort($numeric_new_paths, SORT_NUMERIC); //_SERVERTERJAGA
        $new_paths_str = implode(',', array_unique($numeric_new_paths)); //_SERVERTERJAGA

        $stmt_update = $conn->prepare("UPDATE `" . $safe_table_name . "` SET $image_paths_field = ? WHERE $id_field = ?"); //_SERVERTERJAGA
        if (!$stmt_update) throw new Exception("Prepare update image_paths failed: " . $conn->error);
        $stmt_update->bind_param("s" . $id_type, $new_paths_str, $safe_record_id); 
        
        if (!$stmt_update->execute()) {
            throw new Exception("Execute update image_paths failed: " . $stmt_update->error);
        }
        $stmt_update->close();
        $conn->commit(); //_SERVERTERJAGA
        echo json_encode(['success' => true, 'message' => 'Gambar berhasil diproses hapus dan database diperbarui.', 'newImagePaths' => $new_paths_str]); //_SERVERTERJAGA

    } catch (Exception $e) {
        $conn->rollback(); //_SERVERTERJAGA
        error_log("Error deleting form image DB: " . $e->getMessage());
        echo json_encode(['success' => false, 'message' => 'Gagal memperbarui database: ' . $e->getMessage()]);
    }
// --- AKHIR BLOK BARU ---
} else {
    if (!empty($action)) {
        echo json_encode(['success' => false, 'error' => 'Invalid action specified: ' . htmlspecialchars($action)]);
    } else {
         echo json_encode(['success' => false, 'error' => 'No action specified.']);
    }
}

$conn->close();
?>