kriteria = Kriteria::with('subKriteria')->orderBy('id')->get(); if ($this->kriteria->isEmpty()) { throw new Exception('Tidak ada kriteria untuk perhitungan'); } } public function calculate(PengajuanUkt $pengajuan): array { // Cek apakah total bobot valid (== 1) $totalBobot = $this->kriteria->sum('bobot'); if (abs($totalBobot - 1) > 0.0001) { throw new Exception("Total bobot saat ini adalah ..."); } DB::beginTransaction(); try { if ($pengajuan->status_validasi !== 'valid') { throw new Exception("Pengajuan belum divalidasi"); } if ($pengajuan->details->isEmpty()) { throw new Exception("Data detail pengajuan tidak lengkap"); } // 1. Decision Matrix $decisionMatrix = $this->buildDecisionMatrix($pengajuan); // 2. Normalized Matrix $normalizedMatrix = $this->normalizeMatrix($decisionMatrix); // 3. Preference Value $preferenceValue = $this->calculatePreferenceValue($normalizedMatrix); // 4. Recommendation $recommendation = $this->determineRecommendation($pengajuan->jenis_pengajuan, $preferenceValue); // 5. Save Result $this->saveResult($pengajuan, $preferenceValue, $recommendation); DB::commit(); return [ 'decision_matrix' => $decisionMatrix, 'normalized_matrix' => $normalizedMatrix, 'preference_value' => $preferenceValue, 'recommendation' => $recommendation, ]; } catch (Exception $e) { DB::rollBack(); Log::error("Error pada perhitungan SAW: " . $e->getMessage()); throw $e; } } protected function determineRecommendation(string $jenisPengajuan, float $preferenceValue): string { if ($jenisPengajuan === 'penurunan') { return ($preferenceValue >= 0.65) ? 'Dapat Penurunan UKT' : 'UKT Tetap'; } elseif ($jenisPengajuan === 'pengangsuran') { return ($preferenceValue >= 0.75) ? 'Dapat Pengangsuran UKT' : 'Tidak Dapat Mengangsur'; } return 'UKT Tetap'; } public function processBatch(array $pengajuanIds): array { $successCount = 0; $errorCount = 0; $errors = []; foreach ($pengajuanIds as $id) { try { $pengajuan = PengajuanUkt::findOrFail($id); $this->calculate($pengajuan); $successCount++; } catch (Exception $e) { $errorCount++; $errors[$id] = $e->getMessage(); Log::error("Gagal memproses pengajuan ID {$id}: " . $e->getMessage()); } } return [ 'status' => $errorCount === 0 ? 'success' : ($successCount > 0 ? 'partial' : 'failed'), 'message' => "Berhasil memproses {$successCount} dari " . count($pengajuanIds) . " pengajuan", 'success_count' => $successCount, 'error_count' => $errorCount, 'errors' => $errors ]; } protected function buildDecisionMatrix(PengajuanUkt $pengajuan): array { $decisionMatrix = []; foreach ($this->kriteria as $kriteria) { $detail = $pengajuan->details->firstWhere('kriteria_id', $kriteria->id); if (!$detail) { Log::error("Missing detail for kriteria: {$kriteria->nama_kriteria}"); continue; } $sub = $kriteria->subKriteria->firstWhere('id', $detail->sub_kriteria_id); if (!$sub) { Log::error("Missing subkriteria for detail: {$detail->id}"); continue; } $decisionMatrix[$kriteria->id] = $sub->nilai; } if (count($decisionMatrix) !== count($this->kriteria)) { throw new Exception("Data kriteria tidak lengkap"); } return $decisionMatrix; } protected function normalizeMatrix(array $decisionMatrix): array { $normalizedMatrix = []; foreach ($this->kriteria as $kriteria) { $nilai = $decisionMatrix[$kriteria->id]; $allNilai = $kriteria->subKriteria->pluck('nilai')->toArray(); if (empty($allNilai)) { throw new Exception("Subkriteria untuk kriteria ID {$kriteria->id} kosong"); } if (strtolower($kriteria->attribut) === 'benefit') { $max = max($allNilai); $value = $max != 0 ? ($nilai / $max) : 0; } else { // COST $min = min($allNilai); $value = $nilai != 0 ? ($min / $nilai) : 0; } $normalizedMatrix[$kriteria->id] = $value; } return $normalizedMatrix; } protected function calculatePreferenceValue(array $normalizedMatrix): float { $preferenceValue = 0; foreach ($this->kriteria as $kriteria) { $bobot = $kriteria->bobot; $nilaiNormalisasi = $normalizedMatrix[$kriteria->id]; $preferenceValue += $bobot * $nilaiNormalisasi; } return round($preferenceValue, 5, PHP_ROUND_HALF_EVEN); } protected function saveResult(PengajuanUkt $pengajuan, float $preferenceValue, string $recommendation): void { HasilPenilaian::updateOrCreate( ['pengajuan_id' => $pengajuan->id], [ 'nilai_preferensi' => $preferenceValue, 'rekomendasi_ukt' => $recommendation, 'keterangan' => null, 'processed_by' => auth()->id(), ] ); } public function prosesPerhitunganLengkap(array $pengajuanIds): array { return $this->processBatch($pengajuanIds); } public function getCalculationDetails($pengajuanId) { $pengajuan = PengajuanUkt::with('details.subKriteria')->findOrFail($pengajuanId); // Rebuild the calculation to ensure consistency $decisionMatrix = $this->buildDecisionMatrix($pengajuan); $normalizedMatrix = $this->normalizeMatrix($decisionMatrix); $preferenceValue = $this->calculatePreferenceValue($normalizedMatrix); return [ 'decision_matrix' => $decisionMatrix, 'normalized_matrix' => $normalizedMatrix, 'preference_value' => $pengajuan->hasilPenilaian->nilai_preferensi, 'recommendation' => $pengajuan->hasilPenilaian->rekomendasi_ukt, ]; } }