From 3e9e8daea6dc9fe2d6ed809d7ae8697d1f084dd3 Mon Sep 17 00:00:00 2001 From: Dwi Swandhana Date: Thu, 21 May 2026 06:37:14 +0700 Subject: [PATCH] update --- .../Controllers/BiorepositoryController.php | 38 ++++-- .../app/Http/Controllers/ReportController.php | 121 ++++-------------- ..._alter_bio_tables_for_accnumber_system.php | 33 +++++ .../views/admin/biorepository.blade.php | 27 +++- .../views/dokter/pemeriksaan.blade.php | 9 +- htdocs/resources/views/dokter/ppds.blade.php | 9 +- 6 files changed, 116 insertions(+), 121 deletions(-) create mode 100644 htdocs/database/migrations/2026_05_21_000012_alter_bio_tables_for_accnumber_system.php diff --git a/htdocs/app/Http/Controllers/BiorepositoryController.php b/htdocs/app/Http/Controllers/BiorepositoryController.php index 5cebc63c..fc1fec59 100644 --- a/htdocs/app/Http/Controllers/BiorepositoryController.php +++ b/htdocs/app/Http/Controllers/BiorepositoryController.php @@ -115,18 +115,18 @@ class BiorepositoryController extends Controller public function storeSpecimen(Request $request) { $validator = Validator::make($request->all(), [ - 'rack_id' => 'required|exists:bio_racks,id', - 'kategorisimpan' => 'required|in:A,B,C,D', - 'shelfnomor' => 'required|integer|min:1', - 'raknomor' => 'required|integer|min:1', - 'slotnomor' => 'required|integer|min:1', - 'boxnomor' => 'required|integer|min:1', - 'tubenomor' => 'required|integer|min:1', - 'nmbakteri' => 'nullable|max:200', - 'strain' => 'nullable|in:Gram Negatif,Gram Positif', - 'stored_at' => 'required|date', - 'volume' => 'nullable|numeric|min:0', - 'volume_ambil' => 'nullable|numeric|min:0', + 'rack_id' => 'required|exists:bio_racks,id', + 'kategorisimpan' => 'required|in:A,B,C,D', + 'shelfnomor' => 'required|integer|min:1', + 'raknomor' => 'required|integer|min:1', + 'slotnomor' => 'required|integer|min:1', + 'boxnomor' => 'required|integer|min:1', + 'tubenomor' => 'required|integer|min:1', + 'nmbakteri' => 'nullable|max:200', + 'strain' => 'nullable|in:Gram Negatif,Gram Positif', + 'stored_at' => 'required|date', + 'volume' => 'nullable|numeric|min:0', + 'volume_ambil' => 'nullable|numeric|min:0', 'existing_specimen_id' => 'nullable|integer', ]); @@ -206,6 +206,17 @@ class BiorepositoryController extends Controller $counter++; $generatedCode = $baseCode.'-'.$counter; } + $sampleid = $request->input('sampleid'); + if ($sampleid) { + $cekdata = Periksa::where('nofoto', $sampleid)->first(); + if (!$cekdata) { + $sampleid = $cekdata->id; + } else { + $sampleid = null; + } + } else { + $sampleid = null; + } BioSpecimen::create([ 'cabinet_id' => $rack->cabinet_id, @@ -220,8 +231,9 @@ class BiorepositoryController extends Controller 'tube_number' => $request->input('tubenomor'), 'bacteria_name' => $request->input('nmbakteri'), 'strain' => $request->input('strain'), - 'atcc' => $atccByUser, + 'atcc' => $request->input('atcc') ?? $atccByUser, 'input_by' => Session::get('nama'), + 'sampleid' => $sampleid, 'stored_at' => $request->input('stored_at'), 'storage_condition' => $this->mapStorageCondition($request->input('kategorisimpan')), 'volume' => (string) $request->input('volume'), diff --git a/htdocs/app/Http/Controllers/ReportController.php b/htdocs/app/Http/Controllers/ReportController.php index 37f38168..6e6f67ea 100644 --- a/htdocs/app/Http/Controllers/ReportController.php +++ b/htdocs/app/Http/Controllers/ReportController.php @@ -421,35 +421,19 @@ class ReportController extends Controller ->where('db_komponenjawaban.isidata', '!=', ''); }); } - - /** - * CURSOR → Streaming data, tidak boros memori - */ - $periksaCursor = $query->orderBy($dateColumn, 'ASC')->cursor(); - - $hasil = []; - - /** - * Karena cursor tidak bisa pluck di awal, - * kita kumpulkan accnumber & poli_id sambil streaming - */ - $acc = []; - $poli_ids = []; + $periksaCursor = $query->orderBy($dateColumn, 'ASC')->cursor(); + $hasil = []; + $acc = []; + $poli_ids = []; foreach ($periksaCursor as $r) { $acc[] = $r->nofoto; $poli_ids[] = $r->poli_id; - $hasil[] = $r; // simpan pointer row + $hasil[] = $r; } - - /** Jika tidak ada data */ if (empty($hasil)) { return response()->json([]); } - - /** - * Ambil komponen terkait (1 query saja) - */ $komponen = DB::table('db_komponenjawaban') ->select('accnumber','komponen','isidata') ->whereIn('accnumber', array_unique($acc)) @@ -459,27 +443,17 @@ class ReportController extends Controller ]) ->get() ->groupBy('accnumber'); - - /** - * Ambil poli terkait (1 query) - */ $poli = DB::table('poli') ->select('id','subpoli','modaliti2') ->whereIn('id', array_unique($poli_ids)) ->get() ->keyBy('id'); - - /** - * Proses setiap row → tidak ada map() untuk hemat memori - */ foreach ($hasil as $row) { - - $k = $komponen[$row->nofoto] ?? collect([]); - - $row->id_bakteri01 = optional($k->firstWhere('komponen','id_bakteri01'))->isidata; - $row->id_bakteri02 = optional($k->firstWhere('komponen','id_bakteri02'))->isidata; - $row->bakteri = optional($k->firstWhere('komponen','bakteri'))->isidata; - $row->hasil_mdr = $k->whereIn('komponen', ['bakterisir', 'id_bakterisir01', 'id_bakterisir02']) + $k = $komponen[$row->nofoto] ?? collect([]); + $row->id_bakteri01 = optional($k->firstWhere('komponen','id_bakteri01'))->isidata; + $row->id_bakteri02 = optional($k->firstWhere('komponen','id_bakteri02'))->isidata; + $row->bakteri = optional($k->firstWhere('komponen','bakteri'))->isidata; + $row->hasil_mdr = $k->whereIn('komponen', ['bakterisir', 'id_bakterisir01', 'id_bakterisir02']) ->pluck('isidata') ->filter(function ($value) { return trim(strip_tags((string) $value)) !== ''; @@ -487,15 +461,12 @@ class ReportController extends Controller ->unique() ->implode('; '); - $row->filefoto = "nofoto)."'>$row->nofoto"; - - $p = $poli[$row->poli_id] ?? null; + $row->filefoto = "nofoto)."'>$row->nofoto"; + $p = $poli[$row->poli_id] ?? null; $row->subpoli = $p->subpoli ?? null; $row->modaliti2 = $this->resolveTatTargetDays($p->modaliti2 ?? '1'); - - // Hitung status target if (!empty($row->verifikasi)) { - $selisih = $this->getTatElapsedDays($row->mulai, $row->verifikasi); + $selisih = $this->getTatElapsedDays($row->mulai, $row->verifikasi); $row->selisih_hari = $selisih; $row->status_target = $this->resolveTatStatus($row->mulai, $row->verifikasi, $row->modaliti2); } else { @@ -503,10 +474,6 @@ class ReportController extends Controller $row->status_target = "TIDAK ADA HASIL"; } } - - /** - * Return untuk dua mode (rekaptat / data penuh) - */ if ($jenisreport == 'rekaptat') { $rekap = collect($hasil)->groupBy('kd_spesimen')->map(function($g){ return [ @@ -517,18 +484,14 @@ class ReportController extends Controller 'total' => $g->count(), ]; })->values(); - return response()->json($rekap); } - return response()->json($hasil); - } } public function genRekapAntibiotik(Request $request) { $bulan = $request->input('bulan'); $tahun = $request->input('tahun'); - if ($tahun == '' OR is_null($tahun)){ $getarray = explode('?', $bulan); $bulan = $getarray[0] ?? date('m'); @@ -543,19 +506,14 @@ class ReportController extends Controller if ($bulan != 'ALL' && $bulan != 'Pick Month') { $query->whereMonth('daftar', $bulan); } - - // 3. Pagination (50 Data) - $orderbydate = $query->paginate(50); + $orderbydate = $query->paginate(50); $orderbydate->appends(['bulan' => $bulan, 'tahun' => $tahun]); - - // 4. Mapping Data Antibiotik (Hanya untuk 50 pasien ini) - $pageIds = $orderbydate->pluck('id')->toArray(); - $antibiotikLookup = $this->mapAntibiotikData($pageIds); - + $pageIds = $orderbydate->pluck('id')->toArray(); + $antibiotikLookup = $this->mapAntibiotikData($pageIds); return view('admin.rekapantibiotik', [ 'orderbydate' => $orderbydate, 'antibiotikLookup' => $antibiotikLookup, - 'listAntibiotik' => $listAntibiotik, // Kirim header dinamis ke View + 'listAntibiotik' => $listAntibiotik, 'bulan' => $bulan, 'tahun' => $tahun ]); @@ -998,36 +956,25 @@ class ReportController extends Controller $bulan = str_replace('bulan=', '', $parts[0]); $tahun = str_replace('tahun=', '', $parts[1] ?? date('Y')); } - - // 1. Dapatkan Header Dinamis $listAntibiotik = $this->getDynamicAntibioticHeaders($bulan, $tahun); - $response = new StreamedResponse(function() use ($bulan, $tahun, $listAntibiotik) { - $handle = fopen('php://output', 'w'); - - // Header Statis - $staticHeaders = [ + $handle = fopen('php://output', 'w'); + $staticHeaders = [ 'No', 'Status', 'No.RM', 'Name', 'Order', 'Gender', 'Date', 'Urgensi', 'Comming From', 'Code', 'Spesimen', 'Finish', 'BHP Media', 'BHP Pot Sputum', 'BHP Pot Urine', 'BHP Oshe', 'BHP Obyek Glass', 'BHP Botol BD', 'BHP Parafilm', 'BHP Tips', 'BHP Cotton Swab', 'BHP Ab Tambahan', 'ESBL', 'MRSA' - ]; - - // Gabung Header Statis + Header Dinamis dari DB + ]; fputcsv($handle, array_merge($staticHeaders, $listAntibiotik)); - - // Query Utama (Chunking) $query = Periksa::query()->whereYear('daftar', $tahun); if ($bulan != 'ALL' && $bulan != 'Pick Month') { $query->whereMonth('daftar', $bulan); } $query->chunk(500, function($rows) use ($handle, $listAntibiotik) { - // Ambil data nilai antibiotik untuk chunk ini - $chunkIds = $rows->pluck('id')->toArray(); - $antibiotikLookup = $this->mapAntibiotikData($chunkIds); - + $chunkIds = $rows->pluck('id')->toArray(); + $antibiotikLookup = $this->mapAntibiotikData($chunkIds); foreach ($rows as $row) { $csvRow = [ $row->noloket, @@ -1055,14 +1002,10 @@ class ReportController extends Controller $row->id_esbl, $row->id_mrsa ]; - - // Loop sesuai Header Dinamis foreach ($listAntibiotik as $headerAb) { - // Cek apakah pasien ini punya nilai utk antibiotik tsb - $val = $antibiotikLookup[$row->id][$headerAb] ?? ''; - $csvRow[] = $val; + $val = $antibiotikLookup[$row->id][$headerAb] ?? ''; + $csvRow[] = $val; } - fputcsv($handle, $csvRow); } }); @@ -1132,13 +1075,8 @@ class ReportController extends Controller $bulan = str_replace('bulan=', '', $bulan); $tahun = str_replace('tahun=', '', $tahun); } - - // komponen Ziehl yang mau dianalisa - $komponenList = $this->getZnReportKomponenList(); - - // Nilai Ziehl yang ingin dihitung - $nilaiList = Organisms::where('kelompok', 'Pewarnaan Ziehl Nielsen')->pluck('name')->toArray(); - // 1. Ambil periksa sesuai bulan & tahun + $komponenList = $this->getZnReportKomponenList(); + $nilaiList = Organisms::where('kelompok', 'Pewarnaan Ziehl Nielsen')->pluck('name')->toArray(); if ($bulan == '' OR $bulan == 'ALL'){ $periksa = Periksa::with(['komponen' => function($q) use ($komponenList){ $q->whereIn('komponen', $komponenList); @@ -1153,21 +1091,15 @@ class ReportController extends Controller ->whereYear('mulai', $tahun) ->get(); } - - - // 2. Ambil distinct kd_spesimen → ini akan jadi kolom $kdSpesimenList = $periksa ->pluck('kd_spesimen') ->unique() ->values(); - // 3. Bangun struktur perhitungan $rekapLaki = []; $rekapPerempuan = []; foreach ($nilaiList as $nilai) { foreach ($kdSpesimenList as $spesimen) { - - // hitung laki-laki $rekapLaki[$nilai][$spesimen] = $periksa ->where('kd_spesimen', $spesimen) ->where('jkpasien', 'L') @@ -1175,7 +1107,6 @@ class ReportController extends Controller return $this->periksaHasZnReportValue($px, $nilai, $komponenList); })->count(); - // hitung perempuan $rekapPerempuan[$nilai][$spesimen] = $periksa ->where('kd_spesimen', $spesimen) ->where('jkpasien', 'P') diff --git a/htdocs/database/migrations/2026_05_21_000012_alter_bio_tables_for_accnumber_system.php b/htdocs/database/migrations/2026_05_21_000012_alter_bio_tables_for_accnumber_system.php new file mode 100644 index 00000000..f839fae7 --- /dev/null +++ b/htdocs/database/migrations/2026_05_21_000012_alter_bio_tables_for_accnumber_system.php @@ -0,0 +1,33 @@ +string('atcc', 150)->nullable()->after('strain'); + } + if (!Schema::hasColumn('bio_specimens', 'sampleid')) { + $table->string('sampleid', 150)->nullable()->after('atcc'); + } + }); + } + + public function down(): void + { + Schema::table('bio_specimens', function (Blueprint $table) { + if (Schema::hasColumn('bio_specimens', 'atcc')) { + $table->dropColumn('atcc'); + } + if (Schema::hasColumn('bio_specimens', 'sampleid')) { + $table->dropColumn('sampleid'); + } + }); + } +}; diff --git a/htdocs/resources/views/admin/biorepository.blade.php b/htdocs/resources/views/admin/biorepository.blade.php index a927cf1f..5977fe5e 100644 --- a/htdocs/resources/views/admin/biorepository.blade.php +++ b/htdocs/resources/views/admin/biorepository.blade.php @@ -271,7 +271,9 @@ Lemari Nama Rack Tgl Simpan + ATCC Input By + View Aksi @@ -289,7 +291,9 @@ Lemari Nama Rack Tgl Simpan + ATCC Input By + View @@ -308,7 +312,15 @@ {{ $row->cabinet->code ?? '-' }} {{ $row->rack->name ?? '-' }} {{ $row->stored_at }} + {{ $row->atcc }} {{ $row->input_by }} + + @if ($row->sampleid) + View + @else + - + @endif +
@csrf @@ -463,14 +475,12 @@ -
-
@@ -480,7 +490,18 @@
- +
+
+ + +
+
+
+
+ + +
+
diff --git a/htdocs/resources/views/dokter/pemeriksaan.blade.php b/htdocs/resources/views/dokter/pemeriksaan.blade.php index 7e8ea544..a80c5081 100644 --- a/htdocs/resources/views/dokter/pemeriksaan.blade.php +++ b/htdocs/resources/views/dokter/pemeriksaan.blade.php @@ -5504,20 +5504,19 @@ } }, { text: 'Acc.No', datafield: 'tlsnofoto', width: 100, align: 'center', cellsalign: 'center'}, - { text: 'Asal Sample', datafield: 'modality', width: 100, align: 'center', cellsalign: 'center'}, + { text: 'Kelompok', filtertype: 'checkedlist', datafield: 'kd_spesimen', width: 75, cellsalign: 'left', align: 'center'}, + { text: 'Specimen', filtertype: 'checkedlist', datafield: 'nm_spesimen', width: 230, cellsalign: 'left', align: 'center'}, { text: 'No.RM', datafield: 'tlsnoregister', width: 100, cellsalign: 'left', align: 'center'}, { text: 'Name', datafield: 'tlsnama', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Date', datafield: 'daftartgl', width: 80, cellsalign: 'center', align: 'center'}, { text: 'Time', datafield: 'daftarjam', width: 80, cellsalign: 'center', align: 'center'}, - { text: 'Inputor', filtertype: 'checkedlist', datafield: 'nmpendaftar', width: 110, cellsalign: 'left', align: 'center'}, { text: 'Comming From', filtertype: 'checkedlist', datafield: 'asalpasien', width: 120, cellsalign: 'left', align: 'center'}, { text: 'Service', filtertype: 'checkedlist', datafield: 'tlsreques', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Status', filtertype: 'checkedlist', datafield: 'tlsstatus', width: 150, cellsalign: 'left', align: 'center'}, - { text: 'Themeplate', filtertype: 'checkedlist', datafield: 'dlp', width: 75, cellsalign: 'left', align: 'center'}, - { text: 'Kelompok', filtertype: 'checkedlist', datafield: 'kd_spesimen', width: 75, cellsalign: 'left', align: 'center'}, - { text: 'Specimen', filtertype: 'checkedlist', datafield: 'nm_spesimen', width: 230, cellsalign: 'left', align: 'center'}, { text: 'Updated_at', datafield: 'updated_at', filtertype: 'date', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Duration', cellsrenderer: tlsdurasi, width: 100, cellsalign: 'left', align: 'center' }, + { text: 'Inputor', filtertype: 'checkedlist', datafield: 'nmpendaftar', width: 110, cellsalign: 'left', align: 'center'}, + { text: 'Themeplate', filtertype: 'checkedlist', datafield: 'dlp', width: 75, cellsalign: 'left', align: 'center'}, ] }); $("html, body").animate({ scrollTop: 0 }, "slow"); diff --git a/htdocs/resources/views/dokter/ppds.blade.php b/htdocs/resources/views/dokter/ppds.blade.php index a504faf0..dc902a5b 100644 --- a/htdocs/resources/views/dokter/ppds.blade.php +++ b/htdocs/resources/views/dokter/ppds.blade.php @@ -5539,20 +5539,19 @@ } }, { text: 'Acc.No', datafield: 'tlsnofoto', width: 100, align: 'center', cellsalign: 'center'}, - { text: 'Asal Sample', datafield: 'modality', width: 100, align: 'center', cellsalign: 'center'}, + { text: 'Kelompok', filtertype: 'checkedlist', datafield: 'kd_spesimen', width: 75, cellsalign: 'left', align: 'center'}, + { text: 'Specimen', filtertype: 'checkedlist', datafield: 'nm_spesimen', width: 230, cellsalign: 'left', align: 'center'}, { text: 'No.RM', datafield: 'tlsnoregister', width: 100, cellsalign: 'left', align: 'center'}, { text: 'Name', datafield: 'tlsnama', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Date', datafield: 'daftartgl', width: 80, cellsalign: 'center', align: 'center'}, { text: 'Time', datafield: 'daftarjam', width: 80, cellsalign: 'center', align: 'center'}, - { text: 'Inputor', filtertype: 'checkedlist', datafield: 'nmpendaftar', width: 110, cellsalign: 'left', align: 'center'}, { text: 'Comming From', filtertype: 'checkedlist', datafield: 'asalpasien', width: 120, cellsalign: 'left', align: 'center'}, { text: 'Service', filtertype: 'checkedlist', datafield: 'tlsreques', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Status', filtertype: 'checkedlist', datafield: 'tlsstatus', width: 150, cellsalign: 'left', align: 'center'}, - { text: 'Themeplate', filtertype: 'checkedlist', datafield: 'dlp', width: 75, cellsalign: 'left', align: 'center'}, - { text: 'Kelompok', filtertype: 'checkedlist', datafield: 'kd_spesimen', width: 75, cellsalign: 'left', align: 'center'}, - { text: 'Specimen', filtertype: 'checkedlist', datafield: 'nm_spesimen', width: 230, cellsalign: 'left', align: 'center'}, { text: 'Updated_at', datafield: 'updated_at', filtertype: 'date', width: 150, cellsalign: 'left', align: 'center'}, { text: 'Duration', cellsrenderer: tlsdurasi, width: 100, cellsalign: 'left', align: 'center' }, + { text: 'Inputor', filtertype: 'checkedlist', datafield: 'nmpendaftar', width: 110, cellsalign: 'left', align: 'center'}, + { text: 'Themeplate', filtertype: 'checkedlist', datafield: 'dlp', width: 75, cellsalign: 'left', align: 'center'}, ] }); $("html, body").animate({ scrollTop: 0 }, "slow");