diff --git a/htdocs/app/Http/Controllers/GudangController.php b/htdocs/app/Http/Controllers/GudangController.php index 7d033f02..2554db92 100644 --- a/htdocs/app/Http/Controllers/GudangController.php +++ b/htdocs/app/Http/Controllers/GudangController.php @@ -347,6 +347,238 @@ class GudangController extends Controller return response()->json($response, 200); } + public function jsonExpiredBatches(Request $request) + { + if (Session::get('previlage') == '') { + return response()->json(['message' => 'Unauthorized'], 401); + } + + $jenis = trim((string) $request->query('jenis', '')); + $query = SIMBHPReport::whereNotNull('pemasukan') + ->where('pemasukan', '>', 0); + + if ($jenis !== '') { + $query->where('jenis', $jenis); + } + + $rows = $query->orderByRaw('masa_expired IS NULL ASC') + ->orderBy('masa_expired', 'ASC') + ->orderBy('id', 'DESC') + ->limit(100) + ->get(); + + $items = []; + foreach ($rows as $row) { + $items[] = [ + 'id' => $row->id, + 'jenis' => $row->jenis, + 'deskripsi' => $row->deskripsi ?: '-', + 'tanggal_masuk' => sprintf('%02d-%02d-%04d', (int) $row->tanggal, (int) $row->bulan, (int) $row->tahun), + 'qty' => (int) ($row->pemasukan ?? 0), + 'satuan' => $row->satuan_transaksi ?: 'besar', + 'masa_expired' => $row->masa_expired ? date('d-m-Y', strtotime((string) $row->masa_expired)) : '', + ]; + } + + return response()->json(['data' => $items]); + } + + public function updateExpiredBatch(Request $request) + { + if (Session::get('previlage') == '') { + return response()->json(['message' => 'Unauthorized'], 401); + } + + $batchId = (int) $request->input('batch_id'); + $masaExpired = $this->normalizeDateInput($request->input('masa_expired')); + $alasan = trim((string) $request->input('alasan', 'Update expired barang terbuka')); + + if ($batchId <= 0 || is_null($masaExpired)) { + return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Batch dan tanggal expired baru wajib diisi']); + } + + $row = SIMBHPReport::where('id', $batchId) + ->whereNotNull('pemasukan') + ->where('pemasukan', '>', 0) + ->first(); + + if (!$row) { + return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Batch barang masuk tidak ditemukan']); + } + + $oldExpired = $row->masa_expired; + $keterangan = trim((string) ($row->keterangan ?? '')); + $suffix = 'Expired dibuka: '.($oldExpired ?: '-').' -> '.$masaExpired; + if ($alasan !== '') { + $suffix .= ' | '.$alasan; + } + + $row->masa_expired = $masaExpired; + $row->keterangan = $keterangan !== '' ? $keterangan.' | '.$suffix : $suffix; + $row->updated_at = date("Y-m-d H:i:s"); + $row->save(); + + return response()->json(['status' => 'Success', 'message' => 'Masa expired batch berhasil diupdate']); + } + + public function storeRacikan(Request $request) + { + if (Session::get('previlage') == '') { + return response()->json(['message' => 'Unauthorized'], 401); + } + + $tanggal = trim((string) $request->input('tanggal', date('Y-m-d'))); + $namaRacikan = trim((string) $request->input('nama_racikan', 'Racikan')); + $statusRacikan = strtolower(trim((string) $request->input('status_racikan', 'berhasil'))); + $bahan = $request->input('bahan', []); + if (is_string($bahan)) { + $decoded = json_decode($bahan, true); + $bahan = is_array($decoded) ? $decoded : []; + } + + if ($tanggal === '') { + $tanggal = date('Y-m-d'); + } + if ($namaRacikan === '') { + $namaRacikan = 'Racikan'; + } + if (!in_array($statusRacikan, ['berhasil', 'rusak'], true)) { + return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Status racikan tidak valid']); + } + if (!is_array($bahan) || count($bahan) === 0) { + return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Minimal satu bahan racikan wajib diisi']); + } + + $parts = explode('-', $tanggal); + $tahun = (int) ($parts[0] ?? date('Y')); + $wulan = (int) ($parts[1] ?? date('m')); + $dino = (int) ($parts[2] ?? date('d')); + $marking = 'RACIK-'.date('YmdHis').'-'.random_int(100, 999); + + try { + DB::transaction(function () use ($request, $bahan, $statusRacikan, $namaRacikan, $marking, $dino, $wulan, $tahun) { + $service = app(SimbhpStockService::class); + $prepared = []; + $requiredByJenis = []; + + foreach ($bahan as $line) { + $jenisNama = trim((string) ($line['jenis'] ?? '')); + $qty = (int) str_replace(',', '', (string) ($line['qty'] ?? 0)); + $satuanTransaksi = (string) ($line['satuan_transaksi'] ?? 'besar'); + if (!in_array($satuanTransaksi, ['besar', 'kecil'], true)) { + $satuanTransaksi = 'besar'; + } + if ($jenisNama === '' || $qty <= 0) { + throw new \InvalidArgumentException('Bahan dan jumlah wajib diisi'); + } + + $jenis = SIMBHPJenis::where('jenis', $jenisNama)->first(); + if (!$jenis) { + throw new \InvalidArgumentException('Bahan '.$jenisNama.' tidak ditemukan'); + } + $setting = $service->getUnitSetting($jenis); + if ($satuanTransaksi === 'kecil' && !($setting['has_breakdown'] ?? false)) { + throw new \InvalidArgumentException('Satuan kecil tidak tersedia untuk '.$jenisNama); + } + + $qtyBase = $service->calculateBaseQty($jenis, $qty, $satuanTransaksi); + $requiredByJenis[$jenisNama] = ($requiredByJenis[$jenisNama] ?? 0) + $qtyBase; + $prepared[] = [ + 'jenis' => $jenis, + 'qty' => $qty, + 'qty_base' => $qtyBase, + 'satuan_transaksi' => $satuanTransaksi, + 'batch_id' => (int) ($line['batch_id'] ?? 0), + 'expired_baru' => $this->normalizeDateInput($line['expired_baru'] ?? null), + ]; + } + + foreach ($requiredByJenis as $jenisNama => $requiredBase) { + $stok = $service->getStockBaseByJenis($jenisNama); + if ($requiredBase > $stok) { + throw new \InvalidArgumentException('Stok bahan '.$jenisNama.' tidak cukup'); + } + } + + foreach ($prepared as $line) { + $keterangan = $statusRacikan === 'berhasil' ? 'Bahan racikan berhasil' : 'Bahan racikan rusak'; + if ($line['expired_baru'] && $line['batch_id'] > 0) { + $batch = SIMBHPReport::where('id', $line['batch_id']) + ->where('jenis', $line['jenis']->jenis) + ->whereNotNull('pemasukan') + ->where('pemasukan', '>', 0) + ->first(); + if (!$batch) { + throw new \InvalidArgumentException('Batch expired untuk '.$line['jenis']->jenis.' tidak ditemukan'); + } + $oldExpired = $batch->masa_expired; + $oldKeterangan = trim((string) ($batch->keterangan ?? '')); + $expiredNote = 'Expired dibuka racikan '.$marking.': '.($oldExpired ?: '-').' -> '.$line['expired_baru']; + $batch->masa_expired = $line['expired_baru']; + $batch->keterangan = $oldKeterangan !== '' ? $oldKeterangan.' | '.$expiredNote : $expiredNote; + $batch->updated_at = date("Y-m-d H:i:s"); + $batch->save(); + $keterangan .= ' | Expired dibuka: '.($oldExpired ?: '-').' -> '.$line['expired_baru']; + } + + SIMBHPReport::create([ + 'tanggal' => $dino, + 'bulan' => $wulan, + 'tahun' => $tahun, + 'deskripsi' => 'Racikan '.$namaRacikan.' - bahan', + 'pemasukan' => null, + 'pengeluaran' => $line['qty'], + 'qty_base' => $line['qty_base'], + 'satuan_transaksi' => $line['satuan_transaksi'], + 'jenis' => $line['jenis']->jenis, + 'keterangan' => $keterangan, + 'marking' => $marking, + ]); + } + + if ($statusRacikan === 'berhasil') { + $hasil = $this->resolveRacikanOutputJenis($request); + $qtyHasil = (int) str_replace(',', '', (string) $request->input('hasil_qty', 0)); + $satuanHasil = (string) $request->input('hasil_satuan_transaksi', 'besar'); + if (!in_array($satuanHasil, ['besar', 'kecil'], true)) { + $satuanHasil = 'besar'; + } + if ($qtyHasil <= 0) { + throw new \InvalidArgumentException('Jumlah hasil racikan wajib diisi'); + } + $setting = $service->getUnitSetting($hasil); + if ($satuanHasil === 'kecil' && !($setting['has_breakdown'] ?? false)) { + throw new \InvalidArgumentException('Satuan kecil tidak tersedia untuk hasil racikan'); + } + $qtyBaseHasil = $service->calculateBaseQty($hasil, $qtyHasil, $satuanHasil); + + SIMBHPReport::create([ + 'tanggal' => $dino, + 'bulan' => $wulan, + 'tahun' => $tahun, + 'deskripsi' => 'Hasil racikan '.$namaRacikan, + 'pemasukan' => $qtyHasil, + 'pengeluaran' => null, + 'qty_base' => $qtyBaseHasil, + 'satuan_transaksi' => $satuanHasil, + 'masa_expired' => $this->normalizeDateInput($request->input('hasil_masa_expired')), + 'jenis' => $hasil->jenis, + 'keterangan' => 'Hasil racikan berhasil', + 'marking' => $marking, + ]); + } + }); + } catch (\Throwable $e) { + return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $e->getMessage()]); + } + + $message = $statusRacikan === 'berhasil' + ? 'Racikan berhasil disimpan dan hasil masuk stok' + : 'Racikan rusak disimpan sebagai pengurangan bahan'; + + return response()->json(['status' => 'Success', 'message' => $message]); + } + public function exAddbarang(Request $request) { $deskripsi = $request->input('set01'); $pos = $request->input('set02'); @@ -919,6 +1151,82 @@ class GudangController extends Controller return null; } + private function resolveRacikanOutputJenis(Request $request): SIMBHPJenis + { + $mode = (string) $request->input('hasil_mode', 'existing'); + if ($mode === 'new') { + $nama = trim((string) $request->input('hasil_new_jenis')); + $satuan = trim((string) $request->input('hasil_new_satuan')); + $kode = trim((string) $request->input('hasil_new_kode')); + $satuanKecil = trim((string) $request->input('hasil_new_satuan_kecil')); + $konversi = (int) $request->input('hasil_new_konversi', 1); + $stokMinimum = (int) $request->input('hasil_new_stok_minimum', 0); + + if ($nama === '' || $satuan === '') { + throw new \InvalidArgumentException('Nama dan satuan hasil racikan baru wajib diisi'); + } + if ($konversi <= 0) { + $konversi = 1; + } + if ($stokMinimum < 0) { + $stokMinimum = 0; + } + if ($satuanKecil !== '' && $konversi <= 1) { + throw new \InvalidArgumentException('Konversi hasil racikan baru harus lebih dari 1 jika memakai satuan kecil'); + } + + if ($kode !== '') { + $kode = strtoupper($kode); + $kode = preg_replace('/\s+/', '', $kode); + $kode = preg_replace('/[^A-Z0-9._-]/', '', $kode); + } else { + $kode = strtoupper($nama); + $kode = preg_replace('/\s+/', '', $kode); + $kode = preg_replace('/[^A-Z0-9._-]/', '', $kode); + } + if ($kode === '' || strlen($kode) < 2 || strlen($kode) > 40) { + throw new \InvalidArgumentException('Kode hasil racikan wajib 2-40 karakter'); + } + + $existing = SIMBHPJenis::where('kodejenis', $kode)->first(); + if ($existing) { + return $existing; + } + + $jenis = SIMBHPJenis::create([ + 'kodejenis' => $kode, + 'jenis' => $nama, + 'satuan' => $satuan, + 'satuan_kecil' => $satuanKecil, + 'konversi_kecil' => $konversi, + 'stok_minimum' => $stokMinimum, + ]); + + if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) { + $service = app(SimbhpStockService::class); + $setting = $service->getUnitSetting($jenis); + $jenis->update([ + 'barcode_besar' => $service->makeBarcodeValue((int) $jenis->id, 'besar'), + 'barcode_kecil' => ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $jenis->id, 'kecil') : null, + ]); + } + + return $jenis; + } + + $jenisNama = trim((string) $request->input('hasil_jenis')); + if ($jenisNama === '') { + throw new \InvalidArgumentException('Barang hasil racikan wajib dipilih'); + } + + $jenis = SIMBHPJenis::where('jenis', $jenisNama)->first(); + if (!$jenis) { + throw new \InvalidArgumentException('Barang hasil racikan tidak ditemukan'); + } + + return $jenis; + } + private function getJenisRows() { $rows = []; diff --git a/htdocs/app/Http/Controllers/PasienController.php b/htdocs/app/Http/Controllers/PasienController.php index e5d26791..1af69a98 100644 --- a/htdocs/app/Http/Controllers/PasienController.php +++ b/htdocs/app/Http/Controllers/PasienController.php @@ -106,7 +106,7 @@ class PasienController extends Controller } else { try { $client = new Client(); - $res = $client->request('GET', 'https://gomed.rssa.my.id/api/account/pasien', ['query' => ['limit' => 10, 'nomedis' => $norm]]); + $res = $client->request('GET', 'http://10.10.123.204/api/account/pasien', ['query' => ['limit' => 10, 'nomedis' => $norm]]); $response_data = json_decode($res->getBody()->getContents()); if (isset($response_data->response[0])) { $hasil = $response_data->response[0]; diff --git a/htdocs/resources/views/admin/gudang.blade.php b/htdocs/resources/views/admin/gudang.blade.php index be15a639..6cf423e5 100644 --- a/htdocs/resources/views/admin/gudang.blade.php +++ b/htdocs/resources/views/admin/gudang.blade.php @@ -28,6 +28,9 @@
| Kode | +Bahan | +Qty | +Satuan | +Expired Dibuka | +Aksi | +
|---|---|---|---|---|---|
| Belum ada bahan. | |||||
' + escapeHtml(line.kode || '') + '