session->userdata('company_id'); } private function companyItem($id){ return $this->db->get_where('items',array('id'=>(int)$id,'company_id'=>$this->companyId()))->row(); } public function __construct() { parent::__construct(); if (!$this->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) { if(!$this->companyItem($item_id)) show_404(); $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 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('items.company_id',$this->companyId()) ->group_start()->where('item_barcodes.barcode',$barcode) ->or_where('item_barcodes.serial_number',$barcode)->group_end() ->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->companyItem($item_id); if (!$item) { echo json_encode([ 'status' => false, 'message' => 'Data barang tidak ditemukan.' ]); return; } $next = $this->db ->where('item_id', $item_id) ->where('status', 'pending') ->where('serial_number IS NULL', NULL, false) ->order_by('id', 'ASC') ->get('item_barcodes') ->row(); $start = max(0, (int)$this->input->get('start')); $length = min(100, max(10, (int)($this->input->get('length') ?: 50))); $search = trim((string)$this->input->get('search', true)); $total = (int)$this->db->where('item_id', $item_id)->count_all_results('item_barcodes'); $done = (int)$this->db->where('item_id', $item_id)->where('serial_number IS NOT NULL', NULL, false)->where('serial_number !=', '')->count_all_results('item_barcodes'); $this->db->where('item_id', $item_id); if ($search !== '') { $this->db->group_start()->like('barcode', $search)->or_like('serial_number', $search)->group_end(); } $filtered = (int)$this->db->count_all_results('item_barcodes'); $this->db->where('item_id', $item_id); if ($search !== '') { $this->db->group_start()->like('barcode', $search)->or_like('serial_number', $search)->group_end(); } $list = $this->db ->order_by('id', 'ASC') ->limit($length, $start) ->get('item_barcodes') ->result(); echo json_encode([ 'status' => true, 'item_status' => $item->status, 'item_sale_price' => (float)$item->harga_jual, 'item_purchase_price' => (float)$item->harga_beli, 'next' => $next, 'list' => $list, 'total' => $total, 'done' => $done, 'filtered' => $filtered, 'start' => $start, 'length' => $length, 'percent' => $total > 0 ? round(($done / $total) * 100) : 0 ]); } public function print_barcode($item_id) { if(!$this->companyItem($item_id)) show_404(); require_once APPPATH.'third_party/fpdf/fpdf.php'; require_once APPPATH.'libraries/phpqrcode/qrlib.php'; $printStatus=$this->input->get('status',true)?:'available'; if(!in_array($printStatus,['available','sold_out','all'],true))$printStatus='available'; $this->db->where('item_id',$item_id); if($printStatus==='all')$this->db->where_in('status',['available','sold_out']);else$this->db->where('status',$printStatus); $barcodes=$this->db->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; $safeBarcode=preg_replace('/[^A-Za-z0-9_-]/','_',basename((string)$row->barcode)); $qrFile=$tempDir.$safeBarcode.'.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() { if (strtoupper($this->input->method()) !== 'POST') show_404(); $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->select('item_barcodes.*')->from('item_barcodes')->join('items','items.id=item_barcodes.item_id')->where(array('item_barcodes.id'=>$barcode_id,'items.company_id'=>$this->companyId(),'item_barcodes.status'=>'pending'))->get()->row(); if(!$barcode){echo json_encode(array('status'=>false,'message'=>'Barcode pending tidak ditemukan pada perusahaan aktif.'));return;} $this->db->where('id',$barcode_id); $this->db->update('item_barcodes',array( 'serial_number'=>$serial, 'status'=>'pending' )); // langsung kirim data terbaru $_GET['item_id']=$barcode->item_id; $this->load_data_barcode($barcode->item_id); } public function delete_serial_number() { if (strtoupper($this->input->method()) !== 'POST') show_404(); $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->select('item_barcodes.*')->from('item_barcodes')->join('items','items.id=item_barcodes.item_id')->where(array('item_barcodes.id'=>$barcode_id,'items.company_id'=>$this->companyId(),'item_barcodes.status'=>'pending'))->get()->row(); if (!$barcode) { echo json_encode([ 'status' => false, 'message' => 'Barcode tidak ditemukan.' ]); return; } if ($barcode->status === 'available' || !empty($barcode->activated_at)) { echo json_encode(['status'=>false,'message'=>'Serial unit yang sudah diposting tidak dapat dihapus.']); return; } // Hapus Serial Number $this->db ->where('id', $barcode_id) ->update('item_barcodes', [ 'serial_number' => NULL, 'status' => 'pending' ]); $this->db->where('id',$barcode->item_id)->update('items',['status'=>'draft']); // Kirim ulang data terbaru $this->load_data_barcode($barcode->item_id); } public function post_barcode() { return $this->barcodeError('Posting hanya dapat dilakukan dari tabel Barang Pending.'); $barcodeId = (int)$this->input->post('barcode_id'); $salePrice = (float)$this->input->post('sale_price'); $barcode = $this->db->get_where('item_barcodes', ['id'=>$barcodeId])->row(); if (!$barcode) return $this->barcodeError('Barcode tidak ditemukan.'); if ($barcode->status === 'available') return $this->barcodeError('Unit barcode sudah diposting.'); if (trim((string)$barcode->serial_number) === '') return $this->barcodeError('Serial number wajib diisi sebelum unit diposting.'); if ($salePrice <= 0) return $this->barcodeError('Harga jual satuan wajib lebih dari nol.'); $this->db->trans_start(); $this->db->where('id',$barcodeId)->update('item_barcodes',[ 'sale_price'=>$salePrice, 'status'=>'available', 'activated_at'=>date('Y-m-d H:i:s'), 'activated_by'=>(int)$this->session->userdata('user_id') ]); $this->db->where('id',$barcode->item_id)->update('items',[ 'harga_jual'=>$salePrice, 'status'=>'active' ]); $this->db->trans_complete(); if ($this->db->trans_status() === false) return $this->barcodeError('Unit barcode gagal diposting.'); log_activity('items','posting','Posting unit barcode ID '.$barcodeId.' dengan harga jual '.$salePrice,'success'); $this->load_data_barcode($barcode->item_id); } private function barcodeError($message) { echo json_encode(['status'=>false,'message'=>$message]); } 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; } 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, 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); } // ========================= // GET DATA (FIX) // ========================= public function get_data() { $status = $this->input->get('s') ?? 'all'; $server = (int)$this->input->get('server') === 1; $draw = (int)$this->input->get('draw'); $start = max(0,(int)$this->input->get('start')); $length = min(100,max(10,(int)$this->input->get('length'))); $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, COALESCE((SELECT SUM(CASE WHEN ibs.status IN("available","reserved") THEN GREATEST(ibs.qty_sisa-COALESCE(ibs.reserved_qty,0)-COALESCE(ibs.return_reserved_qty,0),0) ELSE 0 END) FROM item_barcodes ibs WHERE ibs.item_id=items.id),items.stok) operational_available, (SELECT COUNT(*) FROM item_barcodes iba WHERE iba.item_id=items.id AND iba.status IN("available","reserved") AND GREATEST(iba.qty_sisa-COALESCE(iba.reserved_qty,0)-COALESCE(iba.return_reserved_qty,0),0)>0) operational_barcode_count, COALESCE((SELECT SUM(GREATEST(ibp.qty_sisa-COALESCE(ibp.reserved_qty,0)-COALESCE(ibp.return_reserved_qty,0),0)) FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending"),items.stok) pending_available, (SELECT COUNT(*) FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending" AND GREATEST(ibp.qty_sisa-COALESCE(ibp.reserved_qty,0)-COALESCE(ibp.return_reserved_qty,0),0)>0) pending_barcode_count, (SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id) total_barcodes, (SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id AND NULLIF(ibc.serial_number,"") IS NOT NULL) completed_serials, (SELECT COUNT(*) FROM item_barcodes ibc WHERE ibc.item_id=items.id AND ibc.status="available") posted_barcodes, ( 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 ($this->db->field_exists('company_id','items')) { $this->db->where('items.company_id', (int)$this->session->userdata('company_id')); } if ($status === 'draft') { $this->db->where('EXISTS(SELECT 1 FROM goods_receipt_lines grlp WHERE grlp.item_id=items.id)',null,false)->group_start()->where('items.status','draft')->or_where('EXISTS(SELECT 1 FROM item_barcodes ibp WHERE ibp.item_id=items.id AND ibp.status="pending")',null,false)->group_end(); } elseif ($status !== 'all') { $this->db->where('items.status', $status); } if($server){ $search=$this->input->get('search'); $search=is_array($search)?trim((string)($search['value']??'')):''; if($search!=='')$this->db->group_start()->like('items.kode_detail',$search)->or_like('items.nama_barang',$search)->group_end(); } $this->db->from('items'); $filtered=$server?$this->db->count_all_results('',false):0; // urutan custom $this->db->order_by('(items.stok = 0)', 'ASC', false); $this->db->order_by('items.tanggal_pembelian', 'ASC'); if($server)$this->db->limit($length,$start); $data = $this->db->get()->result(); $result = []; $no = $server?$start+1:1; foreach($data as $row){ $btnDetail = ''; $btnEdit = ''; $btnDelete = ''; $btnInsertSn = 'Barcode'; $btnPrintBarcode = ''; $btnPost = ''; if ($row->need_serial_number == 'true') { $btnSN = $btnInsertSn . ((int)$row->posted_barcodes > 0 ? $btnPrintBarcode : ''); $btnPostview = (int)$row->completed_serials > (int)$row->posted_barcodes ? $btnPost : ''; } else { $btnSN = $btnPrintBarcode; $btnPostview = $btnPost; } $isAdmin=$this->session->userdata('role')==='Admin'; $canUpdate=$isAdmin||check_permission('items','can_update'); $canPost=$isAdmin||check_permission('items','can_post'); $canExport=$isAdmin||check_permission('items','can_export'); if($status==='active'){ $action = $btnDetail . ($canExport?$btnPrintBarcode:''); } elseif($status==='draft'){ $action = $btnDetail . ($canPost?$btnPostview:'') . ($canUpdate?$btnSN:''); } else { $action = $btnDetail . ($canExport?$btnPrintBarcode:''); } $barcodeCount = $status === 'active' ? (int)$row->operational_barcode_count : (int)$row->pending_barcode_count; $stockAvailable = $status === 'active' ? (float)$row->operational_available : (float)$row->pending_available; $barcodeStock = ''.number_format($barcodeCount, 0, ',', '.').' / '.number_format($stockAvailable, 0, ',', '.').''; $result[] = [ $no++, $row->tanggal_pembelian, $row->kode_detail, $row->nama_barang, $row->gudang ?? '-', $barcodeStock, number_format($row->harga_beli ?? 0, 2, ',', '.'), $status==='draft'?'':number_format($row->harga_jual??0,2,',','.'), $row->created_at, $action ]; } $payload=array("data"=>$result); if($server){$payload['draw']=$draw;$payload['recordsTotal']=$filtered;$payload['recordsFiltered']=$filtered;} echo json_encode($payload); } // ========================= // DETAIL // ========================= public function detail_simple($id) { echo json_encode( $this->db->get_where('items',['id'=>$id])->row() ); } // ========================= // DETAIL // ========================= public function detail($id) { $item = $this->companyItem($id); if (!$item) { return json_response(false, 'Barang tidak ditemukan.', array(), 404); } $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(); return json_response(true, 'Detail barang berhasil dimuat.', array( 'item' => $item, 'logs' => $logs )); } public function stock_log_pdf($id) { $item=$this->companyItem($id); if(!$item)show_404(); $logs=$this->db->select('sl.*,w.nama gudang')->from('stock_logs sl')->join('warehouses w','w.id=sl.warehouse_id','left')->where('sl.item_id',(int)$id)->order_by('sl.id','ASC')->get()->result(); require_once APPPATH.'third_party/fpdf/fpdf.php'; $pdf=new FPDF('L','mm','A4');$pdf->SetMargins(12,12,12);$pdf->AddPage();$pdf->SetFont('Arial','B',15);$pdf->Cell(0,8,'STOCK LOG BARANG',0,1,'C');$pdf->SetFont('Arial','',9);$pdf->Cell(0,5,$item->kode_detail.' - '.$item->nama_barang,0,1,'C');$pdf->Ln(5); $widths=[12,34,42,20,20,105,35];foreach(['No','Tanggal','Gudang','Qty','Tipe','Keterangan','Referensi']as$i=>$label){$pdf->SetFont('Arial','B',8);$pdf->Cell($widths[$i],7,$label,1,0,'C');}$pdf->Ln();$balance=0;$pdf->SetFont('Arial','',8);foreach($logs as$i=>$log){$signed=$log->tipe==='masuk'?(float)$log->qty:-(float)$log->qty;$balance+=$signed;$pdf->Cell($widths[0],7,$i+1,1,0,'C');$pdf->Cell($widths[1],7,substr($log->created_at,0,19),1);$pdf->Cell($widths[2],7,substr($log->gudang?:'-',0,24),1);$pdf->Cell($widths[3],7,number_format(abs($log->qty),0,',','.'),1,0,'R');$pdf->Cell($widths[4],7,strtoupper($log->tipe),1,0,'C');$pdf->Cell($widths[5],7,substr($log->keterangan?:'-',0,70),1);$pdf->Cell($widths[6],7,substr(($log->ref_type?:'-').' #'.($log->ref_id?:'-'),0,22),1,1);}$pdf->SetFont('Arial','B',9);$pdf->Cell(108,8,'SALDO STOK',1,0,'R');$pdf->Cell(20,8,number_format($balance,0,',','.'),1,0,'R');$pdf->Cell(140,8,'',1,1);$pdf->Output('I','Stock-Log-'.$item->kode_detail.'.pdf'); } public function get_accounts() { $data = $this->db ->where_in('kode_akun', ['1010','1020','2010','1220','1250','1050']) ->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() { return $this->legacyMutationDisabled('Penambahan stok langsung dinonaktifkan. Gunakan Saldo Awal atau Barang Masuk pada Operasional Stok.'); $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 || ($status !== 'draft' && !$account_kas)){ echo json_encode([ 'status'=>false, 'message'=>'Data tidak lengkap / tidak valid', 'account_id' => $account_kas ]); 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; // Barang pending hanya master/rencana pembelian. Belum membentuk stok, // barcode, pergerakan barang, maupun jurnal sampai diterima melalui PO. if ($status === 'draft') { $this->db->insert('items',array('stok'=>0,'planned_qty'=>$qty,'planned_warehouse_id'=>$warehouse_id,'planned_tracking_qty'=>$tracking_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'=>'draft')); $itemId=(int)$this->db->insert_id(); log_activity('items','create','Membuat master barang pending: '.$nama_barang,'success'); echo json_encode(array('status'=>true,'message'=>'Barang pending dibuat. Stok baru aktif setelah penerimaan PO.','item_id'=>$itemId)); return; } $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); $barcode = strtoupper($kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2))); $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') ]); } // ================= 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) 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(); // ================= 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() { return $this->legacyMutationDisabled('Barang aktif tidak dapat diubah melalui form lama. Gunakan master Jenis/Kode Barang atau dokumen persediaan yang sesuai.'); $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) { echo json_encode(array('status'=>false,'message'=>'Barang tidak dapat dihapus. Gunakan status nonaktif pada master bila sudah tidak digunakan.')); 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; } // Master/item yang telah menjadi bagian dokumen pembelian tidak boleh // dihapus. Menghapusnya akan meninggalkan item_id yatim pada baris PO // dan membuat penerimaan gagal saat menulis stock_logs. if ($this->db->table_exists('purchase_order_lines')) { $cekPembelian = $this->db->where('item_id', (int) $id) ->count_all_results('purchase_order_lines'); if ($cekPembelian > 0) { echo json_encode([ 'status'=>false, 'message'=>'Barang tidak dapat dihapus karena sudah digunakan pada Purchase Order. Nonaktifkan barang bila tidak akan digunakan lagi.' ]); return; } } // cek ada transaksi stock $cekStock = $this->db->where('item_id',$id)->count_all_results('stock_logs'); if($cekStock > 0){ 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('item_movements',['item_id'=>$id]); $this->db->delete('item_barcodes',['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() { if (strtoupper($this->input->method()) !== 'POST') show_404(); $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, 'company_id'=>(int)$this->session->userdata('company_id')])->row(); if(!$item){ echo json_encode([ 'status' => false, 'message' => 'Item tidak ditemukan' ]); return; } $catalog = $this->db->get_where('kode_barang',['id'=>$item->kode_id])->row(); $serialRequired=$catalog&&strtolower((string)$catalog->need_serial_number)==='true'; $submittedSalePrice=(float)$this->input->post('harga_jual'); if($submittedSalePrice>0)$item->harga_jual=$submittedSalePrice; if ((float)$item->harga_jual <= 0) { echo json_encode(['status'=>false,'message'=>'Harga jual wajib diisi sebelum barang diposting.']); return; } if ((float)$item->harga_jual < (float)$item->harga_beli) { echo json_encode(['status'=>false,'message'=>'Harga jual tidak boleh lebih rendah dari harga beli Rp '.number_format($item->harga_beli,2,',','.')]); return; } // cegah double posting if(!$serialRequired&&$item->status == 'active'){ echo json_encode([ 'status' => false, 'message' => 'Barang sudah diterima sebelumnya' ]); return; } $this->db->trans_start(); if($serialRequired){ $ready=$this->db->where('item_id',$id)->where('status','pending')->where('serial_number IS NOT NULL',null,false)->where('serial_number !=','')->count_all_results('item_barcodes'); if($ready<=0){$this->db->trans_complete();echo json_encode(['status'=>false,'message'=>'Belum ada serial number yang siap diposting.']);return;} $this->db->where('item_id',$id)->where('status','pending')->where('serial_number IS NOT NULL',null,false)->where('serial_number !=','')->update('item_barcodes',[ 'sale_price'=>$item->harga_jual,'status'=>'available','activated_at'=>date('Y-m-d H:i:s'),'activated_by'=>(int)$this->session->userdata('user_id') ]); $posted=$this->db->affected_rows(); }else{ $this->db->where('item_id',$id)->where('status','pending')->update('item_barcodes',[ 'sale_price'=>$item->harga_jual,'status'=>'available','activated_at'=>date('Y-m-d H:i:s'),'activated_by'=>(int)$this->session->userdata('user_id') ]); $posted=$this->db->affected_rows(); } $notReady=$this->db->where('item_id',$id)->where('status','pending')->count_all_results('item_barcodes'); $this->db->where('id', $id); $this->db->update('items', [ 'status' => 'active', 'harga_jual' => $item->harga_jual ]); $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' => $serialRequired?($posted.' unit berhasil diposting dan '.$notReady.' unit masih belum siap.'):('Barang berhasil diposting. '.$posted.' barcode siap digunakan.'), 'posted_count'=>$posted, 'pending_count'=>$notReady ]); } } // ========================= // KELUAR BARANG (FIX TANPA stok column) // ========================= public function keluarkan() { return $this->legacyMutationDisabled('Pengeluaran langsung dinonaktifkan. Buat dokumen Barang Keluar pada Operasional Stok agar ledger, barcode, dan jurnal tetap konsisten.'); $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.barcode, 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 ]); $this->db->where('barcode', $barcode->barcode)->where('status', 'active')->update('item_technician', ['status' => 'inactive', 'deleted_at' => date('Y-m-d H:i:s')]); // 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'); if(!$barang_id || !$warehouse_id_keluar || $qty_keluar <= 0){ echo json_encode(['status'=>false,'message'=>'Data tidak valid']); return; } $item = $this->db->get_where('items',['id'=>$barang_id])->row(); if(!$item){ echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']); return; } $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; $this->db->trans_start(); $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(); $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(); $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 ]); $this->db->trans_complete(); 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() { return $this->legacyMutationDisabled('Adjustment langsung dinonaktifkan. Gunakan dokumen Penyesuaian dengan workflow approval pada Operasional Stok.'); $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( inventory_ledger.qty * IF(inventory_ledger.direction="in",1,-1) ),0) as stok '); $this->db->where('items.status', 'active'); $this->db->where('items.company_id', $this->companyId()); $this->db->from('items'); $this->db->join('inventory_ledger', 'inventory_ledger.item_id = items.id AND inventory_ledger.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); } public function update_barcode_item() { return $this->legacyMutationDisabled('Pembuatan barcode massal tanpa dokumen sumber dinonaktifkan. Barcode dibuat dari penerimaan atau dokumen persediaan.'); $items = $this->db ->select(" i.id, i.nama_barang, i.stok, i.kode_detail, kb.tracking_type, kb.kode_barang ") ->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) { // =========================================== // Qty per barcode // =========================================== if ($item->tracking_type == 'UNIT') { $qty_per_barcode = 1; } else { $qty_per_barcode = ($item->kode_barang == 'FO1') ? 1000 : 2000; } // =========================================== // Jumlah barcode yang seharusnya // =========================================== $target = (int) ceil($item->stok / $qty_per_barcode); if ($target <= 0) { continue; } // =========================================== // Jumlah barcode yang sudah ada // =========================================== $barcode_exist = $this->db ->where('item_id', $item->id) ->count_all_results('item_barcodes'); if ($barcode_exist >= $target) { continue; } // =========================================== // Total qty barcode yang sudah ada // =========================================== $qty_existing = $this->db ->select_sum('qty_awal') ->where('item_id', $item->id) ->get('item_barcodes') ->row(); $qty_existing = (float) ($qty_existing->qty_awal ?? 0); // Sisa stok yang belum memiliki barcode $qty_remaining = $item->stok - $qty_existing; if ($qty_remaining <= 0) { continue; } $need = $target - $barcode_exist; for ($i = 1; $i <= $need; $i++) { // Barcode terakhir akan berisi sisa stok $qty = min($qty_per_barcode, $qty_remaining); $barcode = strtoupper($item->kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2))); // ============================= // 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' ]); $qty_remaining -= $qty; if ($qty_remaining <= 0) { break; } } } 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.' ]); } private function legacyMutationDisabled($message) { return $this->output->set_status_header(422)->set_content_type('application/json')->set_output(json_encode(array('status'=>false,'message'=>$message))); } }