whereHas('cat', function($q) { $q->where('treatment_status', 1); }); // 2. LOGIKA FILTERING DINAMIS if ($request->filled('search')) { $query->whereHas('cat', function($q) use ($request) { $q->where('nama_kucing', 'like', '%' . $request->search . '%'); }); } if ($request->filled('pic_id')) { $query->where('user_id', $request->pic_id); } if ($request->filled('start_date') && $request->filled('end_date')) { $query->whereBetween('tanggal', [$request->start_date, $request->end_date]); } // 3. LOGIKA EXPORT (Pastikan blok IF ditutup dengan benar) if ($request->filled('export')) { $exportData = new \App\Exports\TreatmentExport($request); $fileName = 'Report_Active_Treatment_' . now()->format('Ymd_His'); if ($request->export === 'excel') { return \Maatwebsite\Excel\Facades\Excel::download($exportData, "{$fileName}.xlsx"); } if ($request->export === 'pdf') { return \Maatwebsite\Excel\Facades\Excel::download($exportData, "{$fileName}.pdf", \Maatwebsite\Excel\Excel::DOMPDF); } } // <--- Pastikan kurung ini menutup blok export, bukan menutup fungsi index // 4. EKSEKUSI DATA UNTUK VIEW (Line 67) $treatments = $query->latest('tanggal')->paginate(20)->withQueryString(); $pics = \App\Models\User::select('id', 'name')->orderBy('name')->get(); return view('pages.treatments.index', compact('treatments', 'pics')); } /** * Form tambah rekam medis. */ public function create(Request $request): View { $selectedCat = null; if ($request->filled('cat_id')) { $selectedCat = Cat::select('id', 'nama_kucing', 'ras')->findOrFail($request->cat_id); $cats = collect([$selectedCat]); } else { $cats = Cat::select('id', 'nama_kucing', 'ras')->get(); } // Menampilkan semua obat (termasuk stok 0 untuk kebutuhan edit/view, // namun di filter di frontend untuk input baru) $medicines = Medicine::orderBy('nama_obat', 'asc')->get(); return view('pages.treatments.create', compact('selectedCat', 'cats', 'medicines')); } /** * Simpan rekam medis baru & potong stok (Mendukung Multi-Obat). */ public function store(Request $request): \Illuminate\Http\RedirectResponse { $validated = $this->validateTreatment($request); $fotoPath = null; if ($request->hasFile('foto_bukti')) { $fotoPath = $request->file('foto_bukti')->store('treatments', 'public'); } try { return \Illuminate\Support\Facades\DB::transaction(function () use ($request, $fotoPath) { $userId = auth()->id() ?? 1; $firstTreatment = null; if ($request->filled('items')) { foreach ($request->items as $item) { $medicine = \App\Models\Medicine::findOrFail($item['medicine_id']); if ($medicine->qty < $item['qty_used']) { throw new \Exception("Stok {$medicine->nama_obat} tidak mencukupi (Sisa: {$medicine->qty})."); } $treatment = \App\Models\Treatment::create([ 'cat_id' => $request->cat_id, 'user_id' => $userId, 'tanggal' => $request->tanggal, 'keluhan' => $request->keluhan, // Langsung masukkan diagnosa tanpa embel-embel 'diagnosa' => $request->diagnosa, 'foto_bukti' => $fotoPath, 'status_perawatan' => 1, 'medicine_id' => $item['medicine_id'], 'qty_used' => $item['qty_used'], ]); if (!$firstTreatment) { $firstTreatment = $treatment; } $medicine->decrement('qty', $item['qty_used']); } } \App\Models\Cat::where('id', $request->cat_id)->update(['treatment_status' => 1]); if ($firstTreatment) { $this->logAction('TREATED', $firstTreatment, "Mencatat pengobatan klinis untuk " . $firstTreatment->cat->nama_kucing); } return redirect()->route('cats.show', $request->cat_id) ->with('success', 'Rekam medis klinis berhasil dicatat dan stok dipotong otomatis.'); }); } catch (\Exception $e) { if ($fotoPath && \Illuminate\Support\Facades\Storage::disk('public')->exists($fotoPath)) { \Illuminate\Support\Facades\Storage::disk('public')->delete($fotoPath); } return back()->withInput()->with('error', 'Gagal menyimpan: ' . $e->getMessage()); } } /** * Menampilkan detail spesifik satu rekam medis. */ public function show(Treatment $treatment): View { // Load details untuk menampilkan list obat $treatment->load(['cat', 'medicine', 'user', 'details.medicine']); return view('pages.treatments.show', compact('treatment')); } /** * Form edit rekam medis. */ public function edit(Treatment $treatment): View { $treatment->load('details'); $cats = Cat::select('id', 'nama_kucing', 'ras')->get(); $medicines = Medicine::orderBy('nama_obat', 'asc')->get(); return view('pages.treatments.edit', compact('treatment', 'cats', 'medicines')); } /** * Update rekam medis & sinkronisasi ulang stok desimal (Multi-Obat). */ public function update(Request $request, Treatment $treatment) { // 1. Validasi Input $validated = $this->validateTreatment($request); try { return DB::transaction(function () use ($request, $treatment) { // 2. Kelola Foto Bukti (Hapus yang lama jika ada foto baru) $fotoPath = $treatment->foto_bukti; if ($request->hasFile('foto_bukti')) { if ($fotoPath && \Illuminate\Support\Facades\Storage::disk('public')->exists($fotoPath)) { \Illuminate\Support\Facades\Storage::disk('public')->delete($fotoPath); } $fotoPath = $request->file('foto_bukti')->store('treatments', 'public'); } // 3. KEMBALIKAN STOK OBAT LAMA (Penting agar stok tidak bocor) $oldMedicine = \App\Models\Medicine::find($treatment->medicine_id); if ($oldMedicine) { $oldMedicine->increment('qty', $treatment->qty_used); } // 4. Ambil data obat PERTAMA dari form untuk memperbarui data saat ini (ID 9) $firstItem = $request->items[0]; $newMedicine = \App\Models\Medicine::findOrFail($firstItem['medicine_id']); if ($newMedicine->qty < $firstItem['qty_used']) { throw new Exception("Stok {$newMedicine->nama_obat} tidak mencukupi (Sisa: {$newMedicine->qty})."); } // Format Diagnosa (Gabungan Diagnosa Utama + Dosis/Ket) $catatanDosis = isset($firstItem['dosis']) ? " | Dosis: " . $firstItem['dosis'] : ""; $catatanKet = isset($firstItem['keterangan']) && $firstItem['keterangan'] != '' ? " (" . $firstItem['keterangan'] . ")" : ""; $diagnosaFinal = $request->diagnosa . $catatanDosis . $catatanKet; // EKSEKUSI UPDATE REKAM MEDIS (Tanpa kolom nama_obat!) $treatment->update([ 'cat_id' => $request->cat_id, 'tanggal' => $request->tanggal, 'keluhan' => $request->keluhan, 'diagnosa' => $diagnosaFinal, 'foto_bukti' => $fotoPath, 'medicine_id' => $firstItem['medicine_id'], 'qty_used' => $firstItem['qty_used'], ]); // Potong stok obat pertama $newMedicine->decrement('qty', $firstItem['qty_used']); // 5. JIKA STAF MENAMBAHKAN OBAT EKSTRA DI HALAMAN EDIT // Buat sebagai record baru agar data tidak hilang if (count($request->items) > 1) { for ($i = 1; $i < count($request->items); $i++) { $extraItem = $request->items[$i]; $extraMed = \App\Models\Medicine::findOrFail($extraItem['medicine_id']); if ($extraMed->qty < $extraItem['qty_used']) { throw new Exception("Stok {$extraMed->nama_obat} tidak mencukupi."); } $catatanDosisEx = isset($extraItem['dosis']) ? " | Dosis: " . $extraItem['dosis'] : ""; $catatanKetEx = isset($extraItem['keterangan']) && $extraItem['keterangan'] != '' ? " (" . $extraItem['keterangan'] . ")" : ""; $diagnosaFinalEx = $request->diagnosa . $catatanDosisEx . $catatanKetEx; \App\Models\Treatment::create([ 'cat_id' => $request->cat_id, 'user_id' => auth()->id() ?? 1, 'tanggal' => $request->tanggal, 'keluhan' => $request->keluhan, 'diagnosa' => $diagnosaFinalEx, 'foto_bukti' => $fotoPath, 'status_perawatan' => $treatment->status_perawatan, // Ikuti status (Misal: 1 / Aktif) 'medicine_id' => $extraItem['medicine_id'], 'qty_used' => $extraItem['qty_used'], ]); $extraMed->decrement('qty', $extraItem['qty_used']); } } // 6. Sinkronisasi Kucing & Log Activity \App\Models\Cat::where('id', $request->cat_id)->update(['treatment_status' => 1]); $this->logAction('UPDATED', $treatment, "Memperbarui rekam medis klinis untuk " . $treatment->cat->nama_kucing); return redirect()->route('cats.show', $request->cat_id) ->with('success', 'Rekam medis berhasil diperbarui dan stok obat disesuaikan ulang secara otomatis!'); }); } catch (Exception $e) { return back()->withInput()->with('error', 'Gagal memperbarui: ' . $e->getMessage()); } } /** * Hapus rekam medis & kembalikan semua stok obat terkait. */ public function destroy(Treatment $treatment): RedirectResponse { try { DB::transaction(function () use ($treatment) { // Kembalikan semua stok dari tabel detail foreach ($treatment->details as $detail) { Medicine::where('id', $detail->medicine_id)->increment('qty', $detail->qty_used); } if ($treatment->foto_bukti) { Storage::disk('public')->delete($treatment->foto_bukti); } $this->logAction('DELETED_TREATMENT', $treatment, "Menghapus catatan medis kucing: {$treatment->cat->nama_kucing}"); // Delete otomatis menghapus details karena cascade di migration $treatment->delete(); }); return back()->with('success', 'Data rekam medis dihapus dan semua stok obat dikembalikan.'); } catch (Exception $e) { return back()->with('error', 'Gagal menghapus data: ' . $e->getMessage()); } } private function validateTreatment(Request $request) { return $request->validate([ 'cat_id' => 'required|exists:cats,id', 'tanggal' => 'required|date', 'keluhan' => 'required|string|max:255', 'diagnosa' => 'required|string', 'foto_bukti' => 'nullable|image|max:2048', // Validasi untuk Single Medicine 'medicine_id' => 'required_without:items|nullable|exists:medicines,id', 'qty_used' => 'required_without:items|nullable|numeric|min:0', // Validasi untuk Multi Medicine // FIX: Wajibkan array items jika mode single (medicine_id) kosong 'items' => 'required_without:medicine_id|nullable|array', 'items.*.medicine_id' => 'required_with:items|exists:medicines,id', 'items.*.qty_used' => 'required_with:items|numeric|min:0.01', ], [ // Pesan Error Custom (Opsional, agar lebih mudah di-debug) 'items.required_without' => 'Anda harus memasukkan minimal 1 obat.', 'items.*.medicine_id.required_with' => 'Ada pilihan obat yang masih kosong di daftar.', 'items.*.qty_used.required_with' => 'Jumlah (Qty) obat harus diisi.', ]); } }