This commit is contained in:
Dwi Swandhana
2026-07-11 06:21:16 +07:00
parent 943282e3f9
commit d6bba3e216
6 changed files with 809 additions and 13 deletions
@@ -347,6 +347,238 @@ class GudangController extends Controller
return response()->json($response, 200); 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) { public function exAddbarang(Request $request) {
$deskripsi = $request->input('set01'); $deskripsi = $request->input('set01');
$pos = $request->input('set02'); $pos = $request->input('set02');
@@ -919,6 +1151,82 @@ class GudangController extends Controller
return null; 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() private function getJenisRows()
{ {
$rows = []; $rows = [];
@@ -106,7 +106,7 @@ class PasienController extends Controller
} else { } else {
try { try {
$client = new Client(); $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()); $response_data = json_decode($res->getBody()->getContents());
if (isset($response_data->response[0])) { if (isset($response_data->response[0])) {
$hasil = $response_data->response[0]; $hasil = $response_data->response[0];
@@ -28,6 +28,9 @@
<li class="nav-item"> <li class="nav-item">
<a href="#tab_hilang" data-toggle="tab" aria-expanded="false" class="nav-link">Stok Opname - Barang Hilang</a> <a href="#tab_hilang" data-toggle="tab" aria-expanded="false" class="nav-link">Stok Opname - Barang Hilang</a>
</li> </li>
<li class="nav-item">
<a href="#tab_racikan" data-toggle="tab" aria-expanded="false" class="nav-link">Racikan</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a href="#tab_statistik" data-toggle="tab" aria-expanded="false" class="nav-link">Statistik</a> <a href="#tab_statistik" data-toggle="tab" aria-expanded="false" class="nav-link">Statistik</a>
</li> </li>
@@ -246,6 +249,190 @@
</div> </div>
</div> </div>
<div class="tab-pane" id="tab_racikan">
<div class="row">
<div class="col-lg-4">
<div class="card-box">
<h4 class="m-t-0">Proses Racikan</h4>
<div class="form-group">
<label>Tanggal Racik</label>
<input type="text" id="racik_tanggal" class="form-control" value="{{$tanggal}}">
</div>
<div class="form-group">
<label>Nama / Keterangan Racikan</label>
<input type="text" id="racik_nama" class="form-control" placeholder="contoh: Media LJ batch 01">
</div>
<div class="form-group">
<label>Bahan</label>
<select id="racik_bahan_pos" class="form-control">
<option value="" selected disabled>-- Pilih Bahan --</option>
@foreach($jjenis as $rjenis)
@php($kode = ($rjenis['kodejenis'] ?? '') ?: preg_replace('/\s+/', '', (string) ($rjenis['jenis'] ?? '')))
<option value="{{ $rjenis['jenis'] }}"
data-kodejenis="{{ $kode }}"
data-jenisid="{{ $rjenis['id'] ?? '' }}"
data-satuan="{{ $rjenis['satuan'] }}"
data-satuan-kecil="{{ $rjenis['satuan_kecil'] ?? '' }}"
data-konversi="{{ $rjenis['konversi_kecil'] ?? 1 }}">{{ $kode }} - {{ $rjenis['jenis'] }} ( {{ $rjenis['satuan'] }} )</option>
@endforeach
</select>
</div>
<div class="form-group">
<label>Satuan Bahan</label>
<select id="racik_bahan_satuan" class="form-control">
<option value="besar">Satuan Besar</option>
<option value="kecil">Satuan Kecil (Pecah Satuan)</option>
</select>
</div>
<div class="form-group">
<label>Jumlah Bahan Diambil</label>
<input type="text" id="racik_bahan_qty" class="form-control">
</div>
<div class="form-group">
<label>Batch Expired yang Dibuka (opsional)</label>
<select id="racik_bahan_batch" class="form-control">
<option value="">-- Pilih batch jika segel dibuka --</option>
</select>
</div>
<div class="form-group">
<label>Expired Baru Setelah Dibuka</label>
<input type="text" id="racik_bahan_expired_baru" class="form-control" placeholder="dd-mm-yyyy">
</div>
<div class="form-group">
<button type="button" class="btn btn-primary btn-block" id="btn_racik_add_bahan">Tambah Bahan</button>
</div>
</div>
<div class="card-box">
<h4 class="m-t-0">Update Expired Barang Terbuka</h4>
<div class="form-group">
<label>Barang</label>
<select id="expired_update_pos" class="form-control">
<option value="" selected disabled>-- Pilih Barang --</option>
@foreach($jjenis as $rjenis)
@php($kode = ($rjenis['kodejenis'] ?? '') ?: preg_replace('/\s+/', '', (string) ($rjenis['jenis'] ?? '')))
<option value="{{ $rjenis['jenis'] }}">{{ $kode }} - {{ $rjenis['jenis'] }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<label>Batch Barang Masuk</label>
<select id="expired_update_batch" class="form-control">
<option value="">-- Pilih batch --</option>
</select>
</div>
<div class="form-group">
<label>Expired Baru</label>
<input type="text" id="expired_update_date" class="form-control" placeholder="dd-mm-yyyy">
</div>
<div class="form-group">
<label>Keterangan</label>
<input type="text" id="expired_update_note" class="form-control" placeholder="contoh: segel dibuka untuk racikan">
</div>
<button type="button" class="btn btn-warning btn-block" id="btn_expired_update">Update Expired Batch</button>
</div>
</div>
<div class="col-lg-8">
<div class="card-box">
<h4 class="m-t-0">Daftar Bahan Racikan</h4>
<div class="table-responsive">
<table class="table table-bordered table-striped m-b-0">
<thead>
<tr>
<th style="width: 110px">Kode</th>
<th>Bahan</th>
<th style="width: 120px">Qty</th>
<th style="width: 120px">Satuan</th>
<th>Expired Dibuka</th>
<th style="width: 90px">Aksi</th>
</tr>
</thead>
<tbody id="racik_lines">
<tr><td colspan="6" class="text-center text-muted">Belum ada bahan.</td></tr>
</tbody>
</table>
</div>
</div>
<div class="card-box">
<h4 class="m-t-0">Hasil Racikan</h4>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Status Racikan</label>
<select id="racik_status" class="form-control">
<option value="berhasil">Berhasil - masuk stok</option>
<option value="rusak">Rusak - hanya kurangi bahan</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Jenis Hasil</label>
<select id="racik_hasil_mode" class="form-control">
<option value="existing">Barang sudah ada</option>
<option value="new">Barang baru</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Masa Expired Hasil</label>
<input type="text" id="racik_hasil_expired" class="form-control" placeholder="dd-mm-yyyy">
</div>
</div>
</div>
<div id="racik_hasil_existing_fields">
<div class="form-group">
<label>Barang Hasil</label>
<select id="racik_hasil_pos" class="form-control">
<option value="" selected disabled>-- Pilih Barang Hasil --</option>
@foreach($jjenis as $rjenis)
@php($kode = ($rjenis['kodejenis'] ?? '') ?: preg_replace('/\s+/', '', (string) ($rjenis['jenis'] ?? '')))
<option value="{{ $rjenis['jenis'] }}"
data-kodejenis="{{ $kode }}"
data-jenisid="{{ $rjenis['id'] ?? '' }}"
data-satuan="{{ $rjenis['satuan'] }}"
data-satuan-kecil="{{ $rjenis['satuan_kecil'] ?? '' }}"
data-konversi="{{ $rjenis['konversi_kecil'] ?? 1 }}">{{ $kode }} - {{ $rjenis['jenis'] }} ( {{ $rjenis['satuan'] }} )</option>
@endforeach
</select>
</div>
</div>
<div id="racik_hasil_new_fields" style="display:none;">
<div class="row">
<div class="col-md-6"><div class="form-group"><label>Kode Barang Hasil</label><input type="text" id="racik_hasil_new_kode" class="form-control" maxlength="40"></div></div>
<div class="col-md-6"><div class="form-group"><label>Nama Barang Hasil</label><input type="text" id="racik_hasil_new_jenis" class="form-control"></div></div>
<div class="col-md-6"><div class="form-group"><label>Satuan Besar</label><input type="text" id="racik_hasil_new_satuan" class="form-control"></div></div>
<div class="col-md-6"><div class="form-group"><label>Satuan Kecil</label><input type="text" id="racik_hasil_new_satuan_kecil" class="form-control"></div></div>
<div class="col-md-6"><div class="form-group"><label>Konversi</label><input type="number" id="racik_hasil_new_konversi" class="form-control" value="1" min="1"></div></div>
<div class="col-md-6"><div class="form-group"><label>Stok Minimum</label><input type="number" id="racik_hasil_new_stok_minimum" class="form-control" value="0" min="0"></div></div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Jumlah Hasil</label>
<input type="text" id="racik_hasil_qty" class="form-control">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Satuan Hasil</label>
<select id="racik_hasil_satuan" class="form-control">
<option value="besar">Satuan Besar</option>
<option value="kecil">Satuan Kecil (Pecah Satuan)</option>
</select>
</div>
</div>
</div>
<div class="text-right">
<button type="button" class="btn btn-custom" id="btn_racik_commit">Simpan Proses Racikan</button>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="tab_report"> <div class="tab-pane" id="tab_report">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
@@ -759,8 +946,12 @@
$("#edit_tanggal").datepicker({format: 'yyyy-mm-dd'}); $("#edit_tanggal").datepicker({format: 'yyyy-mm-dd'});
$("#op_in_tanggal").datepicker({format: 'yyyy-mm-dd'}); $("#op_in_tanggal").datepicker({format: 'yyyy-mm-dd'});
$("#op_adj_tanggal").datepicker({format: 'yyyy-mm-dd'}); $("#op_adj_tanggal").datepicker({format: 'yyyy-mm-dd'});
$("#racik_tanggal").datepicker({format: 'yyyy-mm-dd'});
$("#in_masa_expired").datepicker({format: 'dd-mm-yyyy'}); $("#in_masa_expired").datepicker({format: 'dd-mm-yyyy'});
$("#op_in_masa_expired").datepicker({format: 'dd-mm-yyyy'}); $("#op_in_masa_expired").datepicker({format: 'dd-mm-yyyy'});
$("#racik_bahan_expired_baru").datepicker({format: 'dd-mm-yyyy'});
$("#racik_hasil_expired").datepicker({format: 'dd-mm-yyyy'});
$("#expired_update_date").datepicker({format: 'dd-mm-yyyy'});
$("#edit_masa_expired").datepicker({format: 'dd-mm-yyyy'}); $("#edit_masa_expired").datepicker({format: 'dd-mm-yyyy'});
$("#mulai").datepicker({format: 'yyyy-mm-dd'}); $("#mulai").datepicker({format: 'yyyy-mm-dd'});
$("#akhir").datepicker({format: 'yyyy-mm-dd'}); $("#akhir").datepicker({format: 'yyyy-mm-dd'});
@@ -884,6 +1075,7 @@
var jenisRows = @json($jenisRows ?? []); var jenisRows = @json($jenisRows ?? []);
var opInLines = []; var opInLines = [];
var opAdjLines = []; var opAdjLines = [];
var racikLines = [];
var currentDetail = null; var currentDetail = null;
function escapeHtml(value){ function escapeHtml(value){
@@ -1128,6 +1320,32 @@
} }
syncOpAdjSelected(); syncOpAdjSelected();
var currentRacikBahan = $('#racik_bahan_pos').val();
var htmlRacikBahan = '<option value=\"\" selected disabled>-- Pilih Bahan --</option>';
items.forEach(function(r){ htmlRacikBahan += makeOption(r); });
$('#racik_bahan_pos').html(htmlRacikBahan);
if(currentRacikBahan){
$('#racik_bahan_pos').val(currentRacikBahan);
}
syncRacikBahanSelected();
var currentRacikHasil = $('#racik_hasil_pos').val();
var htmlRacikHasil = '<option value=\"\" selected disabled>-- Pilih Barang Hasil --</option>';
items.forEach(function(r){ htmlRacikHasil += makeOption(r); });
$('#racik_hasil_pos').html(htmlRacikHasil);
if(currentRacikHasil){
$('#racik_hasil_pos').val(currentRacikHasil);
}
syncRacikHasilSelected();
var currentExpiredUpdate = $('#expired_update_pos').val();
var htmlExpiredUpdate = '<option value=\"\" selected disabled>-- Pilih Barang --</option>';
items.forEach(function(r){ htmlExpiredUpdate += makeOption(r); });
$('#expired_update_pos').html(htmlExpiredUpdate);
if(currentExpiredUpdate){
$('#expired_update_pos').val(currentExpiredUpdate);
}
// Legacy selects (modal lama / editor) // Legacy selects (modal lama / editor)
var legacyIds = ['#in_pos', '#out_pos']; var legacyIds = ['#in_pos', '#out_pos'];
legacyIds.forEach(function(sel){ legacyIds.forEach(function(sel){
@@ -1323,10 +1541,14 @@
$('#out_pos').on('change', function(){ syncSatuanMode('#out_pos', '#out_satuan_transaksi'); }); $('#out_pos').on('change', function(){ syncSatuanMode('#out_pos', '#out_satuan_transaksi'); });
$('#op_in_pos').on('change', function(){ syncSatuanMode('#op_in_pos', '#op_in_satuan_transaksi'); }); $('#op_in_pos').on('change', function(){ syncSatuanMode('#op_in_pos', '#op_in_satuan_transaksi'); });
$('#op_adj_pos').on('change', function(){ syncSatuanMode('#op_adj_pos', '#op_adj_satuan_transaksi'); }); $('#op_adj_pos').on('change', function(){ syncSatuanMode('#op_adj_pos', '#op_adj_satuan_transaksi'); });
$('#racik_bahan_pos').on('change', function(){ syncRacikBahanSelected(); });
$('#racik_hasil_pos').on('change', function(){ syncRacikHasilSelected(); });
syncSatuanMode('#in_pos', '#in_satuan_transaksi'); syncSatuanMode('#in_pos', '#in_satuan_transaksi');
syncSatuanMode('#out_pos', '#out_satuan_transaksi'); syncSatuanMode('#out_pos', '#out_satuan_transaksi');
syncSatuanMode('#op_in_pos', '#op_in_satuan_transaksi'); syncSatuanMode('#op_in_pos', '#op_in_satuan_transaksi');
syncSatuanMode('#op_adj_pos', '#op_adj_satuan_transaksi'); syncSatuanMode('#op_adj_pos', '#op_adj_satuan_transaksi');
syncSatuanMode('#racik_bahan_pos', '#racik_bahan_satuan');
syncSatuanMode('#racik_hasil_pos', '#racik_hasil_satuan');
function syncOpInSelected(){ function syncOpInSelected(){
var val = $('#op_in_pos').val(); var val = $('#op_in_pos').val();
@@ -1371,6 +1593,56 @@
}); });
syncOpAdjSelected(); syncOpAdjSelected();
function loadExpiredBatches(jenis, targetSelect, selectedId){
var $target = $(targetSelect);
$target.html('<option value="">Memuat batch...</option>');
if(!jenis){
$target.html('<option value="">-- Pilih batch --</option>');
return;
}
$.ajax({
url: "{{ route('simbhp.expiredBatches') }}",
method: 'GET',
data: { jenis: jenis }
}).done(function(resp){
var rows = (resp && resp.data) ? resp.data : [];
var html = '<option value="">-- Pilih batch --</option>';
rows.forEach(function(row){
var label = (row.tanggal_masuk || '-') + ' | exp: ' + (row.masa_expired || '-') + ' | ' + (row.qty || 0) + ' ' + (row.satuan || '') + ' | ' + (row.deskripsi || '-');
html += '<option value="' + escapeHtml(row.id || '') + '" data-expired="' + escapeHtml(row.masa_expired || '') + '">' + escapeHtml(label) + '</option>';
});
$target.html(html);
if(selectedId){
$target.val(selectedId);
}
}).fail(function(){
$target.html('<option value="">Gagal memuat batch</option>');
});
}
function syncRacikBahanSelected(){
syncSatuanMode('#racik_bahan_pos', '#racik_bahan_satuan');
loadExpiredBatches($('#racik_bahan_pos').val(), '#racik_bahan_batch');
}
function syncRacikHasilSelected(){
syncSatuanMode('#racik_hasil_pos', '#racik_hasil_satuan');
}
syncRacikBahanSelected();
syncRacikHasilSelected();
$('#expired_update_pos').on('change', function(){
loadExpiredBatches($(this).val(), '#expired_update_batch');
});
$('#expired_update_batch').on('change', function(){
var oldExpired = $(this).find('option:selected').data('expired') || '';
if(oldExpired && !$('#expired_update_date').val()){
$('#expired_update_date').val(oldExpired);
}
});
$('#op_in_kode_barang').on('input', function(){ $('#op_in_kode_barang').on('input', function(){
if($('#op_in_pos').val() !== '__NEW__'){ return; } if($('#op_in_pos').val() !== '__NEW__'){ return; }
$(this).val(normalizeKode($(this).val())); $(this).val(normalizeKode($(this).val()));
@@ -1390,6 +1662,38 @@
$('#op_in_new_satuan_kecil').on('input', syncOpInNewSatuanAvailability); $('#op_in_new_satuan_kecil').on('input', syncOpInNewSatuanAvailability);
$('#op_in_new_konversi').on('input', syncOpInNewSatuanAvailability); $('#op_in_new_konversi').on('input', syncOpInNewSatuanAvailability);
function syncRacikHasilMode(){
var isNew = $('#racik_hasil_mode').val() === 'new';
$('#racik_hasil_existing_fields').toggle(!isNew);
$('#racik_hasil_new_fields').toggle(isNew);
if(isNew){
var satuanKecil = ($('#racik_hasil_new_satuan_kecil').val() || '').toString().trim();
var konv = parseInt($('#racik_hasil_new_konversi').val() || 1, 10);
var hasBreakdown = satuanKecil !== '' && konv > 1;
$('#racik_hasil_satuan option[value="kecil"]').prop('disabled', !hasBreakdown);
if(!hasBreakdown && $('#racik_hasil_satuan').val() === 'kecil'){
$('#racik_hasil_satuan').val('besar');
}
}else{
syncRacikHasilSelected();
}
}
$('#racik_hasil_mode').on('change', syncRacikHasilMode);
$('#racik_status').on('change', function(){
var isRusak = $(this).val() === 'rusak';
$('#racik_hasil_mode, #racik_hasil_pos, #racik_hasil_expired, #racik_hasil_qty, #racik_hasil_satuan').prop('disabled', isRusak);
$('#racik_hasil_new_fields input').prop('disabled', isRusak);
});
$('#racik_hasil_new_satuan_kecil, #racik_hasil_new_konversi').on('input', syncRacikHasilMode);
$('#racik_hasil_new_kode').on('input', function(){ $(this).val(normalizeKode($(this).val())); });
$('#racik_hasil_new_jenis').on('input', function(){
if(($('#racik_hasil_new_kode').val() || '') === ''){
$('#racik_hasil_new_kode').val(normalizeKode($(this).val()));
}
});
syncRacikHasilMode();
$('#op_in_satuan_transaksi').on('change', function(){ $('#op_in_satuan_transaksi').on('change', function(){
if($('#op_in_pos').val() === '__NEW__'){ return; } if($('#op_in_pos').val() === '__NEW__'){ return; }
var selected = $('#op_in_pos option:selected'); var selected = $('#op_in_pos option:selected');
@@ -1713,6 +2017,8 @@
$("#out_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} ); $("#out_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} );
$("#op_in_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} ); $("#op_in_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} );
$("#op_adj_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} ); $("#op_adj_total").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} );
$("#racik_bahan_qty").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} );
$("#racik_hasil_qty").autoNumeric( 'init', {aSep: ',', mDec: '0', vMax: '99999999999999999999999999'} );
function renderOpInLines(){ function renderOpInLines(){
if(!opInLines.length){ if(!opInLines.length){
@@ -1752,6 +2058,29 @@
$('#op_adj_lines').html(html); $('#op_adj_lines').html(html);
} }
function renderRacikLines(){
if(!racikLines.length){
$('#racik_lines').html('<tr><td colspan="6" class="text-center text-muted">Belum ada bahan.</td></tr>');
return;
}
var html = '';
racikLines.forEach(function(line, idx){
var expiredInfo = '-';
if(line.batch_label || line.expired_baru){
expiredInfo = (line.batch_label || '-') + (line.expired_baru ? '<br><small>baru: ' + escapeHtml(line.expired_baru) + '</small>' : '');
}
html += '<tr>' +
'<td class="text-center"><code>' + escapeHtml(line.kode || '') + '</code></td>' +
'<td>' + escapeHtml(line.jenis || '') + '</td>' +
'<td class="text-right">' + escapeHtml(line.qty || '') + '</td>' +
'<td class="text-center">' + escapeHtml(line.satuan_transaksi || '') + '</td>' +
'<td>' + expiredInfo + '</td>' +
'<td class="text-center"><button type="button" class="btn btn-sm btn-danger btn_racik_remove" data-index="' + idx + '">Hapus</button></td>' +
'</tr>';
});
$('#racik_lines').html(html);
}
function getSelectedKode(selectId){ function getSelectedKode(selectId){
var selected = $(selectId).find('option:selected'); var selected = $(selectId).find('option:selected');
return normalizeKode(selected.data('kodejenis') || ''); return normalizeKode(selected.data('kodejenis') || '');
@@ -1904,6 +2233,43 @@
if(!isNaN(idx)){ opAdjLines.splice(idx, 1); renderOpAdjLines(); } if(!isNaN(idx)){ opAdjLines.splice(idx, 1); renderOpAdjLines(); }
}); });
$('#btn_racik_add_bahan').on('click', function(){
var jenis = $('#racik_bahan_pos').val();
var qty = $('#racik_bahan_qty').val();
var satuanTransaksi = $('#racik_bahan_satuan').val() || 'besar';
var kode = getSelectedKode('#racik_bahan_pos');
var batchId = $('#racik_bahan_batch').val();
var batchLabel = batchId ? $('#racik_bahan_batch option:selected').text() : '';
var expiredBaru = $('#racik_bahan_expired_baru').val();
if(!jenis || !qty){
$.toast({ heading: 'Gagal', text: 'Bahan dan jumlah wajib diisi', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 3000, stack: 1 });
return;
}
if(expiredBaru && !batchId){
$.toast({ heading: 'Gagal', text: 'Pilih batch yang dibuka jika mengisi expired baru', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 3500, stack: 1 });
return;
}
racikLines.push({
jenis: jenis,
kode: kode,
qty: qty,
satuan_transaksi: satuanTransaksi,
batch_id: batchId,
batch_label: batchLabel,
expired_baru: expiredBaru
});
$('#racik_bahan_qty').val('');
$('#racik_bahan_expired_baru').val('');
renderRacikLines();
});
$(document).on('click', '.btn_racik_remove', function(){
var idx = parseInt($(this).data('index'), 10);
if(!isNaN(idx)){ racikLines.splice(idx, 1); renderRacikLines(); }
});
function runBatchPost(lines, buildPayload, onDone){ function runBatchPost(lines, buildPayload, onDone){
var i = 0; var i = 0;
function next(){ function next(){
@@ -2083,8 +2449,120 @@
nextAdj(); nextAdj();
}); });
$('#btn_racik_commit').on('click', function(){
if(!racikLines.length){
$.toast({ heading: 'Info', text: 'Daftar bahan racikan masih kosong', position: 'top-right', loaderBg: '#3b98b5', icon: 'info', hideAfter: 2500, stack: 1 });
return;
}
var statusRacikan = $('#racik_status').val();
var payload = {
_token: token,
tanggal: $('#racik_tanggal').val(),
nama_racikan: $('#racik_nama').val(),
status_racikan: statusRacikan,
bahan: JSON.stringify(racikLines),
hasil_mode: $('#racik_hasil_mode').val(),
hasil_jenis: $('#racik_hasil_pos').val(),
hasil_qty: $('#racik_hasil_qty').val(),
hasil_satuan_transaksi: $('#racik_hasil_satuan').val(),
hasil_masa_expired: $('#racik_hasil_expired').val(),
hasil_new_kode: $('#racik_hasil_new_kode').val(),
hasil_new_jenis: $('#racik_hasil_new_jenis').val(),
hasil_new_satuan: $('#racik_hasil_new_satuan').val(),
hasil_new_satuan_kecil: $('#racik_hasil_new_satuan_kecil').val(),
hasil_new_konversi: $('#racik_hasil_new_konversi').val(),
hasil_new_stok_minimum: $('#racik_hasil_new_stok_minimum').val()
};
if(statusRacikan === 'berhasil'){
if(payload.hasil_mode === 'existing' && !payload.hasil_jenis){
$.toast({ heading: 'Gagal', text: 'Barang hasil racikan wajib dipilih', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 3000, stack: 1 });
return;
}
if(!payload.hasil_qty){
$.toast({ heading: 'Gagal', text: 'Jumlah hasil racikan wajib diisi', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 3000, stack: 1 });
return;
}
}
var $btn = $(this);
$btn.prop('disabled', true).text('Menyimpan...');
$.ajax({ url: "{{ route('simbhp.racikan') }}", method: 'POST', data: payload })
.done(function(resp){
var ok = resp && resp.status && resp.status.toString().toLowerCase() === 'success';
$.toast({
heading: ok ? 'Success' : 'Gagal',
text: (resp && resp.message) ? resp.message : (ok ? 'Racikan tersimpan' : 'Gagal menyimpan racikan'),
position: 'top-right',
loaderBg: ok ? '#5ba035' : '#bf441d',
icon: ok ? 'success' : 'error',
hideAfter: 4500,
stack: 1
});
if(ok){
racikLines = [];
renderRacikLines();
$('#racik_hasil_qty').val('');
$('#racik_hasil_expired').val('');
if(typeof window.gudangRefresh === 'function'){
window.gudangRefresh();
}
}
})
.fail(function(){
$.toast({ heading: 'Gagal', text: 'Request gagal', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 4500, stack: 1 });
})
.always(function(){
$btn.prop('disabled', false).text('Simpan Proses Racikan');
});
});
$('#btn_expired_update').on('click', function(){
var batchId = $('#expired_update_batch').val();
var newDate = $('#expired_update_date').val();
if(!batchId || !newDate){
$.toast({ heading: 'Gagal', text: 'Batch dan expired baru wajib diisi', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 3000, stack: 1 });
return;
}
var $btn = $(this);
$btn.prop('disabled', true).text('Menyimpan...');
$.ajax({
url: "{{ route('simbhp.updateExpired') }}",
method: 'POST',
data: {
_token: token,
batch_id: batchId,
masa_expired: newDate,
alasan: $('#expired_update_note').val()
}
}).done(function(resp){
var ok = resp && resp.status && resp.status.toString().toLowerCase() === 'success';
$.toast({
heading: ok ? 'Success' : 'Gagal',
text: (resp && resp.message) ? resp.message : 'Gagal update expired',
position: 'top-right',
loaderBg: ok ? '#5ba035' : '#bf441d',
icon: ok ? 'success' : 'error',
hideAfter: 3500,
stack: 1
});
if(ok){
loadExpiredBatches($('#expired_update_pos').val(), '#expired_update_batch');
if(typeof window.gudangRefresh === 'function'){
window.gudangRefresh();
}
}
}).fail(function(){
$.toast({ heading: 'Gagal', text: 'Request gagal', position: 'top-right', loaderBg: '#bf441d', icon: 'error', hideAfter: 4500, stack: 1 });
}).always(function(){
$btn.prop('disabled', false).text('Update Expired Batch');
});
});
renderOpInLines(); renderOpInLines();
renderOpAdjLines(); renderOpAdjLines();
renderRacikLines();
$('#btnexportreport').click(function(){ $('#btnexportreport').click(function(){
var gridContent = $("#gridreportblnini").jqxGrid('exportdata', 'json'); var gridContent = $("#gridreportblnini").jqxGrid('exportdata', 'json');
data = $.parseJSON(gridContent); data = $.parseJSON(gridContent);
@@ -123,23 +123,28 @@
}, 150); }, 150);
} }
$('#gridpemeriksaan').off('bindingcomplete.mikro-expertise').on('bindingcomplete.mikro-expertise', function () { function requestFilteredLoad() {
if (!hasRequestedFilteredLoad) { if (hasRequestedFilteredLoad) {
hasRequestedFilteredLoad = true;
setInitialFilters();
setTimeout(function () {
if (typeof openedpage === 'function') {
openedpage();
} else {
$('#divawal').show();
}
}, 0);
return; return;
} }
hasRequestedFilteredLoad = true;
setInitialFilters();
if (typeof openedpage === 'function') {
openedpage();
} else {
$('#divawal').show();
}
}
$('#gridpemeriksaan').off('bindingcomplete.mikro-expertise').on('bindingcomplete.mikro-expertise', function () {
openFocusedPeriksa(); openFocusedPeriksa();
}); });
$(window).off('load.mikro-expertise').on('load.mikro-expertise', function () {
requestFilteredLoad();
});
$('#btnkembali2').off('click.mikro-expertise').on('click.mikro-expertise', function () { $('#btnkembali2').off('click.mikro-expertise').on('click.mikro-expertise', function () {
window.location.href = returnUrl; window.location.href = returnUrl;
}); });
@@ -5761,7 +5761,9 @@
$("html, body").animate({ scrollTop: 0 }, "slow"); $("html, body").animate({ scrollTop: 0 }, "slow");
} }
$(window).on('load', function () { $(window).on('load', function () {
openedpage(); if (!@json((bool) ($openExpertiseOnLoad ?? false))) {
openedpage();
}
}); });
var start = new Date(); var start = new Date();
CountDownTimer(start, 'timeremaining'); CountDownTimer(start, 'timeremaining');
+3
View File
@@ -78,6 +78,9 @@ Route::group(['middleware' => 'project.ipg'], function() {
Route::post('biorepository/delete-cabinet/{id}', [BiorepositoryController::class, 'deleteCabinet'])->name('biorepository.deleteCabinet'); Route::post('biorepository/delete-cabinet/{id}', [BiorepositoryController::class, 'deleteCabinet'])->name('biorepository.deleteCabinet');
Route::post('simbhp/exaddbarang', [GudangController::class, 'exAddbarang'])->name('exAddBarang'); Route::post('simbhp/exaddbarang', [GudangController::class, 'exAddbarang'])->name('exAddBarang');
Route::get('simbhp/snapshot', [GudangController::class, 'jsonSnapshot'])->name('simbhp.snapshot'); Route::get('simbhp/snapshot', [GudangController::class, 'jsonSnapshot'])->name('simbhp.snapshot');
Route::get('simbhp/expired-batches', [GudangController::class, 'jsonExpiredBatches'])->name('simbhp.expiredBatches');
Route::post('simbhp/racikan', [GudangController::class, 'storeRacikan'])->name('simbhp.racikan');
Route::post('simbhp/update-expired', [GudangController::class, 'updateExpiredBatch'])->name('simbhp.updateExpired');
Route::post('simbhp/reportbhp', [GudangController::class, 'jsonReportbhp'])->name('reportBHP'); Route::post('simbhp/reportbhp', [GudangController::class, 'jsonReportbhp'])->name('reportBHP');
Route::get('simbhp/reportbhp/export', [GudangController::class, 'exportReportbhp'])->name('reportBHPExport'); Route::get('simbhp/reportbhp/export', [GudangController::class, 'exportReportbhp'])->name('reportBHPExport');
Route::post('simbhp/kwitansi', [GudangController::class, 'exKwitansi'])->name('kwitansiBHP'); Route::post('simbhp/kwitansi', [GudangController::class, 'exKwitansi'])->name('kwitansiBHP');