<?php

class PurchaseRequestPersediaan extends MY_Controller
{
    var $db;

    public function index()
    {
        echo "Purchase Request/Requester API";
    }

    public function __construct()
    {
        parent::__construct();
    }

    function search()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];
            $currentPage = isset($payload["currentPage"]) ? $payload["currentPage"] : 1;
            $pageSize = isset($payload["pageSize"]) ? $payload["pageSize"] : 10;
            $offset = ($currentPage - 1) * $pageSize;
            $startDate = isset($payload["startDate"]) ? $payload["startDate"] : "";
            $endDate = isset($payload["endDate"]) ? $payload["endDate"] : "";
            $status = isset($payload["status"]) ? $payload["status"] : "";
            $search = isset($payload["search"]) ? $payload["search"] : "";

            $params = [];
            $params[] = $startDate;
            $params[] = $endDate;
            $params[] = $status;
            $params[] = $status;
            $params[] = '%' . $search . '%';

            $sql = "SELECT M_UserLocationS_RegionalID as S_RegionalID,
                    IFNULL(M_BranchCode, '') as M_BranchCode,
                    IFNULL(M_BranchName, '') as M_BranchName
                    FROM m_userlocation
                    LEFT JOIN m_branch ON M_BranchID = M_UserLocationM_BranchID
                    WHERE
                    M_UserLocationIsActive = 'Y' AND
                    M_UserLocationM_UserID = ?";
            $exec = $this->db->query($sql, [$userId]);
            if (!$exec) {
                $this->sys_error_db("select user location", $this->db);
                exit;
            }
            $rows = $exec->result_array();
            $prRegional = $rows[0]["S_RegionalID"];
            $prBranch = $rows[0]["M_BranchCode"];

            $filterBranch = "";
            if ($prBranch != "") {
                $filterBranch = " AND PurchaseRequestM_BranchCode = ?";
                $params[] = $prBranch;
            }

            $filterRegional = "";
            if ($prBranch == "") {
                $filterRegional = " AND PurchaseRequestS_RegionalID = ?";
                $params[] = $prRegional;
            }




            $totalCount = 0;
            $totalPage = 1;



            $sql = "SELECT COUNT(*) as total
                    FROM purchase_request
                    JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID
                    WHERE 
                    PurchaseRequestIsActive = 'Y' AND 
                    PurchaseRequestItemCategoryID = '1' AND
                    ( PurchaseRequestDate BETWEEN ? AND ? ) AND 
                    ( PurchaseRequestStatus = ? OR ? = '' ) AND 
                    ( PurchaseRequestNumber LIKE ?)
                    $filterBranch
                    $filterRegional";

            $exec = $this->db->query($sql, $params);

            if ($exec) {
                $totalCount = $exec->result_array()[0]["total"];
                $totalPage = ceil($totalCount / $pageSize);
            } else {
                $this->sys_error_db("select purchase request", $this->db);
                exit;
            }

            $params[] = $offset;
            $params[] = $pageSize;

            $sql = "SELECT DISTINCT PurchaseRequestID as prId, 
                    PurchaseRequestNumber as prNumber, 
                    PurchaseRequestDate as prDate, 
                    PurchaseRequestStatus as prStatus, 
                    requestedBy.M_UserUsername as prRequestedBy, 
                    IF(PurchaseRequestApprovedBy IS NULL, '', approvedBy.M_UserUsername) as prApprovedBy,
                    PurchaseRequestS_RegionalID as prRegionalId,
                    S_RegionalName as prRegionalName,
                    PurchaseRequestM_BranchCode as prBranchCode,
                    M_BranchName as prBranchName,
                    PurchaseRequestNote as prNote,
                    PurchaseRequestStatus as prStatus,
                    IFNULL(S_RegionalID, '') as S_RegionalID, 
                    IFNULL(S_RegionalName, '') as S_RegionalName,
                    IFNULL(M_BranchCode, '') as M_BranchCode,
                    IFNULL(M_BranchName, '') as M_BranchName
                    FROM purchase_request
                    JOIN s_regional ON S_RegionalID = PurchaseRequestS_RegionalID
                    JOIN m_user requestedBy ON requestedBy.M_UserID = PurchaseRequestRequestedBy
                    LEFT JOIN m_user approvedBy ON approvedBy.M_UserID = PurchaseRequestApprovedBy
                    LEFT JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode
                    WHERE 
                    PurchaseRequestIsActive = 'Y' AND 
                    PurchaseRequestItemCategoryID = '1' AND
                    ( PurchaseRequestDate BETWEEN ? AND ? ) AND 
                    ( PurchaseRequestStatus = ? OR ? = '' ) AND 
                    ( PurchaseRequestNumber LIKE ? ) 
                    $filterBranch
                    $filterRegional
                    ORDER BY prDate DESC, prId DESC 
                    LIMIT ?, ?";

            $exec = $this->db->query($sql, $params);
            if (!$exec) {
                $this->sys_error_db("select purchase request", $this->db);
                exit;
            }
            //echo $this->db->last_query();
            //exit;

            $rows = $exec->result_array();


            $result = array(
                "total" => $totalPage,
                "totalFilter" => $totalCount,
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function searchDetail()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $payload = $this->sys_input;

            // Menggunakan parameter query
            $queryCount = "SELECT count(*) as total
                           FROM purchase_request_direct_detail
                           WHERE PurchaseRequestDirectDetailIsActive = 'Y' 
                           AND PurchaseRequestDirectDetailPurchaseRequestDirectID = ?";
            $exec = $this->db->query($queryCount, [$payload['PRID']]);

            $totalCount = 0;

            if ($exec) {
                $totalCount = $exec->result_array()[0]["total"];
            } else {
                $this->db->trans_rollback();
                $this->sys_error_db("select purchase request detail", $this->db);;
                exit;
            }

            // Menggunakan parameter query
            $query = "SELECT *, 
                      ROW_NUMBER() OVER(ORDER BY PurchaseRequestDirectDetailID) RowNumber 
                      FROM purchase_request_direct_detail 
                      WHERE PurchaseRequestDirectDetailIsActive = 'Y' 
                      AND PurchaseRequestDirectDetailPurchaseRequestDirectID = ?
                      ORDER BY PurchaseRequestDirectDetailStatus ASC, 
                      PurchaseRequestDirectDetailID ASC";
            $exec = $this->db->query($query, [$payload['PRID']]);

            if ($exec) {
                $rows = $exec->result_array();
            } else {
                $this->db->trans_rollback();
                $this->sys_error_db("select purchase request detail", $this->db);
                exit;
            }

            $result = array(
                "totalFilter" => $totalCount,
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getBranch()
    {
        try {
            $query = "SELECT DISTINCT
                      M_BranchCode,
                      M_BranchName
                      FROM m_branch
                      WHERE M_BranchIsActive = 'Y'
                      ORDER BY M_BranchName ASC";
            $exec = $this->db->query($query, []);
            if ($exec) {
                $rows = $exec->result_array();
            } else {
                $this->db->trans_rollback();
                $this->sys_error_db("select branch", $this->db);
                exit;
            }

            $result = array(
                "records" => $rows,
                "sql" => $this->db->last_query()
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getRegionalBranchByUser()
    {
        try {
            $userId = $this->sys_user["M_UserID"];



            $query = "SELECT 
                        IFNULL(M_BranchID, 0) as M_BranchID,
                        IFNULL(M_BranchCode, '') as M_BranchCode,
                        IFNULL(M_BranchName, '') as M_BranchName
                        FROM m_userlocation 
                        LEFT JOIN m_branch ON M_UserLocationM_BranchID  = M_BranchID
                        WHERE M_UserLocationM_UserID = ? AND 
                            M_UserLocationIsActive = 'Y'
                        LIMIT 1";
            $exec = $this->db->query($query, [$userId]);
            if ($exec) {
                $rows = $exec->result_array();
            } else {
                $this->sys_error_db("select branch", $this->db);
                exit;
            }

            $rst = array();

            $branchId = $rows[0]["M_BranchID"];
            $branchCode = $rows[0]["M_BranchCode"];
            $branchName = $rows[0]["M_BranchName"];

            if (intval($branchId) > 0) {
                $rst = array(
                    array(
                        "M_BranchCode" => $branchCode,
                        "M_BranchName" => $branchName
                    )
                );
            } else {
                $query = "SELECT DISTINCT
                        M_BranchCode,
                        M_BranchName
                        FROM m_branch
                        JOIN m_userlocation ON M_BranchS_RegionalID = M_UserLocationS_RegionalID AND M_UserLocationM_UserID = ? AND M_UserLocationIsActive = 'Y'
                        WHERE M_BranchIsActive = 'Y' 
                        ORDER BY M_BranchName ASC";
                $exec = $this->db->query($query, [$userId]);
                if ($exec) {
                    $rst = $exec->result_array();
                } else {
                    $this->sys_error_db("select branch regional", $this->db);
                    exit;
                }
            }

            $this->sys_ok($rst);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function saveRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $user = $this->sys_user;
            $userId = $this->sys_user["M_UserID"];
            $dateNow = date('Y-m-d');


            // $pdSql = "SELECT `fn_numbering`('PD') AS PD";
            // $exec = $this->db->query($pdSql, []);
            // $pd = "";

            // if (!$exec) {
            //     $this->db->trans_rollback();
            //     $this->sys_error_db("purchase request insert error", $this->db);
            //     exit;
            // } else {
            //     $pd = $exec->result_array()[0]["PD"];
            // }

            $areaid = $user['M_BranchID'];
            $areatype = 'B';
            if ($user['loginLevel'] == 'regional') {
                $areaid = $user['S_RegionalID'];
                $areatype = 'R';
            }

            $sqlusrdivisi = "SELECT M_UserDivisionDivisionID FROM m_userdivision 
                WHERE M_UserDivisionM_UserID = ? AND M_UserDivisionIsActive = 'Y'";
            $queusrdivisi = $this->db->query($sqlusrdivisi, [$userId]);
            if (!$queusrdivisi) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get user divisi", $this->db);
                exit;
            }
            $userDivID = $queusrdivisi->row_array()['M_UserDivisionDivisionID'];

            $sqlnum = "SELECT `fn_penomoran`(?, ?, ?, ?, ?, ?) AS numpd;";
            $quenum = $this->db->query($sqlnum, ['SR', $userDivID, $areatype, $areaid, 'SM', 'Y']);
            if (!$quenum) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] generate number doc", $this->db);
                exit;
            }
            $numpd = $quenum->row_array()['numpd'];

            // Menggunakan parameter query untuk insert
            $sql = "INSERT INTO purchase_request_direct(
                    PurchaseRequestDirectNumber,
                    PurchaseRequestDirectDate,
                    PurchaseRequestDirectDateUse,
                    PurchaseRequestDirectM_BranchCode,
                    PurchaseRequestDirectDescription,
                    PurchaseRequestDirectNote,
                    PurchaseRequestDirectTotalEstimation,
                    PurchaseRequestDirectTotalPaid,
                    PurchaseRequestDirectTotalRealitation,
                    PurchaseRequestDirectStatus,
                    PurchaseRequestApprovedDate,
                    PurchaseRequestApprovedBy,
                    PurchaseRequestConfirmedDate,
                    PurchaseRequestConfirmedBy,
                    PurchaseRequestPaidDate,
                    PurchaseRequestPaidBy,
                    PurchaseRequestDirectIsActive,
                    PurchaseRequestCreated,
                    PurchaseRequestLastUpdated,
                    PurchaseRequestDeleted,
                    PurchaseRequestCreatedUserID,
                    PurchaseRequestLastUpdatedUserID,
                    PurchaseRequestDeletedUserID
                    ) VALUES (?, ?, ?, ?, ?, NULL, 0, 0, 0, 'Draft', NULL, NULL, NULL, NULL, NULL, NULL, 'Y', NOW(), NULL, NULL, ?, NULL, NULL)";

            $insertParams = [
                $numpd,
                $dateNow,
                $payload['PRDateUse'],
                $payload['PRBranch'],
                $payload['PRDescription'],
                $userId
            ];

            $exec = $this->db->query($sql, $insertParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request insert error", $this->db);
                exit;
            }

            $this->db->trans_commit();

            // Menggunakan parameter query untuk select
            $newInsert = "SELECT * FROM purchase_request_direct WHERE PurchaseRequestDirectNumber = ? AND PurchaseRequestDirectIsActive = 'Y'";
            $records = $this->db->query($newInsert, [$numpd])->result_array();

            $result = array("total" => 1, "records" => $records);
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function updateRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];

            // Menggunakan parameter query untuk update
            $sql = "UPDATE purchase_request_direct SET
                    PurchaseRequestDirectDateUse = ?,
                    PurchaseRequestDirectM_BranchCode = ?,
                    PurchaseRequestDirectDescription = ?,
                    PurchaseRequestLastUpdated = NOW(),
                    PurchaseRequestLastUpdatedUserID = ?
                    WHERE PurchaseRequestDirectID = ?";

            $updateParams = [
                $payload['PRDateUse'],
                $payload['PRBranch'],
                $payload['PRDescription'],
                $userId,
                $payload['PRID']
            ];

            $exec = $this->db->query($sql, $updateParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request update error", $this->db);
                exit;
            }

            $this->db->trans_commit();

            // Menggunakan parameter query untuk select
            $newUpdate = "SELECT * FROM purchase_request_direct WHERE PurchaseRequestDirectID = ? AND PurchaseRequestDirectIsActive = 'Y'";
            $records = $this->db->query($newUpdate, [$payload['PRID']])->result_array();

            $result = array("total" => 1, "records" => $records);
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function deletePurchaseRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];

            $prId = isset($payload['PRID']) ? $payload['PRID'] : "";
            $prNumber = isset($payload['PRNumber']) ? $payload['PRNumber'] : "";

            if ($prId == "" || $prId == null) {
                $this->sys_error("Invalid PR ID");
                exit;
            }

            $datas_log = array();
            $sql = "SELECT * FROM purchase_request WHERE PurchaseRequestID = ? AND PurchaseRequestIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);
            if (!$query) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request select", $this->db);
                exit;
            }
            $header = $query->row_array();


            $datas_log['header'] = $header;
            $sql = "SELECT * 
                    FROM purchase_request_detail 
                    JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
                    JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID AND ItemUnitIsActive = 'Y'
                    WHERE PurchaseRequestDetailPurchaseRequestID = ? AND PurchaseRequestDetailIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);
            if (!$query) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request detail select", $this->db);
                exit;
            }
            $details = $query->result_array();
            $datas_log['details'] = $details;

            $sql = "UPDATE purchase_request SET
                    PurchaseRequestDeleted = NOW(),
                    PurchaseRequestDeletedUserID = ?,
                    PurchaseRequestIsActive = 'N'
                    WHERE PurchaseRequestID = ?";
            $deleteParams = [$userId, $prId];
            $exec = $this->db->query($sql, $deleteParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request delete error", $this->db);
                exit;
            }

            $sql = "UPDATE purchase_request_detail 
                    SET 
                        PurchaseRequestDetailIsActive = 'N', 
                        PurchaseRequestDetailDeleted = NOW(), 
                        PurchaseRequestDetailDeletedUserID = ? 
                    WHERE PurchaseRequestDetailPurchaseRequestID = ? AND 
                        PurchaseRequestDetailIsActive = 'Y'";
            $deleteParams = [$userId, $prId];
            $exec = $this->db->query($sql, $deleteParams);
            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request detail delete error", $this->db);
                exit;
            }

            $messages = "Purchase Request dengan kode " . $prNumber . " sudah dihapus";
            $this->insert_act_log("PRP", "Delete", $messages, $prId, $this->safeJsonEncode($datas_log), $userId);

            $this->db->trans_commit();
            $result = array("total" => 1, "records" => array("xId" => 0));
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function saveDetail()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];
            $PRDTotalPrice = intval($payload['PRDAmountRequest']) * intval($payload['PRDEstimationPrice']);

            // Gunakan parameter untuk query existensi
            $query = "SELECT COUNT(*) as exist 
                      FROM purchase_request_direct_detail 
                      WHERE PurchaseRequestDirectDetailIsActive = 'Y' 
                      AND PurchaseRequestDirectDescription = ? 
                      AND PurchaseRequestDirectDetailPurchaseRequestDirectID = ?";
            $existParams = [$payload['PRDDescriptionDetail'], $payload['PRID']];
            $exist = $this->db->query($query, $existParams);

            if ($exist) {
                $row = $exist->row()->exist;
            } else {
                $this->sys_error_db("exist error", $this->db);
                exit;
            }

            if ($row == 0) {
                // Gunakan parameter untuk query insert
                $sql = "INSERT INTO purchase_request_direct_detail(
                        PurchaseRequestDirectDetailPurchaseRequestDirectID,
                        PurchaseRequestDirectDescription,
                        PurchaseRequestDirectDetailAmountRequest,
                        PurchaseRequestDirectDetailAmount,
                        PurchaseRequestDirectDetailEstimationPrice,
                        PurchaseRequestDirectDetailTotalEstimationPrice,
                        PurchaseRequestDirectDetailTotalRealitationPrice,
                        PurchaseRequestDirectDetailStatus,
                        PurchaseRequestDirectDetailIsActive,
                        PurchaseRequestDirectDetailCreated,
                        PurchaseRequestDirectDetailLastUpdated,
                        PurchaseRequestDirectDetailDeleted,
                        PurchaseRequestDirectDetailCreatedUserID,
                        PurchaseRequestDirectDetailLastUpdatedUserID,
                        PurchaseRequestDirectDetailDeletedUserID
                        ) VALUES (?, ?, ?, NULL, ?, ?, NULL, 'Pending', 'Y', NOW(), NULL, NULL, ?, NULL, NULL)";

                $insertParams = [
                    $payload['PRID'],
                    $payload['PRDDescriptionDetail'],
                    $payload['PRDAmountRequest'],
                    $payload['PRDEstimationPrice'],
                    $PRDTotalPrice,
                    $userId
                ];

                $exec = $this->db->query($sql, $insertParams);

                if (!$exec) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("request insert error", $this->db);
                    exit;
                } else {
                    // Gunakan parameter untuk query total harga
                    $sqlTotalPrice = "SELECT SUM(PurchaseRequestDirectDetailTotalEstimationPrice) AS Total
                                      FROM purchase_request_direct_detail
                                      WHERE PurchaseRequestDirectDetailPurchaseRequestDirectID = ?
                                      AND PurchaseRequestDirectDetailIsActive = 'Y'";
                    $totalPriceParams = [$payload['PRID']];
                    $exec = $this->db->query($sqlTotalPrice, $totalPriceParams);
                    $total = 0;

                    if (!$exec) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("order purchase request error", $this->db);
                        exit;
                    } else {
                        $total = $exec->result_array()[0]["Total"];
                    }

                    // Gunakan parameter untuk query update
                    $sql = "UPDATE purchase_request_direct SET
                            PurchaseRequestDirectTotalEstimation = ?,
                            PurchaseRequestLastUpdated = NOW(),
                            PurchaseRequestLastUpdatedUserID = ?
                            WHERE PurchaseRequestDirectID = ?
                            AND PurchaseRequestDirectIsActive = 'Y'";
                    $updateParams = [$total, $userId, $payload['PRID']];
                    $exec = $this->db->query($sql, $updateParams);
                }

                $this->db->trans_commit();

                $result = array("total" => 1, "records" => array("xId" => 0));
                $this->sys_ok($result);
            } else {
                $errors = array();
                if ($row != 0) {
                    array_push($errors, array('msg' => 'Data 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);
        }
    }

    function updateDetail()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];
            $PRDTotalPrice = intval($payload["PRDAmountRequest"]) * intval($payload["PRDEstimationPrice"]);

            // Gunakan parameter untuk query update
            $sql = "UPDATE purchase_request_direct_detail SET
                    PurchaseRequestDirectDescription = ?,
                    PurchaseRequestDirectDetailAmountRequest = ?,
                    PurchaseRequestDirectDetailEstimationPrice = ?,
                    PurchaseRequestDirectDetailTotalEstimationPrice = ?,
                    PurchaseRequestDirectDetailLastUpdated = NOW(),
                    PurchaseRequestDirectDetailLastUpdatedUserID = ?
                    WHERE PurchaseRequestDirectDetailID = ?";

            $updateParams = [
                $payload["PRDDescriptionDetail"],
                $payload["PRDAmountRequest"],
                $payload["PRDEstimationPrice"],
                $PRDTotalPrice,
                $userId,
                $payload["PRDID"]
            ];

            $exec = $this->db->query($sql, $updateParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("request update error", $this->db);
                exit;
            } else {
                // Gunakan parameter untuk query total harga
                $sqlTotalPrice = "SELECT SUM(PurchaseRequestDirectDetailTotalEstimationPrice) AS Total
                                  FROM purchase_request_direct_detail
                                  WHERE PurchaseRequestDirectDetailPurchaseRequestDirectID = ?
                                  AND PurchaseRequestDirectDetailIsActive = 'Y'";
                $totalPriceParams = [$payload["PRID"]];
                $exec = $this->db->query($sqlTotalPrice, $totalPriceParams);
                $total = 0;

                if (!$exec) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("order purchase request error", $this->db);
                    exit;
                } else {
                    $total = $exec->result_array()[0]["Total"];
                }

                // Gunakan parameter untuk query update total
                $sql = "UPDATE purchase_request_direct SET
                        PurchaseRequestDirectTotalEstimation = ?,
                        PurchaseRequestLastUpdated = NOW(),
                        PurchaseRequestLastUpdatedUserID = ?
                        WHERE PurchaseRequestDirectID = ?
                        AND PurchaseRequestDirectIsActive = 'Y'";
                $updateTotalParams = [$total, $userId, $payload["PRID"]];
                $exec = $this->db->query($sql, $updateTotalParams);
            }

            $this->db->trans_commit();

            $result = array("total" => 1, "records" => array("xId" => $payload["PRDID"]));
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function deleteDetail()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];

            // Gunakan parameter untuk query delete
            $sql = "UPDATE purchase_request_direct_detail SET
                    PurchaseRequestDirectDetailDeleted = NOW(),
                    PurchaseRequestDirectDetailDeletedUserID = ?,
                    PurchaseRequestDirectDetailIsActive = 'N'
                    WHERE PurchaseRequestDirectDetailID = ?";
            $deleteParams = [$userId, $payload["PRDID"]];
            $exec = $this->db->query($sql, $deleteParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("request delete error", $this->db);
                exit;
            } else {
                // Gunakan parameter untuk query total harga
                $sqlTotalPrice = "SELECT SUM(PurchaseRequestDirectDetailTotalEstimationPrice) AS Total
                                  FROM purchase_request_direct_detail
                                  WHERE PurchaseRequestDirectDetailPurchaseRequestDirectID = ?
                                  AND PurchaseRequestDirectDetailIsActive = 'Y'";
                $totalPriceParams = [$payload["PRID"]];
                $exec = $this->db->query($sqlTotalPrice, $totalPriceParams);
                $total = 0;

                if (!$exec) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("order purchase request error", $this->db);
                    exit;
                } else {
                    $total = $exec->result_array()[0]["Total"] ?? 0;
                }

                // Gunakan parameter untuk query update total
                $sql = "UPDATE purchase_request_direct SET
                        PurchaseRequestDirectTotalEstimation = ?,
                        PurchaseRequestLastUpdated = NOW(),
                        PurchaseRequestLastUpdatedUserID = ?
                        WHERE PurchaseRequestDirectID = ?
                        AND PurchaseRequestDirectIsActive = 'Y'";
                $updateTotalParams = [$total, $userId, $payload["PRID"]];
                $exec = $this->db->query($sql, $updateTotalParams);
            }

            $this->db->trans_commit();
            $result = array("total" => 1, "records" => array("xId" => 0));
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function orderRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
            }

            $this->db->trans_begin();

            $payload = $this->sys_input;
            $userId = $this->sys_user["M_UserID"];

            // Gunakan parameter untuk query update detail
            $sqlDetail = "UPDATE purchase_request_direct_detail SET
                          PurchaseRequestDirectDetailLastUpdated = NOW(),
                          PurchaseRequestDirectDetailLastUpdatedUserID = ?
                          WHERE PurchaseRequestDirectDetailPurchaseRequestDirectID = ?
                          AND PurchaseRequestDirectDetailIsActive = 'Y'
                          AND PurchaseRequestDirectDetailStatus = 'Pending'";
            $detailParams = [$userId, $payload["PRID"]];
            $exec = $this->db->query($sqlDetail, $detailParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("order purchase request error", $this->db);
                exit;
            }

            // Gunakan parameter untuk query update status
            $sql = "UPDATE purchase_request_direct SET
                    PurchaseRequestDirectStatus = 'Pending',
                    PurchaseRequestLastUpdated = NOW(),
                    PurchaseRequestLastUpdatedUserID = ?
                    WHERE PurchaseRequestDirectID = ?
                    AND PurchaseRequestDirectIsActive = 'Y'";
            $updateParams = [$userId, $payload["PRID"]];
            $exec = $this->db->query($sql, $updateParams);

            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("order purchase request error", $this->db);
                exit;
            }

            $this->db->trans_commit();

            // Gunakan parameter untuk query select
            $sql = "SELECT *, 
                    ROW_NUMBER() OVER(ORDER BY PurchaseRequestDirectNumber) RowNumber 
                    FROM purchase_request_direct 
                    WHERE PurchaseRequestDirectIsActive = 'Y' 
                    AND PurchaseRequestCreatedUserID = ?
                    AND PurchaseRequestDirectID = ?";
            $selectParams = [$userId, $payload["PRID"]];
            $exec = $this->db->query($sql, $selectParams);

            $row = [];
            if (!$exec) {
                $this->db->trans_rollback();
                $this->sys_error_db("order purchase request error", $this->db);
                exit;
            } else {
                $row = $exec->result_array();
            }
            $result = array("total" => 1, "records" => $row);
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getDivisionByUser()
    {
        try {
            $userId = $this->sys_user["M_UserID"];

            $rows = [];
            $rows[] = array("DivisionID" => "", "DivisionCode" => "", "DivisionName" => "Semua Divisi");
            $query = "SELECT DISTINCT
                      DivisionID,
                      DivisionCode,
                      DivisionName
                      FROM division
                      WHERE DivisionIsActive = 'Y'
                      ORDER BY DivisionName ASC";
            $exec = $this->db->query($query);
            if ($exec) {
                $rst = $exec->result_array();
                foreach ($rst as $key => $row) {
                    $rows[] = array("DivisionID" => $row["DivisionID"], "DivisionCode" => $row["DivisionCode"], "DivisionName" => $row["DivisionName"]);
                }
            } else {
                $this->db->trans_rollback();
                $this->sys_error_db("select division", $this->db);
                exit;
            }

            $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) {
                $r_selected = $exec->result_array();
                if (count($r_selected) > 0) {
                    $row = $r_selected[0];
                } else {
                    $row = array("DivisionID" => "", "DivisionCode" => "", "DivisionName" => "Semua Divisi");
                }
            } 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);
        }
    }

    function getItemGroupSubGroup()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $prm = $this->sys_input;
            $division = isset($prm["division"]) ? $prm["division"] : "";

            $rows = [];
            $rows[] = array("Nat_GroupID" => "", "Nat_GroupCode" => "", "Nat_GroupName" => "Semua Grup", "subGroups" => [
                array("Nat_SubGroupID" => "", "Nat_SubGroupCode" => "", "Nat_SubGroupName" => "Semua Sub Grup")
            ]);

            $sql = "SELECT DISTINCT
                    Nat_GroupID,
                    Nat_GroupCode,
                    Nat_GroupName,
                    '' as subGroups
                    FROM nat_group
                    JOIN m_item ON M_ItemNat_GroupID = Nat_GroupID AND M_ItemIsActive = 'Y'
                    JOIN m_itemdivision ON M_ItemDivisionM_ItemID = M_ItemID AND M_ItemDivisionIsActive = 'Y' AND (M_ItemDivisionDivisionID = ? OR ? = '')
                    WHERE Nat_GroupIsActive = 'Y'
                    ORDER BY Nat_GroupID ASC";

            $query = $this->db->query($sql, [$division, $division]);

            if (!$query) {
                $this->sys_error_db("item group list", $this->db);
                exit;
            }

            $rst = $query->result_array();
            foreach ($rst as $key => $row) {
                $row["subGroups"] = array(
                    array("Nat_SubGroupID" => "", "Nat_SubGroupCode" => "", "Nat_SubGroupName" => "Semua Sub Grup")
                );
                if ($row["Nat_GroupID"] != "") {
                    $sqlSubGroup = "SELECT DISTINCT
                                Nat_SubGroupID,
                                Nat_SubGroupCode,
                                Nat_SubGroupName
                                FROM nat_subgroup
                                WHERE Nat_SubGroupIsActive = 'Y'
                                AND Nat_SubGroupNat_GroupID = ?
                                ORDER BY Nat_SubGroupID ASC";
                    $querySubGroup = $this->db->query($sqlSubGroup, [$row["Nat_GroupID"]]);
                    if (!$querySubGroup) {
                        $this->sys_error_db("item sub group list", $this->db);
                        exit;
                    }
                    $subGroups = $querySubGroup->result_array();
                    foreach ($subGroups as $keySubGroup => $subGroup) {
                        $row["subGroups"][] = $subGroup;
                    }
                }
                $rows[] = $row;
            }

            $result = array(
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getItems()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $user = $this->sys_user;
            $prm = $this->sys_input;
            $search = isset($prm["search"]) ? $prm["search"] : "";
            $division = isset($prm["division"]) ? $prm["division"] : "";
            $itemGroup = isset($prm["itemGroup"]) ? $prm["itemGroup"] : "";
            $itemSubGroup = isset($prm["itemSubGroup"]) ? $prm["itemSubGroup"] : "";
            $unitPurchase = isset($prm["unitPurchase"]) ? $prm["unitPurchase"] : "purchase";
            $currentPage = isset($prm["currentPage"]) ? $prm["currentPage"] : 1;

            $params = [];
            // Menambahkan parameter untuk unitPurchase
            $params[] = $unitPurchase;
            $params[] = $unitPurchase;

            $filterDivision = "";
            if ($division != "") {
                $filterDivision = "JOIN m_itemdivision ON M_ItemDivisionM_ItemID = M_ItemID AND M_ItemDivisionIsActive = 'Y' AND (M_ItemDivisionDivisionID = ? OR ? = '')";
                $params[] = $division;
                $params[] = $division;
            }

            $filterItemGroup = "";
            if ($itemGroup != "") {
                $filterItemGroup = "JOIN nat_group ON M_ItemNat_GroupID = Nat_GroupID AND Nat_GroupIsActive = 'Y' AND (Nat_GroupID = ? OR ? = '')";
                $params[] = $itemGroup;
                $params[] = $itemGroup;
            }

            $filterItemSubGroup = "";
            if ($itemSubGroup != "") {
                $filterItemSubGroup = "JOIN nat_subgroup ON M_ItemNat_SubGroupID = Nat_SubGroupID AND Nat_SubGroupIsActive = 'Y' AND (Nat_SubGroupID = ? OR ? = '')";
                $params[] = $itemSubGroup;
                $params[] = $itemSubGroup;
            }


            $params[] = '%' . $search . '%';

            $limit = 10;
            $offset = ($currentPage - 1) * $limit;


            $sql = "SELECT COUNT(*) as total FROM (
                        SELECT
                        DISTINCT M_ItemID 
                        FROM m_item
                        JOIN itemunitmap ON ItemUnitMapM_ItemID = M_ItemID AND 
                            ((ItemUnitMapIsPurchase = 'Y' AND ? = 'purchase') OR
                            (ItemUnitMapIsBase = 'Y' AND ? = 'base')) AND 
                            ItemUnitMapIsActive = 'Y'
                        JOIN itemunit ON ItemUnitID = ItemUnitMapItemUnitID AND 
                            ItemUnitIsActive = 'Y'
                        $filterDivision
                        $filterItemGroup
                        $filterItemSubGroup
                        WHERE M_ItemIsActive = 'Y' AND 
                        M_ItemItem_CategoryID = '1'
                        AND M_ItemDesc LIKE ?
                    ) as item";

            $query = $this->db->query($sql, $params);
            if (!$query) {
                $this->sys_error_db("item list", $this->db);
                exit;
            }

            $row = $query->row_array();
            $total = ceil($row["total"] / $limit);

            $params[] = $limit;
            $params[] = $offset;

            $sql = "SELECT
                    DISTINCT M_ItemID as itemId,
                    M_ItemCode as itemCode,
                    M_ItemDesc as itemDesc,
                    0 as qty,
                    ItemUnitID as unitId,
                    ItemUnitCode as unitCode,
                    ItemUnitName as unitName,
                    0 as detailId,
                    'draft' as status,
                    0 as remaining_stock
                    FROM m_item
                    JOIN itemunitmap ON ItemUnitMapM_ItemID = M_ItemID AND 
                        ((ItemUnitMapIsPurchase = 'Y' AND ? = 'purchase') OR
                        (ItemUnitMapIsBase = 'Y' AND ? = 'base')) AND 
                        ItemUnitMapIsActive = 'Y'
                    JOIN itemunit ON ItemUnitID = ItemUnitMapItemUnitID AND 
                        ItemUnitIsActive = 'Y'
                    $filterDivision
                    $filterItemGroup
                    $filterItemSubGroup
                    WHERE M_ItemIsActive = 'Y' AND 
                    M_ItemItem_CategoryID = '1'
                    AND M_ItemDesc LIKE ?
                    ORDER BY M_ItemDesc ASC
                    LIMIT ? OFFSET ?";

            // Parameter akhir yang digunakan untuk query
            $searchParam = $params;

            $query = $this->db->query($sql, $searchParam);

            if (!$query) {
                $this->sys_error_db("item list", $this->db);
                //$this->sys_error($this->db->error());
                exit;
            }
            //echo $this->db->last_query();
            //exit;

            $rows = $query->result_array();
            if (count($rows) > 0) {
                foreach ($rows as $key => $row) {
                    $rows[$key]["isSelected"] = $row["isSelected"] == "Y" ? true : false;

                    $sql = "SELECT SUM(StockQty) as stockQty
                            FROM stock s
                            JOIN warehouse w ON s.StockWarehouseID = w.WarehouseID
                            WHERE s.StockItemID = ?
                            AND s.StockItemUnitID = ?
                            AND w.WarehouseS_RegionalID = ?
                            AND w.WarehouseIsActive = 'Y'
                            AND (w.WarehouseIsTransit != 'Y' OR w.WarehouseIsTransit IS NULL)
                            AND w.WarehouseM_BranchID = ?
                            AND w.WarehouseIsDefault = 'Y'
                            GROUP BY s.StockItemUnitID";
                    $qry = $this->db->query($sql, [
                        $row['itemId'],
                        $row['unitId'],
                        $user['S_RegionalID'],
                        $user['M_BranchID']
                    ]);
                    if (!$qry) {
                        $this->sys_error_db("Failed get stock", $this->db);
                        exit;
                    }

                    $rowsstock = $qry->row_array();

                    $rows[$key]["remaining_stock"] = $rowsstock["stockQty"];
                }
            }

            $result = array(
                "records" => $rows,
                "total" => $total
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getUnit()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $prm = $this->sys_input;
            $search = isset($prm["search"]) ? $prm["search"] : "";

            // Menggunakan parameter query untuk pencarian unit
            $sql = "SELECT 
                    ItemUnitID,
                    ItemUnitCode,
                    ItemUnitName,
                    ItemUnitCreated,
                    ItemUnitLastUpdated,
                    ItemUnitIsActive,
                    ItemUnitUserID
                    FROM itemunit 
                    WHERE ItemUnitIsActive = 'Y'
                    AND ItemUnitName LIKE ?
                    ORDER BY ItemUnitName ASC";

            $searchParam = ['%' . $search . '%'];

            $query = $this->db->query($sql, $searchParam);

            if (!$query) {
                $this->sys_error_db("item unit list", $this->db);
                exit;
            }

            $rows = $query->result_array();

            $result = array(
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function insert_act_log($code, $status, $description, $refId, $data, $userId)
    {
        $sql = "INSERT INTO user_activity(
                                UserActivityCode,
                                UserActivityStatus,
                                UserActivityDescription,
                                UserActivityRefID,
                                UserActivityData,
                                UserActivityUserID,
                                UserActivityCreated) 
                    VALUES (?,?,?,?,?,?,?)";
        $query = $this->db->query($sql, [$code, $status, $description, $refId, $data, $userId, date("Y-m-d H:i:s")]);
        if (!$query) {
            $this->sys_error_db("user activity", $this->db);
            exit;
        }
    }

    function getSelectedItem()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $prm = $this->sys_input;
            $prId = isset($prm["prId"]) ? $prm["prId"] : "";

            if ($prId == "") {
                $this->sys_error("Invalid PR ID");
                exit;
            }


            $sql = "SELECT PurchaseRequestDetailID as detailId,
                    PurchaseRequestDetailM_ItemID as itemId,
                    M_ItemCode as itemCode,
                    M_ItemDesc as itemDesc,
                    PurchaseRequestDetailItemUnitID as unitId,
                    ItemUnitCode as unitCode,
                    ItemUnitName as unitName,
                    PurchaseRequestDetailQty as qty,
                    PurchaseRequestDetailStatus as status,
                    PurchaseRequestDetailNote as note
                    FROM purchase_request_detail 
                    JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
                    JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID AND ItemUnitIsActive = 'Y'
                    WHERE 
                        PurchaseRequestDetailPurchaseRequestID = ? AND
                        PurchaseRequestDetailIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);

            if (!$query) {
                $this->sys_error_db("item list", $this->db);
                exit;
            }

            $rows = $query->result_array();

            $result = array(
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getUnitByItemId()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $prm = $this->sys_input;
            $itemId = isset($prm["itemId"]) ? $prm["itemId"] : "";

            if ($itemId == "") {
                $this->sys_error("Invalid Item ID");
                exit;
            }

            $sql = "SELECT 
                    DISTINCT ItemUnitID as unitId,
                    ItemUnitCode as unitCode,
                    ItemUnitName as unitName
                    FROM itemunitmap 
                    JOIN itemunit ON ItemUnitID = ItemUnitMapItemUnitID AND ItemUnitIsActive = 'Y'
                    WHERE ItemUnitMapM_ItemID = ?
                    AND ItemUnitMapIsActive = 'Y'";
            $query = $this->db->query($sql, [$itemId]);

            if (!$query) {
                $this->sys_error_db("item unit list", $this->db);
                exit;
            }

            $rows = $query->result_array();

            $result = array(
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function savePurchaseRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $user = $this->sys_user;
            $userId = $this->sys_user["M_UserID"];
            $prm = $this->sys_input;
            $itemCategory = isset($prm["itemcategory"]) ? $prm["itemcategory"] : "1";
            $prDateUse = isset($prm["PRDateUse"]) ? $prm["PRDateUse"] : "";
            $prDateUse = date("Y-m-d", strtotime($prDateUse));
            if ($prDateUse == "" || $prDateUse == null) {
                $this->sys_error("Invalid PR Date Use");
                exit;
            }
            $prRegional = isset($prm["PRRegional"]) ? $prm["PRRegional"] : "";
            if ($prRegional == "" || $prRegional == null) {
                $this->sys_error("Invalid PR Regional");
                exit;
            }

            if ($user["M_BranchID"] != "0") {
                $prBranch = isset($prm["PRBranch"]) ? $prm["PRBranch"] : "";
                if ($prBranch == "" || $prBranch == null) {
                    $this->sys_error("Invalid PR Branch");
                    exit;
                }
            } else if ($user["M_BranchID"] == "0") {
                $prBranch = $prm["PRBranch"];
            }

            $prDescription = isset($prm["PRDescription"]) ? $prm["PRDescription"] : "";
            $items = isset($prm["items"]) ? $prm["items"] : [];
            if (count($items) == 0) {
                $this->sys_error("PR Items cannot be empty");
                exit;
            }

            $this->db->trans_begin();

            $areaid = $user['M_BranchID'];
            $areatype = 'B';
            if ($user['loginLevel'] == 'regional') {
                $areaid = $user['S_RegionalID'];
                $areatype = 'R';
            }

            $sqlusrdivisi = "SELECT M_UserDivisionDivisionID FROM m_userdivision 
                WHERE M_UserDivisionM_UserID = ? AND M_UserDivisionIsActive = 'Y'";
            $queusrdivisi = $this->db->query($sqlusrdivisi, [$userId]);
            if (!$queusrdivisi) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get user divisi", $this->db);
                exit;
            }
            $userDivID = $queusrdivisi->row_array()['M_UserDivisionDivisionID'];

            $sqlnum = "SELECT `fn_penomoran`(?, ?, ?, ?, ?, ?) AS numpd;";
            $quenum = $this->db->query($sqlnum, ['SR', $userDivID, $areatype, $areaid, 'SM', 'Y']);
            if (!$quenum) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] generate number doc", $this->db);
                exit;
            }
            $numpd = $quenum->row_array()['numpd'];


            $sql = "INSERT INTO purchase_request ( 
                                    PurchaseRequestNumber,
                                    PurchaseRequestDate, 
                                    PurchaseRequestS_RegionalID, 
                                    PurchaseRequestM_BranchCode, 
                                    PurchaseRequestNote, 
                                    PurchaseRequestItemCategoryID, 
                                    PurchaseRequestCreated, 
                                    PurchaseRequestUserID, 
                                    PurchaseRequestCreatedUserID, 
                                    PurchaseRequestRequestedBy) 
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
            // -- VALUES (fn_numbering('PR'), ?, ?, ?, ?, ?, ?, ?, ?, ?)";
            $query = $this->db->query($sql, [
                $numpd,
                $prDateUse,
                $prRegional,
                $prBranch,
                $prDescription,
                $itemCategory,
                date("Y-m-d H:i:s"),
                $userId,
                $userId,
                $userId
            ]);

            if (!$query) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request", $this->db);
                exit;
            }

            $prId = $this->db->insert_id();

            foreach ($items as $key => $item) {
                $sql = "INSERT INTO purchase_request_detail (
                            PurchaseRequestDetailPurchaseRequestID, 
                            PurchaseRequestDetailM_ItemID, 
                            PurchaseRequestDetailItemUnitID, 
                            PurchaseRequestDetailQty, 
                            PurchaseRequestDetailPrice,
                            PurchaseRequestDetailTotal,
                            PurchaseRequestDetailCreated, 
                            PurchaseRequestDetailCreatedUserID, 
                            PurchaseRequestDetailUserID) 
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
                $query = $this->db->query($sql, [
                    $prId,
                    $item["itemId"],
                    $item["unitId"],
                    $item["qty"],
                    0,
                    0,
                    date("Y-m-d H:i:s"),
                    $userId,
                    $userId
                ]);
                if (!$query) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("purchase request detail", $this->db);
                    exit;
                }
            }

            $sql = "SELECT * 
                    FROM purchase_request
                    JOIN m_user ON M_UserID = PurchaseRequestCreatedUserID
                    WHERE PurchaseRequestID = ?";
            $query = $this->db->query($sql, [$prId]);
            $row = $query->row_array();
            $sql = "SELECT * FROM purchase_request_detail WHERE PurchaseRequestDetailPurchaseRequestID = ? AND PurchaseRequestDetailIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);
            $rows = $query->result_array();
            $data = array(
                "header" => $row,
                "details" => $rows
            );
            $message = "Nomor PR: " . $row["PurchaseRequestNumber"] . " berhasil dibuat oleh " . $row["M_UserUsername"];
            $this->insert_act_log("PRP", "NEW", $message, $prId, json_encode($data), $userId);

            $this->db->trans_commit();
            $this->sys_ok("Purchase Request created successfully");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function updatePurchaseRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $messages_log = [];
            $datas_log = [];
            $userId = $this->sys_user["M_UserID"];
            $branchId = $this->sys_user["M_BranchID"];
            $regionalId = $this->sys_user["S_RegionalID"];
            $user = $this->sys_user;
            $prm = $this->sys_input;
            $prId = isset($prm["PRID"]) ? $prm["PRID"] : "";
            $prNumber = isset($prm["PRNumber"]) ? $prm["PRNumber"] : "";
            $itemtype = isset($prm["itemtype"]) ? $prm["itemtype"] : "1";
            $prDateUse = isset($prm["PRDateUse"]) ? $prm["PRDateUse"] : "";
            $prDateUse = date("Y-m-d", strtotime($prDateUse));
            if ($prDateUse == "" || $prDateUse == null) {
                $this->sys_error("Invalid PR Date Use");
                exit;
            }
            $prRegional = isset($prm["PRRegional"]) ? $prm["PRRegional"] : "";
            if ($prRegional == "" || $prRegional == null) {
                $this->sys_error("Invalid PR Regional");
                exit;
            }

            if ($user["M_BranchID"] != "0") {
                $prBranch = isset($prm["PRBranch"]) ? $prm["PRBranch"] : "";
                if ($prBranch == "" || $prBranch == null) {
                    $this->sys_error("Invalid PR Branch");
                    exit;
                }
            } else if ($user["M_BranchID"] == "0") {
                $prBranch = $prm["PRBranch"];
            }

            $prDescription = isset($prm["PRDescription"]) ? $prm["PRDescription"] : "";
            $items = isset($prm["items"]) ? $prm["items"] : [];
            if (count($items) == 0) {
                $this->sys_error("PR Items cannot be empty");
                exit;
            }

            $this->db->trans_begin();
            $status = '';
            $status_log = 'Update';
            if ($prm["act"] == "pending") {
                $status = ", PurchaseRequestStatus = 'Pending'";
                $status_log = 'Pending';
            }

            $sql = "SELECT *
                    FROM purchase_request 
                    JOIN item_category ON ItemCategoryID = PurchaseRequestItemCategoryID AND ItemCategoryIsActive = 'Y'
                    WHERE PurchaseRequestID = ? AND PurchaseRequestIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);
            if (!$query) {
                $this->sys_error_db("purchase request select", $this->db);
                exit;
            }
            $row = $query->row_array();

            $number = $row["PurchaseRequestNumber"];

            $datas_log['header'] = $row;

            if ($row["PurchaseRequestDate"] != $prDateUse) {
                $messages_log[] = "Perubahan tanggal PR: " . $row["PurchaseRequestDate"] . " menjadi " . $prDateUse;
            }
            if ($row["PurchaseRequestNote"] != $prDescription) {
                $messages_log[] = "Perubahan keterangan PR: " . $row["PurchaseRequestNote"] . " menjadi " . $prDescription;
            }
            if ($row["PurchaseRequestStatus"] != $status_log && $status_log != 'Update') {
                $messages_log[] = "Perubahan status PR: " . $row["PurchaseRequestStatus"] . " menjadi " . $status_log;
            }

            $sql = "UPDATE purchase_request SET 
                        PurchaseRequestDate = ?, 
                        PurchaseRequestS_RegionalID = ?, 
                        PurchaseRequestM_BranchCode = ?, 
                        PurchaseRequestNote = ?, 
                        PurchaseRequestItemCategoryID = ?,  
                        PurchaseRequestLastUpdated = ?, 
                        PurchaseRequestLastUpdatedUserID = ?
                        $status
                    WHERE PurchaseRequestID = ?";
            $params = [
                $prDateUse,
                $prRegional,
                $prBranch,
                $prDescription,
                $itemtype,
                date("Y-m-d H:i:s"),
                $userId,
                $prId
            ];

            $query = $this->db->query($sql, $params);

            if (!$query) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request update", $this->db);
                exit;
            }


            $detail_ids = [];
            $datas_log['new_details'] = [];
            $datas_log['updated_details'] = [];
            $datas_log['deleted_details'] = [];

            foreach ($items as $key => $item) {
                if (intval($item["detailId"]) == 0) {
                    $sql = "INSERT INTO purchase_request_detail (
                            PurchaseRequestDetailPurchaseRequestID,
                            PurchaseRequestDetailM_ItemID,
                            PurchaseRequestDetailItemUnitID,
                            PurchaseRequestDetailQty,
                            PurchaseRequestDetailCreated,
                            PurchaseRequestDetailCreatedUserID
                        )
                        VALUES (?, ?, ?, ?, ?, ?)";
                    $query = $this->db->query($sql, [
                        $prId,
                        $item["itemId"],
                        $item["unitId"],
                        $item["qty"],
                        date("Y-m-d H:i:s"),
                        $userId
                    ]);

                    if (!$query) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("purchase request detail insert", $this->db);
                        exit;
                    }
                    $prId = $this->db->insert_id();
                    $detail_ids[] = $prId;
                    //$message .= "Item: " . $item["itemCode"] . " " . $item["itemDesc"] . " " . $item["unitCode"] . " " . $item["unitName"] . " " . $item["qty"] . " " . $item["price"] . " " . $item["total"] . " berhasil ditambahkan";

                    $messages_log[] = "Item: " . $item["itemCode"] . " " . $item["itemDesc"] . " " . $item["unitCode"] . " " . $item["unitName"] . " " . $item["qty"] . " berhasil ditambahkan";
                    $datas_log['new_details'][] = $item;
                } else {
                    $sql = "SELECT * , 
                                newitem.M_ItemCode as newitemcode, 
                                olditem.M_ItemCode as olditemcode, 
                                newitem.M_ItemDesc as newitemdesc, 
                                olditem.M_ItemDesc as olditemdesc,
                                newitemunit.ItemUnitCode as newitemunitcode,
                                olditemunit.ItemUnitCode as olditemunitcode,
                                newitemunit.ItemUnitName as newitemunitname,
                                olditemunit.ItemUnitName as olditemunitname
                            FROM purchase_request_detail 
                            JOIN m_item newitem ON newitem.M_ItemID = PurchaseRequestDetailM_ItemID AND newitem.M_ItemIsActive = 'Y'
                            JOIN itemunit newitemunit ON newitemunit.ItemUnitID = ? AND newitemunit.ItemUnitIsActive = 'Y'
                            JOIN m_item olditem ON olditem.M_ItemID = ? AND olditem.M_ItemIsActive = 'Y'
                            JOIN itemunit olditemunit ON olditemunit.ItemUnitID = PurchaseRequestDetailItemUnitID AND olditemunit.ItemUnitIsActive = 'Y'
                            WHERE PurchaseRequestDetailID = ? AND PurchaseRequestDetailIsActive = 'Y'";
                    $query = $this->db->query($sql, [$item["unitId"], $item["itemId"], $item["detailId"]]);

                    if (!$query) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("purchase request detail select", $this->db);
                        exit;
                    }
                    $row = $query->row_array();

                    if ($row["newitemcode"] != $row["olditemcode"] || $row["newitemdesc"] != $row["olditemdesc"]) {
                        $messages_log[] = "Perubahan item : (" . $row["olditemcode"] . ") " . $row["olditemdesc"] . " menjadi (" . $row["newitemcode"] . ") " . $row["newitemdesc"];
                    }
                    if ($row["newitemunitcode"] != $row["olditemunitcode"] || $row["newitemunitname"] != $row["olditemunitname"]) {
                        $messages_log[] = "Perubahan satuan : " . $row["olditemunitname"] . " menjadi " . $row["newitemunitname"] . " pada item : (" . $row["newitemcode"] . ") " . $row["newitemdesc"];
                    }
                    if ($row["PurchaseRequestDetailQty"] != $item["qty"]) {
                        $messages_log[] = "Perubahan kuantitas : " . $row["PurchaseRequestDetailQty"] . " menjadi " . $item["qty"] . " pada item : (" . $row["newitemcode"] . ") " . $row["newitemdesc"];
                    }


                    $sql = "UPDATE purchase_request_detail 
                            SET 
                            PurchaseRequestDetailM_ItemID = ?,
                            PurchaseRequestDetailItemUnitID = ?,
                            PurchaseRequestDetailQty = ?,
                        PurchaseRequestDetailLastUpdated = ?,
                        PurchaseRequestDetailLastUpdatedUserID = ?
                        WHERE PurchaseRequestDetailID = ?";
                    $query = $this->db->query($sql, [
                        $item["itemId"],
                        $item["unitId"],
                        $item["qty"],
                        date("Y-m-d H:i:s"),
                        $userId,
                        $item["detailId"]
                    ]);
                    if (!$query) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("purchase request detail update", $this->db);
                        exit;
                    }

                    $detail_ids[] = $item["detailId"];

                    $datas_log['updated_details'][] = $item;
                }
            }

            $sql = "SELECT * 
                    FROM purchase_request_detail 
                    JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
                    JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID AND ItemUnitIsActive = 'Y'
                    WHERE PurchaseRequestDetailPurchaseRequestID = ? AND 
                    PurchaseRequestDetailIsActive = 'Y' AND 
                    PurchaseRequestDetailID NOT IN (" . implode(",", $detail_ids) . ")";
            $query = $this->db->query($sql, [$prId]);
            $deleted_rows = $query->result_array();
            foreach ($deleted_rows as $row) {
                $sql = "UPDATE purchase_request_detail 
                        SET 
                        PurchaseRequestDetailIsActive = 'N',
                        PurchaseRequestDetailDeleted = ?,
                        PurchaseRequestDetailDeletedUserID = ?
                        WHERE PurchaseRequestDetailID = ?";
                $query = $this->db->query($sql, [date("Y-m-d H:i:s"), $userId, $row["PurchaseRequestDetailID"]]);
                if (!$query) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("purchase request detail delete", $this->db);
                    exit;
                }

                $messages_log[] = "Item: (" . $row["M_ItemCode"] . ") " . $row["M_ItemDesc"] . " | " . $row["ItemUnitName"] . " sebanyak " . $row["PurchaseRequestDetailQty"] . " berhasil dihapus";
                $datas_log['deleted_details'][] = $row;
            }

            if ($prm["act"] == "pending") {
                // notification
                $this->saveNotification($userId, $prDateUse, $number, $prId, $branchId, $regionalId);
            }

            $this->db->trans_commit();

            if (count($messages_log) > 0) {
                $message = "Perubahan PR: " . $prNumber . "\n";
                $message .= implode("\n", $messages_log);
            } else {
                $message = "PR: " . $prNumber . " tanpa perubahan";
            }

            $this->insert_act_log("PRP", $status_log, $message, $prId, json_encode($datas_log), $userId);
            $this->sys_ok("Purchase Request updated successfully");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function saveAndDoPurchaseRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $user = $this->sys_user;
            $userId = $this->sys_user["M_UserID"];
            $branchId = $this->sys_user["M_BranchID"];
            $regionalId = $this->sys_user["S_RegionalID"];
            $prm = $this->sys_input;
            $itemCategory = isset($prm["itemcategory"]) ? $prm["itemcategory"] : "1";
            $prDateUse = isset($prm["PRDateUse"]) ? $prm["PRDateUse"] : "";
            $prDateUse = date("Y-m-d", strtotime($prDateUse));
            if ($prDateUse == "" || $prDateUse == null) {
                $this->sys_error("Invalid PR Date Use");
                exit;
            }
            $prRegional = isset($prm["PRRegional"]) ? $prm["PRRegional"] : "";
            if ($prRegional == "" || $prRegional == null) {
                $this->sys_error("Invalid PR Regional");
                exit;
            }

            if ($user["M_BranchID"] != "0") {
                $prBranch = isset($prm["PRBranch"]) ? $prm["PRBranch"] : "";
                if ($prBranch == "" || $prBranch == null) {
                    $this->sys_error("Invalid PR Branch");
                    exit;
                }
            } else if ($user["M_BranchID"] == "0") {
                $prBranch = $prm["PRBranch"];
            }

            $prDescription = isset($prm["PRDescription"]) ? $prm["PRDescription"] : "";
            $items = isset($prm["items"]) ? $prm["items"] : [];
            if (count($items) == 0) {
                $this->sys_error("PR Items cannot be empty");
                exit;
            }

            $this->db->trans_begin(true);

            $areaid = $user['M_BranchID'];
            $areatype = 'B';
            if ($user['loginLevel'] == 'regional') {
                $areaid = $user['S_RegionalID'];
                $areatype = 'R';
            }

            $sqlusrdivisi = "SELECT M_UserDivisionDivisionID FROM m_userdivision 
                WHERE M_UserDivisionM_UserID = ? AND M_UserDivisionIsActive = 'Y'";
            $queusrdivisi = $this->db->query($sqlusrdivisi, [$userId]);
            if (!$queusrdivisi) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get user divisi", $this->db);
                exit;
            }
            $userDivID = $queusrdivisi->row_array()['M_UserDivisionDivisionID'];

            $sqlnum = "SELECT `fn_penomoran`(?, ?, ?, ?, ?, ?) AS numpd;";
            $quenum = $this->db->query($sqlnum, ['SR', $userDivID, $areatype, $areaid, 'SM', 'Y']);
            if (!$quenum) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] generate number doc", $this->db);
                exit;
            }
            $numpd = $quenum->row_array()['numpd'];

            // Insert purchase request with status set to 'Pending' directly
            $sql = "INSERT INTO purchase_request ( 
                                PurchaseRequestNumber,
                                PurchaseRequestDate, 
                                PurchaseRequestS_RegionalID, 
                                PurchaseRequestM_BranchCode, 
                                PurchaseRequestNote, 
                                PurchaseRequestItemCategoryID, 
                                PurchaseRequestStatus,
                                PurchaseRequestCreated, 
                                PurchaseRequestUserID, 
                                PurchaseRequestCreatedUserID, 
                                PurchaseRequestRequestedBy) 
                    VALUES (?, ?, ?, ?, ?, ?, 'Pending', ?, ?, ?, ?)";
            // VALUES (fn_numbering('PR'), ?, ?, ?, ?, ?, 'Pending', ?, ?, ?, ?)";
            $query = $this->db->query($sql, [
                $numpd,
                $prDateUse,
                $prRegional,
                $prBranch,
                $prDescription,
                $itemCategory,
                date("Y-m-d H:i:s"),
                $userId,
                $userId,
                $userId
            ]);

            if (!$query) {
                $this->db->trans_rollback();
                $this->sys_error_db("purchase request", $this->db);
                exit;
            }

            $prId = $this->db->insert_id();

            // Insert purchase request details
            foreach ($items as $key => $item) {
                $sql = "INSERT INTO purchase_request_detail (
                            PurchaseRequestDetailPurchaseRequestID, 
                            PurchaseRequestDetailM_ItemID, 
                            PurchaseRequestDetailItemUnitID, 
                            PurchaseRequestDetailQty, 
                            PurchaseRequestDetailPrice,
                            PurchaseRequestDetailTotal,
                            PurchaseRequestDetailCreated, 
                            PurchaseRequestDetailCreatedUserID, 
                            PurchaseRequestDetailUserID) 
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
                $query = $this->db->query($sql, [
                    $prId,
                    $item["itemId"],
                    $item["unitId"],
                    $item["qty"],
                    0,
                    0,
                    date("Y-m-d H:i:s"),
                    $userId,
                    $userId
                ]);
                if (!$query) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("purchase request detail", $this->db);
                    exit;
                }
            }

            // Get purchase request data for logging
            $sql = "SELECT * 
                    FROM purchase_request
                    JOIN m_user ON M_UserID = PurchaseRequestCreatedUserID
                    WHERE PurchaseRequestID = ?";
            $query = $this->db->query($sql, [$prId]);
            $row = $query->row_array();

            $sql = "SELECT * FROM purchase_request_detail WHERE PurchaseRequestDetailPurchaseRequestID = ? AND PurchaseRequestDetailIsActive = 'Y'";
            $query = $this->db->query($sql, [$prId]);
            $rows = $query->result_array();

            $data = array(
                "header" => $row,
                "details" => $rows
            );

            $message = "Nomor PR: " . $row["PurchaseRequestNumber"] . " berhasil dibuat dan diajukan langsung oleh " . $row["M_UserUsername"];
            $this->insert_act_log("PRP", "Pending", $message, $prId, json_encode($data), $userId);

            // notification
            $this->saveNotification($userId, $prDateUse, $numpd, $prId, $branchId, $regionalId);

            $this->db->trans_commit();
            $this->sys_ok("Purchase Request created and submitted successfully");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getDetailRequest()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $userId = $this->sys_user["M_UserID"];
            $prm = $this->sys_input;
            $requestId = isset($prm["requestId"]) ? $prm["requestId"] : "";
            if ($requestId == "" || $requestId == null) {
                $this->sys_error("Invalid Request ID");
                exit;
            }

            $sql = "SELECT
                    PurchaseRequestDetailID as detailId,
                    PurchaseRequestDetailM_ItemID as itemId,
                    M_ItemCode as itemCode,
                    M_ItemDesc as itemDesc,
                    PurchaseRequestDetailItemUnitID as unitId,
                    ItemUnitCode as unitCode,
                    ItemUnitName as unitName,
                    PurchaseRequestDetailQty as qty,
                    PurchaseRequestDetailStatus as status
                    FROM purchase_request_detail
                    JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
                    JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID AND ItemUnitIsActive = 'Y'
                    WHERE PurchaseRequestDetailPurchaseRequestID = ? AND PurchaseRequestDetailIsActive = 'Y'";
            $query = $this->db->query($sql, [$requestId]);
            if (!$query) {
                $this->sys_error_db("purchase request detail", $this->db);
                exit;
            }

            $rows = $query->result_array();

            $result = array(
                "records" => $rows
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getRiwayatItem()
    {
        try {
            if (!$this->isLogin) {
                $this->sys_error("Invalid Token");
                exit;
            }

            $para = $this->sys_input;
            $user = $this->sys_user;

            $place = $user['M_BranchID'];
            if ($user['M_BranchID'] == '0') {
                $place = 'R';
            }

            $page = 0;
            $limit = 10;
            if ($para['page'] > 0) {
                $page = ($para['page'] - 1) * $limit;
            }

            $sqlpast = "SELECT
                PurchaseRequestDetailID,
                PurchaseRequestDetailM_ItemID,
                M_ItemDesc,
                PurchaseRequestDetailItemUnitID,
                ItemUnitName,
                PurchaseRequestDetailQty,
                PurchaseRequestStatus,
                PurchaseRequestDetailCreated
            FROM purchase_request_detail
            JOIN purchase_request ON PurchaseRequestID = PurchaseRequestDetailPurchaseRequestID
            JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID
            JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID
            JOIN m_user ON PurchaseRequestRequestedBy = M_UserID
                AND M_UserIsActive = 'Y'
            WHERE PurchaseRequestDetailM_ItemID = ?
                AND PurchaseRequestDetailIsActive = 'Y'
                AND (M_UserM_BranchID = ? OR 'R' = ?)
            ORDER BY PurchaseRequestDetailCreated DESC";

            $sqldata = $sqlpast . ' LIMIT ? OFFSET ?';
            $quepast = $this->db->query($sqldata, [
                $para['itemid'],
                $place,
                $place,
                $limit,
                $page
            ]);
            if (!$quepast) {
                $this->sys_error_db("[Error] get item history");
                exit;
            }

            $sqltotal = "SELECT COUNT(*) AS total FROM ($sqlpast) as x ";
            $quetotal = $this->db->query($sqltotal, [
                $para['itemid'],
                $place,
                $place,
            ]);
            if (!$quetotal) {
                $this->sys_error_db("[Error] get total item history");
                exit;
            }

            $result = array(
                "records" => $quepast->result_array(),
                "total" => $quetotal->row_array()['total']
            );

            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    private function safeJsonEncode($data)
    {
        // Coba encode data ke JSON
        $jsonData = json_encode($data);

        // Cek apakah terjadi error saat encode
        if (json_last_error() !== JSON_ERROR_NONE) {
            $errorMsg = json_last_error_msg();
            error_log("JSON encode error: " . $errorMsg);

            // Lakukan sanitasi dan perbaikan data
            $fixedData = $this->fixJsonEncodeIssues($data, $errorMsg);

            // Coba encode lagi setelah diperbaiki
            $jsonData = json_encode($fixedData);

            // Jika masih error, log dan kembalikan objek kosong
            if (json_last_error() !== JSON_ERROR_NONE) {
                error_log("Failed to fix JSON encode issues: " . json_last_error_msg());
                // Kembalikan objek kosong jika masih gagal
                return '{}';
            }
        }

        return $jsonData;
    }

    // Fungsi untuk memperbaiki masalah encoding JSON
    private function fixJsonEncodeIssues($data, $errorMsg)
    {
        // Buat salinan data untuk dimodifikasi
        $fixedData = $data;

        // Tangani berbagai jenis error
        if (strpos($errorMsg, 'Malformed UTF-8') !== false) {
            // Perbaiki masalah karakter UTF-8
            $fixedData = $this->fixUTF8Issues($fixedData);
        } else if (strpos($errorMsg, 'Inf and NaN cannot be JSON encoded') !== false) {
            // Perbaiki masalah nilai Infinity atau NaN
            $fixedData = $this->fixInfNanIssues($fixedData);
        } else {
            // Konversi semua nilai numerik menjadi string untuk menghindari masalah presisi
            $fixedData = $this->convertNumericValuesToStrings($fixedData);

            // Perbaiki masalah referensi recursif
            $fixedData = $this->fixRecursiveReferences($fixedData);
        }

        return $fixedData;
    }

    // Perbaiki masalah karakter UTF-8
    private function fixUTF8Issues($data)
    {
        if (is_string($data)) {
            return mb_convert_encoding($data, 'UTF-8', 'UTF-8');
        } else if (is_array($data)) {
            foreach ($data as $key => $value) {
                $data[$key] = $this->fixUTF8Issues($value);
            }
        }
        return $data;
    }

    // Perbaiki masalah nilai Infinity atau NaN
    private function fixInfNanIssues($data)
    {
        if (is_array($data)) {
            foreach ($data as $key => $value) {
                if (is_float($value) && (is_nan($value) || is_infinite($value))) {
                    $data[$key] = (string)$value; // Konversi ke string
                } else if (is_array($value)) {
                    $data[$key] = $this->fixInfNanIssues($value);
                }
            }
        }
        return $data;
    }

    // Perbaiki masalah referensi recursif
    private function fixRecursiveReferences($data, $depth = 0)
    {
        // Batasi kedalaman rekursi untuk menghindari infinite loop
        if ($depth > 50) {
            return "[MAX_DEPTH_REACHED]";
        }

        if (is_array($data)) {
            $result = [];
            foreach ($data as $key => $value) {
                if (is_array($value)) {
                    $result[$key] = $this->fixRecursiveReferences($value, $depth + 1);
                } else {
                    $result[$key] = $value;
                }
            }
            return $result;
        }

        return $data;
    }

    // Cari dan konversi numerik ke string secara rekursif
    private function convertNumericValuesToStrings($data)
    {
        if (is_array($data)) {
            foreach ($data as $key => $value) {
                if (is_array($value)) {
                    $data[$key] = $this->convertNumericValuesToStrings($value);
                } else if (is_numeric($value)) {
                    $data[$key] = (string)$value;
                } else if (is_bool($value)) {
                    $data[$key] = $value ? "true" : "false";
                }
            }
        }
        return $data;
    }

    // notifications
    function saveNotification($userId, $dateRequest, $number, $refID, $branchId, $regionalId)
    {
        try {
            $this->db->trans_begin();

            $xdate = date('d-m-Y');

            $sql_user = "SELECT M_UserID, M_UserUsername
                FROM m_user
                WHERE M_UserIsActive = 'Y'
                AND M_UserID = ?";
            $qry_user = $this->db->query($sql_user, [$userId]);
            if ($qry_user) {
                $username = $qry_user->row_array();
            } else {
                $this->sys_error_db("select user error", $this->db);
                exit;
            }

            $description = "Request <strong>SR PERSEDIAAN</strong> pada tanggal <strong>" . $xdate . "</strong> oleh <strong>" . $username['M_UserUsername'] . "</strong> dengan nomor <strong>" . $number . "</strong>";

            $sql_menu = "SELECT S_MenuID,
                S_MenuName,
                S_MenuUrl,
                S_MenuLevel
                FROM s_menu
                WHERE S_MenuID = 31";
            $qry_menu = $this->db->query($sql_menu, []);
            if ($qry_menu) {
                $rowurl = $qry_menu->row_array();
            } else {
                $this->sys_error_db("select menu error", $this->db);
                exit;
            }

            $url = $rowurl['S_MenuUrl'] . "?flag=N&status=Pending&startdate=" . $dateRequest . "&search=" . $number;

            // cari user penerima notifikasi
            if ($branchId != 0) {
                // user cabang
                $sql_receive_user = "SELECT * FROM m_user
                JOIN m_branch ON  M_UserM_BranchID = M_BranchID
                AND M_BranchIsActive = 'Y'
                AND M_BranchID = ?
                WHERE M_UserIsActive = 'Y'
                AND M_UserM_ApproveLevelID IN (1,2)";
                $qry_receive_user = $this->db->query($sql_receive_user, [$branchId]);
                if ($qry_receive_user) {
                    $rowsuser = $qry_receive_user->result_array();
                } else {
                    $this->sys_error_db("select receive user error", $this->db);
                    exit;
                }
            } else if ($branchId == 0) {
                // user regional
                $sql_receive_user = "SELECT * FROM m_user
                JOIN s_regional ON M_UserS_RegionalID = S_RegionalID
                AND S_RegionalIsActive = 'Y'
                AND S_RegionalID = ?
                WHERE M_UserIsActive = 'Y'
                AND M_UserM_BranchID = 0
                AND M_UserM_ApproveLevelID IN (1,2)";
                $qry_receive_user = $this->db->query($sql_receive_user, [$regionalId]);
                if ($qry_receive_user) {
                    $rowsuser = $qry_receive_user->result_array();
                } else {
                    $this->sys_error_db("select receive user error", $this->db);
                    exit;
                }
            }

            $sql = "INSERT INTO notification(
                NotificationRefID,
                NotificationType,
                NotificationDescription,
                NotificationUrl,
                NotificationUserID,
                NotificationCreated) VALUES(?,?,?,?,?,NOW())";
            $qry = $this->db->query($sql, [
                $refID,
                'Stock Request (P)',
                $description,
                $url,
                $userId
            ]);
            if (!$qry) {
                $this->db->trans_rollback();
                $this->sys_error_db("insert notification error", $this->db);
                exit;
            }

            $lastid = $this->db->insert_id();

            // insert ke detail notification
            foreach ($rowsuser as $row) {
                $sql_detail = "INSERT INTO notification_detail(
                    NotificationDetailNotificationID,
                    NotificationDetailM_UserID,
                    NotificationDetailM_ApproveLevelID,
                    NotificationDetailStatus,
                    NotificationDetailCreated,
                    NotificationDetailUserID) VALUES(?,?,?,?,NOW(),?)";
                $qry_detail = $this->db->query($sql_detail, [
                    $lastid,
                    $row['M_UserID'], // user penerima notifikasi
                    $row['M_UserM_ApproveLevelID'],
                    'unread',
                    $userId
                ]);
                if (!$qry_detail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("insert notification detail error", $this->db);
                    exit;
                }
            }

            $this->db->trans_commit();
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }
}
