diff --git a/htdocs/app/Http/Controllers/GudangController.php b/htdocs/app/Http/Controllers/GudangController.php index ac62cbae..d5641857 100644 --- a/htdocs/app/Http/Controllers/GudangController.php +++ b/htdocs/app/Http/Controllers/GudangController.php @@ -64,6 +64,7 @@ class GudangController extends Controller $tasks['stat_perjenis_bulanan'] = $this->getUsagePerJenis('bulanan'); $tasks['stat_perjenis_tahunan'] = $this->getUsagePerJenis('tahunan'); $tasks['warningstok'] = $this->getLowStockWarnings(); + $tasks['expiringSoon'] = $this->getExpiringSoonItems(); $tasks['jenisRows'] = $this->getJenisRows(); return view('admin.gudang', $tasks); } @@ -134,6 +135,7 @@ class GudangController extends Controller 'pemasukan' => number_format( $pemasukan , 0 , '.' , ',' ), 'pengeluaran' => number_format( $pengeluaran , 0 , '.' , ',' ), 'jenis' => $rdata->jenis, + 'masa_expired' => $rdata->masa_expired, 'keterangan' => $rdata->keterangan, 'tgllengkap' => $tgllengkap, 'total' => $total, @@ -142,6 +144,52 @@ class GudangController extends Controller echo json_encode($hasil); } + public function exportReportbhp(Request $request) + { + $bulan = strtoupper((string) $request->query('bulan', date('m'))); + $tahun = strtoupper((string) $request->query('tahun', date('Y'))); + + if ($tahun === 'ALL'){ + $bulan = date("m"); + $tahun = date("Y"); + $query = SIMBHPReport::where('bulan', (int) $bulan)->where('tahun', (int) $tahun); + } else { + $query = SIMBHPReport::where('tahun', (int) $tahun); + if ($bulan !== 'ALL') { + $query->where('bulan', (int) $bulan); + } + } + + $rows = $query->orderBy('id', 'DESC')->get(); + $filename = 'laporan-gudang-bln-'.$bulan.'_thn-'.$tahun.'.csv'; + $headers = [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="'.$filename.'"', + ]; + + $callback = function () use ($rows) { + $file = fopen('php://output', 'w'); + fputcsv($file, ['Tanggal', 'Bulan', 'Tahun', 'Jenis', 'Deskripsi', 'Pemasukan', 'Pengeluaran', 'Satuan Transaksi', 'Masa Expired', 'Keterangan']); + foreach ($rows as $row) { + fputcsv($file, [ + $row->tanggal, + $row->bulan, + $row->tahun, + $row->jenis, + $row->deskripsi, + (int) ($row->pemasukan ?? 0), + (int) ($row->pengeluaran ?? 0), + $row->satuan_transaksi ?: '-', + $row->masa_expired ?: '-', + $row->keterangan ?: '-', + ]); + } + fclose($file); + }; + + return response()->stream($callback, 200, $headers); + } + public function jsonReportbhpPaginated(Request $request) { $tanggal = $request->input('tanggal'); $deskripsi = $request->input('deskripsi'); @@ -208,6 +256,7 @@ class GudangController extends Controller 'pemasukan' => number_format( $pemasukan , 0 , '.' , ',' ), 'pengeluaran' => number_format( $pengeluaran , 0 , '.' , ',' ), 'jenis' => $kodejenis, + 'masa_expired' => $rdata->masa_expired, 'keterangan' => $rdata->keterangan, 'tgllengkap' => $tgllengkap, 'created_at' => $rdata->created_at, @@ -237,6 +286,7 @@ class GudangController extends Controller $set09 = $request->input('set09'); $set10 = $request->input('set10'); $satuanTransaksi = $request->input('set11'); + $masaExpired = $this->normalizeDateInput($request->input('set12')); if ($tanggal == '' OR is_null($tanggal)){ $tanggal = date("d-m-Y"); } @@ -317,6 +367,7 @@ class GudangController extends Controller 'pengeluaran' => null, 'qty_base' => $qtyBase, 'satuan_transaksi' => $satuanTransaksi ?: 'besar', + 'masa_expired' => $masaExpired, 'jenis' => $pos, 'keterangan' => '', 'marking' => '', @@ -387,6 +438,7 @@ class GudangController extends Controller 'pemasukan' => $total, 'qty_base' => $qtyBaseEdit, 'satuan_transaksi' => $satuanEdit, + 'masa_expired' => $masaExpired, 'keterangan' => $alasan, 'updated_at' => date("Y-m-d H:i:s") ]); @@ -592,6 +644,50 @@ class GudangController extends Controller return $warnings; } + private function getExpiringSoonItems() + { + $today = date('Y-m-d'); + $limitDate = date('Y-m-d', strtotime('+30 days')); + + $rows = SIMBHPReport::whereNotNull('masa_expired') + ->whereNotNull('pemasukan') + ->where('pemasukan', '>', 0) + ->where('masa_expired', '>=', $today) + ->where('masa_expired', '<=', $limitDate) + ->orderBy('masa_expired', 'ASC') + ->orderBy('id', 'DESC') + ->get(); + + $items = []; + foreach ($rows as $row) { + $expiredAt = (string) $row->masa_expired; + $daysLeft = (int) floor((strtotime($expiredAt) - strtotime($today)) / 86400); + if ($daysLeft < 0) { + continue; + } + + $badge = 'warning'; + if ($daysLeft <= 7) { + $badge = 'danger'; + } elseif ($daysLeft <= 14) { + $badge = 'warning'; + } + + $items[] = [ + 'jenis' => $row->jenis ?: '-', + 'deskripsi' => $row->deskripsi ?: '-', + 'qty' => (int) ($row->pemasukan ?? 0), + 'satuan' => $row->satuan_transaksi ?: 'besar', + 'tanggal_masuk' => sprintf('%02d-%02d-%04d', (int) $row->tanggal, (int) $row->bulan, (int) $row->tahun), + 'masa_expired' => date('d-m-Y', strtotime($expiredAt)), + 'sisa_hari' => $daysLeft, + 'badge' => $badge, + ]; + } + + return $items; + } + private function getAggregateBaseQty($direction, $scope = 'harian') { $now = date('Y-m-d'); @@ -656,6 +752,25 @@ class GudangController extends Controller return array_values($grouped); } + private function normalizeDateInput($value) + { + $value = trim((string) $value); + if ($value === '') { + return null; + } + + if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) { + return $value; + } + + if (preg_match('/^\d{2}-\d{2}-\d{4}$/', $value)) { + $parts = explode('-', $value); + return $parts[2].'-'.$parts[1].'-'.$parts[0]; + } + + return null; + } + private function getJenisRows() { $rows = []; diff --git a/htdocs/database/migrations/2026_02_23_000006_alter_simbhpreport_add_masa_expired.php b/htdocs/database/migrations/2026_02_23_000006_alter_simbhpreport_add_masa_expired.php new file mode 100644 index 00000000..4866b42a --- /dev/null +++ b/htdocs/database/migrations/2026_02_23_000006_alter_simbhpreport_add_masa_expired.php @@ -0,0 +1,26 @@ +date('masa_expired')->nullable()->after('satuan_transaksi'); + } + }); + } + + public function down(): void + { + Schema::table('simbhpreport', function (Blueprint $table) { + if (Schema::hasColumn('simbhpreport', 'masa_expired')) { + $table->dropColumn('masa_expired'); + } + }); + } +}; diff --git a/htdocs/resources/views/admin/gudang.blade.php b/htdocs/resources/views/admin/gudang.blade.php index 6dc4268f..1db87727 100644 --- a/htdocs/resources/views/admin/gudang.blade.php +++ b/htdocs/resources/views/admin/gudang.blade.php @@ -76,6 +76,9 @@ +
@@ -106,9 +109,12 @@
-
+
+
+ +
@@ -253,6 +259,47 @@
+
+
+
+
+

Barang Mendekati Expired (H-30)

+ @if(isset($expiringSoon) && count($expiringSoon) > 0) +
+ + + + + + + + + + + + + + @foreach($expiringSoon as $e) + + + + + + + + + + @endforeach + +
JenisDeskripsiTanggal MasukMasa ExpiredJumlahSatuanSisa Hari
{{ $e['jenis'] }}{{ $e['deskripsi'] }}{{ $e['tanggal_masuk'] }}{{ $e['masa_expired'] }}{{ number_format($e['qty'], 0, '.', ',') }}{{ strtoupper($e['satuan']) }}{{ $e['sisa_hari'] }} hari
+
+ @else +
Tidak ada barang yang mendekati masa expired dalam 30 hari ke depan.
+ @endif +
+
+
+
@@ -333,6 +380,10 @@
+
+
+ +
@@ -419,6 +470,10 @@
+
+ + +
@@ -446,6 +501,8 @@ $("#in_tanggal").datepicker({format: 'dd-mm-yyyy'}); $("#out_tanggal").datepicker({format: 'dd-mm-yyyy'}); $("#edit_tanggal").datepicker({format: 'dd-mm-yyyy'}); + $("#in_masa_expired").datepicker({format: 'dd-mm-yyyy'}); + $("#edit_masa_expired").datepicker({format: 'dd-mm-yyyy'}); }); function openedpage( jQuery ){ var set01=document.getElementById('cekbln').value; @@ -462,6 +519,7 @@ { name: 'pemasukan',type: 'text'}, { name: 'pengeluaran',type: 'text'}, { name: 'jenis',type: 'text'}, + { name: 'masa_expired',type: 'text'}, { name: 'keterangan',type: 'text'}, { name: 'tgllengkap',type: 'text'}, { name: 'total',type: 'text'}, @@ -490,7 +548,8 @@ { text: 'Deskripsi', datafield: 'deskripsi', width: '25%', cellsalign: 'left', align: 'center' }, { text: 'MASUK', datafield: 'pemasukan', width: '10%', cellsalign: 'right', align: 'center' }, { text: 'KELUAR', datafield: 'pengeluaran', width: '10%', cellsalign: 'right', align: 'center' }, - { text: 'Keterangan', datafield: 'keterangan', width: '20%', cellsalign: 'right', align: 'center' }, + { text: 'Masa Expired', datafield: 'masa_expired', width: '12%', cellsalign: 'center', align: 'center' }, + { text: 'Keterangan', datafield: 'keterangan', width: '8%', cellsalign: 'right', align: 'center' }, { text: 'Edit', columntype: 'button', width: '10%', cellsrenderer: function () { return "Edit"; }, buttonclick: function (row) { editrow = row; @@ -505,6 +564,7 @@ $("#edit_pos").val(dataRecord.jenis); $("#edit_total").val(dataRecord.total); $("#edit_tanggal").val(tulis); + $("#edit_masa_expired").val(dataRecord.masa_expired || ''); $("#modaleditor").modal('show'); } }, @@ -613,7 +673,8 @@ $(document).ready(function() { var val09=''; var val10=''; var val11=document.getElementById('in_satuan_transaksi').value; - $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11 }, + var val12=document.getElementById('in_masa_expired').value; + $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11, set12: val12 }, function(data){ $("#modalpemasukan").modal('hide'); var status = data.status; @@ -644,7 +705,8 @@ $(document).ready(function() { var val09=''; var val10=''; var val11=document.getElementById('out_satuan_transaksi').value; - $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11 }, + var val12=''; + $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11, set12: val12 }, function(data){ $("#modalpengeluaran").modal('hide'); var status = data.status; @@ -675,7 +737,8 @@ $(document).ready(function() { var val09=''; var val10=''; var val11='besar'; - $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11 }, + var val12=document.getElementById('edit_masa_expired').value; + $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11, set12: val12 }, function(data){ $("#modaleditor").modal('hide'); var status = data.status; @@ -706,7 +769,8 @@ $(document).ready(function() { var val09=''; var val10=''; var val11='besar'; - $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11 }, + var val12=''; + $.post('simbhp/exaddbarang', { _token: token, set01: val01, set02: val02, set03: val03, set04: val04, set05: val05, set06: val06, set07: val07, set08: val08, set09: val09, set10: val10, set11: val11, set12: val12 }, function(data){ $("#modaleditor").modal('hide'); var status = data.status; @@ -730,80 +794,11 @@ $(document).ready(function() { }); $("#in_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} ); $("#out_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} ); - $('#export').click(function(){ - var gridContent = $("#gridreportblnini").jqxGrid('exportdata', 'json'); - data = $.parseJSON(gridContent); - var noOfContacts = data.length; - if(noOfContacts>0){ - var table = document.createElement("table"); - table.style.width = '100%'; - table.setAttribute('border', '1'); - table.setAttribute('cellspacing', '0'); - table.setAttribute('cellpadding', '5'); - table.setAttribute('id', 'tabelcetak'); - table.setAttribute('class', 'text'); - var col = []; - for (var i = 0; i < noOfContacts; i++) { - for (var key in data[i]) { - if (col.indexOf(key) === -1) { - col.push(key); - } - } - } - var tHead = document.createElement("thead"); - var hRow = document.createElement("tr"); - for (var i = 0; i < col.length; i++) { - var th = document.createElement("th"); - th.innerHTML = col[i]; - hRow.appendChild(th); - } - tHead.appendChild(hRow); - table.appendChild(tHead); - var tBody = document.createElement("tbody"); - for (var i = 0; i < noOfContacts; i++) { - var bRow = document.createElement("tr"); - for (var j = 0; j < col.length; j++) { - var td = document.createElement("td"); - var isi = data[i][col[j]]; - var isi2 = isi.toString(); - var pjg = isi2.length; - if (pjg > 8){ - if (pjg == 9 || pjg == 10){ - if( isi2.indexOf(',') != -1 ){ - var res = isi2.replace(/,/g, ""); - td.innerHTML = res; - } - else { - var res = isi2; - td.setAttribute('style', 'mso-number-format: "\@";'); - td.innerHTML = res; - } - } - else { - var res = isi2; - td.setAttribute('style', 'mso-number-format: "\@";'); - td.innerHTML = res; - } - } - else { - var res = isi2.replace(/,/g, ""); - td.innerHTML = res; - } - - bRow.appendChild(td); - } - tBody.appendChild(bRow) - } - table.appendChild(tBody); - var divContainer = document.getElementById("tabel_cetak"); - divContainer.innerHTML = ""; - divContainer.appendChild(table); - } - - $("#tabel_cetak").btechco_excelexport({ - containerid: "tabel_cetak" - , datatype: $datatype.Table - }); + $('#btnexportreport').click(function(){ + var bln = $('#cekbln').val(); + var thn = $('#cekthn').val(); + var url = "{{ route('reportBHPExport') }}" + "?bulan=" + encodeURIComponent(bln) + "&tahun=" + encodeURIComponent(thn); + window.open(url, '_blank'); return false; }); }); diff --git a/htdocs/routes/web.php b/htdocs/routes/web.php index d6b20550..616d359f 100644 --- a/htdocs/routes/web.php +++ b/htdocs/routes/web.php @@ -76,6 +76,7 @@ Route::group(['middleware' => 'project.ipg'], function() { Route::post('biorepository/delete-cabinet/{id}', [BiorepositoryController::class, 'deleteCabinet'])->name('biorepository.deleteCabinet'); Route::post('simbhp/exaddbarang', [GudangController::class, 'exAddbarang'])->name('exAddBarang'); Route::post('simbhp/reportbhp', [GudangController::class, 'jsonReportbhp'])->name('reportBHP'); + Route::get('simbhp/reportbhp/export', [GudangController::class, 'exportReportbhp'])->name('reportBHPExport'); Route::post('simbhp/kwitansi', [GudangController::class, 'exKwitansi'])->name('kwitansiBHP'); Route::get('simbhp/rekapbhp', [GudangController::class, 'jsonRekapbhp'])->name('rekapBHP');