isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $date = isset($prm["date"]) ? $prm["date"] : ""; if ($date == "" || $date == null) { $this->sys_error("Invalid Date Use"); exit; } $warehouseID = isset($prm["warehouseID"]) ? $prm["warehouseID"] : ""; if ($warehouseID == "" || $warehouseID == null) { $this->sys_error("Warehouse masih kosong"); exit; } $note = ""; if (isset($prm["note"])) { $note = trim($prm["note"]); } $divisionID = isset($prm["divisionID"]) ? $prm["divisionID"] : ""; if ($divisionID == "" || $divisionID == null) { $this->sys_error("Division masih kosong"); exit; } $items = isset($prm["items"]) ? $prm["items"] : []; if (count($items) == 0) { $this->sys_error("Items cannot be empty"); exit; } $sqlnum = "SELECT `fn_numbering`('IO') as IOnumber"; $qrynum = $this->db->query($sqlnum, []); $IOnumber = $qrynum->row()->IOnumber; $sqlIO = "INSERT INTO t_item_out ( T_ItemOutDate, T_ItemOutNumber, T_ItemOutWarehouseID, T_ItemOutNote, T_ItemOutDivisionID, T_ItemOutStatus, T_ItemOutIsActive, T_ItemOutUserID, T_ItemOutCreated, T_ItemOutLastUpdated) VALUES(?,?,?,?,?,'Draft','Y',?,NOW(),NOW())"; $qryIO = $this->db->query($sqlIO, [ $date, $IOnumber, $warehouseID, $note, $divisionID, $userId ]); if (!$qryIO) { $this->db->trans_rollback(); $this->sys_error_db("Failed to insert data table item out", $this->db); } $ioId = $this->db->insert_id(); // Kelompokkan item berdasarkan rioID $grouped = []; foreach ($items as $item) { $grouped[$item['rioID']][] = $item; } foreach ($grouped as $rioID => $itemGroup) { $statusHeader = 'Completed'; foreach ($itemGroup as $item) { $receiveQty = intval($item['rioDReceiveQty']); // encode json list batch number $jsonBatchNo = "[]"; if (isset($item['ItemRequest']) && is_array($item['ItemRequest'])) { $batches = $item['ItemRequest']; if (sizeof($batches) > 0) { $jsonResult = json_encode($batches); if ($jsonResult === false) { throw new Exception("[Error] Failed to encode json batch number"); } else { $jsonBatchNo = $jsonResult; } } } // transaksi insert item out detail $sql = "INSERT INTO t_item_out_detail( T_ItemOutDetailT_ItemOutID, T_ItemOutDetailRequestItemOutID, T_ItemOutDetailRequestItemOutDetailID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailItemBatchNo, T_ItemOutDetailQty, T_ItemOutDetailCreated, T_ItemOutDetailUserID) VALUES(?, ?, ?, ?, ?, ?, ?, NOW(), ?)"; $qry = $this->db->query($sql, [ $ioId, $item["rioID"], $item["rioDID"], $item["itemID"], $item["unitID"], $jsonBatchNo, $receiveQty, $userId ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("failed to insert data table item out detail", $this->db); exit; } $isPartial = intval($receiveQty) < intval($item['rioDQty']); $detailStatus = $isPartial ? 'Partial' : 'Completed'; if ($isPartial) { $statusHeader = 'Partial'; // Jika ada satu saja partial, status header ikut partial } // Update request item out detail $sqlDetail = "UPDATE request_item_out_detail SET RequestItemOutDetailStatus = ?, RequestItemOutDetailLastUpdated = NOW(), RequestItemOutDetailUserID = ? WHERE RequestItemOutDetailID = ?"; $queryDetail = $this->db->query($sqlDetail, [ $detailStatus, $userId, $item['rioDID'] ]); if (!$queryDetail) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update detail RIO", $this->db); exit; } } $sqlHeader = "UPDATE request_item_out SET RequestItemOutStatus = ?, RequestItemOutUserID = ?, RequestItemOutLastUpdated = NOW() WHERE RequestItemOutID = ?"; $queryHeader = $this->db->query($sqlHeader, [ $statusHeader, $userId, $rioID ]); if (!$queryHeader) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update header RIO", $this->db); exit; } $userdesc = "Transaksi item out dengan kode " . $IOnumber . " telah di buat"; $this->insertUserActivityIo($ioId, $userId, "DRAFT", $userdesc); // Insert user activity (log) $this->insertUserActivityRio($rioID, $userId, $statusHeader); } // transaksi pengurangan stockqty foreach ($items as $key => $item) { // array stock request per batch foreach ($item["ItemRequest"] as $k => $v) { // update stock $stockupd = "UPDATE stock SET StockQty = StockQty - ? WHERE StockID = ? AND StockWarehouseID = ?"; $stockupd = $this->db->query($stockupd, [ intval($v["QtyReq"]), $v["StockID"], $v["WarehouseID"] ]); if (!$stockupd) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update stock qty item out", $this->db); exit; } $receiveQty = intval($v['QtyReq']); $currentQty = intval($v['StockQty']); $remainingQty = max(0, $currentQty - $receiveQty); // insert stockcard $sqlcard = "INSERT INTO stockcard ( StockCardWarehouseID, StockCardDatetime, StockCardItemID, StockCardItemUnitID, StockCardBatchNo, StockCardED, StockCardReffID, StockCardStatus, StockCardBefore, StockCardIn, StockCardOut, StockCardAfter, StockCardUserID ) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'IO', ?, 0, ?, ?, ?)"; $qrycard = $this->db->query($sqlcard, array( $v["WarehouseID"], $v["StockItemID"], $v["StockItemUnitID"], $v["StockBatchNo"], $v["StockED"], $v["StockID"], $currentQty, $receiveQty, $remainingQty, $userId )); if (!$qrycard) { $this->db->trans_rollback(); $this->sys_error_db("Error insert stock card", $this->db); exit; } // insert into stocklog $sqllog = "INSERT INTO stocklog ( StockLogWarehouseID, StockLogDateTime, StockLogItemID, StockLogItemUnitID, StockLogStockNumber, StockLogBatchNo, StockLogED, StockLogReffID, StockLogStatus, StockLogQty, StockLogUserID ) VALUES (?,NOW(),?,?,?,?,?,?,?,?,?)"; $qrylog = $this->db->query($sqllog, [ $v["WarehouseID"], $v["StockItemID"], $v["StockItemUnitID"], $v["StockStockNumber"], $v["StockBatchNo"], $v["StockED"], $v["StockID"], 'IO', $receiveQty, $userId, ]); if (!$qrylog) { $this->db->trans_rollback(); $this->sys_error_db("Error insert stocklog", $this->db); exit; } } } $this->db->trans_commit(); $result = array( "total" => 1, "affected_rows" => $this->db->affected_rows() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getItemOuts() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $ioID = isset($prm["ioID"]) ? $prm["ioID"] : ""; if ($ioID == "" || $ioID == null) { $this->sys_error("Invalid item out ID"); exit; } $sql = "SELECT T_ItemOutDetailID, T_ItemOutDetailT_ItemOutID, T_ItemOutDetailRequestItemOutID, T_ItemOutDetailRequestItemOutDetailID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailItemBatchNo, T_ItemOutDetailQty, T_ItemOutID, T_ItemOutDate, T_ItemOutNumber, T_ItemOutNote, RequestItemOutDetailID, RequestItemOutDetailReceiveQty, RequestItemOutDetailQty, IFNULL(RequestItemOutDetailQty, 0) - IFNULL(T_ItemOutDetailQty, 0) as remainingRequestQty, M_ItemID, M_ItemDesc, ItemUnitID, ItemUnitCode, ItemUnitName, CASE WHEN WarehouseType = 'B' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) WHEN WarehouseType = 'R' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) ELSE '' END WarehouseName, WarehouseID, StockID, StockWarehouseID, StockWarehouseAlmariID, StockWarehouseRackID, StockStockNumber, StockItemID, StockItemUnitID, StockItemPrice, StockBatchNo, StockED, StockLastUpdated, StockUserID, SUM(stock.StockQty) AS StockQty FROM t_item_out_detail JOIN t_item_out ON T_ItemOutDetailT_ItemOutID = T_ItemOutID AND T_ItemOutIsActive = 'Y' JOIN warehouse ON T_ItemOutWarehouseID = WarehouseID JOIN request_item_out_detail ON T_ItemOutDetailRequestItemOutDetailID = RequestItemOutDetailID AND RequestItemOutDetailIsActive = 'Y' JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN itemunit ON T_ItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' JOIN stock ON StockItemID = T_ItemOutDetailM_ItemID AND StockItemUnitID = T_ItemOutDetailItemUnitID AND StockWarehouseID = T_ItemOutWarehouseID JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID AND S_RegionalIsActive = 'Y' LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID AND M_BranchIsActive = 'Y' WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? GROUP BY T_ItemOutDetailID ORDER BY T_ItemOutDetailID DESC"; $qry = $this->db->query($sql, [$ioID]); if (!$qry) { $this->sys_error_db("ERROR, get detail item out data", $this->db); exit; } $rows = $qry->result_array(); foreach ($rows as $key => $value) { $jsonStr = $value['T_ItemOutDetailItemBatchNo']; $batches = []; if (!empty($jsonStr)) { $decode = json_decode($jsonStr); if ($decode !== null && json_last_error() === JSON_ERROR_NONE) { $batches = $decode; } else { $this->sys_error_db("[Error] decode json for batches number"); exit; } } $rows[$key]['ItemRequest'] = $batches; unset($rows[$key]['T_ItemOutDetailItemBatchNo']); } $result = array( "records" => $rows ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function confirmIO() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $ioID = isset($prm["ioID"]) ? $prm["ioID"] : ""; if ($ioID == "" || $ioID == null) { $this->sys_error("Item out ID not found"); exit; } $sql_update = "UPDATE t_item_out SET T_ItemOutIsConfirmDate = NOW(), T_ItemOutIsConfirm = 'Y', T_ItemOutUserID = ?, T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qry_update = $this->db->query($sql_update, [$userId, $ioID]); if (!$qry_update) { $this->db->trans_rollback(); $this->sys_error_db("Gagal konfirmasi", $this->db); exit; } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$ioID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di konfirmasi"; $this->insertUserActivityIo($ioID, $userId, "CONFIRM", $userdesc); $this->db->trans_commit(); $result = array( "total" => 1, "affected_rows" => $this->db->affected_rows() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function approveIO() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $ioID = isset($prm["ioID"]) ? $prm["ioID"] : ""; if ($ioID == "" || $ioID == null) { $this->sys_error("Item out ID not found"); exit; } $sqlApp = "UPDATE t_item_out SET T_ItemOutApproveDate = NOW(), T_ItemOutApproveID = ? WHERE T_ItemOutID = ?"; $qryApp = $this->db->query($sqlApp, [ $userId, $ioID ]); if (!$qryApp) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update approve failed", $this->db); exit; } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$ioID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di approve"; $this->insertUserActivityIo($ioID, $userId, "Approve", $userdesc); $this->db->trans_commit(); $result = array( "total" => 1, "affected_rows" => $this->db->affected_rows() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function processIO() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $ioID = isset($prm["ioID"]) ? $prm["ioID"] : ""; if ($ioID == "" || $ioID == null) { $this->sys_error("Item out ID not found"); exit; } $items = isset($prm["items"]) ? $prm["items"] : []; if (count($items) == 0) { $this->sys_error("Items cannot be empty"); exit; } $sqlApp = "UPDATE t_item_out SET T_ItemOutStatus = 'Process', T_ItemOutReceiveUserID = ?, T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qryApp = $this->db->query($sqlApp, [ $userId, $ioID ]); if (!$qryApp) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update Process failed", $this->db); exit; } foreach ($items as $key => $item) { $sqlQty = "UPDATE request_item_out_detail SET RequestItemOutDetailQty = RequestItemOutDetailQty - ?, RequestItemOutDetailLastUpdated = NOW(), RequestItemOutDetailUserID = ? WHERE RequestItemOutDetailID = ?"; $qryQty = $this->db->query($sqlQty, [ intval($item["T_ItemOutDetailQty"]), $userId, $item["T_ItemOutDetailRequestItemOutDetailID"] ]); if (!$qryQty) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update qty request failed", $this->db); exit; } } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$ioID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di approve"; $this->insertUserActivityIo($ioID, $userId, "Process", $userdesc); // generate jurnal sudah tidak dipakai 22-08-2025 // $this->generateJurnal($ioID); // Insert item usage $this->insertUsage($ioID, $userId); $this->db->trans_commit(); $result = array( "total" => 1, "affected_rows" => $this->db->affected_rows() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } function insertUserActivityIo($ioID, $userId, $status, $userdesc) { try { // insert log $sql_json = "SELECT t_item_out.*, '' as detail FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry_json = $this->db->query($sql_json, [$ioID]); if (!$qry_json) { $this->db->trans_rollback(); $this->sys_error_db("select json io error"); exit; } $row_json = $qry_json->row_array(); $sql_json_detail = "SELECT t_item_out_detail.* FROM t_item_out_detail WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ?"; $qry_json_detail = $this->db->query($sql_json_detail, [$ioID]); if ($qry_json_detail) { $row_json["detail"] = $qry_json_detail->result_array(); } else { $this->sys_error_db("select detail io error", $this->db); exit; } $data_json = $row_json; $sql_log = "INSERT INTO user_activity( UserActivityCode, UserActivityStatus, UserActivityDescription, UserActivityRefID, UserActivityData, UserActivityUserID, UserActivityCreated ) VALUES('IO',?,?,?,?,?,NOW())"; $qry_log = $this->db->query($sql_log, [ $status, $userdesc, $ioID, json_encode($data_json), $userId ]); if (!$qry_log) { $this->db->trans_rollback(); $this->sys_error_db("insert user activity error", $this->db); exit; } } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } function insertUserActivityRio($rioID, $userId, $status) { try { // insert log $sql_json = "SELECT request_item_out.*, '' as detail FROM request_item_out WHERE RequestItemOutIsActive = 'Y' AND RequestItemOutID = ?"; $qry_json = $this->db->query($sql_json, [$rioID]); if (!$qry_json) { $this->db->trans_rollback(); $this->sys_error_db("select json rio error"); exit; } $row_json = $qry_json->row_array(); if ($status == "Partial") { $userdesc = "Request Item out dengan kode " . $row_json['RequestItemOutNumber'] . " telah di kirim sebagian"; } else { $userdesc = "Request Item out dengan kode " . $row_json['RequestItemOutNumber'] . " telah komplit"; } $sql_json_detail = "SELECT request_item_out_detail.* FROM request_item_out_detail WHERE RequestItemOutDetailIsActive = 'Y' AND RequestItemOutDetailRequestItemOutID = ?"; $qry_json_detail = $this->db->query($sql_json_detail, [$rioID]); if ($qry_json_detail) { $row_json["detail"] = $qry_json_detail->result_array(); } else { $this->sys_error_db("select detail rio error", $this->db); exit; } $data_json = $row_json; $sql_log = "INSERT INTO user_activity( UserActivityCode, UserActivityStatus, UserActivityDescription, UserActivityRefID, UserActivityData, UserActivityUserID, UserActivityCreated ) VALUES('RIO',?,?,?,?,?,NOW())"; $qry_log = $this->db->query($sql_log, [ $status, $userdesc, $rioID, json_encode($data_json), $userId ]); if (!$qry_log) { $this->db->trans_rollback(); $this->sys_error_db("insert user activity error", $this->db); exit; } } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } function formatRupiah($angka) { // Ganti koma ke titik agar jadi desimal yang valid $angka = str_replace(',', '.', $angka); // Validasi input if (!is_numeric($angka)) { return 'Input tidak valid'; } // Konversi ke float $angka = (float)$angka; // Bulatkan ke 3 angka desimal $angka = round($angka, 3); // Cek apakah angka desimalnya nol if (fmod($angka, 1) == 0.0) { // Tanpa desimal $formatted = number_format($angka, 0, ',', '.'); } else { // Dengan 3 angka desimal $formatted = number_format($angka, 3, ',', '.'); } return 'Rp ' . $formatted; } public function search() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $startDate = $prm["startDate"]; $endDate = $prm["endDate"]; $search = ""; if (isset($prm['search'])) { $search = trim($prm["search"]); if ($search != "") { $search = '%' . $prm['search'] . '%'; } else { $search = '%%'; } } $status = $prm["status"]; $filter = ""; if ($status != 'All') { $filter .= " AND T_ItemOutStatus = '{$status}'"; } $number_offset = 0; $number_limit = 10; if ($prm["current_page"] > 0) { $number_offset = ($prm["current_page"] - 1) * $number_limit; } $sqlCount = "SELECT count(*) as total FROM t_item_out JOIN warehouse ON T_ItemOutWarehouseID = WarehouseID AND WarehouseIsActive = 'Y' JOIN division ON T_ItemOutDivisionID = DivisionID AND DivisionIsActive = 'Y' JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID AND S_RegionalIsActive = 'Y' LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID AND M_BranchIsActive = 'Y' WHERE T_ItemOutIsActive = 'Y' AND (T_ItemOutDate BETWEEN ? AND ?) AND (T_ItemOutNumber LIKE ?) $filter ORDER BY T_ItemOutNumber DESC"; $qryCount = $this->db->query($sqlCount, [ $startDate, $endDate, $search ]); $tot_count = 0; $tot_page = 0; if ($qryCount) { $tot_count = $qryCount->result_array()[0]["total"]; $tot_page = ceil($tot_count / $number_limit); } else { $this->sys_error_db("item out count error", $this->db); exit; } $sql = "SELECT T_ItemOutID, T_ItemOutDate, T_ItemOutNumber, T_ItemOutNote, T_ItemOutReceiveUserID, T_ItemOutStatus, T_ItemOutApproveID, T_ItemOutIsConfirm, WarehouseID, WarehouseCode, CASE WHEN WarehouseType = 'B' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) WHEN WarehouseType = 'R' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) ELSE '' END WarehouseName, WarehouseType, DivisionID, DivisionCode, DivisionName FROM t_item_out JOIN warehouse ON T_ItemOutWarehouseID = WarehouseID AND WarehouseIsActive = 'Y' JOIN division ON T_ItemOutDivisionID = DivisionID AND DivisionIsActive = 'Y' JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID AND S_RegionalIsActive = 'Y' LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID AND M_BranchIsActive = 'Y' WHERE T_ItemOutIsActive = 'Y' AND (T_ItemOutDate BETWEEN ? AND ?) AND (T_ItemOutNumber LIKE ?) $filter ORDER BY T_ItemOutNumber DESC LIMIT ? OFFSET ?"; $qry = $this->db->query($sql, [ $startDate, $endDate, $search, $number_limit, $number_offset ]); // echo $this->db->last_query(); // exit; if ($qry) { $rows = $qry->result_array(); } else { $this->sys_error_db("get item out error", $this->db); exit; } $result = array( "totalPage" => $tot_page, "totalFilter" => $tot_count, "records" => $rows ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getWarehouse() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $regionalID = $prm["regionalID"]; $branchCode = $prm["branchCode"]; $filterBranch = ""; if (!$branchCode == "") { $filterBranch .= " AND M_BranchCode = '{$branchCode}'"; } $sql = "SELECT WarehouseID, WarehouseCode, CASE WHEN WarehouseType = 'B' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) WHEN WarehouseType = 'R' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) ELSE '' END WarehouseName, WarehouseType, WarehouseIsDefault, S_RegionalID, S_RegionalName FROM warehouse JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID AND S_RegionalIsActive = 'Y' LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID AND M_BranchIsActive = 'Y' WHERE WarehouseIsActive = 'Y' AND WarehouseIsTransit = 'N' AND WarehouseS_RegionalID = ? $filterBranch ORDER BY WarehouseID ASC "; $qry = $this->db->query($sql, [$regionalID]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("get warehouse", $this->db); exit; } $rows = $qry->result_array(); $result = array( "records" => $rows, "sql" => $this->db->last_query() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getDepartment() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $sql = "SELECT M_DepartmentID, M_DepartmentCode, M_DepartmentName FROM m_department WHERE M_DepartmentIsActive = 'Y' ORDER BY M_DepartmentName ASC"; $qry = $this->db->query($sql, []); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("get Department", $this->db); exit; } $rows = $qry->result_array(); $result = array( "records" => $rows, "sql" => $this->db->last_query() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getDivisionByUser() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } // $userId = $this->sys_user["M_UserID"]; $prm = $this->sys_input; $userId = $prm["userId"]; $rows = []; $query = "SELECT DISTINCT DivisionID, DivisionCode, DivisionName FROM division WHERE DivisionIsActive = 'Y' ORDER BY DivisionName ASC"; $exec = $this->db->query($query); if ($exec) { $rows = $exec->result_array(); } else { $this->db->trans_rollback(); $this->sys_error_db("select division", $this->db); exit; } if ($prm["act"] == "edit") { $sql = "SELECT DISTINCT DivisionID, DivisionCode, DivisionName FROM division JOIN m_userdivision ON M_UserDivisionDivisionID = DivisionID WHERE DivisionIsActive = 'Y' AND DivisionID = ? ORDER BY M_UserDivisionID DESC LIMIT 1"; $exec = $this->db->query($sql, [$prm["DivisionID"]]); if ($exec) { $row = $exec->result_array(); } else { $this->sys_error_db("select division", $this->db); exit; } } else { $sql = "SELECT DISTINCT DivisionID, DivisionCode, DivisionName FROM division JOIN m_userdivision ON M_UserDivisionDivisionID = DivisionID AND M_UserDivisionM_UserID = ? WHERE DivisionIsActive = 'Y' ORDER BY M_UserDivisionID DESC LIMIT 1"; $exec = $this->db->query($sql, [$userId]); if ($exec) { $row = $exec->result_array(); } else { $this->sys_error_db("select division by user", $this->db); exit; } } $result = array( "records" => $rows, "selected" => $row ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getDetailItemOut() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $itemoutID = isset($prm["itemoutID"]) ? $prm["itemoutID"] : ""; if ($itemoutID == "" || $itemoutID == null) { $this->sys_error("Invalid item out ID"); exit; } $sql = "SELECT T_ItemOutDetailID as ioDetailID, T_ItemOutDetailT_ItemOutID as ioDetailioID, T_ItemOutDetailRequestItemOutID as ioDetailRequestioID, T_ItemOutDetailM_ItemID as ioDetailItemID, T_ItemOutDetailItemUnitID as ioDetailUnitID, T_ItemOutDetailItemBatchNo, IFNULL(T_ItemOutDetailQty, 0) as rioDReceiveQty, T_ItemOutID as ioID, T_ItemOutDate as ioDate, T_ItemOutNumber as ioNumber, T_ItemOutWarehouseID as ioWarehouseID, T_ItemOutNote as ioNote, T_ItemOutDivisionID as ioDivisionID, RequestItemOutID as rioID, RequestItemOutNumber as rioNumber, RequestItemOutDetailID as rioDID, RequestItemOutDetailM_ItemID as rioDItemID, RequestItemOutDetailItemUnitID as rioDItemUnitID, RequestItemOutDetailQty as rioDQty, M_ItemID as itemID, M_ItemCode as itemCode, M_ItemDesc as itemDesc, ItemUnitID as unitID, ItemUnitCode as unitCode, ItemUnitName as unitName, StockID, StockWarehouseID, StockWarehouseAlmariID, StockWarehouseRackID, StockStockNumber, StockItemID, StockItemUnitID, StockItemPrice, StockBatchNo, StockED, StockLastUpdated, StockUserID, SUM(stock.StockQty) as StockQty FROM t_item_out_detail JOIN t_item_out ON T_ItemOutDetailT_ItemOutID = T_ItemOutID AND T_ItemOutIsActive = 'Y' JOIN request_item_out ON T_ItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' JOIN request_item_out_detail ON RequestItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutDetailM_ItemID = T_ItemOutDetailM_ItemID AND RequestItemOutDetailItemUnitID = T_ItemOutDetailItemUnitID AND RequestItemOutDetailIsActive = 'Y' JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN itemunit ON T_ItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' JOIN stock ON StockItemID = T_ItemOutDetailM_ItemID AND StockItemUnitID = T_ItemOutDetailItemUnitID AND StockWarehouseID = T_ItemOutWarehouseID WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? GROUP BY T_ItemOutDetailID ORDER BY T_ItemOutDetailID DESC"; $qry = $this->db->query($sql, [$itemoutID]); if (!$qry) { $this->sys_error_db('ERROR, get detail item out', $this->db); exit; } $rows = $qry->result_array(); foreach ($rows as $key => $value) { $jsonStr = $value['T_ItemOutDetailItemBatchNo']; $batches = []; if (!empty($jsonStr)) { $decode = json_decode($jsonStr); if ($decode !== null && json_last_error() === JSON_ERROR_NONE) { $batches = $decode; } else { $this->sys_error_db("[Error] decode json for batches number"); exit; } } $rows[$key]['ItemRequest'] = $batches; unset($rows[$key]['T_ItemOutDetailItemBatchNo']); } $result = array( "records" => $rows ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function updateRequest() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $date = isset($prm["date"]) ? $prm["date"] : ""; if ($date == "" || $date == null) { $this->sys_error("Invalid Date Use"); exit; } $warehouseID = isset($prm["warehouseID"]) ? $prm["warehouseID"] : ""; if ($warehouseID == "" || $warehouseID == null) { $this->sys_error("Warehouse masih kosong"); exit; } $note = ""; if (isset($prm["note"])) { $note = trim($prm["note"]); } $divisionID = isset($prm["divisionID"]) ? $prm["divisionID"] : ""; if ($divisionID == "" || $divisionID == null) { $this->sys_error("Division masih kosong"); exit; } $IOID = isset($prm["IOID"]) ? $prm["IOID"] : ""; if ($IOID == "" || $IOID == null) { $this->sys_error("item out ID masih kosong"); exit; } $items = isset($prm["items"]) ? $prm["items"] : []; if (count($items) == 0) { $this->sys_error("Items cannot be empty"); exit; } $sql = "UPDATE t_item_out SET T_ItemOutDate = ?, T_ItemOutWarehouseID = ?, T_ItemOutNote = ?, T_ItemOutDivisionID = ?, T_ItemOutUserID = ?, T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qry = $this->db->query($sql, [ $date, $warehouseID, $note, $divisionID, $userId, $IOID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed update item out", $this->db); exit; } $sql = "SELECT T_ItemOutDetailID FROM t_item_out_detail WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ?"; $qry = $this->db->query($sql, [$IOID]); if (!$qry) { $this->sys_error_db("select item out detail error", $this->db); exit; } $dbIoDetailIDs = array_column($qry->result_array(), 'T_ItemOutDetailID'); // Buat array ioDetailID dari frontend $activeIoDetailIDs = []; foreach ($items as $item) { if (!empty($item["ioDetailID"])) { $activeIoDetailIDs[] = $item["ioDetailID"]; } } // Cari ID yang harus di-nonaktifkan (yang ada di database tapi tidak ada di frontend) $toDeactivateIDs = array_diff($dbIoDetailIDs, $activeIoDetailIDs); // Jika ada yang perlu di-deactivate if (!empty($toDeactivateIDs)) { $placeholders = implode(',', array_fill(0, count($toDeactivateIDs), '?')); $sql = "UPDATE t_item_out_detail SET T_ItemOutDetailIsActive = 'N', T_ItemOutDetailLastUpdated = NOW(), T_ItemOutDetailUserID = ? WHERE T_ItemOutDetailID IN ($placeholders)"; $params = array_merge([$userId], $toDeactivateIDs); $qry = $this->db->query($sql, $params); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, batch deactivate item out detail"); exit; } } // Sekarang update atau insert yang dikirim frontend foreach ($items as $item) { $ioupdetailID = $item["ioDetailID"]; // encode json list batch number $jsonBatchNo = "[]"; if (isset($item['ItemRequest']) && is_array($item['ItemRequest'])) { $batches = $item['ItemRequest']; if (sizeof($batches) > 0) { $jsonResult = json_encode($batches); if ($jsonResult === false) { throw new Exception("[Error] Failed to encode json batch number"); } else { $jsonBatchNo = $jsonResult; } } } if ($ioupdetailID) { // UPDATE existing $sql = "UPDATE t_item_out_detail SET T_ItemOutDetailItemBatchNo = ?, T_ItemOutDetailQty = ?, T_ItemOutDetailLastUpdated = NOW(), T_ItemOutDetailUserID = ? WHERE T_ItemOutDetailID = ?"; $qry = $this->db->query($sql, [ $jsonBatchNo, intval($item["rioDReceiveQty"]), $userId, $ioupdetailID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("ERROR, update item out detail"); exit; } } else { // INSERT baru $sql = "INSERT INTO t_item_out_detail( T_ItemOutDetailT_ItemOutID, T_ItemOutDetailRequestItemOutID, T_ItemOutDetailRequestItemOutDetailID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailItemBatchNo, T_ItemOutDetailQty, T_ItemOutDetailCreated, T_ItemOutDetailUserID) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), ?)"; $qry = $this->db->query($sql, [ $IOID, $item["rioID"], $item["rioDID"], $item["itemID"], $item["unitID"], $jsonBatchNo, intval($item["rioDReceiveQty"]), $userId ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("failed to insert data table item out detail", $this->db); exit; } } } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$IOID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di edit"; $this->insertUserActivityIo($IOID, $userId, "UPDATE", $userdesc); $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function rejectRequest() { try { $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $IOID = ""; if (isset($prm["IOID"])) { $IOID = trim($prm["IOID"]); } $sql = "UPDATE t_item_out SET T_ItemOutStatus = 'Rejected', T_ItemOutUserID = ?, T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qry = $this->db->query($sql, [ $userId, $IOID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed delete item out", $this->db); exit; } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$IOID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di reject"; $this->insertUserActivityIo($IOID, $userId, "REJECTED", $userdesc); $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function deleteRequest() { try { $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $IOID = ""; if (isset($prm["IOID"])) { $IOID = trim($prm["IOID"]); } $sql = "UPDATE t_item_out SET T_ItemOutIsActive = 'N', T_ItemOutUserID = ?, T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qry = $this->db->query($sql, [ $userId, $IOID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed delete item out", $this->db); exit; } $sqldetail = "UPDATE t_item_out_detail SET T_ItemOutDetailIsActive = 'N', T_ItemOutDetailLastUpdated = NOW(), T_ItemOutDetailUserID = ? WHERE T_ItemOutDetailT_ItemOutID = ?"; $qrydetail = $this->db->query($sqldetail, [ $userId, $IOID ]); if (!$qrydetail) { $this->db->trans_rollback(); $this->sys_error_db("Failed delete item out detail", $this->db); exit; } $sqlstatus = "UPDATE request_item_out SET RequestItemOutStatus = 'Process', RequestItemOutUserID = ?, RequestItemOutLastUpdated = NOW() WHERE RequestItemOutID = ?"; $qrystatus = $this->db->query($sqlstatus, [ $userId, $prm["RIOID"] ]); if (!$qrystatus) { $this->db->trans_rollback(); $this->sys_error_db("Failed update request item out status", $this->db); exit; } $sqlstatusdetail = "UPDATE request_item_out_detail SET RequestItemOutDetailStatus = 'Process', RequestItemOutDetailLastUpdated = NOW(), RequestItemOutDetailUserID = ? WHERE RequestItemOutDetailID = ?"; $qrystatusdetail = $this->db->query($sqlstatusdetail, [ $userId, $prm["RIODID"] ]); if (!$qrystatusdetail) { $this->db->trans_rollback(); $this->sys_error_db("Failed update request item out detail status", $this->db); exit; } $sql = "SELECT t_item_out.* FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qry = $this->db->query($sql, [$IOID]); if (!$qry) { $this->sys_error_db("select item out error", $this->db); exit; } $row = $qry->row(); $userdesc = "Transaksi item out dengan kode " . $row->T_ItemOutNumber . " telah di hapus"; $this->insertUserActivityIo($IOID, $userId, "DELETE", $userdesc); $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function searchDetail() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $search = ""; if (isset($prm['search'])) { $search = trim($prm["search"]); if ($search != "") { $search = '%' . $prm['search'] . '%'; } else { $search = '%%'; } } $IOID = ""; if (isset($prm["IOID"])) { $IOID = trim($prm["IOID"]); } $number_offset = 0; $number_limit = 10; if ($prm["current_page"] > 0) { $number_offset = ($prm["current_page"] - 1) * $number_limit; } $sqlCount = "SELECT count(*) as total FROM t_item_out_detail JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN itemunit ON T_ItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? ORDER BY T_ItemOutDetailID DESC"; $qryCount = $this->db->query($sqlCount, [ $IOID ]); $tot_count = 0; $tot_page = 0; if ($qryCount) { $tot_count = $qryCount->result_array()[0]["total"]; $tot_page = ceil($tot_count / $number_limit); } else { $this->sys_error_db("item out detail count error", $this->db); exit; } $sql = "SELECT T_ItemOutDetailID, T_ItemOutDetailT_ItemOutID, T_ItemOutDetailM_ItemID, T_ItemOutDetailQty, M_ItemID, M_ItemCode, M_ItemDesc, ItemUnitID, ItemUnitCode, ItemUnitName, ROW_NUMBER() OVER(ORDER BY T_ItemOutDetailID) RowNumber FROM t_item_out_detail JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN itemunit ON T_ItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? ORDER BY T_ItemOutDetailID DESC LIMIT ? OFFSET ?"; $qry = $this->db->query($sql, [ $IOID, $number_limit, $number_offset ]); if ($qry) { $rows = $qry->result_array(); } else { $this->sys_error_db("get item out detail error", $this->db); exit; } $result = array( "totalPage" => $tot_page, "totalFilter" => $tot_count, "records" => $rows ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function searchItem() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $prm = $this->sys_input; $search = ""; if (isset($prm["search"])) { $search = trim($prm["search"]); if ($search != "") { $search = "%" . $prm["search"] . "%"; } else { $search = "%%"; } } $warehouseID = $prm["warehouseID"]; $number_limit = 10; $sql = "SELECT M_ItemID, M_ItemCode, M_ItemDesc, StockQty, StockItemPrice, StockBatchNo, StockED, StockWarehouseID FROM stock JOIN m_item ON M_ItemID = StockItemID WHERE StockWarehouseID = ? AND StockQty > 0 AND M_ItemIsActive = 'Y' AND (M_ItemCode LIKE ? OR M_ItemDesc LIKE ?)"; $qry = $this->db->query($sql, array($warehouseID, $search, $search)); if ($qry) { $rows = $qry->result_array(); } else { $this->db->trans_rollback(); $this->sys_error_db("select item error", $this->db); exit; } $result = array( "records" => $rows, "total_filter" => sizeof($rows) ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getUnit() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $search = ""; if (isset($prm["search"])) { $search = trim($prm["search"]); if ($search != "") { $search = "%" . $prm["search"] . "%"; } else { $search = "%%"; } } $itemID = $prm["itemID"]; $sql = "SELECT ItemUnitID, ItemUnitCode, ItemUnitName, ItemUnitCreated, ItemUnitLastUpdated, ItemUnitIsActive, ItemUnitUserID, ItemUnitMapM_ItemID, ItemUnitMapIsPurchase, ItemUnitMapIsReport, ItemUnitMapIsBase FROM itemunit JOIN itemunitmap ON ItemUnitMapItemUnitID = ItemUnitID AND ItemUnitMapIsActive ='Y' AND ItemUnitMapM_ItemID = ? WHERE ItemUnitIsActive = 'Y' AND ItemUnitName LIKE ? ORDER BY ItemUnitName ASC"; $query = $this->db->query($sql, [ $itemID, $search ]); if (!$query) { $this->sys_error_db("item unit list", $this->db); exit; } $rows = $query->result_array(); $result = array( "records" => $rows, "sql" => $this->db->last_query() ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function saveDetail() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $itemID = ""; if (isset($prm["itemID"])) { $itemID = trim($prm["itemID"]); } $itemUnitID = ""; if (isset($prm["itemUnitID"])) { $itemUnitID = trim($prm["itemUnitID"]); } $qty = ""; if (isset($prm["qty"])) { $qty = trim($prm["qty"]); } $IOID = ""; if (isset($prm["IOID"])) { $IOID = trim($prm["IOID"]); } $qryChek = "SELECT count(*) as exist FROM t_item_out_detail WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? AND T_ItemOutDetailM_ItemID = ?"; $exist = $this->db->query($qryChek, [ $IOID, $itemID ]); if ($exist) { $row = $exist->row()->exist; } else { $this->sys_error_db("exist error", $this->db); exit; } if ($row == 0) { $sql = "INSERT INTO t_item_out_detail( T_ItemOutDetailT_ItemOutID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailQty, T_ItemOutDetailIsActive, T_ItemOutDetailCreated, T_ItemOutDetailUserID) VALUES(?,?,?,?,'Y',NOW(),?)"; $qry = $this->db->query($sql, [ $IOID, $itemID, $itemUnitID, $qty, $userId ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item out detail", $this->db); exit; } $this->db->trans_commit(); $result = array("total" => 1, "records" => array("xId" => 0)); $this->sys_ok($result); } else { $errors = array(); $qryItem = "SELECT M_ItemDesc FROM m_item WHERE M_ItemID = ?"; $qryItem = $this->db->query($qryItem, [ $itemID ]); if ($qryItem) { $rowItem = $qryItem->row()->M_ItemDesc; } else { $this->db->trans_rollback(); $this->sys_error_db("Failed get item error", $this->db); exit; } if ($row != 0) { array_push($errors, array("msg" => "Data dengan item " . $rowItem . " sudah ada")); } $result = array("total" => -1, "errors" => $errors, "records" => array('status' => 'ERROR')); $this->sys_ok($result); } } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function updateDetail() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $itemID = ""; if (isset($prm["itemID"])) { $itemID = trim($prm["itemID"]); } $itemUnitID = ""; if (isset($prm["itemUnitID"])) { $itemUnitID = trim($prm["itemUnitID"]); } $qty = ""; if (isset($prm["qty"])) { $qty = trim($prm["qty"]); } $IOID = ""; if (isset($prm["IOID"])) { $IOID = trim($prm["IOID"]); } $IODID = ""; if (isset($prm["IODID"])) { $IODID = trim($prm["IODID"]); } $sqlCheck = "SELECT count(*) as exist FROM t_item_out_detail WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ? AND T_ItemOutDetailM_ItemID = ? AND T_ItemOutDetailItemUnitID = ? AND T_ItemOutDetailID != ?"; $qryCheck = $this->db->query($sqlCheck, [ $IOID, $itemID, $itemUnitID, $IODID ]); if ($qryCheck && $qryCheck->result_array()[0]["exist"] > 0) { $this->sys_error("Item dan satuan sudah ada"); exit; } $sql = "UPDATE t_item_out_detail SET T_ItemOutDetailT_ItemOutID = ?, T_ItemOutDetailM_ItemID = ?, T_ItemOutDetailItemUnitID = ?, T_ItemOutDetailQty = ?, T_ItemOutDetailLastUpdated = NOW(), T_ItemOutDetailUserID = ? WHERE T_ItemOutDetailID = ?"; $qry = $this->db->query($sql, [ $IOID, $itemID, $itemUnitID, $qty, $userId, $IODID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item out detail error", $this->db); exit; } $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function deleteDetail() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $IODID = ""; if (isset($prm["IODID"])) { $IODID = trim($prm["IODID"]); } $sql = "UPDATE t_item_out_detail SET T_ItemOutDetailIsActive = 'N', T_ItemOutDetailLastUpdated = NOW(), T_ItemOutDetailUserID = ? WHERE T_ItemOutDetailID = ?"; $qry = $this->db->query($sql, [ $userId, $IODID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed delete item out detail error", $this->db); exit; } $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function orderConfirm() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $itemoutID = $prm["itemoutID"]; $sqlDetail = "SELECT T_ItemOutDetailID, T_ItemOutDetailT_ItemOutID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailQty FROM t_item_out_detail WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ?"; $qryDetail = $this->db->query($sqlDetail, [$itemoutID]); if ($qryDetail) { $rows = $qryDetail->result_array(); } else { $this->db->tans_rollback(); $this->sys_error_db("select item out detail error", $this->db); exit; } // Ambil Warehouse ID $sqlHeader = "SELECT T_ItemOutWarehouseID FROM t_item_out WHERE T_ItemOutIsActive = 'Y' AND T_ItemOutID = ?"; $qryHeader = $this->db->query($sqlHeader, [$itemoutID]); if (!$qryHeader || $qryHeader->num_rows() == 0) { $this->db->trans_rollback(); $this->sys_error("Item Out tidak ditemukan"); return; } $warehouseID = $qryHeader->row()->T_ItemOutWarehouseID; // loop detail foreach ($rows as $row) { $itemID = $row["T_ItemOutDetailM_ItemID"]; $unitID = $row["T_ItemOutDetailItemUnitID"]; $qtyNeeded = $row["T_ItemOutDetailQty"]; // Ambil stok berdasarkan Warehouse, Item dan Unit $sqlStock = "SELECT StockID, StockQty FROM stock WHERE StockWarehouseID = ? AND StockItemID = ? AND StockItemUnitID = ? AND StockQty > 0 ORDER BY StockID ASC"; $qryStock = $this->db->query($sqlStock, [$warehouseID, $itemID, $unitID]); if (!$qryStock) { $this->db->trans_rollback(); $this->sys_error_db("select stock error", $this->db); exit; } $stocks = $qryStock->result_array(); foreach ($stocks as $stock) { if ($qtyNeeded <= 0) break; $stockID = $stock["StockID"]; $stockQty = $stock["StockQty"]; if ($stockQty >= $qtyNeeded) { // Kurangi stok langsung dengan SQL $sqlUpdate = "UPDATE stock SET StockQty = StockQty - ? WHERE StockID = ?"; $this->db->query($sqlUpdate, [$qtyNeeded, $stockID]); $qtyNeeded = 0; } } // Jika setelah looping qtyNeeded masih > 0, berarti stok tidak cukup if ($qtyNeeded > 0) { $this->db->trans_rollback(); $this->sys_error("Stok tidak cukup untuk item ID: $itemID"); return; } } // update approved $sqlUpdate = "UPDATE t_item_out SET T_ItemOutReceiveUserID= ?, T_ItemOutStatus = 'Process', T_ItemOutLastUpdated = NOW() WHERE T_ItemOutID = ?"; $qryUpdate = $this->db->query($sqlUpdate, [$userId, $itemoutID]); if (!$qryUpdate) { $this->db->trans_rollback(); $this->sys_error_db("update item out error", $this->db); exit; } $this->db->trans_commit(); $result = array("total" => 1); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function listRequest() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $startDate = $prm["startDate"]; $endDate = $prm["endDate"]; $search = isset($prm["search"]) ? $prm["search"] : ""; $status = isset($prm["status"]) ? $prm["status"] : ""; $number_offset = 0; $number_limit = 20; if ($prm["current_page"] > 0) { $number_offset = ($prm["current_page"] - 1) * $number_limit; } $sqlCount = "SELECT count(*) as total FROM request_item_out_detail JOIN request_item_out ON RequestItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' AND (RequestItemOutNumber LIKE ? ) AND (RequestItemOutStatus = ? OR ? = '' ) AND RequestItemOutDate BETWEEN ? AND ? JOIN m_item ON RequestItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN stock ON StockItemID = RequestItemOutDetailM_ItemID AND StockItemUnitID = RequestItemOutDetailItemUnitID AND StockWarehouseID = RequestItemOutWarehouseID WHERE RequestItemOutDetailIsActive = 'Y' GROUP BY RequestItemOutDetailID ORDER BY RequestItemOutDetailID DESC"; $qryCount = $this->db->query($sqlCount, ['%' . $search . '%', $status, $status, $startDate, $endDate]); $tot_count = 0; $tot_page = 0; if ($qryCount) { $tot_count = $qryCount->result_array()[0]["total"]; $tot_page = ceil($tot_count / $number_limit); } else { $this->sys_error_db("request item out count error", $this->db); exit; } $sql = "SELECT RequestItemOutDetailID as rioDID, RequestItemOutDetailM_ItemID as rioDItemID, RequestItemOutDetailItemUnitID as rioDItemUnitID, RequestItemOutDetailQty as rioDQty, RequestItemOutDetailStatus as rioDStatus, RequestItemOutID as rioID, RequestItemOutDate as rioDate, RequestItemOutNumber as rioNumber, RequestItemOutNote as rioNote, RequestItemOutDivisionID as rioDivisionID, RequestItemOutStatus as rioStatus, M_ItemID, M_ItemCode, M_ItemDesc, StockID, StockItemPrice, SUM(StockQty) AS StockQty FROM request_item_out_detail JOIN request_item_out ON RequestItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' AND (RequestItemOutNumber LIKE ?) AND (RequestItemOutStatus = ? OR ? = '' ) AND RequestItemOutDate BETWEEN ? AND ? JOIN m_item ON RequestItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' JOIN stock ON StockItemID = RequestItemOutDetailM_ItemID AND StockItemUnitID = RequestItemOutDetailItemUnitID AND StockWarehouseID = RequestItemOutWarehouseID WHERE RequestItemOutDetailIsActive = 'Y' GROUP BY RequestItemOutDetailID ORDER BY RequestItemOutDetailID DESC LIMIT ? OFFSET ?"; $qry = $this->db->query($sql, [ '%' . $search . '%', $status, $status, $startDate, $endDate, $number_limit, $number_offset ]); // echo $this->db->last_query(); // exit; if ($qry) { $rows = $qry->result_array(); } else { $this->sys_error_db("request item out error", $this->db); exit; } $result = array( "totalPage" => $tot_page, "totalFilter" => $tot_count, "records" => $rows ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } function getDivisionUser() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $payload = $this->sys_input; $user = $this->sys_user; $regionalID = $user['S_RegionalID']; $branchCode = $user['M_BranchCode']; $loginType = $user['M_UserLocationFlag']; $userID = $user['M_UserID']; $sql = "SELECT M_UserDivisionID, M_UserDivisionM_UserID, M_UserDivisionDivisionID, M_UserUsername, DivisionName FROM m_user JOIN m_userdivision ON M_UserID = M_UserDivisionM_UserID AND M_UserDivisionIsActive = 'Y' JOIN division ON M_UserDivisionDivisionID = DivisionID AND DivisionIsActive = 'Y' WHERE M_UserIsActive = 'Y' AND M_UserDivisionM_UserID = ?"; $qry = $this->db->query($sql, [$userID]); if (!$qry) { $this->sys_error_db("Error cek approval level"); exit; } $approvalDivision = $qry->result_array(); if (count($approvalDivision) == 0) { $result = array( 'total' => 0, 'records' => [] ); $this->sys_ok($result); exit; } else { $result = array( "total" => 1, "records" => $approvalDivision, "sql" => $this->db->last_query() ); $this->sys_ok($result); } } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function approveRequest() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $arrRequest = $prm["arrRequest"]; foreach ($arrRequest as $key => $value) { $sql = "UPDATE request_item_out SET RequestItemOutStatus = 'Process', RequestItemOutApprovedDate = NOW(), RequestItemOutApprovedUserID = ?, RequestItemOutLastUpdated = NOW() WHERE RequestItemOutID = ?"; $qry = $this->db->query($sql, [ $userId, $value["rioID"] ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("update request item out error", $this->db); exit; } $sqlDetail = "UPDATE request_item_out_detail SET RequestItemOutDetailStatus = 'Process', RequestItemOutDetailLastUpdated = NOW(), RequestItemOutDetailUserID = ? WHERE RequestItemOutDetailID = ?"; $qryDetail = $this->db->query($sqlDetail, [ $userId, $value["rioDID"] ]); if (!$qryDetail) { $this->db->trans_rollback(); $this->sys_error_db("update request item out detail error", $this->db); exit; } } // insert log $sql_json = "SELECT request_item_out.*, '' as detail FROM request_item_out WHERE RequestItemOutIsActive = 'Y' AND RequestItemOutID = ?"; $qry_json = $this->db->query($sql_json, [$value["rioID"]]); if (!$qry_json) { $this->db->trans_rollback(); $this->sys_error_db("select json rio error"); exit; } $row_json = $qry_json->row_array(); $userdesc = "Request Item out dengan kode " . $row_json['RequestItemOutNumber'] . " telah di approve"; $sql_json_detail = "SELECT request_item_out_detail.* FROM request_item_out_detail WHERE RequestItemOutDetailIsActive = 'Y' AND RequestItemOutDetailRequestItemOutID = ?"; $qry_json_detail = $this->db->query($sql_json_detail, [$value["rioID"]]); if ($qry_json_detail) { $row_json["detail"] = $qry_json_detail->result_array(); } else { $this->sys_error_db("select detail rio error", $this->db); exit; } $data_json = $row_json; $sql_log = "INSERT INTO user_activity( UserActivityCode, UserActivityStatus, UserActivityDescription, UserActivityRefID, UserActivityData, UserActivityUserID, UserActivityCreated ) VALUES('RIO','Process',?,?,?,?,NOW())"; $qry_log = $this->db->query($sql_log, [ $userdesc, $value["rioID"], json_encode($data_json), $userId ]); if (!$qry_log) { $this->db->trans_rollback(); $this->sys_error_db("insert user activity error", $this->db); exit; } $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function rejectedRequest() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $this->db->trans_begin(); $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $arrRequest = $prm["arrRequest"]; foreach ($arrRequest as $key => $value) { $sql = "UPDATE request_item_out SET RequestItemOutStatus = 'Rejected', RequestItemOutUserID = ?, RequestItemOutLastUpdated = NOW() WHERE RequestItemOutID = ?"; $qry = $this->db->query($sql, [ $userId, $value["rioID"] ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("update request item out error", $this->db); exit; } $sqlDetail = "UPDATE request_item_out_detail SET RequestItemOutDetailStatus = 'Rejected', RequestItemOutDetailLastUpdated = NOW(), RequestItemOutDetailUserID = ? WHERE RequestItemOutDetailID = ?"; $qryDetail = $this->db->query($sqlDetail, [ $userId, $value["rioDID"] ]); if (!$qryDetail) { $this->db->trans_rollback(); $this->sys_error_db("update request item out detail error", $this->db); exit; } } // insert log $sql_json = "SELECT request_item_out.*, '' as detail FROM request_item_out WHERE RequestItemOutIsActive = 'Y' AND RequestItemOutID = ?"; $qry_json = $this->db->query($sql_json, [$value["rioID"]]); if (!$qry_json) { $this->db->trans_rollback(); $this->sys_error_db("select json rio error"); exit; } $row_json = $qry_json->row_array(); $userdesc = "Request Item out dengan kode " . $row_json['RequestItemOutNumber'] . " telah di reject"; $sql_json_detail = "SELECT request_item_out_detail.* FROM request_item_out_detail WHERE RequestItemOutDetailIsActive = 'Y' AND RequestItemOutDetailRequestItemOutID = ?"; $qry_json_detail = $this->db->query($sql_json_detail, [$value["rioID"]]); if ($qry_json_detail) { $row_json["detail"] = $qry_json_detail->result_array(); } else { $this->sys_error_db("select detail rio error", $this->db); exit; } $data_json = $row_json; $sql_log = "INSERT INTO user_activity( UserActivityCode, UserActivityStatus, UserActivityDescription, UserActivityRefID, UserActivityData, UserActivityUserID, UserActivityCreated ) VALUES('RIO','Rejected',?,?,?,?,NOW())"; $qry_log = $this->db->query($sql_log, [ $userdesc, $value["rioID"], json_encode($data_json), $userId ]); if (!$qry_log) { $this->db->trans_rollback(); $this->sys_error_db("insert user activity error", $this->db); exit; } $this->db->trans_commit(); $result = array( "total" => 1 ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getItemRequest() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $userId = $this->sys_user["M_UserID"]; $warehouseID = isset($prm["warehouseID"]) ? $prm["warehouseID"] : ""; $divisionID = isset($prm["divisionID"]) ? $prm["divisionID"] : ""; $search = isset($prm["search"]) ? $prm["search"] : ""; $currentPage = isset($prm["currentPage"]) ? $prm["currentPage"] : 1; $limit = 10; $offset = ($currentPage - 1) * $limit; $sqlCount = "SELECT COUNT(*) as total FROM ( SELECT * FROM request_item_out_detail JOIN request_item_out ON RequestItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' AND (RequestItemOutStatus = 'Process' OR RequestItemOutStatus = 'Partial') AND RequestItemOutWarehouseID = ? AND RequestItemOutDivisionID = ? JOIN m_item ON RequestItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' AND (M_ItemDesc LIKE ?) JOIN itemunit ON RequestItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' JOIN stock ON StockItemID = M_ItemID AND StockItemUnitID = RequestItemOutDetailItemUnitID AND StockWarehouseID = RequestItemOutWarehouseID LEFT JOIN t_item_out_detail ON T_ItemOutDetailRequestItemOutID = RequestItemOutDetailRequestItemOutID AND T_ItemOutDetailM_ItemID = RequestItemOutDetailM_ItemID AND T_ItemOutDetailItemUnitID = RequestItemOutDetailItemUnitID WHERE RequestItemOutDetailIsActive = 'Y' AND (RequestItemOutDetailStatus = 'Process' OR RequestItemOutDetailStatus = 'Partial') GROUP BY RequestItemOutDetailID) x"; $qryCount = $this->db->query($sqlCount, [$warehouseID, $divisionID, '%' . $search . '%']); if (!$qryCount) { $this->sys_error_db("count item list request error", $this->db); exit; } $rowsCount = $qryCount->result_array(); $total = ceil($rowsCount[0]["total"] / $limit); $sql = "SELECT RequestItemOutDetailID as rioDID, RequestItemOutDetailRequestItemOutID as rioDrioID, RequestItemOutDetailM_ItemID as rioDItemID, RequestItemOutDetailItemUnitID as rioDItemUnitID, IFNULL(RequestItemOutDetailQty, 0) as rioDQty, IFNULL(T_ItemOutDetailQty, 0) as rioDReceiveQty, RequestItemOutDetailStatus as rioDStatus, RequestItemOutID as rioID, RequestItemOutDate as rioDate, RequestItemOutNumber as rioNumber, RequestItemOutWarehouseID as rioWarehouseID, RequestItemOutS_RegionalID as rioRegionalID, RequestItemOutM_BranchCode as rioBranchCode, RequestItemOutNote as rioNote, RequestItemOutDivisionID as rioDivisionID, RequestItemOutStatus as rioStatus, M_ItemID as itemID, M_ItemCode as itemCode, M_ItemDesc as itemDesc, ItemUnitID as unitID, ItemUnitCode as unitCode, ItemUnitName as unitName, StockID, StockWarehouseID, StockWarehouseAlmariID, StockWarehouseRackID, StockStockNumber, StockItemID, StockItemUnitID, StockItemPrice, StockBatchNo, StockED, StockLastUpdated, StockUserID, -- GROUP_CONCAT(CONCAT(StockBatchNo, ' (', StockQty, ')')) AS stockFull, SUM(StockQty) AS StockQty FROM request_item_out_detail JOIN request_item_out ON RequestItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' AND (RequestItemOutStatus = 'Process' OR RequestItemOutStatus = 'Partial') AND RequestItemOutWarehouseID = ? AND RequestItemOutDivisionID = ? JOIN m_item ON RequestItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' AND (M_ItemDesc LIKE ?) JOIN itemunit ON RequestItemOutDetailItemUnitID = ItemUnitID AND ItemUnitIsActive = 'Y' JOIN stock ON StockItemID = M_ItemID AND StockItemUnitID = RequestItemOutDetailItemUnitID AND StockWarehouseID = RequestItemOutWarehouseID LEFT JOIN t_item_out_detail ON T_ItemOutDetailRequestItemOutID = RequestItemOutDetailRequestItemOutID AND T_ItemOutDetailM_ItemID = RequestItemOutDetailM_ItemID AND T_ItemOutDetailItemUnitID = RequestItemOutDetailItemUnitID WHERE RequestItemOutDetailIsActive = 'Y' AND (RequestItemOutDetailStatus = 'Process' OR RequestItemOutDetailStatus = 'Partial') GROUP BY RequestItemOutDetailID ORDER BY RequestItemOutNumber DESC LIMIT ? OFFSET ?"; $qry = $this->db->query($sql, [$warehouseID, $divisionID, '%' . $search . '%', $limit, $offset]); if (!$qry) { $this->sys_error_db("select item request error", $this->db); exit; } // echo $this->db->last_query(); // exit; $rows = $qry->result_array(); foreach ($rows as $key => $value) { $rows[$key]['ItemRequest'] = []; } $result = array( "records" => $rows, "total" => $total ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } // dijagain itemid dan itemunit harus sama // agar di stock pemakaian tidak terjadi rancu jumlah stocknya karena berdasarkan itemunit public function insertUsage($ioID, $userID) { try { // 1. Ambil data dari t_item_out_detail $this->db->trans_begin(); $sql_tio = "SELECT T_ItemOutID, T_ItemOutDate, T_ItemOutWarehouseID, T_ItemOutDivisionID, T_ItemOutDetailID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailItemBatchNo, T_ItemOutDetailQty, RequestItemOutS_RegionalID, RequestItemOutM_BranchCode, M_ItemDesc FROM t_item_out_detail JOIN t_item_out ON T_ItemOutDetailT_ItemOutID = T_ItemOutID AND T_ItemOutIsActive = 'Y' JOIN request_item_out ON T_ItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ?"; $qry_tio = $this->db->query($sql_tio, [$ioID]); if (!$qry_tio) { $this->sys_error_db("select t_item_out error", $this->db); exit; } $rows_tio = $qry_tio->result_array(); if (count($rows_tio) == 0) { $this->sys_error("No data found in t_item_out_detail"); exit; } // 2. Ambil data existing dari item_usage $sqlusage = "SELECT ItemUsageID, ItemUsageS_RegionalID, ItemUsageM_BranchCode, ItemUsageDivisionID, ItemUsageM_ItemID, ItemUsageItemUnitID, ItemUsageQty, ItemUsagePrice FROM item_usage WHERE ItemUsageIsActive = 'Y'"; $qryusage = $this->db->query($sqlusage, []); if (!$qryusage) { $this->db->trans_rollback(); $this->sys_error_db('get select usage error', $this->db); exit; } $rowsusage = $qryusage->result_array(); // 3. Buat mapping untuk pencarian cepat (ItemID dan ItemUsageItemUnitID sebagai key) $usage_map = []; foreach ($rowsusage as $usage) { $key = $usage['ItemUsageM_ItemID'] . '_' . $usage['ItemUsageItemUnitID']; $usage_map[$key] = $usage; } // print_r($usage_map); // exit; // 4. Loop data t_item_out_detail foreach ($rows_tio as $key => $value) { // Decode batch JSON $jsonStr = $value["T_ItemOutDetailItemBatchNo"]; $batches = []; if (!empty($jsonStr)) { $decode = json_decode($jsonStr, true); if ($decode !== null && json_last_error() === JSON_ERROR_NONE) { $batches = $decode; } else { $this->sys_error_db("[Error] decode json for batches number"); exit; } } // Buat key untuk pengecekan $check_key = $value["T_ItemOutDetailM_ItemID"] . '_' . $value["T_ItemOutDetailItemUnitID"]; // Cek apakah sudah ada di item_usage if (isset($usage_map[$check_key])) { // SUDAH ADA - Update qty dan proses detail $existing_usage = $usage_map[$check_key]; $item_usage_id = $existing_usage['ItemUsageID']; // Update qty di item_usage (opsional, sesuai kebutuhan) $sql_update = "UPDATE item_usage SET ItemUsageQty = ItemUsageQty + ?, ItemUsageUserID = ?, ItemUsageLastUpdated = NOW() WHERE ItemUsageID = ?"; $qry_update = $this->db->query($sql_update, [ $value["T_ItemOutDetailQty"], $userID, $item_usage_id ]); if (!$qry_update) { $this->db->trans_rollback(); $this->sys_error_db("Failed update item usage", $this->db); exit; } } else { // BELUM ADA - Insert baru $sql = "INSERT INTO item_usage( ItemUsageS_RegionalID, ItemUsageM_BranchCode, ItemUsageDivisionID, ItemUsageM_ItemID, ItemUsageItemUnitID, ItemUsageQty, ItemUsagePrice, ItemUsageIsActive, ItemUsageUserID, ItemUsageCreated) VALUES(?,?,?,?,?,?,?,?,'Y',?,NOW())"; $qry = $this->db->query($sql, [ $value["RequestItemOutS_RegionalID"], $value["RequestItemOutM_BranchCode"], $value["T_ItemOutDivisionID"], $value["T_ItemOutDetailM_ItemID"], $value["T_ItemOutDetailItemUnitID"], $value["T_ItemOutDetailQty"], $batches[0]["StockItemPrice"], // Ambil dari batch pertama $userID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item usage", $this->db); exit; } $item_usage_id = $this->db->insert_id(); } // 5. Ambil existing detail untuk item_usage_id ini $sql_detail = "SELECT ItemUsageDetailID, ItemUsageDetailStockBatchNo, ItemUsageDetailQtyReq FROM item_usage_detail WHERE ItemUsageDetailItemUsageID = ?"; $qry_detail = $this->db->query($sql_detail, [$item_usage_id]); if (!$qry_detail) { $this->db->trans_rollback(); $this->sys_error_db("Failed get item usage detail", $this->db); exit; } $existing_details = $qry_detail->result_array(); // Buat mapping batch no untuk pencarian cepat $detail_map = []; foreach ($existing_details as $detail) { $detail_map[$detail['ItemUsageDetailStockBatchNo']] = $detail; } // 6. Proses setiap batch foreach ($batches as $k => $v) { $batch_no = $v["StockBatchNo"]; // Cek apakah batch no sudah ada if (isset($detail_map[$batch_no])) { // BATCH SUDAH ADA - Update qty $existing_detail = $detail_map[$batch_no]; $sql_update_detail = "UPDATE item_usage_detail SET ItemUsageDetailQtyReq = ItemUsageDetailQtyReq + ?, ItemUsageDetailUserID = ?, ItemUsageDetailLastUpdated = NOW() WHERE ItemUsageDetailID = ?"; $qry_update_detail = $this->db->query($sql_update_detail, [ floatval($v["QtyReq"]), $userID, $existing_detail['ItemUsageDetailID'] ]); if (!$qry_update_detail) { $this->db->trans_rollback(); $this->sys_error_db("Failed update item usage detail", $this->db); exit; } } else { // BATCH BELUM ADA - Insert baru $sql_insert_detail = "INSERT INTO item_usage_detail( ItemUsageDetailItemUsageID, ItemUsageDetailWarehouseID, ItemUsageDetailWarehouseName, ItemUsageDetailStockID, ItemUsageDetailStockNumber, ItemUsageDetailStockItemID, ItemUsageDetailStockItemUnitID, ItemUsageDetailItemUnitCode, ItemUsageDetailItemUnitName, ItemUsageDetailStockItemPrice, ItemUsageDetailStockBatchNo, ItemUsageDetailStockED, ItemUsageDetailStockQty, ItemUsageDetailQtyReq, ItemUsageDetailUserID, ItemUsageDetailCreated) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())"; $qry_insert_detail = $this->db->query($sql_insert_detail, [ $item_usage_id, $v["WarehouseID"], $v["WarehouseName"], $v["StockID"], $v["StockStockNumber"], $v["StockItemID"], $v["StockItemUnitID"], $v["ItemUnitCode"], $v["ItemUnitName"], $v["StockItemPrice"], $v["StockBatchNo"], $v["StockED"], $v["StockQty"], $v["QtyReq"], $userID ]); if (!$qry_insert_detail) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item usage detail", $this->db); exit; } } } } $this->db->trans_commit(); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function insertItemUsage($ioID, $userID) { try { $this->db->trans_begin(); $sql_tio = "SELECT T_ItemOutID, T_ItemOutDate, T_ItemOutWarehouseID, T_ItemOutDivisionID, T_ItemOutDetailID, T_ItemOutDetailM_ItemID, T_ItemOutDetailItemUnitID, T_ItemOutDetailItemBatchNo, T_ItemOutDetailQty, RequestItemOutS_RegionalID, RequestItemOutM_BranchCode, M_ItemDesc FROM t_item_out_detail JOIN t_item_out ON T_ItemOutDetailT_ItemOutID = T_ItemOutID AND T_ItemOutIsActive = 'Y' JOIN request_item_out ON T_ItemOutDetailRequestItemOutID = RequestItemOutID AND RequestItemOutIsActive = 'Y' JOIN m_item ON T_ItemOutDetailM_ItemID = M_ItemID AND M_ItemIsActive = 'Y' WHERE T_ItemOutDetailIsActive = 'Y' AND T_ItemOutDetailT_ItemOutID = ?"; $qry_tio = $this->db->query($sql_tio, [$ioID]); if (!$qry_tio) { $this->sys_error_db("select t_item_out error", $this->db); exit; } $rows_tio = $qry_tio->result_array(); if (count($rows_tio) == 0) { $this->sys_error("No data found in t_item_out_detail"); exit; } foreach ($rows_tio as $key => $value) { $jsonStr = $value["T_ItemOutDetailItemBatchNo"]; $batches = []; if (!empty($jsonStr)) { $decode = json_decode($jsonStr, true); if ($decode !== null && json_last_error() === JSON_ERROR_NONE) { $batches = $decode; } else { $this->sys_error_db("[Error] decode json for batches number"); exit; } } // cari harga per item, $sqlstock = "SELECT * FROM stock WHERE StockItemID = ? AND StockItemUnitID = ? AND StockWarehouseID = ?"; $qrystock = $this->db->query($sqlstock, [ $value["T_ItemOutDetailM_ItemID"], $value["T_ItemOutDetailItemUnitID"], $value["T_ItemOutWarehouseID"] ]); if (!$qrystock) { $this->sys_error_db("select stock error", $this->db); exit; } $rowstock = $qrystock->row_array(); if (!$rowstock) { $this->sys_error("No stock found for item " . $value["M_ItemDesc"]); exit; } $sql = "INSERT INTO item_usage( ItemUsageS_RegionalID, ItemUsageM_BranchCode, ItemUsageDivisionID, ItemUsageM_ItemID, ItemUsageItemUnitID, ItemUsageQty, ItemUsagePrice, ItemUsageIsActive, ItemUsageUserID, ItemUsageCreated) VALUES(?,?,?,?,?,?,?,?,'Y',?,NOW())"; $qry = $this->db->query($sql, [ $value["RequestItemOutS_RegionalID"], $value["RequestItemOutM_BranchCode"], $value["T_ItemOutDivisionID"], $value["T_ItemOutDetailM_ItemID"], $value["T_ItemOutDetailItemUnitID"], $value["T_ItemOutDetailQty"], $rowstock["StockItemPrice"], $userID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item usage", $this->db); exit; } $last_id = $this->db->insert_id(); foreach ($batches as $k => $v) { $sql = "INSERT INTO item_usage_detail( ItemUsageDetailItemUsageID, ItemUsageDetailWarehouseID, ItemUsageDetailWarehouseName, ItemUsageDetailStockID, ItemUsageDetailStockNumber, ItemUsageDetailStockItemID, ItemUsageDetailStockItemUnitID, ItemUsageDetailItemUnitCode, ItemUsageDetailItemUnitName, ItemUsageDetailStockItemPrice, ItemUsageDetailStockBatchNo, ItemUsageDetailStockED, ItemUsageDetailStockQty, ItemUsageDetailQtyReq, ItemUsageDetailUserID, ItemUsageDetailCreated) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())"; $qry = $this->db->query($sql, [ $last_id, $v["WarehouseID"], $v["WarehouseName"], $v["StockID"], $v["StockStockNumber"], $v["StockItemID"], $v["StockItemUnitID"], $v["ItemUnitCode"], $v["ItemUnitName"], $v["StockItemPrice"], $v["StockBatchNo"], $v["StockED"], $v["StockQty"], $v["QtyReq"], $userID ]); if (!$qry) { $this->db->trans_rollback(); $this->sys_error_db("Failed insert item usage detail", $this->db); exit; } } } $this->db->trans_commit(); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getItemBatchListing() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); exit; } $prm = $this->sys_input; $batchNo = "%"; if ($prm['batchno'] != '') { $batchNo = $prm['batchno'] . "%"; } $sqlbatch = "SELECT WarehouseID, WarehouseName, StockID, StockStockNumber, StockItemID, StockItemUnitID, ItemUnitCode, ItemUnitName, StockItemPrice, StockBatchNo, StockED, StockQty FROM warehouse JOIN stock ON StockWarehouseID = WarehouseID AND StockQty > 0 JOIN itemunit ON ItemUnitID = StockItemUnitID AND ItemUnitIsActive= 'Y' WHERE WarehouseIsActive = 'Y' AND WarehouseIsTransit = 'N' AND WarehouseS_RegionalID = ? AND WarehouseM_BranchID = ? AND StockItemID = ? AND StockItemUnitID = ? AND StockBatchNo LIKE ?"; $quebatch = $this->db->query($sqlbatch, [ $prm['regionalid'], $prm['branchid'], $prm['itemid'], $prm['unitid'], $batchNo ]); if (!$quebatch) { $this->sys_error_db("error get data draft detail transfer"); exit; } $batch = $quebatch->result_array(); $result = array( 'records' => $batch, 'total' => sizeof($batch) ); $this->sys_ok($result); } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } public function getlevel() { try { if (!$this->isLogin) { $this->sys_error("Invalid Token"); } $payload = $this->sys_input; $user = $this->sys_user; $regionalID = $user['S_RegionalID']; $branchCode = $user['M_BranchCode']; $loginType = $user['M_UserLocationFlag']; $userID = $user['M_UserID']; $sql = "SELECT m_approve_level.* FROM m_user JOIN m_approve_level ON M_UserM_ApproveLevelID = M_ApproveLevelID WHERE M_UserID = ?"; $qry = $this->db->query($sql, [$userID]); if (!$qry) { $this->sys_error_db("Error cek approval level"); exit; } $approvalLevel = $qry->result_array(); if (count($approvalLevel) == 0) { $result = array( 'total' => 0, 'records' => [] ); $this->sys_ok($result); exit; } else { $result = array( "total" => 1, "records" => $approvalLevel, "sql" => $this->db->last_query() ); $this->sys_ok($result); } } catch (Exception $exc) { $message = $exc->getMessage(); $this->sys_error($message); } } }