0.4, // Lower stock is more urgent (40% importance) 'price_value' => 0.3, // Higher price might indicate higher priority (30% importance) 'turnover_rate' => 0.3 // Higher turnover rate means faster selling (30% importance) ]; } // 1. Get product data from database $query = "SELECT b.id_barang, b.nama_barang, b.stok, b.harga, COALESCE(COUNT(t.id_transaksi), 0) as sales_count FROM barang b LEFT JOIN detail_transaksi dt ON b.id_barang = dt.id_barangK LEFT JOIN transaksi t ON dt.id_transaksi = t.id_transaksi AND t.tanggal BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW() GROUP BY b.id_barang"; $result = mysqli_query($conn, $query); if (!$result) { return ["error" => "Database query failed: " . mysqli_error($conn)]; } $products = []; while ($row = mysqli_fetch_assoc($result)) { // Convert price from varchar to numeric $row['harga'] = (float)str_replace(['Rp', '.', ','], ['', '', '.'], $row['harga']); // Calculate turnover rate (sales per stock unit) $row['turnover_rate'] = $row['stok'] > 0 ? $row['sales_count'] / $row['stok'] : 0; $products[] = $row; } if (empty($products)) { return ["message" => "No products found"]; } // 2. Create decision matrix (X) $decision_matrix = []; foreach ($products as $product) { $decision_matrix[] = [ 'id_barang' => $product['id_barang'], 'nama_barang' => $product['nama_barang'], 'stock_level' => max(1, $product['stok']), // Ensure no zero values 'price_value' => $product['harga'], 'turnover_rate' => $product['turnover_rate'], ]; } // 3. Normalize the decision matrix (R) according to formula 3.1 $normalized_matrix = normalizeMatrixEuclidean($decision_matrix); // 4. Apply weights to normalized matrix (Y) according to formula 3.2 $weighted_matrix = applyWeightsToMatrix($normalized_matrix, $criteria_weights); // 5. Determine ideal and negative-ideal solutions (A+, A-) according to formula 3.3 // Define benefit criteria (higher is better) and cost criteria (lower is better) $benefit_criteria = ['price_value', 'turnover_rate']; $cost_criteria = ['stock_level']; $ideal_solution = []; $negative_ideal_solution = []; // For benefit criteria: higher values are better // For cost criteria: lower values are better foreach ($benefit_criteria as $criterion) { $ideal_solution[$criterion] = max(array_column($weighted_matrix, $criterion)); $negative_ideal_solution[$criterion] = min(array_column($weighted_matrix, $criterion)); } foreach ($cost_criteria as $criterion) { $ideal_solution[$criterion] = min(array_column($weighted_matrix, $criterion)); $negative_ideal_solution[$criterion] = max(array_column($weighted_matrix, $criterion)); } // 6. Calculate separation measures (D+, D-) according to formula 3.4 $separation_positive = []; $separation_negative = []; foreach ($weighted_matrix as $i => $product) { // Distance to positive ideal solution (D+) $sum_positive = 0; foreach (array_merge($benefit_criteria, $cost_criteria) as $criterion) { $sum_positive += pow($product[$criterion] - $ideal_solution[$criterion], 2); } $separation_positive[$i] = sqrt($sum_positive); // Distance to negative ideal solution (D-) $sum_negative = 0; foreach (array_merge($benefit_criteria, $cost_criteria) as $criterion) { $sum_negative += pow($product[$criterion] - $negative_ideal_solution[$criterion], 2); } $separation_negative[$i] = sqrt($sum_negative); } // 7. Calculate relative closeness to ideal solution (V) according to formula 3.5 $preference_scores = []; foreach ($weighted_matrix as $i => $product) { // Avoid division by zero $denominator = $separation_negative[$i] + $separation_positive[$i]; $preference_scores[$i] = ($denominator > 0) ? ($separation_negative[$i] / $denominator) : 0; } // 8. Rank products based on preference scores $topsis_results = []; foreach ($preference_scores as $i => $score) { $topsis_results[] = [ 'id_barang' => $decision_matrix[$i]['id_barang'], 'nama_barang' => $decision_matrix[$i]['nama_barang'], 'stok' => $products[$i]['stok'], 'harga' => $products[$i]['harga'], 'topsis_score' => $score, 'rank' => 0 // Will be filled later ]; } // Sort by preference score (descending) usort($topsis_results, function($a, $b) { return $b['topsis_score'] <=> $a['topsis_score']; }); // Assign ranks foreach ($topsis_results as $i => $product) { $topsis_results[$i]['rank'] = $i + 1; } return $topsis_results; } /** * Normalize the decision matrix using Euclidean length of vector (Formula 3.1) * Rij = xij / sqrt(sum(xij^2)) */ function normalizeMatrixEuclidean($matrix) { $normalized = []; $criteria = ['stock_level', 'price_value', 'turnover_rate']; // Calculate the square root of the sum of squares for each criterion $denominators = []; foreach ($criteria as $criterion) { $sum_squares = 0; foreach ($matrix as $product) { $sum_squares += pow($product[$criterion], 2); } $denominators[$criterion] = sqrt($sum_squares); } // Normalize each value according to formula 3.1 foreach ($matrix as $i => $product) { $normalized[$i] = [ 'id_barang' => $product['id_barang'], 'nama_barang' => $product['nama_barang'] ]; foreach ($criteria as $criterion) { // Avoid division by zero $normalized[$i][$criterion] = ($denominators[$criterion] > 0) ? ($product[$criterion] / $denominators[$criterion]) : 0; } } return $normalized; } /** * Apply weights to the normalized matrix (Formula 3.2) * yij = wi * rij */ function applyWeightsToMatrix($matrix, $weights) { $weighted = []; $criteria = ['stock_level', 'price_value', 'turnover_rate']; foreach ($matrix as $i => $product) { $weighted[$i] = [ 'id_barang' => $product['id_barang'], 'nama_barang' => $product['nama_barang'] ]; foreach ($criteria as $criterion) { $weighted[$i][$criterion] = $product[$criterion] * $weights[$criterion]; } } return $weighted; } ?>