diff --git a/application/controllers/Items.php b/application/controllers/Items.php index c1081f7..a28302b 100644 --- a/application/controllers/Items.php +++ b/application/controllers/Items.php @@ -28,6 +28,359 @@ class Items extends CI_Controller { $this->load->view('partials/footer'); } + public function item_barcodes($item_id) + { + $data = [ + "active_menu" => "draft_items", + "item_id" => $item_id + ]; + + $this->load->view('partials/header', $data); + $this->load->view('items/barcode_sn', $data); + $this->load->view('partials/footer'); + } + + public function load_data_barcode($item_id) + { + $item = $this->db + ->where('id', $item_id) + ->get('items') + ->row(); + + if (!$item) { + echo json_encode([ + 'status' => false, + 'message' => 'Data barang tidak ditemukan.' + ]); + return; + } + + $next = $this->db + ->where('item_id', $item_id) + ->where('serial_number IS NULL', NULL, false) + ->order_by('id', 'ASC') + ->get('item_barcodes') + ->row(); + + $list = $this->db + ->where('item_id', $item_id) + ->order_by('id', 'ASC') + ->get('item_barcodes') + ->result(); + + $total = count($list); + + $done = 0; + foreach ($list as $row) { + if (!empty($row->serial_number)) { + $done++; + } + } + + echo json_encode([ + 'status' => true, + 'item_status' => $item->status, + 'next' => $next, + 'list' => $list, + 'total' => $total, + 'done' => $done, + 'percent' => $total > 0 ? round(($done / $total) * 100) : 0 + ]); + } + + public function print_barcode($item_id) + { + require_once APPPATH.'third_party/fpdf/fpdf.php'; + require_once APPPATH.'libraries/phpqrcode/qrlib.php'; + + $barcodes = $this->db + ->where('item_id',$item_id) + ->order_by('id','ASC') + ->get('item_barcodes') + ->result(); + + if(empty($barcodes)){ + show_error('Barcode tidak ditemukan.'); + } + + $column=(int)$this->input->get('column'); + if(!in_array($column,[1,2,3])){ + $column=3; + } + + $duplicate=(int)$this->input->get('duplicat'); + if($duplicate<1){ + $duplicate=1; + } + + $labelWidth=33.3; + $labelHeight=23.2; + + $spaceX=0; + $spaceY=0; + + $rowPerPage=6; + + $paperWidth=$labelWidth*$column; + $paperHeight=$labelHeight*$rowPerPage; + + $marginLeft=0; + $marginTop=0; + + $labelPerPage=$column*$rowPerPage; + + $pdf=new FPDF('P','mm',array($paperWidth,$paperHeight)); + $pdf->SetMargins(0,0,0); + $pdf->SetAutoPageBreak(false); + $pdf->AddPage(); + + $tempDir=FCPATH.'uploads/temp_qr/'; + if(!is_dir($tempDir)){ + mkdir($tempDir,0777,true); + } + + $printData=[]; + foreach($barcodes as $row){ + for($i=0;$i<$duplicate;$i++){ + $printData[]=$row; + } + } + + foreach($printData as $i=>$row){ + + if($i>0 && $i%$labelPerPage==0){ + $pdf->AddPage(); + } + + $pageIndex=$i%$labelPerPage; + + $col=$pageIndex%$column; + $line=floor($pageIndex/$column); + + // $x=$marginLeft+(($labelWidth+$spaceX)*$col); + // $y=$marginTop+(($labelHeight+$spaceY)*$line); + + $x=$col*$labelWidth; + $y=$line*$labelHeight; + + $qrFile=$tempDir.$row->barcode.'.png'; + + if(!file_exists($qrFile)){ + QRcode::png( + $row->barcode, + $qrFile, + QR_ECLEVEL_L, + 4, + 1 + ); + } + + $qrSize=15; + + $pdf->Image( + $qrFile, + $x+16, + $y+4, + $qrSize, + $qrSize + ); + + $textWidth=14; + + $pdf->SetFont('Arial','B',7); + + $pdf->SetXY($x+2,$y+4); + + $pdf->MultiCell( + $textWidth, + 3, + $row->barcode, + 0, + 'L' + ); + + $serial=empty($row->serial_number)?'-':$row->serial_number; + + $pdf->SetFont('Arial','B',4.5); + + $pdf->SetXY($x+2,$y+11); + + $pdf->MultiCell( + $textWidth+2, + 3, + 'SN: ' . $serial, + 0, + 'L' + ); + } + + $pdf->Output( + 'I', + 'Barcode-'.$item_id.'.pdf' + ); + } + + public function save_serial_number() + { + $barcode_id = $this->input->post('barcode_id'); + $serial = trim($this->input->post('serial_number')); + + if($serial==""){ + echo json_encode(array( + 'status'=>false, + 'message'=>'Serial Number wajib diisi' + )); + return; + } + + $cek = $this->db + ->where('serial_number',$serial) + ->count_all_results('item_barcodes'); + if($cek){ + echo json_encode(array( + 'status'=>false, + 'message'=>'Serial Number sudah digunakan' + )); + return; + } + + $barcode = $this->db + ->where('id',$barcode_id) + ->get('item_barcodes') + ->row(); + $this->db->where('id',$barcode_id); + $this->db->update('item_barcodes',array( + 'serial_number'=>$serial + )); + // langsung kirim data terbaru + $_GET['item_id']=$barcode->item_id; + $this->load_data_barcode($barcode->item_id); + } + + public function delete_serial_number() + { + $barcode_id = (int)$this->input->post('barcode_id'); + + if ($barcode_id <= 0) { + echo json_encode([ + 'status' => false, + 'message' => 'Barcode tidak valid.' + ]); + return; + } + + // Ambil data barcode + $barcode = $this->db + ->where('id', $barcode_id) + ->get('item_barcodes') + ->row(); + + if (!$barcode) { + echo json_encode([ + 'status' => false, + 'message' => 'Barcode tidak ditemukan.' + ]); + return; + } + + // Hapus Serial Number + $this->db + ->where('id', $barcode_id) + ->update('item_barcodes', [ + 'serial_number' => NULL + ]); + + // Kirim ulang data terbaru + $this->load_data_barcode($barcode->item_id); + } + +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.barcode',$barcode) + ->get() + ->row(); + + if(!$row){ + echo json_encode([ + 'status'=>false, + 'message'=>'Barcode tidak ditemukan.' + ]); + return; + } + + 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) // ========================= @@ -51,6 +404,7 @@ class Items extends CI_Controller { items.stok, items.status, items.kode_id, + kode.need_serial_number, ( SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ") FROM stock_logs sl @@ -58,6 +412,8 @@ class Items extends CI_Controller { WHERE sl.item_id = items.id ) as gudang '); + + $this->db->join('kode_barang kode', 'items.kode_id = kode.id', 'left'); if ($status !== 'all') { $this->db->where('items.status', $status); @@ -70,72 +426,66 @@ class Items extends CI_Controller { $this->db->from('items'); $data = $this->db->get()->result(); - -// $this->db->select(' -// items.id, -// items.nama_barang, -// items.kode_detail, -// items.harga_beli, -// items.harga_jual, -// items.tanggal_pembelian, -// items.created_at, -// items.stok, -// items.status, -// items.kode_id, -// ( -// SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ") -// FROM stock_logs sl -// LEFT JOIN warehouses w ON sl.warehouse_id = w.id -// WHERE sl.item_id = items.id -// ) as gudang -// '); - -// if ($status !== 'all') { -// $this->db->where('items.status', $status); -// } - -// // 🔥 filter utama per kode_id -// $this->db->where(" -// (items.stok > 0) -// OR -// ( -// items.stok = 0 -// AND items.tanggal_pembelian = ( -// SELECT MAX(i2.tanggal_pembelian) -// FROM items i2 -// WHERE i2.stok = 0 -// AND i2.kode_id = items.kode_id -// ) -// ) -// ", null, false); - -// // urutan -// $this->db->order_by('items.stok = 0', 'ASC', false); -// $this->db->order_by('items.tanggal_pembelian', 'ASC'); - -// $this->db->from('items'); - -// $data = $this->db->get()->result(); - + $result = []; $no = 1; foreach($data as $row){ - $btnDetail = ''; - $btnEdit = ''; - $btnDelete = ''; + $btnDetail = ''; + $btnEdit = ''; + $btnDelete = ''; + $btnInsertSn = 'Barcode'; + $btnPrintBarcode = ''; + + if ($row->need_serial_number == 'true') { + + $list = $this->db + ->where('item_id', $row->id) + ->order_by('id', 'ASC') + ->get('item_barcodes') + ->result(); + + $total = count($list); + + $done = 0; + foreach ($list as $barcode) { + if (!empty($barcode->serial_number)) { + $done++; + } + } + + $total_sn = $total; + $done_sn = $done; + + if ($row->status == 'draft') { + if ($total_sn == $done_sn) { + $btnSN = $btnInsertSn . $btnPrintBarcode; + } else { + $btnSN = $btnInsertSn ; + } + } else { + $btnSN = $btnPrintBarcode; + } + + } else { + + $btnSN = $btnPrintBarcode; + } if($row->status == 'draft'){ $btnPost = ''; - $action = $btnDetail . $btnPost . $btnDelete; + $action = $btnDetail . $btnPost . $btnDelete . $btnSN; + } else { + if($this->session->userdata('role') == 'Admin') { $btnAdjust = ''; } else { $btnAdjust = ''; } - $action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete; + $action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete . $btnSN; + } $result[] = [ @@ -221,6 +571,7 @@ class Items extends CI_Controller { $account_kas = $this->input->post('account_kas'); $kode_id = $this->input->post('kode_id'); $tanggal_beli = $this->input->post('tanggal_beli'); + $tracking_qty = $this->input->post('tracking_qty'); $status = $this->input->post('status'); // ================= VALIDASI ================= @@ -246,6 +597,33 @@ class Items extends CI_Controller { $tanggal_kode = date('dmy', strtotime($tanggal_beli)); $kode_detail = $kode->kode_barang . '-' . $tanggal_kode; + if ($kode->tracking_type != 'UNIT') { + + if (empty($tracking_qty) || $tracking_qty <= 0) { + echo json_encode(array( + 'status' => false, + 'message' => 'Jumlah Per (Roll, Box, Dus) harus diisi' + )); + return; + } + + if ($qty % $tracking_qty != 0) { + echo json_encode(array( + 'status' => false, + 'message' => 'Jumlah Barang harus habis dibagi Jumlah Per (Roll, Box, Dus)' + )); + return; + } + + } + + if ($kode->tracking_type == 'UNIT') { + $unit_qty = $qty; + $tracking_qty = 1; + } else { + $unit_qty = $qty / $tracking_qty; + } + $total = $qty * $harga_beli; $this->db->trans_start(); @@ -274,6 +652,68 @@ class Items extends CI_Controller { 'created_at'=>date('Y-m-d H:i:s') ]); + // ================= Item Barcode Masuk ================= + for ($i = 1; $i <= $unit_qty; $i++) { + + $barcode = sprintf('%s-%04d', $kode_detail, $i); + + $this->db->insert('item_barcodes',[ + 'item_id' => $item_id, + 'barcode' => $barcode, + 'serial_number' => null, + 'warehouse_id' => $warehouse_id, + 'qty_awal' => $tracking_qty, + 'qty_sisa' => $tracking_qty, + 'created_at' => date('Y-m-d H:i:s') + ]); + + $barcode_id = $this->db->insert_id(); + + $this->db->insert('item_movements',[ + 'item_id' => $item_id, + 'barcode_id' => $barcode_id, + 'qty' => $tracking_qty, + 'from_type' => 'supplier', //enum('supplier','warehouse','technician','customer') + 'to_type' => 'warehouse', //enum('supplier','warehouse','technician','customer') + 'movement_type' => 'purchase', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment') + 'notes' => 'Pembelian barang dari supplier', + '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; diff --git a/application/views/invoices/detail.php b/application/views/invoices/detail.php index 2606a9a..0f10541 100644 --- a/application/views/invoices/detail.php +++ b/application/views/invoices/detail.php @@ -89,7 +89,7 @@