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 @@
Warning
+
+ Mendekati Expired
+
@@ -106,9 +109,12 @@
-
+
+
+
+
@@ -253,6 +259,47 @@
+
+
+
+
+
Barang Mendekati Expired (H-30)
+ @if(isset($expiringSoon) && count($expiringSoon) > 0)
+
+
+
+
+ | Jenis |
+ Deskripsi |
+ Tanggal Masuk |
+ Masa Expired |
+ Jumlah |
+ Satuan |
+ Sisa Hari |
+
+
+
+ @foreach($expiringSoon as $e)
+
+ | {{ $e['jenis'] }} |
+ {{ $e['deskripsi'] }} |
+ {{ $e['tanggal_masuk'] }} |
+ {{ $e['masa_expired'] }} |
+ {{ number_format($e['qty'], 0, '.', ',') }} |
+ {{ strtoupper($e['satuan']) }} |
+ {{ $e['sisa_hari'] }} hari |
+
+ @endforeach
+
+
+
+ @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');