session->userdata('logged_in')) { redirect('auth'); } } public function index() { $data = ["active_menu" => "items"]; $this->load->view('partials/header', $data); $this->load->view('items/layout'); $this->load->view('partials/footer'); } public function draft_items() { $data = ["active_menu" => "draft_items"]; $this->load->view('partials/header', $data); $this->load->view('items/draft_items'); $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) // ========================= public function get_data() { $status = $this->input->get('s') ?? 'all'; $allowed = ['draft','active','all']; if (!in_array($status, $allowed)) { $status = 'all'; } $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, kode.need_serial_number, ( 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 '); $this->db->join('kode_barang kode', 'items.kode_id = kode.id', 'left'); if ($status !== 'all') { $this->db->where('items.status', $status); } // urutan custom $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 = ''; $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 . $btnSN; } else { if($this->session->userdata('role') == 'Admin') { $btnAdjust = ''; } else { $btnAdjust = ''; } $action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete . $btnSN; } $result[] = [ $no++, $row->tanggal_pembelian, $row->kode_detail, $row->nama_barang, $row->gudang ?? '-', $row->stok, number_format($row->harga_beli ?? 0, 2), number_format($row->harga_jual ?? 0, 2), $row->created_at, $action ]; } echo json_encode(["data"=>$result]); } // ========================= // DETAIL // ========================= public function detail_simple($id) { echo json_encode( $this->db->get_where('items',['id'=>$id])->row() ); } // ========================= // DETAIL // ========================= public function detail($id) { $item = $this->db->get_where('items',['id'=>$id])->row(); $logs = $this->db ->select('stock_logs.*, warehouses.nama as gudang') ->join('warehouses','warehouses.id = stock_logs.warehouse_id','left') ->where('item_id',$id) ->order_by('stock_logs.id','DESC') ->get('stock_logs') ->result(); echo json_encode([ 'item'=>$item, 'logs'=>$logs ]); } public function get_accounts() { $data = $this->db ->where_in('kode_akun', [101,102,201]) ->order_by('kode_akun','ASC') ->get('accounts') ->result(); echo json_encode($data); } public function get_accounts_biaya() { $data = $this->db ->where(['tipe' => 'expense', 'posisi'=> 'debit', 'kategori'=> 'laba_rugi']) ->order_by('kode_akun','ASC') ->get('accounts') ->result(); echo json_encode($data); } // ========================= // SAVE (FIX TOTAL) // ========================= public function save() { $nama_barang = $this->input->post('nama_barang'); $qty = (int)$this->input->post('qty'); $harga_beli = (float)$this->input->post('harga_beli'); $harga_jual = (float)$this->input->post('harga_jual'); $warehouse_id = $this->input->post('warehouse_id'); $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 ================= if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){ echo json_encode([ 'status'=>false, 'message'=>'Data tidak lengkap / tidak valid' ]); return; } // ================= VALIDASI HARGA ================= if($harga_beli > $harga_jual){ echo json_encode([ 'status'=>false, 'message'=>'Harga beli tidak boleh lebih besar dari harga jual' ]); return; } $kode = $this->db->get_where('kode_barang',['id'=>$kode_id])->row(); $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(); // ================= INSERT ITEM ================= $this->db->insert('items',[ 'stok' =>$qty, 'kode_detail' =>$kode_detail, 'kode_id' =>$kode_id, 'nama_barang' =>$nama_barang, 'harga_beli' =>$harga_beli, 'harga_jual' =>$harga_jual, 'tanggal_pembelian' => $tanggal_beli, 'status' => $status ]); $item_id = $this->db->insert_id(); // ================= STOCK MASUK ================= $this->db->insert('stock_logs',[ 'item_id'=>$item_id, 'warehouse_id'=>$warehouse_id, 'qty'=>$qty, 'tipe'=>'masuk', 'keterangan'=>'Pembelian awal', '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; $this->db->insert('journals',[ 'tanggal'=>$tanggal_beli, 'no_ref'=>$no_ref, 'ref_type'=> 'new_items', 'ref_id'=>$item_id, 'keterangan'=>'Pembelian '.$nama_barang, 'created_by' => $this->session->userdata('user_id') ]); $jid = $this->db->insert_id(); // 🔥 Debit Persediaan $this->db->insert('journal_details',[ 'journal_id'=>$jid, 'account_id'=>21, // persediaan 'debit'=>$total, 'kredit'=>0 ]); // 🔥 Kredit Kas (DINAMIS) $this->db->insert('journal_details',[ 'journal_id'=>$jid, 'account_id'=>$account_kas, 'debit'=>0, 'kredit'=>$total ]); $this->db->trans_complete(); // ================= CEK TRANSAKSI ================= if ($this->db->trans_status() === FALSE){ echo json_encode([ 'status'=>false, 'message'=>'Gagal simpan data' ]); } else { log_activity( 'items', 'create', 'Menambahkan item: ' . $nama_barang, 'success' ); echo json_encode([ 'status'=>true, 'message'=>'Berhasil tambah item' ]); } } // ========================= // UPDATE (FIX AKUN) // ========================= public function update() { $id = $this->input->post('id'); $nama_barang = $this->input->post('nama_barang'); $harga_baru = (float)$this->input->post('harga_beli'); $harga_jual = (float)$this->input->post('harga_jual'); $item = $this->db->get_where('items',['id'=>$id])->row(); if(!$item){ echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']); return; } $this->db->trans_start(); $this->db->where('id',$id)->update('items',[ 'nama_barang'=>$nama_barang, 'harga_beli'=>$harga_baru, 'harga_jual'=>$harga_jual ]); if($item->harga_beli != $harga_baru){ $stok = $this->db->select('SUM(qty * IF(tipe="masuk",1,-1)) as stok') ->where('item_id',$id) ->get('stock_logs') ->row()->stok ?? 0; $selisih = ($harga_baru - $item->harga_beli) * $stok; if($selisih != 0){ $this->db->insert('journals',[ 'tanggal'=>date('Y-m-d'), 'no_ref'=>'ADJ-'.date('YmdHis').'-'.$id, 'keterangan'=>'Penyesuaian harga '.$item->nama_barang, 'created_by' => $this->session->userdata('user_id') ]); $jid = $this->db->insert_id(); if($selisih > 0){ // 🔥 naik → pendapatan $this->db->insert_batch('journal_details',[ [ 'journal_id'=>$jid, 'account_id'=>21, 'debit'=>$selisih, 'kredit'=>0 ], [ 'journal_id'=>$jid, 'account_id'=>65, // ✅ BENAR 'debit'=>0, 'kredit'=>$selisih ] ]); }else{ // 🔥 turun → beban $this->db->insert_batch('journal_details',[ [ 'journal_id'=>$jid, 'account_id'=>64, // ✅ BENAR 'debit'=>abs($selisih), 'kredit'=>0 ], [ 'journal_id'=>$jid, 'account_id'=>21, 'debit'=>0, 'kredit'=>abs($selisih) ] ]); } } } $this->db->trans_complete(); log_activity( 'items', 'update', 'Update item: ' . $nama_barang, 'success' ); $this->update_item_stok($id); echo json_encode(['status'=>true,'message'=>'Berhasil update + adjustment']); } public function delete($id) { // cek item ada $item = $this->db->get_where('items',['id'=>$id])->row(); if(!$item){ echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']); return; } // cek ada transaksi stock $cekStock = $this->db->where('item_id',$id)->count_all_results('stock_logs'); if($cekStock > 1){ echo json_encode([ 'status'=>false, 'message'=>'Tidak bisa dihapus, sudah ada transaksi stok' ]); return; } $this->db->trans_start(); // hapus item $this->db->delete('stock_logs',['item_id'=>$id]); $this->db->delete('items',['id'=>$id]); $j = $this->db->get_where('journals', [ 'ref_type'=>'new_items', 'ref_id'=>$id ])->result(); foreach($j as $row){ $this->db->delete('journal_details', ['journal_id'=>$row->id]); $this->db->delete('journals', ['id'=>$row->id]); } $this->db->trans_complete(); if ($this->db->trans_status() === FALSE){ echo json_encode(['status'=>false,'message'=>'Gagal hapus']); } else { log_activity( 'items', 'delete', 'Hapus item ID: ' . $id, 'success' ); echo json_encode(['status'=>true,'message'=>'Berhasil dihapus']); } } public function posting() { $id = $this->input->post('id'); if(!$id){ echo json_encode([ 'status' => false, 'message' => 'ID tidak valid' ]); return; } // cek item ada $item = $this->db->get_where('items', ['id' => $id])->row(); if(!$item){ echo json_encode([ 'status' => false, 'message' => 'Item tidak ditemukan' ]); return; } // cegah double posting if($item->status == 'active'){ echo json_encode([ 'status' => false, 'message' => 'Barang sudah diterima sebelumnya' ]); return; } $this->db->trans_start(); // update status saja $this->db->where('id', $id); $this->db->update('items', [ 'status' => 'active' ]); $this->db->trans_complete(); if ($this->db->trans_status() === FALSE){ echo json_encode([ 'status' => false, 'message' => 'Gagal memproses data' ]); } else { // log aktivitas log_activity( 'items', 'posting', 'Barang diterima | ID: '.$id.' | Nama: '.$item->nama_barang, 'success' ); echo json_encode([ 'status' => true, 'message' => 'Barang sudah diterima dan stok siap digunakan' ]); } } // ========================= // KELUAR BARANG (FIX TANPA stok column) // ========================= public function keluarkan() { $account_biaya = (int)$this->input->post('account_biaya'); $tanggal_keluar = $this->input->post('tanggal_keluar'); $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 ') ->where('item_id',$barang_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, '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').'-'.$barang_id, 'keterangan'=>'Pengeluaran Barang '.$item->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(); // 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']); } public function list() { echo json_encode( $this->db->get('warehouses')->result() ); } public function adjust() { $item_id = $this->input->post('item_id'); $qty = (int)$this->input->post('qty'); $warehouse_id = $this->input->post('warehouse_id'); $ket = $this->input->post('keterangan'); if($qty == 0){ echo json_encode(['status'=>false,'message'=>'Qty tidak boleh 0']); return; } $item = $this->db->get_where('items',['id'=>$item_id])->row(); $nilai = abs($qty) * $item->harga_beli; $this->db->trans_start(); // 🔥 stock log $this->db->insert('stock_logs',[ 'item_id'=>$item_id, 'warehouse_id'=>$warehouse_id, 'qty'=>abs($qty), 'tipe'=> $qty > 0 ? 'masuk' : 'keluar', 'keterangan'=>'Adjustment: '.$ket ]); // 🔥 jurnal $this->db->insert('journals',[ 'tanggal'=>date('Y-m-d'), 'no_ref'=>'ADJ-'.date('Ymd').'-'.$item_id, 'keterangan'=>'Penyesuaian '.$item->nama_barang, 'created_by' => $this->session->userdata('user_id') ]); $jid = $this->db->insert_id(); if($qty > 0){ // stok naik $this->db->insert_batch('journal_details',[ [ 'journal_id'=>$jid, 'account_id'=>21, // persediaan 'debit'=>$nilai, 'kredit'=>0 ], [ 'journal_id'=>$jid, 'account_id'=>65, // modal / selisih 'debit'=>0, 'kredit'=>$nilai ] ]); }else{ // stok turun $this->db->insert_batch('journal_details',[ [ 'journal_id'=>$jid, 'account_id'=>64, 'debit'=>$nilai, 'kredit'=>0 ], [ 'journal_id'=>$jid, 'account_id'=>21, 'debit'=>0, 'kredit'=>$nilai ] ]); } $this->db->trans_complete(); log_activity( 'items', 'adjust', 'Adjustment item ID: ' . $item_id . ' qty: ' . $qty, 'success' ); $this->update_item_stok($item_id); echo json_encode(['status'=>true,'message'=>'Adjustment berhasil']); } private function update_item_stok($item_id) { $stok = $this->db->query(" SELECT COALESCE(SUM(qty * IF(tipe='masuk',1,-1)),0) as stok FROM stock_logs WHERE item_id = ? ", [$item_id])->row()->stok; $this->db->where('id', $item_id) ->update('items', [ 'stok' => $stok ]); } public function get_items_by_wh_id($warehouse_id) { $this->db->select(' items.id, items.nama_barang, items.harga_jual, items.kode_detail, COALESCE(SUM( stock_logs.qty * IF(stock_logs.tipe="masuk",1,-1) ),0) as stok '); $this->db->where('status', 'active'); $this->db->from('items'); $this->db->join('stock_logs', 'stock_logs.item_id = items.id AND stock_logs.warehouse_id = '.$this->db->escape($warehouse_id), 'left'); $this->db->group_by('items.id'); // ✅ INI KUNCINYA $this->db->having('stok >', 0); $data = $this->db->get()->result(); echo json_encode($data); } }