Barcode Scan + Barcode Print

This commit is contained in:
Wian Drs
2026-06-30 11:16:31 +07:00
parent be5a02150e
commit 3ac6316da4
226 changed files with 1564 additions and 107 deletions
+641
View File
@@ -0,0 +1,641 @@
<style>
body {
background: #f5f7fb;
}
/* ===========================
CARD
=========================== */
.main-card,
.scanner-card,
.table-card {
border: none;
border-radius: 18px;
overflow: hidden;
/*box-shadow: 0 8px 25px rgba(0, 0, 0, .08);*/
}
/* ===========================
HEADER
=========================== */
.header-gradient {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: #fff;
padding: 22px 28px;
}
.info-box {
background: rgba(255,255,255,.18);
border-radius: 12px;
padding: 12px 16px;
}
.progress {
height: 8px;
border-radius: 20px;
}
/* ===========================
SCANNER
=========================== */
.barcode-avatar {
width: 110px;
height: 110px;
margin: auto;
border-radius: 50%;
background: #fff7e6;
color: #f59e0b;
display: flex;
justify-content: center;
align-items: center;
font-size: 45px;
}
.barcode-code {
font-size: 22px;
font-weight: 700;
letter-spacing: 2px;
}
.form-control-lg {
height: 60px;
font-size: 22px;
border-radius: 12px;
}
.form-control:focus {
border-color: #f59e0b;
box-shadow: 0 0 0 .2rem rgba(245, 158, 11, .2);
}
.scan-note {
color: #6c757d;
font-size: 13px;
}
.camera-btn {
height: 55px;
border-radius: 12px;
font-size: 17px;
}
/* ===========================
TABLE
=========================== */
.table thead th {
background: #d97706;
color: #fff;
border: none;
vertical-align: middle;
}
.table tbody td {
vertical-align: middle;
}
.table tbody tr:hover {
background: #fffaf0;
}
/* ===========================
BADGE
=========================== */
.badge-success-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #d1fae5;
color: #065f46;
font-weight: 600;
}
.badge-danger-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #fee2e2;
color: #991b1b;
font-weight: 600;
}
.badge-warning-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #fef3c7;
color: #92400e;
font-weight: 600;
}
#reader{
width:100%;
min-height:280px;
border-radius:12px;
overflow:hidden;
}
</style>
<div class="container mt-4">
<div class="card main-card">
<!-- HEADER -->
<div class="header-gradient">
<div class="row align-items-center">
<div class="col-md-8">
<h3 class="mb-1">
<i class="fa fa-barcode"></i>
Input Serial Number
</h3>
<div class="small">
Silahkan scan Serial Number perangkat.
Data akan otomatis tersimpan.
</div>
</div>
<div class="col-md-4 text-right">
<div class="info-box">
<div class="small">
Progress
</div>
<h5 class="font-weight-bold mb-2" id="progress_text">
0 / 0 Barcode
</h5>
<div class="progress">
<div class="progress-bar bg-success" style="width:35%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body">
<div class="row">
<!-- LEFT -->
<div class="col-lg-4 mb-4">
<div class="card scanner-card">
<div class="card-body">
<div class="barcode-avatar">
<i class="bi bi-qr-code-scan"></i>
</div>
<div class="text-center mt-3">
<div class="barcode-code">
ONU-250626-0007
</div>
<div class="scan-note mt-2">
Barcode yang sedang menunggu Serial Number
</div>
</div>
<hr>
<center>
<label class="font-weight-bold">
Serial Number
</label>
<!-- <input
type="text"
id="serial_number"
class="form-control form-control-lg text-center"
placeholder="Scan Serial Number"
autocomplete="off"
autofocus
>
<small class="scan-note">
Scanner akan otomatis menyimpan.
</small>
<button class="btn btn-warning btn-block camera-btn mt-4">
<i class="fa fa-camera mr-2"></i>
Scan dengan Kamera
</button> -->
<div id="scannerArea">
<input
type="text"
id="serial_number"
class="form-control form-control-lg text-center"
placeholder="Scan Serial Number"
autocomplete="off"
autofocus>
<small class="scan-note">
Scanner akan otomatis menyimpan.
</small>
<button
type="button"
class="btn btn-warning btn-block camera-btn mt-4"
id="btnCamera">
<i class="fa fa-camera mr-2"></i>
Scan dengan Kamera
</button>
</div>
<div id="cameraArea" style="display:none;">
<div id="reader"></div>
<small class="scan-note d-block mt-2">
Arahkan QR Code ke kamera.
</small>
<button
type="button"
class="btn btn-secondary btn-block mt-3"
id="btnCloseCamera">
Tutup Kamera
</button>
</div>
</center>
<div class="alert alert-warning mt-3 mb-0 text-center">
<i class="fa fa-check-circle"></i>
Auto Save Aktif
</div>
<input type="hidden" id="barcode_id">
</div>
</div>
</div>
<!-- RIGHT -->
<div class="col-lg-8">
<div class="card table-card">
<div class="card-header bg-white">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
Daftar Barcode
</h5>
<span class="badge badge-warning px-3 py-2">
20 Data
</span>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover mb-0" id="tableBarcode">
<thead>
<tr>
<th width="60">No</th>
<th>Barcode</th>
<th>Serial Number</th>
<th width="120">Status</th>
<th width="80" class="text-center">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>
<strong></strong>
</td>
<td></td>
<td>
<span class="badge-success-soft">
<i class="fa fa-check mr-1"></i>
</span>
</td>
<td class="text-center">
<button class="btn btn-sm btn-outline-danger">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let ITEM_ID = `<?= $item_id; ?>` ;
let isSaving = false;
$(document).ready(function () {
loadData();
$("#serial_number").focus();
$("#serial_number").on("keypress", function (e) {
if (e.which == 13) {
e.preventDefault();
if (!isSaving) {
saveSerial();
}
}
});
});
function loadData() {
$.getJSON( "<?= base_url(); ?>" + "items/load_data_barcode/" + ITEM_ID)
.done(function (r) {
render(r);
})
.fail(function () {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Gagal mengambil data barcode.'
});
});
}
function saveSerial() {
let serial = $("#serial_number").val().trim();
if (serial == "") {
$("#serial_number").focus();
return;
}
isSaving = true;
$("#serial_number")
.prop("disabled", true);
$.ajax({
url: "<?= base_url(); ?>" + "items/save_serial_number",
type: "POST",
dataType: "json",
data: {
barcode_id: $("#barcode_id").val(),
serial_number: serial
},
success: function (r) {
isSaving = false;
render(r);
},
error: function () {
isSaving = false;
$("#serial_number")
.prop("disabled", false)
.focus();
Swal.fire({
icon: "error",
title: "Error",
text: "Terjadi kesalahan pada server."
});
}
});
}
function render(r) {
if (!r.status) {
$("#serial_number")
.val("")
.prop("disabled", false)
.focus();
Swal.fire({
icon: "error",
title: "Oops...",
text: r.message
});
return;
}
// =======================
// Semua SN sudah selesai
// =======================
if (r.next == null) {
$("#barcode_id").val("");
$(".barcode-code").html("SELESAI");
$(".progress-bar").css("width", "100%");
$("#progress_text").html(r.done + " / " + r.total);
if (r.item_status == "draft") {
$("#serial_number")
.val("")
.prop("disabled", false)
.attr("placeholder", "Semua SN selesai. Edit melalui daftar di bawah jika ada yang salah.")
.blur();
Swal.fire({
icon: "success",
title: "Selesai",
text: "Semua Serial Number telah diinput. Karena status barang masih Draft, Anda masih dapat mengubah Serial Number melalui daftar di bawah."
});
} else {
$("#serial_number")
.val("")
.prop("disabled", true);
Swal.fire({
icon: "success",
title: "Selesai",
text: "Semua Serial Number berhasil diinput."
});
}
} else {
$("#barcode_id").val(r.next.id);
$(".barcode-code").text(r.next.barcode);
$("#progress_text").text(r.done + " / " + r.total);
$(".progress-bar").css("width", r.percent + "%");
$("#serial_number")
.val("")
.prop("disabled", false)
.attr("placeholder", "Scan Serial Number")
.focus();
}
// =======================
// Table
// =======================
let html = "";
$.each(r.list, function (i, v) {
html += "<tr>";
html += "<td>" + (i + 1) + "</td>";
html += "<td><strong>" + v.barcode + "</strong></td>";
html += "<td>" + (v.serial_number ? v.serial_number : "-") + "</td>";
html += "<td>";
if (v.serial_number) {
html += '<span class="badge-success-soft"><i class="fa fa-check mr-1"></i>Selesai</span>';
} else {
html += '<span class="badge-warning-soft">Menunggu</span>';
}
html += "</td>";
html += '<td class="text-center">';
if (r.item_status == "draft") {
html += '<button class="btn btn-sm btn-outline-danger btnDelete" data-id="' + v.id + '">Delete</button>';
} else {
html += '<button class="btn btn-sm btn-outline-secondary" disabled>Locked</button>';
}
html += "</td>";
html += "</tr>";
});
$("#tableBarcode tbody").html(html);
}
// function render(r) {
// if (!r.status) {
// $("#serial_number")
// .val("")
// .prop("disabled", false)
// .focus();
// Swal.fire({
// icon: "error",
// title: "Oops...",
// text: r.message
// });
// return;
// }
// if (r.next == null) {
// $("#barcode_id").val("");
// $(".barcode-code").html("SELESAI");
// $("#serial_number")
// .val("")
// .prop("disabled", true);
// $(".progress-bar")
// .css("width", "100%");
// $("#progress_text").html(r.done + " / " + r.total);
// Swal.fire({
// icon: "success",
// title: "Selesai",
// text: "Semua Serial Number berhasil diinput."
// });
// return;
// }
// // Barcode Aktif
// $("#barcode_id").val(r.next.id);
// $(".barcode-code").text(r.next.barcode);
// // Progress
// $("#progress_text").text(r.done + " / " + r.total);
// $(".progress-bar")
// .css("width", r.percent + "%");
// // Table
// let html = "";
// $.each(r.list, function (i, v) {
// html += "<tr>";
// html += "<td>" + (i + 1) + "</td>";
// html += "<td><strong>" + v.barcode + "</strong></td>";
// html += "<td>" + (v.serial_number ? v.serial_number : "-") + "</td>";
// html += "<td>";
// if (v.serial_number) {
// html += '<span class="badge-success-soft"><i class="fa fa-check mr-1"></i>Selesai</span>';
// } else {
// html += '<span class="badge-warning-soft">Menunggu</span>';
// }
// html += "</td>";
// html += '<td class="text-center">';
// html += '<button class="btn btn-sm btn-outline-danger btnDelete" data-id="' + v.id + '">';
// html += 'Delete';
// html += '</button>';
// html += '</td>';
// html += "</tr>";
// });
// $("#tableBarcode tbody").html(html);
// // Reset Input
// $("#serial_number")
// .val("")
// .prop("disabled", false)
// .focus();
// }
$(document).on("click", ".btnDelete", function () {
let id = $(this).data("id");
Swal.fire({
title: "Hapus Serial Number?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Ya",
cancelButtonText: "Batal"
}).then(function (result) {
if (!result.isConfirmed) return;
$.post(
"<?= base_url(); ?>" + "items/delete_serial_number",
{
barcode_id: id
},
function (r) {
render(r);
},
"json"
);
});
});
let html5QrCode=null;
$("#btnCamera").click(function(){
$("#scannerArea").hide();
$("#cameraArea").show();
html5QrCode=new Html5Qrcode("reader");
html5QrCode.start(
{
facingMode:"environment"
},
{
fps:10,
qrbox:220
},
function(decodedText){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number")
.val(decodedText);
saveSerial();
});
}
);
});
$("#btnCloseCamera").click(function(){
if(html5QrCode){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number").focus();
});
}
});
</script>
+114 -7
View File
@@ -63,6 +63,11 @@
<input type="number" id="qty" class="form-control">
</div>
<div id="group_tracking_qty">
<label class="mt-2">Jumlah Per (Roll, Box, Dus)</label>
<input type="number" id="tracking_qty" class="form-control">
</div>
<label class="mt-2">Harga Beli</label>
<input type="text" id="harga_beli" class="form-control format-rupiah">
@@ -252,16 +257,47 @@ function loadGudangList(){
},'json');
}
function loadKode(){
$.get("<?= base_url('kodebarang/list'); ?>",res=>{
let opt='<option value="">-- PILIH KODE --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.kode_barang} - ${a.nama}</option>`;
// function loadKode(){
// $.get("<?= base_url('kodebarang/list'); ?>",res=>{
// let opt='<option value="">-- PILIH KODE --</option>';
// res.forEach(a=>{
// opt += `<option value="${a.id}" data-type="${a.tracking_type}">${a.kode_barang} - ${a.nama}</option>`;
// });
// $('#kode_barang').html(opt);
// },'json');
// }
function loadKode() {
$.get("<?= base_url('kodebarang/list'); ?>", function(res) {
let opt = '<option value="">-- PILIH KODE --</option>';
res.forEach(function(a){
opt += `
<option value="${a.id}"
data-type="${a.tracking_type}">
${a.kode_barang} - ${a.nama}
</option>
`;
});
$('#kode_barang').html(opt);
},'json');
$('#kode_barang').html(opt).trigger('change');
}, 'json');
}
$(document).on('change', '#kode_barang', function () {
let type = $(this).find(':selected').data('type');
if(type == 'QTY'){
$('#group_tracking_qty').show();
}else{
$('#group_tracking_qty').hide();
$('#tracking_qty').val('');
}
});
// ================= RESET FORM =================
function resetForm(){
$('#id').val('');
@@ -270,6 +306,8 @@ function resetForm(){
$('#harga_beli').val('');
$('#harga_jual').val('');
$('#tanggal_beli').val('');
$('#tracking_qty').val('');
$('#group_tracking_qty').hide();
}
// ================= INIT LOAD =================
@@ -334,6 +372,7 @@ $('#btnSimpan').click(function(){
warehouse_id: $('#warehouse_id').val(),
account_kas: $('#account_kas').val(),
tanggal_beli: $('#tanggal_beli').val(),
tracking_qty: $('#tracking_qty').val(),
status: `draft`
};
@@ -501,6 +540,74 @@ $('#btnKeluar').click(function(){
// ================= END URUSAN KELUAR ===============
// =====================================================
// ================= PRINT BARCODE =================
$(document).on('click', '.btn-print', function () {
let id = $(this).data('id');
Swal.fire({
title: 'Cetak Barcode',
html: `
<div class="text-left">
<div class="form-group">
<label for="print_column" class="font-weight-bold">
Jumlah Kolom
</label>
<select id="print_column" class="form-control">
<option value="1">1 Kolom</option>
<option value="2">2 Kolom</option>
<option value="3" selected>3 Kolom (Rekomendasi)</option>
</select>
</div>
<div class="form-group mb-0">
<label for="duplicat" class="font-weight-bold">
Jumlah Cetak per Barcode
</label>
<select id="duplicat" class="form-control">
<option value="1" selected>1 Label</option>
<option value="2">2 Label</option>
</select>
</div>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Cetak',
cancelButtonText: 'Batal',
confirmButtonColor: '#f0ad4e',
preConfirm: () => {
let column = $('#print_column').val();
let duplicat = $('#duplicat').val();
return {
column: column,
duplicat: duplicat
};
}
}).then((result) => {
if (!result.isConfirmed) {
return;
}
window.open(
"<?= base_url('items/print_barcode/'); ?>" +
id +
"?column=" + result.value.column +
"&duplicat=" + result.value.duplicat,
"_blank"
);
});
});
// ================= DELETE =================
$(document).on('click','.btn-delete',function(){
+258 -41
View File
@@ -6,8 +6,7 @@
<h5>Data Barang</h5>
<div class="text-end">
<button class="btn btn-add bg-danger btn-out mr-2">Pengeluaran</button>
<button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button>
<button class="btn btn-add bg-primary" id="generate_pdf">Generate PDF</button>
<!-- <button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button> -->
</div>
</div>
@@ -93,7 +92,7 @@
<!-- MODAL BARANAG KELUAR -->
<div class="modal fade" id="modalKeluar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
@@ -102,6 +101,55 @@
<div class="modal-body">
<center>
<div id="scannerArea">
<input
type="text"
id="serial_number"
class="form-control form-control-lg text-center"
placeholder="Scan QR Barang"
autocomplete="off"
autofocus>
<small class="scan-note">
Scanner akan otomatis menyimpan.
</small><br>
<button
type="button"
class="btn btn-warning btn-block camera-btn mt-4"
id="btnCamera">
<i class="fa fa-camera mr-2"></i>
Scan dengan Kamera
</button>
</div>
<div id="cameraArea" style="display:none;">
<div id="reader"></div>
<small class="scan-note d-block mt-2">
Arahkan QR Code ke kamera.
</small>
<button
type="button"
class="btn btn-secondary btn-block mt-3"
id="btnCloseCamera">
Tutup Kamera
</button>
</div>
</center>
<div id="tableBarcode"></div>
<hr>
<div id="group_account_biaya">
<label class="mt-2">Akun Biaya</label>
<select id="account_biaya" class="form-control"></select>
@@ -112,7 +160,7 @@
<input type="date" id="tanggal_keluar" class="form-control">
</div>
<div>
<!-- <div>
<label class="mt-2">Gudang</label>
<select id="warehouse_id_keluar" class="form-control"></select>
</div>
@@ -125,9 +173,8 @@
<div class="d-none" id="group_qty_keluar">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty_keluar" class="form-control">
</div>
<div class="mb-2 d-none" id="group_keterangan">
</div> -->
<div class="mb-2" id="group_keterangan">
<label for="keterangan" class="form-label">Keterangan</label>
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
</div>
@@ -196,6 +243,191 @@
<script>
// URUSAN BARCODE INPUT DAN CAMSCANNER
let daftarBarang = [];
let isSaving = false;
$(document).ready(function () {
$("#serial_number").focus();
$("#serial_number").keypress(function(e){
if(e.which==13){
e.preventDefault();
if(!isSaving){
getBarcodeId();
}
}
});
});
function getBarcodeId(){
let barcode = ($("#serial_number").val() || "").trim();
if(barcode==""){
return;
}
$.post(
"<?=base_url('items/get_barcode_item');?>",
{
barcode:barcode
},
function(r){
if(!r.status){
Swal.fire({
icon:"error",
title:"Oops...",
text:r.message
});
$("#serial_number").val("").focus();
return;
}
let exist = daftarBarang.find(function(v){
return v.barcode_id == r.data.barcode_id;
});
if(exist){
Swal.fire({
icon: "warning",
title: "Barcode sudah discan",
timer: 1000,
showConfirmButton: false
});
$("#serial_number").val("").focus();
return;
}
daftarBarang.push(r.data);
renderTable();
$("#serial_number").val("").focus();
},
"json"
);
}
function renderTable(){
let html='';
html+='<div class="table-responsive">';
html+='<table class="table table-bordered table-hover table-sm mb-0">';
html+='<thead>';
html+='<tr>';
html+='<th width="50">No</th>';
html+='<th width="180">Barcode</th>';
html+='<th width="150">Kode Barang</th>';
html+='<th>Nama Barang</th>';
html+='<th width="180">Serial Number</th>';
html+='<th width="80" class="text-center">Action</th>';
html+='</tr>';
html+='</thead>';
html+='<tbody>';
if(daftarBarang.length==0){
html+='<tr>';
html+='<td colspan="6" class="text-center text-muted py-4">';
html+='Belum ada barcode yang discan.';
html+='</td>';
html+='</tr>';
}else{
$.each(daftarBarang,function(i,v){
html+='<tr>';
html+='<td>'+(i+1)+'</td>';
html+='<td><strong>'+v.barcode+'</strong></td>';
html+='<td>'+v.kode_detail+'</td>';
html+='<td>'+v.nama_barang+'</td>';
html+='<td>'+(v.serial_number?v.serial_number:'-')+'</td>';
html+='<td class="text-center">';
html+='<button class="btn btn-sm btn-outline-danger btnRemove" data-id="'+v.barcode_id+'">';
html+='<i class="bi bi-trash"></i>';
html+='</button>';
html+='</td>';
html+='</tr>';
});
}
html+='</tbody>';
html+='</table>';
html+='</div>';
$("#tableBarcode").html(html);
}
$(document).on("click",".btnRemove",function(){
let id=$(this).data("id");
daftarBarang=daftarBarang.filter(function(v){
return v.barcode_id!=id;
});
renderTable();
$("#serial_number").focus();
});
let html5QrCode=null;
$("#btnCamera").click(function(){
$("#scannerArea").hide();
$("#cameraArea").show();
html5QrCode=new Html5Qrcode("reader");
html5QrCode.start(
{
facingMode:"environment"
},
{
fps:10,
qrbox:220
},
function(decodedText){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number")
.val(decodedText);
saveSerial();
});
}
);
});
$("#btnCloseCamera").click(function(){
if(html5QrCode){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number").focus();
});
}
});
// URUSAN BARCODE INPUT DAN CAMSCANNER
$(function(){
let action = 'add';
@@ -279,20 +511,20 @@ loadGudang();
loadKas();
loadKode();
// ================= ADD =================
$('.btn-add-item').click(function(){
action='add';
resetForm();
// // ================= ADD =================
// $('.btn-add-item').click(function(){
// action='add';
// resetForm();
$('#qty').closest('div').show();
$('#kode_barang').closest('div').show();
$('#warehouse_id').closest('div').show();
$('#account_kas').closest('div').show();
$('#modalItem').modal('show');
loadGudang();
loadKas();
loadKode();
});
// $('#qty').closest('div').show();
// $('#kode_barang').closest('div').show();
// $('#warehouse_id').closest('div').show();
// $('#account_kas').closest('div').show();
// $('#modalItem').modal('show');
// loadGudang();
// loadKas();
// loadKode();
// });
// ================= EDIT =================
$(document).on('click','.btn-editItem',function(){
@@ -412,6 +644,12 @@ $('.btn-out').click(function(){
loadKasOut();
});
$('#modalKeluar').on('shown.bs.modal', function () {
setTimeout(function () {
$('#serial_number').trigger('focus').select();
}, 100);
});
$('#warehouse_id_keluar').on('change', function(){
let warehouse_id = $(this).val();
@@ -682,26 +920,5 @@ $('#btnAdjust').click(function(){
});
//PDF GENERATE//
$('#generate_pdf').on('click', function(){
let btn = $(this);
btn.prop('disabled', true)
.html('Generate PDF...');
window.open(
"<?= base_url('generateitems/generatepdf'); ?>",
'_blank'
);
setTimeout(function(){
btn.prop('disabled', false)
.html('Generate PDF');
},1000);
});
});
</script>