feat(inventaris): stok keluar/retur/opname + detail unit, dan edit slip payroll (port upstream)
- inventoryService: port penuh dari upstream (issueStock FIFO/SN, returnStock, adjustStock opname, serial tersedia, statistik, detail barang) + kompatibilitas fork - schema: kolom price/supplier (stock) dan remaining_stock/recipient/reference_no (log) via migrasi aman - admin: route stok keluar/retur/opname + API detail & serial; modal + tombol aksi di halaman inventaris; viewStock yang tadinya undefined - payroll: updateSlip() + route /payroll/slip/:id/update
This commit is contained in:
@@ -93,6 +93,9 @@
|
||||
<div class="act-menu">
|
||||
<button type="button" class="act-item" <%- `onclick="viewStock(${item.id}, '${item.name}')"` %>><i class="bi bi-eye"></i> <%= t('admin.inventory.detail_stock', 'Detail Stok') %></button>
|
||||
<button type="button" class="act-item success" <%- `onclick="openAddStock(${item.id}, '${item.name}')"` %>><i class="bi bi-plus-circle"></i> <%= t('admin.inventory.add_stock', 'Tambah Stok') %></button>
|
||||
<button type="button" class="act-item" <%- `onclick="openIssueStock(${item.id}, '${item.name}')"` %>><i class="bi bi-box-arrow-up"></i> Stok Keluar</button>
|
||||
<button type="button" class="act-item" <%- `onclick="openReturnStock(${item.id}, '${item.name}')"` %>><i class="bi bi-arrow-return-left"></i> Retur</button>
|
||||
<button type="button" class="act-item" <%- `onclick="openAdjustStock(${item.id}, '${item.name}')"` %>><i class="bi bi-clipboard-check"></i> Opname</button>
|
||||
<button type="button" class="act-item" <%- `onclick='openEditItem(${JSON.stringify(item)})'` %>><i class="bi bi-pencil"></i> Edit Barang</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -250,6 +253,76 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- MODAL: STOK KELUAR -->
|
||||
<div class="mo" id="modal-issue">
|
||||
<div class="mb">
|
||||
<div class="mh"><div class="mt">Stok Keluar</div><button class="mc" onclick="closeModal('modal-issue')">×</button></div>
|
||||
<form action="/admin/inventory/stock/out" method="POST">
|
||||
<input type="hidden" name="item_id" id="o_item_id">
|
||||
<div class="mbody">
|
||||
<div class="fg"><label class="fl">Nama Barang</label><input class="fc" id="o_item_name" readonly></div>
|
||||
<div class="fg"><label class="fl">Serial Number (kosongkan untuk barang tanpa SN)</label><input class="fc" name="serial_number" list="o_serials" placeholder="Pilih / scan SN"><datalist id="o_serials"></datalist></div>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="fl">Jumlah</label><input class="fc" type="number" name="quantity" value="1" min="1"></div>
|
||||
<div class="fg"><label class="fl">Tipe</label>
|
||||
<select class="fs" name="type"><option value="out">Keluar / Operasional</option><option value="assigned">Terpasang ke Pelanggan</option><option value="broken">Rusak</option><option value="loss">Hilang</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fg"><label class="fl">Penerima / Teknisi</label><input class="fc" name="recipient" placeholder="Nama teknisi / pelanggan"></div>
|
||||
<div class="fg"><label class="fl">Catatan</label><textarea class="fc" name="note"></textarea></div>
|
||||
</div>
|
||||
<div class="mf"><button type="button" class="btn btn-g" onclick="closeModal('modal-issue')">Batal</button><button class="btn btn-p">Simpan</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL: RETUR -->
|
||||
<div class="mo" id="modal-return">
|
||||
<div class="mb">
|
||||
<div class="mh"><div class="mt">Retur Barang</div><button class="mc" onclick="closeModal('modal-return')">×</button></div>
|
||||
<form action="/admin/inventory/stock/return" method="POST">
|
||||
<input type="hidden" name="item_id" id="r_item_id">
|
||||
<div class="mbody">
|
||||
<div class="fg"><label class="fl">Nama Barang</label><input class="fc" id="r_item_name" readonly></div>
|
||||
<div class="fg"><label class="fl">Serial Number (untuk unit ber-SN)</label><input class="fc" name="serial_number" placeholder="Kosongkan untuk barang tanpa SN"></div>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="fl">Jumlah</label><input class="fc" type="number" name="quantity" value="1" min="1"></div>
|
||||
<div class="fg"><label class="fl">Kondisi</label>
|
||||
<select class="fs" name="condition"><option value="used">Bekas / Copotan</option><option value="new">Baru</option><option value="broken">Rusak</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fg"><label class="fl">Catatan</label><textarea class="fc" name="note"></textarea></div>
|
||||
</div>
|
||||
<div class="mf"><button type="button" class="btn btn-g" onclick="closeModal('modal-return')">Batal</button><button class="btn btn-p">Simpan</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL: OPNAME -->
|
||||
<div class="mo" id="modal-adjust">
|
||||
<div class="mb">
|
||||
<div class="mh"><div class="mt">Stock Opname</div><button class="mc" onclick="closeModal('modal-adjust')">×</button></div>
|
||||
<form action="/admin/inventory/stock/adjust" method="POST">
|
||||
<input type="hidden" name="item_id" id="a_item_id">
|
||||
<div class="mbody">
|
||||
<div class="fg"><label class="fl">Nama Barang</label><input class="fc" id="a_item_name" readonly></div>
|
||||
<div class="fg"><label class="fl">Jumlah Fisik di Gudang</label><input class="fc" type="number" name="actual_quantity" min="0" required></div>
|
||||
<div class="fg"><label class="fl">Catatan</label><textarea class="fc" name="note"></textarea></div>
|
||||
</div>
|
||||
<div class="mf"><button type="button" class="btn btn-g" onclick="closeModal('modal-adjust')">Batal</button><button class="btn btn-p">Simpan</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL: DETAIL STOK -->
|
||||
<div class="mo" id="modal-detail">
|
||||
<div class="mb">
|
||||
<div class="mh"><div class="mt" id="d_title">Detail Stok</div><button class="mc" onclick="closeModal('modal-detail')">×</button></div>
|
||||
<div class="mbody" id="d_body"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var I18N_INV = {
|
||||
addTitle: "<%= t('admin.inventory.modal_add', 'Tambah Barang Baru') %>",
|
||||
@@ -288,6 +361,48 @@
|
||||
document.getElementById('modal-stock').classList.add('show');
|
||||
}
|
||||
|
||||
|
||||
function openIssueStock(itemId, itemName) {
|
||||
document.getElementById('o_item_id').value = itemId;
|
||||
document.getElementById('o_item_name').value = itemName;
|
||||
var dl = document.getElementById('o_serials');
|
||||
dl.innerHTML = '';
|
||||
fetch('/admin/api/inventory/serials/' + itemId).then(r => r.json()).then(d => {
|
||||
(d.serials || []).forEach(function (s) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = s.serial_number;
|
||||
dl.appendChild(opt);
|
||||
});
|
||||
}).catch(function () {});
|
||||
document.getElementById('modal-issue').classList.add('show');
|
||||
}
|
||||
|
||||
function openReturnStock(itemId, itemName) {
|
||||
document.getElementById('r_item_id').value = itemId;
|
||||
document.getElementById('r_item_name').value = itemName;
|
||||
document.getElementById('modal-return').classList.add('show');
|
||||
}
|
||||
|
||||
function openAdjustStock(itemId, itemName) {
|
||||
document.getElementById('a_item_id').value = itemId;
|
||||
document.getElementById('a_item_name').value = itemName;
|
||||
document.getElementById('modal-adjust').classList.add('show');
|
||||
}
|
||||
|
||||
function viewStock(itemId, itemName) {
|
||||
document.getElementById('d_title').innerText = 'Detail Stok — ' + itemName;
|
||||
var body = document.getElementById('d_body');
|
||||
body.innerHTML = 'Memuat…';
|
||||
fetch('/admin/api/inventory/item/' + itemId).then(r => r.json()).then(function (d) {
|
||||
if (!d.success) { body.innerText = d.message || 'Gagal memuat'; return; }
|
||||
var rows = (d.stockList || []).map(function (s) {
|
||||
return '<tr><td>' + (s.serial_number || '-') + '</td><td>' + s.status + '</td><td class="num">' + s.quantity + '</td><td>' + (s.customer_name || '-') + '</td></tr>';
|
||||
}).join('');
|
||||
body.innerHTML = '<table class="dtbl"><thead><tr><th>SN</th><th>Status</th><th class="num">Qty</th><th>Pelanggan</th></tr></thead><tbody>' + (rows || '<tr><td colspan="4" class="text-muted">Belum ada unit stok</td></tr>') + '</tbody></table>';
|
||||
}).catch(function () { body.innerText = 'Gagal memuat'; });
|
||||
document.getElementById('modal-detail').classList.add('show');
|
||||
}
|
||||
|
||||
function closeModal(id) { document.getElementById(id).classList.remove('show'); }
|
||||
|
||||
function filterItems(q) {
|
||||
|
||||
Reference in New Issue
Block a user