db ->select('e.id, e.employee_code, e.full_name, p.position_name, d.department_name') ->from('k_employees e') ->join('k_positions p', 'p.id = e.position_id', 'left') ->join('k_departments d', 'd.id = e.department_id', 'left') ->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')') ->where_in('LOWER(p.position_name)', ['teknisi', 'noc']) ->order_by('e.full_name', 'ASC') ->get() ->result(); } public function get_barang_dibawa($user_id = null, $status = 'active') { $this->db ->select(" it.barcode, it.user_id, it.item_id, it.status, it.created_at, ib.serial_number, i.nama_barang, i.kode_detail, ke.full_name as teknisi_name, ke.employee_code ") ->from('item_technician it') ->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left') ->join('items i', 'i.id = it.item_id', 'left') ->join('k_employees ke', 'ke.id = it.user_id', 'left'); if ($user_id) { $this->db->where('it.user_id', $user_id); } if ($status && in_array($status, ['active', 'inactive'])) { $this->db->where('it.status', $status); } $this->db->order_by('it.created_at', 'DESC'); return $this->db->get()->result(); } public function get_item_by_barcode($barcode) { $barcode = trim($barcode); if ($barcode == '') return null; $row = $this->db ->select('ib.id as barcode_id, ib.item_id, ib.barcode, ib.serial_number, ib.qty_sisa, ib.status as barcode_status, i.nama_barang, i.kode_detail') ->from('item_barcodes ib') ->join('items i', 'i.id = ib.item_id', 'left') ->where('ib.barcode', $barcode) ->get() ->row(); if (!$row) { $row = $this->db ->select('ib.id as barcode_id, ib.item_id, ib.barcode, ib.serial_number, ib.qty_sisa, ib.status as barcode_status, i.nama_barang, i.kode_detail') ->from('item_barcodes ib') ->join('items i', 'i.id = ib.item_id', 'left') ->where('ib.serial_number', $barcode) ->get() ->row(); } return $row; } public function get_current_teknisi($barcode) { return $this->db ->select('it.user_id, ke.full_name, ke.employee_code') ->from('item_technician it') ->join('k_employees ke', 'ke.id = it.user_id', 'left') ->where('it.barcode', $barcode) ->where('it.status', 'active') ->get() ->row(); } public function batch_kembalikan(array $barcodes) { $now = date('Y-m-d H:i:s'); $errors = []; $this->db->trans_start(); foreach ($barcodes as $barcode) { $barcode = trim($barcode); if ($barcode == '') continue; $item = $this->db ->select('it.*, ib.id as barcode_id, tec.full_name as teknisi_name') ->from('item_technician it') ->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left') ->join('k_employees tec', 'tec.id = it.user_id', 'left') ->where('it.barcode', $barcode) ->where('it.status', 'active') ->get() ->row(); if (!$item) { $errors[] = "Barcode $barcode tidak ditemukan atau sudah dikembalikan"; continue; } $this->db->insert('item_movements', [ 'item_id' => $item->item_id, 'barcode_id' => $item->barcode_id, 'qty' => 1, 'from_type' => 'technician', 'from_id' => $item->user_id, 'to_type' => 'warehouse', 'to_id' => null, 'movement_type' => 'transfer', 'notes' => 'Pengembalian dari: ' . $item->teknisi_name . ' ke gudang', 'created_at' => $now ]); $this->db ->where('barcode', $barcode) ->where('status', 'active') ->update('item_technician', [ 'status' => 'inactive', 'deleted_at' => $now ]); if ($item->barcode_id) { $this->db ->where('id', $item->barcode_id) ->update('item_barcodes', [ 'status' => 'available' ]); } } $this->db->trans_complete(); if ($this->db->trans_status() === FALSE) { return ['status' => false, 'message' => 'Gagal memproses pengembalian']; } $success_count = count($barcodes) - count($errors); $msg = "$success_count barang berhasil dikembalikan"; if (count($errors) > 0) { $msg .= '. ' . implode(', ', $errors); } return ['status' => true, 'message' => $msg, 'errors' => $errors]; } public function batch_dibawa(array $barcodes, $user_id) { $now = date('Y-m-d H:i:s'); $errors = []; $skipped = 0; $technician = $this->db ->select('id, full_name, employee_code') ->from('k_employees') ->where('id', $user_id) ->get() ->row(); $this->db->trans_start(); foreach ($barcodes as $barcode) { $barcode = trim($barcode); if ($barcode == '') continue; $barcode_row = $this->db ->select('ib.id as barcode_id, ib.item_id, ib.barcode') ->from('item_barcodes ib') ->where('ib.barcode', $barcode) ->where_not_in('ib.status', ['damaged', 'lost', 'returned_supplier', 'sold_out']) ->get() ->row(); if (!$barcode_row) { $errors[] = "Barcode $barcode tidak valid atau tidak tersedia"; continue; } $already = $this->db ->where('barcode', $barcode_row->barcode) ->where('status', 'active') ->get('item_technician') ->row(); if ($already) { if ($already->user_id == $user_id) { $skipped++; continue; } $errors[] = "Barang $barcode sedang dibawa teknisi lain"; continue; } $this->db->insert('item_technician', [ 'user_id' => $user_id, 'barcode' => $barcode_row->barcode, 'item_id' => $barcode_row->item_id, 'status' => 'active', 'created_at' => $now ]); $last_movement = $this->db ->where('barcode_id', $barcode_row->barcode_id) ->order_by('id', 'DESC') ->limit(1) ->get('item_movements') ->row(); $from_type = 'warehouse'; $from_id = null; if ($last_movement) { $from_type = $last_movement->to_type; $from_id = $last_movement->to_id; } $this->db->insert('item_movements', [ 'item_id' => $barcode_row->item_id, 'barcode_id' => $barcode_row->barcode_id, 'qty' => 1, 'from_type' => $from_type, 'from_id' => $from_id, 'to_type' => 'technician', 'to_id' => $user_id, 'movement_type' => 'transfer', 'notes' => 'Dibawa oleh: ' . $technician->full_name . ' dari gudang', 'created_at' => $now ]); $this->db ->where('id', $barcode_row->barcode_id) ->update('item_barcodes', [ 'status' => 'installed' ]); } $this->db->trans_complete(); if ($this->db->trans_status() === FALSE) { return ['status' => false, 'message' => 'Gagal memproses penugasan barang']; } $success_count = count($barcodes) - count($errors) - $skipped; $msg = "$success_count barang berhasil ditugaskan"; if ($skipped > 0) { $msg .= ". $skipped barang sudah ditugaskan ke teknisi yang sama"; } if (count($errors) > 0) { $msg .= '. ' . implode(', ', $errors); } return ['status' => true, 'message' => $msg, 'errors' => $errors]; } public function get_teknisi_table_data($start, $length, $search, $order_col, $order_dir) { $sort_cols = [ 1 => 'e.full_name', 2 => 'e.employee_code', 3 => 'd.department_name', 4 => 'item_count' ]; $this->db ->select(" e.id, e.full_name, e.employee_code, p.position_name, d.department_name, (SELECT COUNT(*) FROM item_technician it WHERE it.user_id = e.id AND it.status = 'active') as item_count ") ->from('k_employees e') ->join('k_positions p', 'p.id = e.position_id', 'left') ->join('k_departments d', 'd.id = e.department_id', 'left') ->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')') ->where_in('LOWER(p.position_name)', ['teknisi', 'noc']); if (!empty($search)) { $this->db->group_start() ->like('LOWER(e.full_name)', strtolower($search)) ->or_like('LOWER(e.employee_code)', strtolower($search)) ->or_like('LOWER(d.department_name)', strtolower($search)) ->group_end(); } if (isset($sort_cols[$order_col])) { $this->db->order_by($sort_cols[$order_col], $order_dir); } else { $this->db->order_by('e.full_name', 'ASC'); } if ($length > 0) { $this->db->limit($length, $start); } return $this->db->get()->result(); } public function count_all_teknisi() { $this->db ->from('k_employees e') ->join('k_positions p', 'p.id = e.position_id', 'left') ->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')') ->where_in('LOWER(p.position_name)', ['teknisi', 'noc']); return $this->db->count_all_results(); } public function count_filtered_teknisi($search) { $this->db ->from('k_employees e') ->join('k_positions p', 'p.id = e.position_id', 'left') ->join('k_departments d', 'd.id = e.department_id', 'left') ->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')') ->where_in('LOWER(p.position_name)', ['teknisi', 'noc']); if (!empty($search)) { $this->db->group_start() ->like('LOWER(e.full_name)', strtolower($search)) ->or_like('LOWER(e.employee_code)', strtolower($search)) ->or_like('LOWER(d.department_name)', strtolower($search)) ->group_end(); } return $this->db->count_all_results(); } public function get_teknisi_info($user_id) { return $this->db ->select('e.id, e.full_name, e.employee_code, p.position_name, d.department_name') ->from('k_employees e') ->join('k_positions p', 'p.id = e.position_id', 'left') ->join('k_departments d', 'd.id = e.department_id', 'left') ->where('e.id', $user_id) ->get() ->row(); } public function get_teknisi_items_data($user_id, $start, $length, $search, $order_col, $order_dir) { $sort_cols = [ 1 => 'it.barcode', 2 => 'i.nama_barang', 3 => 'ib.serial_number', 4 => 'it.status' ]; $this->db ->select(" it.barcode, i.nama_barang, ib.serial_number, ib.qty_sisa, it.status, it.created_at ") ->from('item_technician it') ->join('items i', 'i.id = it.item_id', 'left') ->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left') ->where('it.user_id', $user_id) ->where('it.status', 'active'); if (!empty($search)) { $this->db->group_start() ->like('it.barcode', $search) ->or_like('LOWER(i.nama_barang)', strtolower($search)) ->or_like('ib.serial_number', $search) ->group_end(); } if (isset($sort_cols[$order_col])) { $this->db->order_by($sort_cols[$order_col], $order_dir); } else { $this->db->order_by('it.created_at', 'DESC'); } if ($length > 0) { $this->db->limit($length, $start); } return $this->db->get()->result(); } public function count_all_teknisi_items($user_id) { $this->db ->from('item_technician it') ->where('it.user_id', $user_id) ->where('it.status', 'active'); return $this->db->count_all_results(); } public function count_filtered_teknisi_items($user_id, $search) { $this->db ->from('item_technician it') ->join('items i', 'i.id = it.item_id', 'left') ->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left') ->where('it.user_id', $user_id) ->where('it.status', 'active'); if (!empty($search)) { $this->db->group_start() ->like('it.barcode', $search) ->or_like('LOWER(i.nama_barang)', strtolower($search)) ->or_like('ib.serial_number', $search) ->group_end(); } return $this->db->count_all_results(); } public function get_transfer_data() { return $this->db ->select(" it.barcode, i.nama_barang, ib.serial_number, from_ke.full_name as from_name, from_ke.employee_code as from_code, to_ke.full_name as to_name, to_ke.employee_code as to_code, im.created_at, im.movement_type ") ->from('item_movements im') ->join('items i', 'i.id = im.item_id', 'left') ->join('item_barcodes ib', 'ib.id = im.barcode_id', 'left') ->join('k_employees from_ke', 'from_ke.id = im.from_id', 'left') ->join('k_employees to_ke', 'to_ke.id = im.to_id', 'left') ->where('im.movement_type', 'installation') ->where('im.from_type', 'technician') ->where('im.to_type', 'technician') ->order_by('im.created_at', 'DESC') ->limit(200) ->get() ->result(); } public function detail_by_barcode($barcode) { return $this->db ->select(" it.*, ib.serial_number, ib.qty_sisa, ib.status as barcode_status, i.nama_barang, i.kode_detail, ke.full_name as teknisi_name, ke.employee_code ") ->from('item_technician it') ->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left') ->join('items i', 'i.id = it.item_id', 'left') ->join('k_employees ke', 'ke.id = it.user_id', 'left') ->where('it.barcode', $barcode) ->get() ->row(); } }