Perubahan barcode dan inventory

This commit is contained in:
Wian Drs
2026-07-12 22:10:02 +07:00
parent 3ac6316da4
commit 3113d77242
21 changed files with 2989 additions and 1138 deletions
+461 -159
View File
@@ -40,6 +40,98 @@ class Items extends CI_Controller {
$this->load->view('partials/footer');
}
public function item_barcodes_history()
{
$data = [
"active_menu" => "item_historys"
];
$this->load->view('partials/header', $data);
$this->load->view('items/item_historys', $data);
$this->load->view('partials/footer');
}
public function scan_history()
{
$barcode = trim($this->input->post('barcode'));
if(empty($barcode)){
echo json_encode([
'status'=>false,
'message'=>'QR ID tidak boleh kosong'
]);
return;
}
$item = $this->db
->select("
item_barcodes.id,
item_barcodes.barcode,
item_barcodes.serial_number,
item_barcodes.status,
item_barcodes.qty_awal,
item_barcodes.qty_sisa,
items.nama_barang,
items.kode_detail
")
->from('item_barcodes')
->join('items','items.id=item_barcodes.item_id')
->where('item_barcodes.barcode',$barcode)
->or_where('item_barcodes.serial_number',$barcode)
->get()
->row();
if(!$item){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.'
]);
return;
}
$history = $this->db
->select("
item_movements.*,
w1.nama as from_warehouse,
w2.nama as to_warehouse
")
->from('item_movements')
->join(
'warehouses w1',
'w1.id=item_movements.from_id AND item_movements.from_type="warehouse"',
'left'
)
->join(
'warehouses w2',
'w2.id=item_movements.to_id AND item_movements.to_type="warehouse"',
'left'
)
->where('barcode_id',$item->id)
->order_by('created_at','DESC')
->get()
->result();
echo json_encode([
'status'=>true,
'item'=>$item,
'history'=>$history
]);
}
public function load_data_barcode($item_id)
{
$item = $this->db
@@ -294,92 +386,103 @@ class Items extends CI_Controller {
$this->load_data_barcode($barcode->item_id);
}
public function get_barcode_item()
{
$barcode = trim($this->input->post('barcode'));
public function get_barcode_item()
{
$barcode = trim($this->input->post('barcode'));
if ($barcode == '') {
echo json_encode([
'status' => false,
'message' => 'Barcode kosong.'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i', 'i.id = ib.item_id')
->where('ib.qty_sisa !=', 0)
->where('i.status', 'active')
->group_start()
->where('ib.barcode', $barcode)
->or_like('i.nama_barang', $barcode, 'both')
->or_like('ib.serial_number', $barcode, 'both')
->group_end()
->limit(15)
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.',
'barcode' => $barcode
]);
return;
}
if ($barcode == '') {
echo json_encode([
'status' => false,
'message' => 'Barcode kosong.'
'status'=>true,
'data'=>$row
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.barcode',$barcode)
->get()
->row();
public function get_list_barcode_id($item_id)
{
// $item_id = trim($this->input->post('item_id'));
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.'
]);
return;
if ($item_id == '') {
echo json_encode([
'status' => false,
'message' => 'Item belum dipilih'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
ib.qty_awal,
ib.qty_sisa,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.item_id',$item_id)
->where('ib.qty_sisa !=', 0)
->where_in('ib.status', ['installed', 'reserved', 'available'])
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Item barang tidak ditemukan.'
]);
return;
}
echo json_encode($row);
}
echo json_encode([
'status'=>true,
'data'=>$row
]);
}
public function get_list_barcode_id($item_id)
{
// $item_id = trim($this->input->post('item_id'));
if ($item_id == '') {
echo json_encode([
'status' => false,
'message' => 'Item belum dipilih'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.item_id',$item_id)
// ->where('ib.status','warehouse')
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Item barang tidak ditemukan.'
]);
return;
}
echo json_encode($row);
}
// =========================
// GET DATA (FIX)
@@ -484,7 +587,7 @@ public function get_list_barcode_id($item_id)
} else {
$btnAdjust = '';
}
$action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete . $btnSN;
$action = $btnDetail . $btnEdit . $btnAdjust . $btnSN;
}
@@ -578,7 +681,8 @@ public function get_list_barcode_id($item_id)
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){
echo json_encode([
'status'=>false,
'message'=>'Data tidak lengkap / tidak valid'
'message'=>'Data tidak lengkap / tidak valid',
'account_id' => $account_kas
]);
return;
}
@@ -655,7 +759,9 @@ public function get_list_barcode_id($item_id)
// ================= Item Barcode Masuk =================
for ($i = 1; $i <= $unit_qty; $i++) {
$barcode = sprintf('%s-%04d', $kode_detail, $i);
// $barcode = sprintf('%s-%04d', $kode_detail, $i);
$barcode = strtoupper($kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2)));
$this->db->insert('item_barcodes',[
'item_id' => $item_id,
@@ -680,39 +786,6 @@ public function get_list_barcode_id($item_id)
'created_at' => date('Y-m-d H:i:s')
]);
}
// CREATE TABLE `item_barcodes` (
// `id` int NOT NULL AUTO_INCREMENT,
// `item_id` int NOT NULL,
// `barcode` varchar(100) NOT NULL,
// `serial_number` varchar(100) DEFAULT NULL,
// `warehouse_id` int DEFAULT NULL,
// `qty_awal` decimal(15,2) DEFAULT NULL,
// `qty_sisa` decimal(15,2) DEFAULT NULL,
// `status` enum('available','reserved','installed','damaged','lost','returned_supplier') DEFAULT 'available',
// `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
// PRIMARY KEY (`id`),
// UNIQUE KEY `uk_barcode` (`barcode`),
// KEY `idx_item` (`item_id`),
// KEY `idx_warehouse` (`warehouse_id`),
// CONSTRAINT `fk_barcode_item` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
// CREATE TABLE `item_movements` (
// `id` int NOT NULL AUTO_INCREMENT,
// `item_id` int NOT NULL,
// `barcode_id` int DEFAULT NULL,
// `qty` decimal(15,2) NOT NULL DEFAULT '1.00',
// `from_type` enum('supplier','warehouse','technician','customer') DEFAULT NULL,
// `from_id` int DEFAULT NULL,
// `to_type` enum('supplier','warehouse','technician','customer') DEFAULT NULL,
// `to_id` int DEFAULT NULL,
// `movement_type` enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment') NOT NULL,
// `reference_type` varchar(50) DEFAULT NULL,
// `reference_id` int DEFAULT NULL,
// `notes` varchar(255) DEFAULT NULL,
// `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
// ================= JURNAL =================
$no_ref = 'BLI-'.date('Ymd').'-'.$item_id;
@@ -737,12 +810,16 @@ public function get_list_barcode_id($item_id)
]);
// 🔥 Kredit Kas (DINAMIS)
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_kas,
'debit'=>0,
'kredit'=>$total
]);
foreach ($account_kas as $row) {
$this->db->insert('journal_details', [
'journal_id' => $jid,
'account_id' => $row['account_id'],
'debit' => 0,
'kredit' => str_replace('.', '', $row['nominal'])
]);
}
$this->db->trans_complete();
@@ -988,33 +1065,157 @@ public function get_list_barcode_id($item_id)
// =========================
public function keluarkan()
{
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$keterangan_keluar = $this->input->post('keterangan_keluar');
// mode baru: single barcode terpilih
$selected_barcode = (int)$this->input->post('selected_barcode');
if ($selected_barcode > 0) {
if (!$account_biaya || !$tanggal_keluar) {
echo json_encode(['status'=>false,'message'=>'Data tidak lengkap']);
return;
}
$barcode = $this->db
->select('
ib.id as barcode_id,
ib.item_id,
ib.qty_sisa,
ib.warehouse_id,
ib.status as barcode_status,
i.nama_barang,
i.harga_beli
')
->from('item_barcodes ib')
->join('items i', 'i.id = ib.item_id')
->where('ib.id', $selected_barcode)
->get()
->row();
if (!$barcode) {
echo json_encode(['status'=>false,'message'=>'Barcode tidak ditemukan']);
return;
}
if ((float)$barcode->qty_sisa <= 0) {
echo json_encode(['status'=>false,'message'=>'Qty barcode sudah habis']);
return;
}
if (empty($barcode->warehouse_id)) {
echo json_encode(['status'=>false,'message'=>'Warehouse barcode tidak valid']);
return;
}
$qty_keluar = 1;
$nilai = $qty_keluar * (float)$barcode->harga_beli;
$this->db->trans_start();
// stock log keluar per item
$this->db->insert('stock_logs',[
'item_id' => $barcode->item_id,
'warehouse_id' => $barcode->warehouse_id,
'qty' => $qty_keluar,
'tipe' => 'keluar',
'keterangan' => $keterangan_keluar
]);
$stock_log_id = $this->db->insert_id();
// jurnal
$this->db->insert('journals',[
'tanggal' => $tanggal_keluar,
'no_ref' => 'GOUT-'.date('YmdHis').'-'.$barcode->item_id,
'keterangan' => 'Pengeluaran Barang '.$barcode->nama_barang.' ( '.$keterangan_keluar.' )',
'ref_type' => 'stock_logs',
'ref_id' => $stock_log_id,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_biaya,
'debit'=>$nilai,
'kredit'=>0
]);
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>$nilai
]);
// START - Item Movement & item barcodes update status
$this->db->where('barcode_id', $barcode->barcode_id);
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$item_move = $this->db->get('item_movements')->row();
if($barcode->qty_sisa < $qty_keluar) {
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$barcode->qty_sisa]);
$this->db->trans_rollback();
return;
}
$this->db->insert('item_movements',[
'item_id' => $barcode->item_id,
'barcode_id' => $barcode->barcode_id,
'qty' => $qty_keluar,
'from_type' => $item_move ? $item_move->to_type : 'warehouse', //enum('supplier','warehouse','technician','customer','invoice','asset')
'to_type' => 'technician', //enum('supplier','warehouse','technician','customer','invoice','asset')
'movement_type' => 'installation', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out','asset')
'notes' => $keterangan_keluar,
'created_at' => date('Y-m-d H:i:s')
]);
// update qty_sisa barcode
$new_qty_sisa = (float)$barcode->qty_sisa - $qty_keluar;
$this->db->where('id', $barcode->barcode_id)->update('item_barcodes', [
'qty_sisa' => $new_qty_sisa,
'status' => $new_qty_sisa <= 0 ? 'installed' : $barcode->barcode_status
]);
// END - Item Movement & item barcodes update status
$this->db->trans_complete();
if (!$this->db->trans_status()) {
echo json_encode(['status'=>false,'message'=>'Gagal proses']);
return;
}
$this->update_item_stok($barcode->item_id);
log_activity(
'items',
'stock_out',
'Keluar barcode: '.$barcode->barcode_id.' | '.$barcode->nama_barang.' qty: '.$qty_keluar.' ( '.$keterangan_keluar.' )',
'success'
);
echo json_encode(['status'=>true,'message'=>'Barang berhasil dikeluarkan']);
return;
}
// fallback flow lama
$warehouse_id_keluar = $this->input->post('warehouse_id_keluar');
$barang_id = $this->input->post('barang_id');
$qty_keluar = (int)$this->input->post('qty_keluar');
$keterangan_keluar = $this->input->post('keterangan_keluar');
// =========================
// VALIDASI
// =========================
if(!$barang_id || !$warehouse_id_keluar || $qty_keluar <= 0){
echo json_encode(['status'=>false,'message'=>'Data tidak valid']);
return;
}
// =========================
// CEK ITEM
// =========================
$item = $this->db->get_where('items',['id'=>$barang_id])->row();
if(!$item){
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
return;
}
// =========================
// HITUNG STOK
// =========================
$stok = $this->db->select('
COALESCE(SUM(qty * IF(tipe="masuk",1,-1)),0) as stok
')
@@ -1022,20 +1223,16 @@ public function get_list_barcode_id($item_id)
->where('warehouse_id',$warehouse_id_keluar)
->get('stock_logs')
->row()->stok;
if($stok < $qty_keluar){
echo json_encode(['status'=>false,'message'=>'Stok tidak cukup']);
return;
}
$nilai = $qty_keluar * (float)$item->harga_beli;
// =========================
// TRANSACTION
// =========================
$this->db->trans_start();
// log keluar
$this->db->insert('stock_logs',[
'item_id'=>$barang_id,
'warehouse_id'=>$warehouse_id_keluar,
@@ -1043,10 +1240,9 @@ public function get_list_barcode_id($item_id)
'tipe'=>'keluar',
'keterangan'=>$keterangan_keluar
]);
$stock_log_id = $this->db->insert_id();
// jurnal
$this->db->insert('journals',[
'tanggal'=>$tanggal_keluar,
'no_ref'=>'GOUT-'.date('YmdHis').'-'.$barang_id,
@@ -1055,44 +1251,39 @@ public function get_list_barcode_id($item_id)
'ref_id' => $stock_log_id,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
// Debit biaya / HPP
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_biaya,
'debit'=>$nilai,
'kredit'=>0
]);
// Kredit persediaan
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>$nilai
]);
$this->db->trans_complete();
// =========================
// CEK TRANSACTION
// =========================
if (!$this->db->trans_status()) {
echo json_encode(['status'=>false,'message'=>'Gagal proses']);
return;
}
log_activity(
'items',
'stock_out',
'Keluar barang: ' . $item->nama_barang . ' qty: ' . $qty_keluar . ' ( ' . $keterangan_keluar . ' )',
'success'
);
$this->update_item_stok($barang_id);
echo json_encode(['status'=>true,'message'=>'Barang berhasil dikeluarkan']);
}
@@ -1232,4 +1423,115 @@ public function get_list_barcode_id($item_id)
echo json_encode($data);
}
public function update_barcode_item()
{
$items = $this->db
->select("
i.id,
i.nama_barang,
i.stok,
i.kode_detail,
kb.tracking_type
")
->from('items i')
->join('kode_barang kb', 'kb.id=i.kode_id')
->where('i.status', 'active')
->get()
->result();
$this->db->trans_begin();
foreach ($items as $item) {
// ===========================================
// Hitung jumlah barcode yang seharusnya
// ===========================================
if ($item->tracking_type == 'UNIT') {
$target = (int)$item->stok;
} else {
$target = (int)floor($item->stok / 1000);
}
if ($target <= 0) {
continue;
}
// ===========================================
// Barcode yang sudah ada
// ===========================================
$barcode_exist = $this->db
->where('item_id', $item->id)
->count_all_results('item_barcodes');
if ($barcode_exist >= $target) {
continue;
}
$need = $target - $barcode_exist;
for ($i = 1; $i <= $need; $i++) {
$barcode = strtoupper($item->kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2)));
$qty = ($item->tracking_type == 'UNIT')
? 1
: 1000;
// =============================
// Insert Barcode
// =============================
$this->db->insert('item_barcodes', [
'item_id' => $item->id,
'barcode' => $barcode,
'warehouse_id' => null,
'qty_awal' => $qty,
'qty_sisa' => $qty,
'status' => 'available'
]);
$barcode_id = $this->db->insert_id();
// =============================
// Insert Movement
// =============================
$this->db->insert('item_movements', [
'item_id' => $item->id,
'barcode_id' => $barcode_id,
'qty' => $qty,
'from_type' => 'supplier',
'from_id' => null,
'to_type' => 'warehouse',
'to_id' => null,
'movement_type' => 'purchase',
'reference_type' => 'system_generate',
'reference_id' => null,
'notes' => 'Generate barcode otomatis'
]);
}
}
if ($this->db->trans_status() == false) {
$this->db->trans_rollback();
echo json_encode([
'status' => false,
'message' => 'Gagal generate barcode.'
]);
return;
}
$this->db->trans_commit();
echo json_encode([
'status' => true,
'message' => 'Barcode berhasil diperbarui.'
]);
}
}