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'); } // ========================= // 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, ( 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); } // 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(); // $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 = ''; if($row->status == 'draft'){ $btnPost = ''; $action = $btnDetail . $btnPost . $btnDelete; } else { if($this->session->userdata('role') == 'Admin') { $btnAdjust = ''; } else { $btnAdjust = ''; } $action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete; } $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'); $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; $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') ]); // ================= 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); } }