isLogin) { $this->sys_error("invalid token"); exit; } $para = $this->sys_input; $startdate = $para["startdate"]; $enddate = $para["enddate"]; $regional = $para["regional"]; $branch = $para['branch']; $cabang = ""; if ($branch == "ALL" || $branch == "") { $cabang = "%%"; } else { $cabang = "%" . $branch . "%"; } $f_stat = []; $status = $para["status"]; switch ($status) { case 'READ': $f_stat = ["Read", "X"]; break; case 'NEW': $f_stat = ["Approved", "X"]; break; default: $f_stat = ["Approved", "Read"]; break; } $currpage = $para["currpage"]; $page = 0; $limit = 10; if ($currpage > 0) { $page = ($currpage - 1) * $limit; } $sqltotal = "SELECT COUNT(DISTINCT PurchaseRequestDetailID) as total FROM purchase_request JOIN purchase_request_detail ON PurchaseRequestDetailPurchaseRequestID = PurchaseRequestID AND PurchaseRequestDetailIsActive = 'Y' JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID AND S_RegionalIsActive = 'Y' JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y' LEFT JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode AND M_BranchIsActive = 'Y' LEFT JOIN warehouse ON WarehouseS_RegionalID = PurchaseRequestS_RegionalID AND WarehouseIsActive = 'Y' AND (WarehouseIsTransit != 'Y' OR WarehouseIsTransit IS NULL) LEFT JOIN stock ON StockItemId = M_ItemID AND StockWarehouseID = warehouse.WarehouseID LEFT JOIN purchase_request_flag ON PurchaseRequestFlagPurchaseRequestDetailID = PurchaseRequestDetailID AND PurchaseRequestFlagIsActive = 'Y' AND PurchaseRequestFlagIsClosed = 'N' WHERE PurchaseRequestDetailStatus IN (?,?) AND PurchaseRequestDate >= DATE(?) AND PurchaseRequestDate <= DATE(?) AND PurchaseRequestS_RegionalID = ? AND (? = '%%' OR (PurchaseRequestM_BranchCode LIKE ? OR (PurchaseRequestM_BranchCode IS NULL AND ? = '%%'))) -- GROUP BY PurchaseRequestDetailID ORDER BY PurchaseRequestDetailStatus ASC, PurchaseRequestDetailID DESC, PurchaseRequestDate DESC, PurchaseRequestFlagID DESC "; $qrytot = $this->db->query($sqltotal, [ $f_stat[0], $f_stat[1], $startdate, $enddate, $regional, $cabang, $cabang, $cabang ]); $total = $qrytot->result_array()[0]['total']; $sqldata = "SELECT COALESCE(PurchaseRequestFlagID, 'new') as PurchaseRequestFlagID, PurchaseRequestDate, PurchaseRequestNumber, S_RegionalID, S_RegionalName, COALESCE(M_BranchCode, '') AS M_BranchCode, COALESCE(M_BranchName, '') AS M_BranchName, PurchaseRequestDetailID, M_ItemID, PurchaseRequestDetailIsCito, M_ItemDesc, PurchaseRequestDetailQty, COALESCE(SUM(CASE WHEN warehouse.WarehouseID IS NOT NULL AND stock.StockItemId IS NOT NULL THEN stock.StockQty ELSE 0 END), 0) as StockQty, COALESCE(warehouse.WarehouseID, '') as StockWarehouseID, PurchaseRequestDetailStatus, COALESCE(PurchaseRequestFlagStatus, '') as PurchaseRequestFlagStatus FROM purchase_request JOIN purchase_request_detail ON PurchaseRequestDetailPurchaseRequestID = PurchaseRequestID AND PurchaseRequestDetailIsActive = 'Y' JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID AND S_RegionalIsActive = 'Y' JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y' LEFT JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode AND M_BranchIsActive = 'Y' LEFT JOIN warehouse ON WarehouseS_RegionalID = PurchaseRequestS_RegionalID AND WarehouseIsActive = 'Y' AND (WarehouseIsTransit != 'Y' OR WarehouseIsTransit IS NULL) LEFT JOIN stock ON StockItemId = M_ItemID AND StockWarehouseID = warehouse.WarehouseID LEFT JOIN purchase_request_flag ON PurchaseRequestFlagPurchaseRequestDetailID = PurchaseRequestDetailID AND PurchaseRequestFlagIsActive = 'Y' AND PurchaseRequestFlagIsClosed = 'N' WHERE PurchaseRequestDetailStatus IN (?,?) AND PurchaseRequestDate >= DATE(?) AND PurchaseRequestDate <= DATE(?) AND PurchaseRequestS_RegionalID = ? AND (? = '%%' OR (PurchaseRequestM_BranchCode LIKE ? OR (PurchaseRequestM_BranchCode IS NULL AND ? = '%%'))) GROUP BY PurchaseRequestDetailID ORDER BY PurchaseRequestDetailStatus ASC, PurchaseRequestDetailID DESC, PurchaseRequestDate DESC, PurchaseRequestFlagID DESC LIMIT ? OFFSET ?"; $qrydata = $this->db->query($sqldata, [ $f_stat[0], $f_stat[1], $startdate, $enddate, $regional, $cabang, $cabang, $cabang, $limit, $page ]); $data = $qrydata->result_array(); $result = array( "records" => $data, "total" => $total ); $this->sys_ok($result); } catch (Exception $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } /** * Refactor 18 Juni 2025 by Mario * Whats new: * > Perhitungan stok mempertimbangkan: * > - Stock hanya diambil dari gudang regional * > - Stock hanya diambil dari gudang default (untuk handle kasus jakarta ada 2 gudang regional) * > - Mempertimbangkan unitItem sebelum menjumlahkan kuantitas stok karena ada PR dengan unit berbeda * > - Menghitung stok dengan subquery daripada join * > - Memperhatikan StockED untuk ItemCategoryID == 1 (Persediaan), jika ItemID dan ItemUnitID sama maka jumlah semua stok dari Gudang Regional selama StockED >= hari ini */ public function list_purchaserequest() { try { if (!$this->isLogin) { $this->sys_error("invalid token"); exit; } $para = $this->sys_input; $startdate = $para["startdate"]; $enddate = $para["enddate"]; $regional = $para["regional"]; $branch = $para['branch']; $cabang = ""; if ($branch == "ALL" || $branch == "") { $cabang = "%%"; } else { $cabang = "%" . $branch . "%"; } $f_stat = []; $status = $para["status"]; switch ($status) { case 'READ': $f_stat = ["Read", "X"]; break; case 'NEW': $f_stat = ["Approved", "X"]; break; default: $f_stat = ["Approved", "Read"]; break; } $currpage = $para["currpage"]; $page = 0; $limit = 10; if ($currpage > 0) { $page = ($currpage - 1) * $limit; } $sqltotal = "SELECT COUNT(DISTINCT PurchaseRequestDetailID) as total FROM purchase_request_detail JOIN purchase_request ON PurchaseRequestDetailPurchaseRequestID = PurchaseRequestID AND PurchaseRequestDetailIsActive = 'Y' JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID AND S_RegionalIsActive = 'Y' JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y' LEFT JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode AND M_BranchIsActive = 'Y' LEFT JOIN itemunit unit_req ON unit_req.ItemUnitID = PurchaseRequestDetailItemUnitID AND unit_req.ItemUnitIsActive = 'Y' LEFT JOIN purchase_request_flag ON PurchaseRequestFlagPurchaseRequestDetailID = PurchaseRequestDetailID AND PurchaseRequestFlagIsActive = 'Y' AND PurchaseRequestFlagIsClosed = 'N' WHERE PurchaseRequestDetailStatus IN (?,?) AND PurchaseRequestDate >= DATE(?) AND PurchaseRequestDate <= DATE(?) AND PurchaseRequestS_RegionalID = ? AND (? = '%%' OR (PurchaseRequestM_BranchCode LIKE ? OR (PurchaseRequestM_BranchCode IS NULL AND ? = '%%')))"; $qrytot = $this->db->query($sqltotal, [ $f_stat[0], $f_stat[1], $startdate, $enddate, $regional, $cabang, $cabang, $cabang ]); $total = $qrytot->result_array()[0]['total']; $sqldata = "SELECT COALESCE(PurchaseRequestFlagID, 'new') as PurchaseRequestFlagID, PurchaseRequestDate, PurchaseRequestNumber, S_RegionalID, S_RegionalName, COALESCE(M_BranchCode, '') AS M_BranchCode, COALESCE(M_BranchName, '') AS M_BranchName, PurchaseRequestDetailID, M_ItemID, PurchaseRequestDetailIsCito, M_ItemDesc, PurchaseRequestDetailQty, unit_req.ItemUnitName AS UnitRequest, unit_req.ItemUnitID AS UnitIDRequest, PurchaseRequestDetailStatus, COALESCE(PurchaseRequestFlagStatus, '') as PurchaseRequestFlagStatus FROM purchase_request_detail JOIN purchase_request ON PurchaseRequestDetailPurchaseRequestID = PurchaseRequestID AND PurchaseRequestDetailIsActive = 'Y' JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID AND S_RegionalIsActive = 'Y' LEFT JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode AND M_BranchIsActive = 'Y' JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y' JOIN itemunit unit_req ON unit_req.ItemUnitID = PurchaseRequestDetailItemUnitID AND unit_req.ItemUnitIsActive = 'Y' LEFT JOIN warehouse ON WarehouseS_RegionalID = PurchaseRequestS_RegionalID AND WarehouseIsActive = 'Y' AND (WarehouseIsTransit != 'Y' OR WarehouseIsTransit IS NULL) AND WarehouseM_BranchID = 0 AND WarehouseIsDefault = 'Y' LEFT JOIN purchase_request_flag ON PurchaseRequestFlagPurchaseRequestDetailID = PurchaseRequestDetailID AND PurchaseRequestFlagIsActive = 'Y' AND PurchaseRequestFlagIsClosed = 'N' WHERE PurchaseRequestDetailStatus IN (?,?) AND PurchaseRequestDate >= DATE(?) AND PurchaseRequestDate <= DATE(?) AND PurchaseRequestS_RegionalID = ? AND (? = '%%' OR (PurchaseRequestM_BranchCode LIKE ? OR (PurchaseRequestM_BranchCode IS NULL AND ? = '%%'))) ORDER BY PurchaseRequestDetailStatus ASC, PurchaseRequestDetailID DESC, PurchaseRequestDate DESC, PurchaseRequestFlagID DESC LIMIT ? OFFSET ?"; $qrydata = $this->db->query($sqldata, [ $f_stat[0], $f_stat[1], $startdate, $enddate, $regional, $cabang, $cabang, $cabang, $limit, $page ]); if (!$qrydata) { throw new Exception(json_encode($this->db->error())); } // TODO: Hapus Last Query jika tidak diperlukan // $lastQuery = $this->db->last_query(); $data = $qrydata->result_array(); // For each PR Detail, fetch the stock data across all units foreach ($data as &$row) { $stockSql = "SELECT SUM(s.StockQty) as QtyTotal, u.ItemUnitName, s.StockItemUnitID FROM stock s JOIN warehouse w ON s.StockWarehouseID = w.WarehouseID JOIN itemunit u ON s.StockItemUnitID = u.ItemUnitID WHERE s.StockItemId = ? AND w.WarehouseS_RegionalID = ? AND w.WarehouseIsActive = 'Y' AND (w.WarehouseIsTransit != 'Y' OR w.WarehouseIsTransit IS NULL) AND w.WarehouseM_BranchID = 0 AND w.WarehouseIsDefault = 'Y' AND ( ? != 1 OR (? = 1 AND (s.StockED IS NULL OR s.StockED >= CURDATE())) ) GROUP BY s.StockItemUnitID, u.ItemUnitName"; $stockQry = $this->db->query($stockSql, [ $row['M_ItemID'], $row['S_RegionalID'], $row['PurchaseRequestItemCategoryID'] ?? 0, $row['PurchaseRequestItemCategoryID'] ?? 0 ]); if (!$stockQry) { throw new Exception(json_encode($this->db->error())); } // Kalau item belum ada di stock, returnnya [] $row['StockQtyArray'] = $stockQry->result_array(); } $result = array( "records" => $data, // "lastQuery" => $lastQuery, "total" => $total ); $this->sys_ok($result); } catch (Exception $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } public function listing_regional() { try { if (!$this->isLogin) { $this->sys_error("invalid token"); exit; } $prm = $this->sys_input; $search = "%" . $prm['search'] . "%"; $sql = "SELECT S_RegionalID, S_RegionalName FROM s_regional WHERE S_RegionalIsActive = 'Y' AND S_RegionalName LIKE ?"; $query = $this->db->query($sql, [$search]); if (!$query) { $this->sys_error_db("error get listing regional"); exit; } $rows = $query->result_array(); $result = array( "records" => $rows, "total" => sizeof($rows) ); $this->sys_ok($result); } catch (Exception $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } public function listing_branch() { try { if (!$this->isLogin) { $this->sys_error("invalid token"); exit; } $prm = $this->sys_input; $regID = $prm['regionalID']; $sql = "SELECT M_BranchCode, M_BranchName FROM m_branch WHERE M_BranchS_RegionalID = ? AND M_BranchIsActive = 'Y'"; $query = $this->db->query($sql, [$regID]); if (!$query) { $this->sys_error_db("error get branch based on regional"); exit; } $rows = $query->result_array(); $result = array( "result" => $rows, "total" => sizeof($rows) ); $this->sys_ok($result); } catch (Exception $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } public function insertstatus() { try { if (!$this->isLogin) { $this->sys_error("invalid token"); exit; } $this->db->trans_begin(); $userid = $this->sys_user["M_UserID"]; $prm = $this->sys_input; $reqnumber = $prm['reqnumber']; $reqdetailid = $prm['reqdetailid']; $regionalid = $prm['regionalid']; $branchcode = $prm['branchcode']; $qty = $prm['qty']; $stock = $prm['stock']; $status = $prm['status']; $UnitIDRequest = $prm['UnitIDRequest']; /* Validasi ItemUnit Request dengan Stock */ $stock = $this->calculateStockByItemUnitReq($stock, $UnitIDRequest); $sqlin = "INSERT INTO purchase_request_flag ( PurchaseRequestFlagPurchaseRequestDetailID, PurchaseRequestFlagS_RegionalID, PurchaseRequestFlagM_BranchCode, PurchaseRequestFlagQty, PurchaseRequestFlagQtyRest, PurchaseRequestFlagStatus, PurchaseRequestFlagUserID, PurchaseRequestFlagCreated ) VALUES (?,?,?,?,?,?,?,NOW())"; $query = $this->db->query($sqlin, [ $reqdetailid, $regionalid, $branchcode, $qty, $qty, $status, $userid ]); if (!$query) { $this->db->trans_rollback(); $this->sys_error_db("error insert status request"); exit; } $reqflagid = $this->db->insert_id(); $sqlget = "SELECT * FROM purchase_request_flag WHERE PurchaseRequestFlagID = ?"; $queget = $this->db->query($sqlget, [$reqdetailid]); if (!$queget) { $this->db->trans_rollback(); $this->sys_error_db("error get latest inserted flag"); exit; } $data = $queget->result_array()[0]; $json = json_encode($data); $sqlog = "INSERT INTO acc_one_log.purchaserequestflag_log ( PurchaseRequestFlagLogPurchaseRequestFlagID, PurchaseRequestFlagLogStatus, PurchaseRequestFlagLogQty, PurchaseRequestFlagLogStock, PurchaseRequestFlagLogJson, PurchaseRequestFlagLogUserID, PurchaseRequestFlagLogCreated ) VALUES (?, ?, ?, ?, ?, ?, NOW())"; $quelog = $this->db->query($sqlog, [$reqflagid, $status, $qty, $stock, $json, $userid]); if (!$quelog) { $this->db->trans_rollback(); $this->sys_error_db("error insert log purchase request flag"); exit; } $sqlread = "UPDATE purchase_request_detail SET PurchaseRequestDetailStatus = 'Read' WHERE PurchaseRequestDetailID = ?"; $queread = $this->db->query($sqlread, [$reqdetailid]); if (!$queread) { $this->db->trans_rollback(); $this->sys_error_db("error update status purchase request detail to read"); exit; } $sqlpart = "UPDATE purchase_request SET PurchaseRequestStatus = 'Partial' WHERE PurchaseRequestRefNumber = ?"; $quepart = $this->db->query($sqlpart, [$reqnumber]); if (!$quepart) { $this->db->trans_rollback(); $this->sys_error_db("error update status purchase request to partial"); exit; } $this->db->trans_commit(); $this->sys_ok("Success insert status request"); } catch (\Throwable $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } public function changestatus() { try { if (!$this->isLogin) { $this->sys_error("invalid token"); exit; } $this->db->trans_begin(); $userid = $this->sys_user["M_UserID"]; $prm = $this->sys_input; $reqnumber = $prm['reqnumber']; $reqflagid = $prm['requestflagid']; $reqdetailid = $prm['reqdetailid']; $regionalid = $prm['regionalid']; $branchcode = $prm['branchcode']; $reqqty = $prm['qty']; $reqstock = $prm['stock']; $reqstatus = $prm['status']; $UnitIDRequest = $prm['UnitIDRequest']; /* Validasi ItemUnit Request dengan Stock */ $stock = $this->calculateStockByItemUnitReq($reqstock, $UnitIDRequest); $sqlcha = "UPDATE purchase_request_flag SET PurchaseRequestFlagPurchaseRequestDetailID = ?, PurchaseRequestFlagS_RegionalID = ?, PurchaseRequestFlagM_BranchCode = ?, PurchaseRequestFlagQty = ?, PurchaseRequestFlagQtyRest = ?, PurchaseRequestFlagStatus = ?, PurchaseRequestFlagUserID = ?, PurchaseRequestFlagLastUpdated = NOW() WHERE PurchaseRequestFlagID = ?"; $query = $this->db->query($sqlcha, [ $reqdetailid, $regionalid, $branchcode, $reqqty, $reqqty, $reqstatus, $userid, $reqflagid ]); if (!$query) { $this->db->trans_rollback(); $this->sys_error_db("error update status flag requset"); exit; } $sqlget = "SELECT * FROM purchase_request_flag WHERE PurchaseRequestFlagID = ?"; $queget = $this->db->query($sqlget, [$reqflagid]); if (!$queget) { $this->db->trans_rollback(); $this->sys_error_db("error get data change"); exit; } $data = $queget->result_array()[0]; $json = json_encode($data); $sqlog = "INSERT INTO acc_one_log.purchaserequestflag_log ( PurchaseRequestFlagLogPurchaseRequestFlagID, PurchaseRequestFlagLogStatus, PurchaseRequestFlagLogQty, PurchaseRequestFlagLogStock, PurchaseRequestFlagLogJson, PurchaseRequestFlagLogUserID, PurchaseRequestFlagLogCreated ) VALUES (?, ?, ?, ?, ?, ?, NOW())"; $quelog = $this->db->query($sqlog, [$reqflagid, $reqstatus, $reqqty, $stock, $json, $userid]); if (!$quelog) { $this->db->trans_rollback(); $this->sys_error_db("error insert log purchase request flag"); exit; } $this->db->trans_commit(); $this->sys_ok("success"); } catch (\Throwable $ex) { $message = $ex->getMessage(); $this->sys_error($message); } } private function calculateStockByItemUnitReq(array $stock, string $UnitIDRequest): int { // Cek $stock empty array atau tidak, jika iya $stok = 0 if (!is_array($stock) || empty($stock)) { return 0; } // Cari object di $stock di mana StockItemUnitID == $UnitIDRequest, // Jika tidak ada, set $stock = 0 $equivalentStock = array_filter($stock, function ($item) use ($UnitIDRequest) { return $item['StockItemUnitID'] == $UnitIDRequest; }); if (empty($equivalentStock)) { return 0; } return array_values($equivalentStock)[0]['QtyTotal'] ?? 0; } }