Files
accounting_dev_v2/application/views/items/barcode_sn.php
T
2026-09-11 16:03:00 +07:00

604 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php $inventory_title='Kelola Barcode & Serial';$inventory_subtitle='Lengkapi serial unit pending sebelum diaktifkan.';$this->load->view('inventory_professional/_module_nav',get_defined_vars());?>
<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: 14px 28px;
}
.info-box {
background: rgba(255,255,255,.18);
border-radius: 12px;
padding: 10px 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">
<h5 class="mb-1">
<i class="fa fa-barcode"></i>
Input Serial Number
</h5>
<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>
<h6 class="font-weight-bold mb-2" id="progress_text">
0 / 0 Barcode
</h6>
<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 flex-wrap justify-content-between align-items-center gap-2">
<h5 class="mb-0">
Daftar Barcode
</h5>
<div class="d-flex align-items-center gap-2">
<input type="search" id="barcodeSearch" class="form-control form-control-sm" placeholder="Cari barcode / SN" autocomplete="off">
<span class="badge badge-warning px-3 py-2" id="barcodeCount">0 Data</span>
</div>
</div>
</div>
<div class="card-footer bg-white d-flex justify-content-between align-items-center">
<small id="barcodePageInfo" class="text-muted">Memuat data...</small>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-secondary" id="barcodePrev">Sebelumnya</button>
<button type="button" class="btn btn-outline-secondary" id="barcodeNext">Berikutnya</button>
</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="150" 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;
let barcodeStart = 0;
const barcodeLength = 50;
let barcodeSearchTimer = null;
$(document).ready(function () {
loadData();
$("#barcodeSearch").on("input", function () {
clearTimeout(barcodeSearchTimer);
barcodeSearchTimer = setTimeout(function () { barcodeStart = 0; loadData(); }, 300);
});
$("#barcodePrev").on("click", function () { barcodeStart = Math.max(0, barcodeStart - barcodeLength); loadData(); });
$("#barcodeNext").on("click", function () { barcodeStart += barcodeLength; 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, {
start: barcodeStart,
length: barcodeLength,
search: $("#barcodeSearch").val() || ""
})
.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;
if (r.status) loadData(); else 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 = "";
const escapeHtml = function (value) { return $("<div>").text(value == null ? "" : String(value)).html(); };
$.each(r.list, function (i, v) {
html += "<tr>";
html += "<td>" + (r.start + i + 1) + "</td>";
html += "<td><strong>" + escapeHtml(v.barcode) + "</strong></td>";
html += "<td>" + escapeHtml(v.serial_number ? v.serial_number : "-") + "</td>";
html += "<td>";
if (v.status === 'available') {
html += '<span class="badge-success-soft"><i class="fa fa-check mr-1"></i>Aktif</span>';
} else if (v.serial_number) {
html += '<span class="badge-warning-soft">Siap Diposting</span>';
} else {
html += '<span class="badge-warning-soft">Menunggu</span>';
}
html += "</td>";
html += '<td class="text-center">';
if (v.status === 'available') {
html += '<button class="btn btn-sm btn-outline-secondary" disabled>Sudah Aktif</button>';
} else if (v.serial_number) {
html += '<button class="btn btn-sm btn-outline-danger btnDelete" data-id="' + v.id + '">Hapus SN</button>';
} else {
html += '<button class="btn btn-sm btn-outline-secondary" disabled>Belum Siap</button>';
}
html += "</td>";
html += "</tr>";
});
$("#tableBarcode tbody").html(html);
$("#barcodeCount").text(r.total + " Data");
const shownTo = Math.min(r.start + r.list.length, r.filtered);
$("#barcodePageInfo").text(r.filtered ? ((r.start + 1) + "–" + shownTo + " dari " + r.filtered) : "Tidak ada data");
$("#barcodePrev").prop("disabled", r.start <= 0);
$("#barcodeNext").prop("disabled", r.start + r.length >= r.filtered);
}
$(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) {
if (r.status) loadData(); else 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>