// Simple prediction: next 7 days demand $predicted_7days = round($avg_daily * 7, 0); // Confidence based on consistency $confidence = min($trend['total_sales'] > 5 ? 0.9 : 0.6, 0.95); // Generate recommendation $recommendation = $this->generateRecommendation( $product_id, $predicted_7days, $product['stock'], $product['min_stock'], $avg_daily ); return [ 'product_id' => $product_id, 'predicted_demand_7days' => $predicted_7days, 'predicted_monthly_demand' => round($avg_daily * 30, 0), 'confidence_level' => $confidence, 'current_stock' => $product['stock'], 'min_stock' => $product['min_stock'], 'recommendation' => $recommendation, 'analysis_date' => date('Y-m-d H:i:s') ]; } /** * Generate rekomendasi stok berdasarkan prediksi */ private function generateRecommendation($product_id, $predicted_demand, $current_stock, $min_stock, $avg_daily) { $safety_stock = round($avg_daily * 3, 0); $optimal_stock = $predicted_demand + $safety_stock; if ($current_stock < $min_stock) { return [ 'action'=> 'URGENT_RESTOCK', 'message' => "Stok kritis! Pesan segera min {$optimal_stock} unit", 'suggested_quantity' => $optimal_stock, 'priority' => 'HIGH' ]; } elseif ($current_stock <= $predicted_demand) { return [ 'action'=> 'RESTOCK_SOON', 'message' => "Stok akan habis dalam 7 hari. Pesan {$optimal_stock} unit", 'suggested_quantity' => $optimal_stock, 'priority' => 'MEDIUM' ]; } elseif ($current_stock > $predicted_demand * 2) { return [ 'action' => 'REDUCE_STOCK', 'message' => "Stok berlebih. Pertimbangkan promosi atau review harga", 'suggested_quantity' => $optimal_stock, 'priority' => 'LOW' ]; } else { return [ 'action' => 'OPTIMAL', 'message' => "Stok dalam kondisi optimal", 'suggested_quantity' => $optimal_stock, 'priority' => 'INFO' ]; } } /** * Auto-categorize produk menggunakan keyword analysis */ public function autoCategorizProduct($name, $description) { $categories = [ 'Elektronik' => ['laptop', 'komputer', 'monitor', 'keyboard', 'mouse', 'cpu', 'gpu', 'ram', 'ssd', 'hardisk'], 'Peralatan Kantor' => ['meja', 'kursi', 'lemari', 'rak', 'meja kerja', 'filing', 'kabinet'], 'Perlengkapan Tulis' => ['pena', 'pensil', 'kertas', 'buku', 'notepad', 'sticky note', 'marker'], 'Furniture' => ['sofa', 'meja makan', 'tempat tidur', 'almari', 'rak buku', 'rak dinding'], 'Peralatan Rumah Tangga' => ['pot', 'panci', 'wajan', 'piring', 'gelas', 'sendok', 'garpu'], 'Fashion' => ['baju', 'celana', 'jaket', 'topi', 'sepatu', 'kemeja', 'dress'], 'Kesehatan' => ['vitamin', 'obat', 'serum', 'masker', 'hand sanitizer', 'thermometer'] ]; $text = strtolower($name . ' ' . $description); $scores = []; foreach ($categories as $category => $keywords) { $score = 0; foreach ($keywords as $keyword) { if (strpos($text, $keyword) !== false) { $score += 2; } } $scores[$category] = $score; } $best_category = array_key_first($scores); $best_score = max($scores); return [ 'category' => $best_score > 0 ? $best_category : 'Umum', 'confidence' => min($best_score / 4, 1), 'all_scores' => $scores ]; } /** * Deteksi anomali dalam stok */ public function detectAnomalies() { $query = "SELECT p.id, p.name, p.stock, p.min_stock, (SELECT COUNT(*) FROM stock_movements WHERE product_id = p.id AND type = 'out' AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)) as recent_outs FROM products p"; $result = $this->conn->query($query); $anomalies = []; while ($row = $result->fetch_assoc()) { // Deteksi: stok habis tapi tidak ada aktivitas if ($row['stock'] == 0 && $row['recent_outs'] == 0) { $anomalies[] = [ 'type' => 'DEAD_STOCK', 'product_id' => $row['id'], 'product_name' => $row['name'], 'message' => 'Barang tidak bergerak selama 7 hari' ]; } // Deteksi: stok negatif (error) if ($row['stock'] < 0) { $anomalies[]=[ 'type'=> 'NEGATIVE_STOCK', 'product_id' => $row['id'], 'product_name' => $row['name'], 'message' => 'Stok negatif, kemungkinan ada kesalahan input' ]; } } return $anomalies; } /** * Generate AI insights summary */ public function generateInsights() { $query = "SELECT p.id, p.name, p.stock, (SELECT COUNT(*) FROM transactions WHERE product_id = p.id AND transaction_type = 'sale' AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)) as monthly_sales FROM products p ORDER BY monthly_sales DESC LIMIT 10"; $result = $this->conn->query($query); $insights = []; $top_products = []; $slow_moving = []; while ($row = $result->fetch_assoc()) { if ($row['monthly_sales'] > 20) { $top_products[] = [ 'name' => $row['name'], 'sales' => $row['monthly_sales'] ]; } elseif ($row['monthly_sales'] < 3 && $row['stock']> 5) { $slow_moving[] = [ 'name' => $row['name'], 'sales' => $row['monthly_sales'], 'stock' => $row['stock'] ]; } } return [ 'top_products' => $top_products, 'slow_moving_products' => $slow_moving, 'total_insights' => count($top_products) + count($slow_moving), 'generated_at' => date('Y-m-d H:i:s') ]; } }