Files
ilycats/app/Http/Controllers/TreatmentController.php
T

332 lines
14 KiB
PHP
Raw Normal View History

2026-02-08 15:16:35 +07:00
<?php
namespace App\Http\Controllers;
use App\Models\Cat;
use App\Models\Treatment;
use App\Models\Medicine;
2026-09-23 12:45:02 +07:00
use App\Models\TreatmentDetail; // Menambahkan model Detail
2026-02-08 15:16:35 +07:00
use App\Traits\LogsActivity;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Exception;
class TreatmentController extends Controller
{
use LogsActivity;
/**
2026-09-23 12:45:02 +07:00
* Menampilkan daftar rekam medis dengan optimasi Eager Loading & Filtering.
2026-02-08 15:16:35 +07:00
*/
2026-09-23 12:45:02 +07:00
public function index(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
{
// 1. QUERY DASAR DENGAN OPTIMASI EAGER LOADING
$query = Treatment::with([
'cat:id,nama_kucing,foto,treatment_status',
'details.medicine:id,nama_obat,unit',
'user:id,name'
])
->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]);
2026-02-08 15:16:35 +07:00
}
2026-09-23 12:45:02 +07:00
// 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'));
}
2026-02-08 15:16:35 +07:00
/**
* Form tambah rekam medis.
*/
public function create(Request $request): View
{
2026-09-23 12:45:02 +07:00
$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();
2026-02-08 15:16:35 +07:00
return view('pages.treatments.create', compact('selectedCat', 'cats', 'medicines'));
}
/**
2026-09-23 12:45:02 +07:00
* Simpan rekam medis baru & potong stok (Mendukung Multi-Obat).
2026-02-08 15:16:35 +07:00
*/
2026-09-23 12:45:02 +07:00
public function store(Request $request): \Illuminate\Http\RedirectResponse
2026-02-08 15:16:35 +07:00
{
$validated = $this->validateTreatment($request);
2026-09-23 12:45:02 +07:00
$fotoPath = null;
if ($request->hasFile('foto_bukti')) {
$fotoPath = $request->file('foto_bukti')->store('treatments', 'public');
}
2026-02-08 15:16:35 +07:00
try {
2026-09-23 12:45:02 +07:00
return \Illuminate\Support\Facades\DB::transaction(function () use ($request, $fotoPath) {
$userId = auth()->id() ?? 1;
$firstTreatment = null;
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
if ($request->filled('items')) {
foreach ($request->items as $item) {
$medicine = \App\Models\Medicine::findOrFail($item['medicine_id']);
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
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']);
}
2026-02-08 15:16:35 +07:00
}
2026-09-23 12:45:02 +07:00
\App\Models\Cat::where('id', $request->cat_id)->update(['treatment_status' => 1]);
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
if ($firstTreatment) {
$this->logAction('TREATED', $firstTreatment, "Mencatat pengobatan klinis untuk " . $firstTreatment->cat->nama_kucing);
}
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
return redirect()->route('cats.show', $request->cat_id)
->with('success', 'Rekam medis klinis berhasil dicatat dan stok dipotong otomatis.');
2026-02-08 15:16:35 +07:00
});
2026-09-23 12:45:02 +07:00
} catch (\Exception $e) {
if ($fotoPath && \Illuminate\Support\Facades\Storage::disk('public')->exists($fotoPath)) {
\Illuminate\Support\Facades\Storage::disk('public')->delete($fotoPath);
2026-02-08 15:16:35 +07:00
}
return back()->withInput()->with('error', 'Gagal menyimpan: ' . $e->getMessage());
}
}
2026-09-23 12:45:02 +07:00
/**
* 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'));
}
2026-02-08 15:16:35 +07:00
/**
* Form edit rekam medis.
*/
public function edit(Treatment $treatment): View
{
2026-09-23 12:45:02 +07:00
$treatment->load('details');
2026-02-08 15:16:35 +07:00
$cats = Cat::select('id', 'nama_kucing', 'ras')->get();
2026-09-23 12:45:02 +07:00
$medicines = Medicine::orderBy('nama_obat', 'asc')->get();
2026-02-08 15:16:35 +07:00
return view('pages.treatments.edit', compact('treatment', 'cats', 'medicines'));
}
/**
2026-09-23 12:45:02 +07:00
* Update rekam medis & sinkronisasi ulang stok desimal (Multi-Obat).
2026-02-08 15:16:35 +07:00
*/
2026-09-23 12:45:02 +07:00
public function update(Request $request, Treatment $treatment)
2026-02-08 15:16:35 +07:00
{
2026-09-23 12:45:02 +07:00
// 1. Validasi Input
2026-02-08 15:16:35 +07:00
$validated = $this->validateTreatment($request);
try {
2026-09-23 12:45:02 +07:00
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);
2026-02-08 15:16:35 +07:00
if ($oldMedicine) {
$oldMedicine->increment('qty', $treatment->qty_used);
}
2026-09-23 12:45:02 +07:00
// 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}).");
2026-02-08 15:16:35 +07:00
}
2026-09-23 12:45:02 +07:00
// 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']);
}
2026-02-08 15:16:35 +07:00
}
2026-09-23 12:45:02 +07:00
// 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);
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
return redirect()->route('cats.show', $request->cat_id)
->with('success', 'Rekam medis berhasil diperbarui dan stok obat disesuaikan ulang secara otomatis!');
2026-02-08 15:16:35 +07:00
});
} catch (Exception $e) {
return back()->withInput()->with('error', 'Gagal memperbarui: ' . $e->getMessage());
}
}
/**
2026-09-23 12:45:02 +07:00
* Hapus rekam medis & kembalikan semua stok obat terkait.
2026-02-08 15:16:35 +07:00
*/
public function destroy(Treatment $treatment): RedirectResponse
{
try {
DB::transaction(function () use ($treatment) {
2026-09-23 12:45:02 +07:00
// 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);
2026-02-08 15:16:35 +07:00
}
$this->logAction('DELETED_TREATMENT', $treatment, "Menghapus catatan medis kucing: {$treatment->cat->nama_kucing}");
2026-09-23 12:45:02 +07:00
// Delete otomatis menghapus details karena cascade di migration
2026-02-08 15:16:35 +07:00
$treatment->delete();
});
2026-09-23 12:45:02 +07:00
return back()->with('success', 'Data rekam medis dihapus dan semua stok obat dikembalikan.');
2026-02-08 15:16:35 +07:00
} catch (Exception $e) {
2026-09-23 12:45:02 +07:00
return back()->with('error', 'Gagal menghapus data: ' . $e->getMessage());
2026-02-08 15:16:35 +07:00
}
}
2026-09-23 12:45:02 +07:00
private function validateTreatment(Request $request)
2026-02-08 15:16:35 +07:00
{
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',
2026-09-23 12:45:02 +07:00
// 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.',
2026-02-08 15:16:35 +07:00
]);
}
}