<?php

class ReceiveItemPoV2 extends MY_Controller {

    var $db;
    public function index() {
        echo "Receive Item API versi 2.0";
    }

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

    ## QUERY ##

    function getSupplier() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $payload = $this->sys_input;
            $search = $payload['search'] . '%';

            $sql = "SELECT 
                SupplierID,
                SupplierCode,
                SupplierName
            FROM supplier
            WHERE SupplierIsActive = 'Y'
                AND SupplierName LIKE ?
            ORDER BY SupplierName ASC ";
            $que = $this->db->query($sql, [$search]);
            if (!$que) {
                $this->sys_error_db("[Error] query listing supplier");;
                exit;
            }

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

    function getPurchaseOrder() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

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

            $const_params = [
                $user['M_BranchID'] ?? 0, 
                $user['S_RegionalID'],
                $payload['SupplierID'],
                $payload['search'] . '%'
            ];

            $placeholders = '?';
            $list_includeID = ["0"];
            $localPO_deleted = $payload['localPO_deleted'];
            if (sizeof($localPO_deleted) > 0) {
                $placeholders = implode(", ", array_fill(0, count($localPO_deleted), '?'));
                $list_includeID = $payload['localPO_deleted'];
            }
            
            $params = array_merge($list_includeID, $const_params);

            $sql = "SELECT 
                GROUP_CONCAT(DISTINCT PurchaseOrderDetailWarehouseID) AS arrWarehouse,
                purchase_order.* 
            FROM purchase_order             
            JOIN purchase_order_detail ON PurchaseOrderID = PurchaseOrderDetailPurchaseOrderID
                AND PurchaseOrderDetailIsActive  = 'Y'
                AND 
                    ((SELECT IFNULL(SUM(PoItemReceiveQty - PoItemReceiveQtyReceived), 0)
                    FROM po_item_receive 
                    WHERE PoItemReceivePurchaseOrderID = PurchaseOrderID) > 0)
                    OR
                    (SELECT PoItemReceiveID
                    FROM po_item_receive 
                    WHERE PoItemReceivePurchaseOrderID = PurchaseOrderID
                    AND PoItemReceiveID IN ({$placeholders}))
            JOIN warehouse ON PurchaseOrderDetailWarehouseID = WarehouseID
                AND WarehouseM_BranchID = ?
            WHERE WarehouseS_RegionalID = ?
                AND PurchaseOrderSupplierID = ?
                AND PurchaseOrderStatus = 'Approved'
                AND PurchaseOrderItemCategoryID = 1
                AND PurchaseOrderNumber LIKE ?
                AND PurchaseOrderIsActive = 'Y'
            GROUP BY PurchaseOrderID 
            ORDER BY PurchaseOrderID DESC LIMIT 50";
            $que = $this->db->query($sql, $params);
            if (!$que) {
                $this->sys_error_db("[Error] query purhcase order");
                exit;
            }
            $data = $que->result_array();
            $this->sys_ok($data);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getWarehouse() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $user = $this->sys_user;
            $whtype = ($user['M_UserLocationFlag'] == 'R') ? 'R' : 'B';

            $sql = "SELECT 
                WarehouseID,
                WarehouseCode,
                CASE
                    WHEN WarehouseType = 'B' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) 
                    WHEN WarehouseType = 'R' THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) 
                    ELSE ''
                END WarehouseName,
                WarehouseType,
                WarehouseIsDefault,
                WarehouseS_RegionalID,
                WarehouseM_BranchID,
                S_RegionalID,
                S_RegionalName
            FROM warehouse 
            JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID AND S_RegionalIsActive = 'Y'
            LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID AND M_BranchIsActive = 'Y' 
            WHERE WarehouseS_RegionalID = ? 
                AND WarehouseIsActive = 'Y' AND WarehouseIsTransit = 'N'
                AND WarehouseType = ? AND WarehouseM_BranchID = ? 
            ORDER BY WarehouseName ASC";
            $que = $this->db->query($sql, [
                $user['S_RegionalID'], $whtype, $user['M_BranchID']
            ]);
            if (!$que) {
                $this->sys_error_db("[Error] get list warehouse");
                exit;
            }
            $data = $que->result_array();
            $this->sys_ok($data);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }    

    function getItem() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

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

            $POID = $param['POID'];
            $warehouseID = $param['WarehouseID'];

            $sql = "SELECT 
                CONCAT(PoItemReceiveID,',','-') as keyID,
                PoItemReceiveID,
                PurchaseOrderNumber,
                PurchaseOrderShippingCost,
                PurchaseOrderShippingCostStatus,
                PoItemReceivePurchaseOrderID,
                PoItemReceivePurchaseOrderSummaryID,
                PoItemReceivePurchaseOrderDetailID,
                WarehouseID,
                WarehouseCode,
                CASE
                    WHEN WarehouseType = 'B' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) 
                    WHEN WarehouseType = 'R' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) 
                    ELSE ''
                END WarehouseName,
                M_ItemID,
                M_ItemCode,
                M_ItemDesc,
                ItemUnitID,
                ItemUnitName,
                M_ItemItem_CategoryID,
                PoItemReceiveQty as originalQty,
                PoItemReceiveQtyReceived as receivedQty,
                0 as qty,
                PoItemReceiveQty - PurchaseOrderSummaryQtyReceived AS qtyRest,
                PoItemReceivePrice,
                'N' as alreadySave
            FROM po_item_receive 
            JOIN purchase_order ON PoItemReceivePurchaseOrderID = PurchaseOrderID 
                AND PurchaseOrderIsActive = 'Y'
                AND PoItemReceiveIsActive = 'Y'
            --    AND (PoItemReceiveQty - PoItemReceiveQtyReceived) > 0
            JOIN purchase_order_summary ON PurchaseOrderSummaryID = PoItemReceivePurchaseOrderSummaryID 
            JOIN warehouse ON PoItemReceiveWarehouseID = WarehouseID 
                AND WarehouseIsActive = 'Y'
            JOIN m_item ON PoItemReceiveItemID = M_ItemID 
            JOIN itemunit ON PoItemReceiveItemUnitID= ItemUnitID 
            JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID 
            LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID 
            WHERE PurchaseOrderID = ? AND WarehouseID = ?";
            $que = $this->db->query($sql, [$POID, $warehouseID]);
            if (!$que) {
                $this->sys_error_db("[Error] ger item PO");;
                exit;
            }
            $data = $que->result_array();
            foreach ($data as $key => $value) {
                $data[$key]['batchlist'] = [];
            }
            
            $this->sys_ok($data);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getItemUpdate() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $param = $this->sys_input;
            $ROID = $param['ROID'];

            $sql = "SELECT 
                ReceiveOrderPoDetailID, 
                CONCAT(ReceiveOrderPoDetailPoItemReceiveID,',',ReceiveOrderPoDetailID) AS keyID,
                ReceiveOrderPoDetailPoItemReceiveID AS PoItemReceiveID,
                PurchaseOrderNumber,
                PurchaseOrderShippingCost,
                PurchaseOrderShippingCostStatus,
                ReceiveOrderPoDetailPurchaseOrderID AS PoItemReceivePurchaseOrderID,
                ReceiveOrderPoDetailPurchaseOrderSummaryID AS PoItemReceivePurchaseOrderSummaryID,
                ReceiveOrderPoDetailPurchaseOrderDetailID AS PoItemReceivePurchaseOrderDetailID,
                WarehouseID,
                WarehouseCode,
                CASE
                    WHEN WarehouseType = 'B' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) 
                    WHEN WarehouseType = 'R' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) 
                    ELSE ''
                END WarehouseName,
                M_ItemID,
                M_ItemCode,
                M_ItemDesc,
                ItemUnitID,
                ItemUnitName,
                M_ItemItem_CategoryID,
                PoItemReceiveQty AS originalQty,
                PoItemReceiveQtyReceived AS receivedQty,
                ReceiveOrderPoDetailQty AS qty,
                ReceiveOrderPoDetailQty AS tmpQty,
                PoItemReceiveQty - PurchaseOrderSummaryQtyReceived AS qtyRest,
                ReceiveOrderPoDetailQty,
                PoItemReceivePrice,
                'Y' AS alreadySave,
                ReceiveOrderPoDetailBatchNo AS batchNo,
                ReceiveOrderPoDetailEd AS itemEd,
                ReceiveOrderPoDetailBatchList
            FROM receive_order_po_detail 
            JOIN receive_order_po ON ReceiveOrderPoDetailReceiveOrderPoID = ReceiveOrderPoID 
            JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID 
            JOIN po_item_receive ON ReceiveOrderPoDetailPoItemReceiveID = PoItemReceiveID
            JOIN purchase_order_summary ON PurchaseOrderSummaryID = PoItemReceivePurchaseOrderSummaryID 
            JOIN warehouse ON ReceiveOrderPoWarehouseID = WarehouseID 
            JOIN m_item ON ReceiveOrderPoItemID = M_ItemID 
            JOIN itemunit ON ReceiveOrderPoItemUnitID = ItemUnitID 
            JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID 
            LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID 
            WHERE ReceiveOrderPoDetailIsActive = 'Y' 
                AND ReceiveOrderPoDetailReceiveOrderPoID = ?
            ORDER BY ReceiveOrderPoDetailID ASC";
            $que = $this->db->query($sql, [$ROID]);
            if (!$que) {
                $this->sys_error_db("[Error] get item untuk update");
                exit;
            }

            $data = $que->result_array();

            foreach ($data as $key => $item) {
                $jsonstr = $item['ReceiveOrderPoDetailBatchList'];
                $batchlist = [];

                if (!empty($jsonstr)) {
                    $decode = json_decode($jsonstr);
                    if ($decode !== null && json_last_error() === JSON_ERROR_NONE) {
                        $batchlist = $decode;
                    } else {
                        $this->sys_error("[Error] decode json batch list item received");
                        exit;
                    }
                    $data[$key]['batchlist'] = $batchlist;
                    unset($data[$key]['ReceiveOrderPoDetailBatchList']);
                }
            }

            $detailID = array_column($data, 'ReceiveOrderPoDetailID');
            $placeholders = implode(", ", array_fill(0, count($detailID), '?'));

            $sqlInspeksi  = "SELECT
                ReceiveOrderPoInspeksiID,
                ReceiveOrderPoInspeksiReceiveOrderPoDetailID,
                ReceiveOrderPoInspeksiQtyPesan AS qty_po,
                ReceiveOrderPoInspeksiQtyActual AS qty_ro,
                ReceiveOrderPoInspeksiPricePesan AS price_po,
                ReceiveOrderPoInspeksiPriceActual AS price_ro,
                ReceiveOrderPoInspeksiDatePesan AS date_po,
                ReceiveOrderPoInspeksiDateActual AS date_ro,
                ReceiveOrderPoInspeksiExpireDate AS expire_date,
                ReceiveOrderPoInspeksiKeadaanKemasan AS condt_kemasan,
                ReceiveOrderPoInspeksiKeadaanKemasanNote AS catatan_kemasan,
                ReceiveOrderPoInspeksiKondisiPengiriman AS condt_pengiriman,
                ReceiveOrderPoInspeksiKondisiPengirimanNote AS catatan_kirim,
                ReceiveOrderPoInspeksiSimpulan AS summary,
                ReceiveOrderPoInspeksiCatatan AS catatan,
                ReceiveOrderPoInspeksiStaffPenerimaID AS pemeriksa,
                ReceiveOrderPoInspeksiPengirim AS pengirim
            FROM receive_order_po_inspeksi 
            WHERE ReceiveOrderPoInspeksiIsActive = 'Y' 
                AND ReceiveOrderPoInspeksiReceiveOrderPoDetailID IN ({$placeholders})";
            $queInspeksi = $this->db->query($sqlInspeksi, $detailID);
            if (!$queInspeksi) {
                $this->sys_error_db("[Error] get item inspeksi data");
                exit;
            }
            $list_inspeksi = $queInspeksi->result_array();

            $lookup_map = [];
            foreach ($list_inspeksi as $inspeksi) {
                $RODetailID = $inspeksi['ReceiveOrderPoInspeksiReceiveOrderPoDetailID'];
                $lookup_map[$RODetailID] = $inspeksi;
            }

            $result = [];
            foreach ($data as $rodetail) {
                $rodetailID = $rodetail['ReceiveOrderPoDetailID'];

                if (isset($lookup_map[$rodetailID])) {
                    $rodetail['inspeksi_item'] = $lookup_map[$rodetailID];
                }
                $result[] = $rodetail;
            }


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

    function searchRO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

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

            $search = $param['search'] . '%';
            $startdate = $param['startdate'];
            $enddate = $param['enddate'];
            $regID = $user['S_RegionalID'];
            $brnCD = $user['M_BranchCode'] . '%';

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

            $sql = "SELECT
                ReceiveOrderPoNumber,
                ReceiveOrderPoID,
                ReceiveOrderPoSupplierID,
                ReceiveOrderPoIDate, 
                ReceiveOrderShippingCostAmount,
                ReceiveOrderPoNote,
                ReceiveOrderPoRefNumber,
                ReceiveOrderPoDONumber,
                SupplierID,
                SupplierName,
                WarehouseID,
                WarehouseCode,
                CASE
                    WHEN WarehouseType = 'B' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) 
                    WHEN WarehouseType = 'R' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) 
                    ELSE ''
                END WarehouseName,
                ReceiveOrderPoConfirmed,
                DATE_FORMAT(ReceiveOrderPoConfirmedDate, '%d-%m-%Y %H:%i') AS ReceiveOrderPoConfirmedDate,
                M_UserUsername AS ReceiveOrderPoConfirmedUserName
            FROM receive_order_po 
            JOIN warehouse ON ReceiveOrderPoWarehouseID = WarehouseID 
            JOIN supplier ON ReceiveOrderPoSupplierID = SupplierID 
            JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID 
                AND S_RegionalIsActive = 'Y' 
            LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID 
                AND M_BranchIsActive = 'Y'
            LEFT JOIN m_user ON ReceiveOrderPoConfirmedUserID = M_UserID
            WHERE ReceiveOrderPoIsActive = 'Y'
                AND (ReceiveOrderPoNumber LIKE ? OR ReceiveOrderPoDONumber LIKE ?)
                AND ReceiveOrderPoIDate BETWEEN DATE(?) AND DATE(?)
                AND ReceiveOrderPoS_RegionalID = ? 
                AND ReceiveOrderPoM_BranchCode LIKE ? 
                AND ReceiveOrderPoID NOT IN (
                    SELECT DISTINCT ReceiveOrderPoDetailReceiveOrderPoID 
                    FROM receive_order_po_detail 
                    JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID
                    WHERE PurchaseOrderItemCategoryID = 1
                        AND ReceiveOrderPoDetailIsActive = 'Y'
                )
            ORDER BY ReceiveOrderPoID DESC";

            $sqltotal = "SELECT COUNT(*) as total FROM ($sql) as x";
            $quetotal = $this->db->query($sqltotal, [$search, $search, $startdate, $enddate, $regID, $brnCD]);
            if (!$quetotal) {
                $this->sys_error_db("[Error] get total data RO");
                exit;
            }
            $total = $quetotal->row_array()['total'];

            $sqldata = $sql . " LIMIT ? OFFSET ?";
            $quedata = $this->db->query($sqldata, [
                $search, $search, $startdate, $enddate, 
                $regID, $brnCD, $limit, $page
            ]);
            if (!$quedata) {
                $this->sys_error_db("[Error] get list data RO");
                exit;
            }

            $result = array(
                "records" => $quedata->result_array(),
                "total" => ceil($total / $limit)
            );
            $this->sys_ok($result);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function getItemDataInspeksi() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $param = $this->sys_input;
            $RODetailID = $param['RODetailID'];

            $sql = "SELECT 
                ReceiveOrderPoInspeksiID,
                ReceiveOrderPoInspeksiReceiveOrderPoDetailID,
                ReceiveOrderPoInspeksiQtyPesan,
                ReceiveOrderPoInspeksiQtyActual,
                ReceiveOrderPoInspeksiPricePesan,
                ReceiveOrderPoInspeksiPriceActual,
                ReceiveOrderPoInspeksiDatePesan,
                ReceiveOrderPoInspeksiDateActual,
                ReceiveOrderPoInspeksiExpireDate,
                ReceiveOrderPoInspeksiKeadaanKemasan,
                ReceiveOrderPoInspeksiKondisiPengiriman,
                ReceiveOrderPoInspeksiSimpulan,
                ReceiveOrderPoInspeksiCatatan,
                ReceiveOrderPoInspeksiStaffPenerima,
                ReceiveOrderPoInspeksiStaffPengirim
            FROM receive_order_po_inspeksi 
            WHERE ReceiveOrderPoInspeksiIsActive = 'Y' 
                AND ReceiveOrderPoInspeksiReceiveOrderPoDetailID = ?";
            $que = $this->db->query($sql, [$RODetailID]);
            if (!$que) {
                $this->sys_error_db("[Error] get data inspeksi item");
                exit;
            }
            $data = $que->row_array();

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

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

            $user = $this->sys_user;

            $sql = "SELECT 
                M_UserID,
                M_UserUsername
            FROM m_user
            WHERE M_UserM_BranchID = ?
                AND M_UserS_RegionalID = ?
                AND M_UserIsActive = 'Y'
            ORDER BY M_UserUsername";
            $que = $this->db->query($sql, [$user['M_BranchID'], $user['S_RegionalID']]);
            if (!$que) {
                $this->sys_error_db("[Error] get staff");
                exit;
            }
            $data = $que->result_array();
            $this->sys_ok($data);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    private function generateNoLogistik($user, $typeLogistik) : string {
        $areaid = ($user['loginLevel'] == 'regional') ? $user['S_RegionalID'] : $user['M_BranchID'];
        $areatype = ($user['loginLevel'] == 'regional') ? 'R' : 'B';

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

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

        $numPL = $quenum->row_array()['numpd'];
        if (!isset($numPL)) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] number PI null");
            exit;
        }

        return $numPL;
    }

    private function getPOItemReceived($id) {
        $sql = "SELECT * FROM po_item_receive
        JOIN warehouse ON PoItemReceiveWarehouseID= WarehouseID 
        JOIN itemunit ON PoItemReceiveItemUnitID = ItemUnitID 
        WHERE PoItemReceiveID = ?";
        $que = $this->db->query($sql, [$id]);
        if (!$que) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] getDataPoItemReceive");;
            exit;
        }
        return $que->row_array();
    }

    private function getDataROPO($id) {
        $sql = "SELECT * FROM receive_order_po WHERE ReceiveOrderPoID = ?";
        $que = $this->db->query($sql, [$id]);
        if (!$que) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] getDataReceiveOrderPO ");;
            exit;
        }
        $ROheader = $que->row_array();

        $sqldetail = "SELECT * FROM receive_order_po_detail 
        WHERE ReceiveOrderPoDetailReceiveOrderPoID = ? AND ReceiveOrderPoDetailIsActive = 'Y'";
        $quedetail = $this->db->query($sqldetail, [$id]);
        if (!$quedetail) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] getDataReceiveOrderPO detail");;
            exit;
        }
        $ROdetail = $quedetail->result_array();
        $ROheader['details'] = $ROdetail;
        return $ROheader;
    }

    ## MUTATION ##

    function saveRO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $this->db->trans_begin();
            $param = $this->sys_input;
            $user = $this->sys_user;

            # generate nomor purchase invoice #
            $numGR = $this->generateNoLogistik($user, 'GR');

            # insert table ro #
            $sqlInsRO = "INSERT INTO receive_order_po (
                ReceiveOrderPoNumber,
                ReceiveOrderPoSupplierID,
                ReceiveOrderPoIDate,
                ReceiveOrderPoWarehouseID,
                ReceiveOrderPoM_BranchCode,
                ReceiveOrderPoS_RegionalID,
                ReceiveOrderShippingCostAmount,
                ReceiveOrderShippingCostIsPaid,
                ReceiveOrderPoNote,
                ReceiveOrderPoRefNumber,
                ReceiveOrderPoDONumber,
                ReceiveOrderPoCreatedUserID
            ) VALUE (?,?,?,?,?,?,?,?,?,?,?,?)";
            $queInsRO = $this->db->query($sqlInsRO, [
                $numGR, $param['supplierID'], $param['date'], $param['warehouseID'], 
                $user['M_BranchCode'], $user['S_RegionalID'], $param['shippingCost'], 
                $param['shippingIsPaid'], $param['note'], $param['refNumber'], 
                $param['DONumber'], $user['M_UserID']
            ]);
            if (!$queInsRO) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert into table receive order po");
                exit;
            }
            $roID = $this->db->insert_id();

            # inset to table ro detail #
            $logDesc = [];
            foreach ($param['detail'] as $key => $obj) {
                $sqlRcvdItem = "SELECT * FROM po_item_receive WHERE PoItemReceiveID = ?";
                $queRcvdItem = $this->db->query($sqlRcvdItem, [$obj['PoItemReceiveID']]);
                if (!$queRcvdItem) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("Error cek data item po_item_receive");
                    exit;
                }
                $cekReceived = $queRcvdItem->row_array();

                # cek item received #
                if ($cekReceived['PoItemReceiveIsActive'] == 'N') {
                    $this->db->trans_rollback();
                    $this->sys_error("[Error] Item {$obj['M_ItemDesc']} sudah diterima");
                    exit;
                }

                if ($cekReceived['PoItemReceiveQtyReceived'] == $cekReceived['PoItemReceiveQty']) {
                    $this->db->trans_rollback();
                    $this->sys_error("[Error] Item {$obj['M_ItemDesc']} sudah diterima semuanya");
                    exit;
                }

                if (intval($obj['qty']) > intval($cekReceived['PoItemReceiveQty'])) {
                    $this->db->trans_rollback();
                    $this->sys_error("Jumlah item {$obj['M_ItemDesc']} yang tersedia sudah berubah silahkan ulangi pilih item");
                    exit;
                }

                $jsonBatch = "[]";
                if (isset($obj['batchlist']) && is_array($obj['batchlist'])) {
                    $batchlist = $obj['batchlist'];
                    if (sizeof($batchlist) > 0) {
                        $json = json_encode($batchlist);
                        if ($json === false) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] encode json batch number");
                            exit;
                        } else {
                            $jsonBatch = $json;
                        }
                    }
                }

                $sqlInsRODetail = "INSERT INTO receive_order_po_detail (
                    ReceiveOrderPoDetailReceiveOrderPoID,
                    ReceiveOrderPoDetailPurchaseOrderID,
                    ReceiveOrderPoDetailPurchaseOrderSummaryID,
                    ReceiveOrderPoDetailPurchaseOrderDetailID,
                    ReceiveOrderPoDetailPoItemReceiveID,
                    ReceiveOrderPoDetailQty,
                    ReceiveOrderPoDetailPrice,
                    ReceiveOrderPoDetailTotal,
                    ReceiveOrderPoDetailCreatedUserID,
                    ReceiveOrderPoItemID,
                    ReceiveOrderPoItemUnitID,
                    ReceiveOrderPoDetailBatchNo,
                    ReceiveOrderPoDetailEd,
                    ReceiveOrderPoDetailBatchList
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
                $queInsRODetail = $this->db->query($sqlInsRODetail, [
                    $roID, $obj['PoItemReceivePurchaseOrderID'], 
                    $obj['PoItemReceivePurchaseOrderSummaryID'],
                    $obj['PoItemReceivePurchaseOrderDetailID'],
                    $obj['PoItemReceiveID'], $obj['qty'], $obj['PoItemReceivePrice'],
                    (doubleval($obj['PoItemReceivePrice']) * intval($obj['qty'])),
                    $user['M_UserID'], $obj['M_ItemID'], $obj['ItemUnitID'],
                    $obj['batchNo'], $obj['itemEd'], $jsonBatch
                ]);
                if (!$queInsRODetail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] receive order detail");
                    exit;
                }

                $logDesc[] = "- ADD {$obj['M_ItemDesc']} {$obj['qty']} {$obj['ItemUnitName']} nomor batch {$obj['WarehouseName']}";

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

                # INSERT INTO receive_order_po_inspeksi #
                $inspeksi = $obj['inspeksi_item'];
                $sqlInspeksi = "INSERT INTO receive_order_po_inspeksi (
                    ReceiveOrderPoInspeksiReceiveOrderPoDetailID,
                    ReceiveOrderPoInspeksiQtyPesan,
                    ReceiveOrderPoInspeksiQtyActual,
                    ReceiveOrderPoInspeksiPricePesan,
                    ReceiveOrderPoInspeksiPriceActual,
                    ReceiveOrderPoInspeksiDatePesan,
                    ReceiveOrderPoInspeksiDateActual,
                    ReceiveOrderPoInspeksiExpireDate,
                    ReceiveOrderPoInspeksiKeadaanKemasan,
                    ReceiveOrderPoInspeksiKeadaanKemasanNote,
                    ReceiveOrderPoInspeksiKondisiPengiriman,
                    ReceiveOrderPoInspeksiKondisiPengirimanNote,
                    ReceiveOrderPoInspeksiSimpulan,
                    ReceiveOrderPoInspeksiCatatan,
                    ReceiveOrderPoInspeksiStaffPenerimaID,
                    ReceiveOrderPoInspeksiPengirim,
                    ReceiveOrderPoInspeksiUserID
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
                $queInspeksi = $this->db->query($sqlInspeksi, [
                    $RODetailID, $inspeksi['qty_po'], $inspeksi['qty_ro'], $inspeksi['price_po'], 
                    $inspeksi['price_ro'], $inspeksi['date_po'], $inspeksi['date_ro'], 
                    $inspeksi['expire_date'], $inspeksi['condt_kemasan'], $inspeksi['catatan_kemasan'], 
                    $inspeksi['condt_pengiriman'], $inspeksi['catatan_kirim'], $inspeksi['summary'],
                    $inspeksi['catatan'], $inspeksi['pemeriksa'], $inspeksi['pengirim'], $user['M_UserID']
                ]);
                if (!$queInspeksi) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] insert data inspeksi item");
                    exit;
                }

                # po item received && log item change #
                $jsonBefore = $this->getPOItemReceived($obj['PoItemReceiveID']);
                $sqlupd = "UPDATE po_item_receive SET
                    PoItemReceiveQtyReceived = ?, 
                    PoItemReceiveUpdatedUserID = ?
                WHERE PoItemReceiveID = ?";
                $queupd = $this->db->query($sqlupd, [
                    (intval($cekReceived['PoItemReceiveQtyReceived']) + intval($obj['qty'])),
                    $user['M_UserID'], $obj['PoItemReceiveID']
                ]);
                if (!$queupd) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update item receive");
                    exit;
                }

                $jsonAfter = $this->getPOItemReceived($obj['PoItemReceiveID']);
                $desc = "Diterima {$obj['M_ItemDesc']} sejumlah {$obj['qty']} {$obj['ItemUnitName']} di RO nomor {$numGR} *RO belum di konfirmasi";
                $this->insertLogPoItemReceive(
                    $obj['PoItemReceiveID'], 'UPDATE', $roID, 
                    $jsonBefore, $jsonAfter, $user['M_UserID'], $desc
                );
            }

            $dataROAfter = $this->getDataROPO($roID);
            $this->insertLogReceiveOrderPo($roID, 'ADD', '', $dataROAfter, $user['M_UserID'], implode("\n", $logDesc));
            $this->insertUserActivty($roID, 'RO', 'ADD', $user['M_UserID'], implode("\n", $logDesc));

            $this->db->trans_commit();
            $this->sys_ok("Berhasil terima barang dengan nomor {$numGR}");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }
    
    function saveEditRO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $this->db->trans_begin();
            $param = $this->sys_input;
            $user = $this->sys_user;

            $sql_ro = "UPDATE receive_order_po SET
                ReceiveOrderPoNumber = ?,
                ReceiveOrderPoSupplierID = ?,
                ReceiveOrderPoIDate = ?,
                ReceiveOrderPoWarehouseID = ?,
                ReceiveOrderPoM_BranchCode = ?,
                ReceiveOrderPoS_RegionalID = ?,
                ReceiveOrderShippingCostAmount = ?,
                ReceiveOrderShippingCostIsPaid = ?,
                ReceiveOrderPoNote = ?,
                ReceiveOrderPoRefNumber = ?,
                ReceiveOrderPoDONumber = ?
            WHERE ReceiveOrderPoID = ?";
            $que_ro = $this->db->query($sql_ro, [
                $param['numGR'], $param['supplierID'], $param['date'], $param['warehouseID'], 
                $user['M_BranchCode'], $user['S_RegionalID'], $param['shippingCost'], $param['shippingIsPaid'], 
                $param['note'], $param['refNumber'], $param['DONumber'], $param['ReceiveOrderID']
            ]);
            if (!$que_ro) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert into table receive order po");
                exit;
            }

            # CHECK IF DETAIL ALREADY IN TABLE RECEIVE #
            $sql_check_ro = "SELECT ReceiveOrderPoDetailID, ReceiveOrderPoDetailPoItemReceiveID 
            FROM receive_order_po_detail 
            WHERE ReceiveOrderPoDetailReceiveOrderPoID = ? 
                AND ReceiveOrderPoDetailIsActive = 'Y'";
            $que_check_ro = $this->db->query($sql_check_ro, [$param['ReceiveOrderID']]);
            if (!$que_check_ro) {
                $this->db->trans_rollback();
                $this->sys_error("[Error] get list ro detail id");
                exit;
            }
            $list_detailID = $que_check_ro->result_array();
            $master_detailID = array_column($list_detailID, 'ReceiveOrderPoDetailID');
            $input_detailID = array_column($param['detail'], 'ReceiveOrderPoDetailID');

            $update = [];
            $insert = [];
            $delete = [];
            
            foreach ($param['detail'] as $key => $obj) {
                if (!isset($obj['ReceiveOrderPoDetailID']) ) {
                    $insert[] = $obj;
                    continue;
                }
                if (in_array($obj['ReceiveOrderPoDetailID'], $master_detailID)) {
                    $update[] = $obj;
                }
            }

            foreach ($list_detailID as $key => $obj) {
                if (!in_array($obj['ReceiveOrderPoDetailID'], $input_detailID)) {
                    $delete[] = $obj;
                }
            }

            # UPDATE #
            foreach ($update as $key => $obj) {
                # cek item received #
                $sqlRcvdItem = "SELECT * FROM po_item_receive WHERE PoItemReceiveID = ?";
                $queRcvdItem = $this->db->query($sqlRcvdItem, [$obj['PoItemReceiveID']]);
                if (!$queRcvdItem) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("Error cek data item po_item_receive");
                    exit;
                }
                $cekReceived = $queRcvdItem->row_array();
                if ($cekReceived['PoItemReceiveIsActive'] == 'N') {
                    $this->db->trans_rollback();
                    $this->sys_error("[Error] Item {$obj['M_ItemDesc']} tidak aktif");
                    exit;
                }

                if (intval($obj['qty']) > intval($cekReceived['PoItemReceiveQty'])) {
                    $this->db->trans_rollback();
                    $this->sys_error("Jumlah item {$obj['M_ItemDesc']} yang tersedia sudah berubah silahkan ulangi pilih item");
                    exit;
                }

                # UPDATE po item receive #
                $sql_up_po_receive = "UPDATE po_item_receive 
                JOIN receive_order_po_detail ON ReceiveOrderPoDetailPoItemReceiveID = PoItemReceiveID
                    AND ReceiveOrderPoDetailID = ?
                SET 
                    PoItemReceiveQtyReceived = (PoItemReceiveQtyReceived - ReceiveOrderPoDetailQty) + ?, 
                    PoItemReceiveUpdatedUserID = ?
                WHERE PoItemReceiveID = ?";
                $que_up_po_receive = $this->db->query($sql_up_po_receive, [
                    $obj['ReceiveOrderPoDetailID'], $obj['qty'], 
                    $user['M_UserID'], $obj['PoItemReceiveID']
                ]);
                if (!$que_up_po_receive) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update receive order detail");
                    exit;
                }

                # UPDATE receive detail po #
                $jsonBatch = "[]";
                if (isset($obj['batchlist']) && is_array($obj['batchlist'])) {
                    $batchlist = $obj['batchlist'];
                    if (sizeof($batchlist) > 0) {
                        $json = json_encode($batchlist);
                        if ($json === false) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] encode json batch number");
                            exit;
                        } else {
                            $jsonBatch = $json;
                        }
                    }
                }

                $sql_update_detail = "UPDATE receive_order_po_detail SET 
                    ReceiveOrderPoDetailReceiveOrderPoID = ?,
                    ReceiveOrderPoDetailPurchaseOrderID = ?,
                    ReceiveOrderPoDetailPurchaseOrderSummaryID = ?,
                    ReceiveOrderPoDetailPurchaseOrderDetailID = ?,
                    ReceiveOrderPoDetailPoItemReceiveID = ?,
                    ReceiveOrderPoDetailQty = ?,
                    ReceiveOrderPoDetailPrice = ?,
                    ReceiveOrderPoDetailTotal = ?,
                    ReceiveOrderPoItemID = ?,
                    ReceiveOrderPoItemUnitID = ?,
                    ReceiveOrderPoDetailBatchNo = ?,
                    ReceiveOrderPoDetailEd = ?,
                    ReceiveOrderPoDetailBatchList = ?
                WHERE ReceiveOrderPoDetailID = ?";
                $que_update_detail = $this->db->query($sql_update_detail, [
                    $param['ReceiveOrderID'], $obj['PoItemReceivePurchaseOrderID'], 
                    $obj['PoItemReceivePurchaseOrderSummaryID'], 
                    $obj['PoItemReceivePurchaseOrderDetailID'], 
                    $obj['PoItemReceiveID'], $obj['qty'], $obj['PoItemReceivePrice'], 
                    (doubleval($obj['PoItemReceivePrice']) * intval($obj['qty'])), 
                    $obj['M_ItemID'], $obj['ItemUnitID'], $obj['batchNo'], 
                    $obj['itemEd'], $jsonBatch, $obj['ReceiveOrderPoDetailID']
                ]);
                if (!$que_update_detail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update receive order detail");
                    exit;
                }

                # update form inspeksi item #
                $inspeksi = $obj['inspeksi_item'];
                $sql_inspeksi = "UPDATE receive_order_po_inspeksi SET 
                    ReceiveOrderPoInspeksiQtyPesan = ?,
                    ReceiveOrderPoInspeksiQtyActual = ?,
                    ReceiveOrderPoInspeksiPricePesan = ?,
                    ReceiveOrderPoInspeksiPriceActual = ?,
                    ReceiveOrderPoInspeksiDatePesan = ?,
                    ReceiveOrderPoInspeksiDateActual = ?,
                    ReceiveOrderPoInspeksiExpireDate = ?,
                    ReceiveOrderPoInspeksiKeadaanKemasan = ?,
                    ReceiveOrderPoInspeksiKeadaanKemasanNote = ?,
                    ReceiveOrderPoInspeksiKondisiPengiriman = ?,
                    ReceiveOrderPoInspeksiKondisiPengirimanNote = ?,
                    ReceiveOrderPoInspeksiSimpulan = ?,
                    ReceiveOrderPoInspeksiCatatan = ?,
                    ReceiveOrderPoInspeksiStaffPenerimaID = ?,
                    ReceiveOrderPoInspeksiPengirim = ?
                WHERE ReceiveOrderPoInspeksiID = ?";
                $que_inspeksi = $this->db->query($sql_inspeksi, [
                    $inspeksi['qty_po'], $inspeksi['qty_ro'], $inspeksi['price_po'], 
                    $inspeksi['price_ro'], $inspeksi['date_po'], $inspeksi['date_ro'], 
                    $inspeksi['expire_date'], $inspeksi['condt_kemasan'], 
                    $inspeksi['catatan_kemasan'], $inspeksi['condt_pengiriman'], 
                    $inspeksi['catatan_kirim'], $inspeksi['summary'], $inspeksi['catatan'], 
                    $inspeksi['pemeriksa'], $inspeksi['pemeriksa'], $inspeksi['ReceiveOrderPoInspeksiID']
                ]);
                if (!$que_inspeksi) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update form inspeksi");
                    exit;
                }
            }

            # INSERT #
            foreach ($insert as $key => $obj) {
                # cek item received #
                $sqlRcvdItem = "SELECT * FROM po_item_receive WHERE PoItemReceiveID = ?";
                $queRcvdItem = $this->db->query($sqlRcvdItem, [$obj['PoItemReceiveID']]);
                if (!$queRcvdItem) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("Error cek data item po_item_receive");
                    exit;
                }
                $cekReceived = $queRcvdItem->row_array();
                if ($cekReceived['PoItemReceiveIsActive'] == 'N') {
                    $this->db->trans_rollback();
                    $this->sys_error("[Error] Item {$obj['M_ItemDesc']} tidak aktif");
                    exit;
                }

                if (intval($obj['qty']) > intval($cekReceived['PoItemReceiveQty'])) {
                    $this->db->trans_rollback();
                    $this->sys_error("Jumlah item {$obj['M_ItemDesc']} yang tersedia sudah berubah silahkan ulangi pilih item");
                    exit;
                }

                # UPDATE po item receive #
                $sql_up_po_receive = "UPDATE po_item_receive SET 
                    PoItemReceiveQtyReceived = ?, 
                    PoItemReceiveUpdatedUserID = ?
                WHERE PoItemReceiveID = ?";
                $que_up_po_receive = $this->db->query($sql_up_po_receive, [
                    (intval($cekReceived['PoItemReceiveQtyReceived']) + intval($obj['qty'])),
                    $user['M_UserID'], $obj['PoItemReceiveID']
                ]);
                if (!$que_up_po_receive) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update receive order detail");
                    exit;
                }

                $jsonBatch = "[]";
                if (isset($obj['batchlist']) && is_array($obj['batchlist'])) {
                    $batchlist = $obj['batchlist'];
                    if (sizeof($batchlist) > 0) {
                        $json = json_encode($batchlist);
                        if ($json === false) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] encode json batch number");
                            exit;
                        } else {
                            $jsonBatch = $json;
                        }
                    }
                }

                # RE-INSERT ITEM DETAIL #
                $sql_insert_rodetail = "INSERT INTO receive_order_po_detail (
                    ReceiveOrderPoDetailReceiveOrderPoID,
                    ReceiveOrderPoDetailPurchaseOrderID,
                    ReceiveOrderPoDetailPurchaseOrderSummaryID,
                    ReceiveOrderPoDetailPurchaseOrderDetailID,
                    ReceiveOrderPoDetailPoItemReceiveID,
                    ReceiveOrderPoDetailQty,
                    ReceiveOrderPoDetailPrice,
                    ReceiveOrderPoDetailTotal,
                    ReceiveOrderPoDetailCreatedUserID,
                    ReceiveOrderPoItemID,
                    ReceiveOrderPoItemUnitID,
                    ReceiveOrderPoDetailBatchNo,
                    ReceiveOrderPoDetailEd,
                    ReceiveOrderPoDetailBatchList
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
                $que_insert_rodetail = $this->db->query($sql_insert_rodetail, [
                    $param['ReceiveOrderID'], $obj['PoItemReceivePurchaseOrderID'], 
                    $obj['PoItemReceivePurchaseOrderSummaryID'],
                    $obj['PoItemReceivePurchaseOrderDetailID'],
                    $obj['PoItemReceiveID'], $obj['qty'], $obj['PoItemReceivePrice'], 
                    (doubleval($obj['PoItemReceivePrice']) * intval($obj['qty'])),
                    $user['M_UserID'], $obj['M_ItemID'], $obj['ItemUnitID'],
                    $obj['batchNo'], $obj['itemEd'], $jsonBatch
                ]);
                if (!$que_insert_rodetail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] insert receive order detail");
                    exit;
                }
                $ro_detail_ID = $this->db->insert_id();

                # INSERT form inspeksi item #
                $inspekitem = $obj['inspeksi_item'];
                $sql_insert_inspeksi = " INSERT INTO receive_order_po_inspeksi (
                    ReceiveOrderPoInspeksiReceiveOrderPoDetailID,
                    ReceiveOrderPoInspeksiQtyPesan,
                    ReceiveOrderPoInspeksiQtyActual,
                    ReceiveOrderPoInspeksiPricePesan,
                    ReceiveOrderPoInspeksiPriceActual,
                    ReceiveOrderPoInspeksiDatePesan,
                    ReceiveOrderPoInspeksiDateActual,
                    ReceiveOrderPoInspeksiExpireDate,
                    ReceiveOrderPoInspeksiKeadaanKemasan,
                    ReceiveOrderPoInspeksiKeadaanKemasanNote,
                    ReceiveOrderPoInspeksiKondisiPengiriman,
                    ReceiveOrderPoInspeksiKondisiPengirimanNote,
                    ReceiveOrderPoInspeksiSimpulan,
                    ReceiveOrderPoInspeksiCatatan,
                    ReceiveOrderPoInspeksiStaffPenerimaID,
                    ReceiveOrderPoInspeksiPengirim,
                    ReceiveOrderPoInspeksiUserID
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
                $que_insert_inspeksi = $this->db->query($sql_insert_inspeksi, [
                    $ro_detail_ID, $inspekitem['qty_po'], $inspekitem['qty_ro'], 
                    $inspekitem['price_po'], $inspekitem['price_ro'], $inspekitem['date_po'], 
                    $inspekitem['date_ro'], $inspekitem['expire_date'], $inspekitem['condt_kemasan'], 
                    $inspekitem['catatan_kemasan'], $inspekitem['condt_pengiriman'], 
                    $inspekitem['catatan_kirim'], $inspekitem['summary'], $inspekitem['catatan'], 
                    $inspekitem['pemeriksa'], $inspekitem['pengirim'], $user['M_UserID']
                ]);
                if (!$que_insert_inspeksi) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] insert hasil form inspeksi");
                    exit;
                }
            }

            # DELETE #
            if (count($delete) > 0) {
                $deleted_id = array_column($delete, 'ReceiveOrderPoDetailID');
                $placeholders = implode(', ', array_fill(0, count($deleted_id), '?'));

                $deleted_poid = array_column($delete , 'ReceiveOrderPoDetailPoItemReceiveID');
                $placeholders_po = implode(', ', array_fill(0, count($deleted_poid), '?'));
                
                # UPDATE po item receive #
                $del_param = array_merge(array($user['M_UserID']), $deleted_poid, $deleted_id);

                $sql_up_po_receive = "UPDATE po_item_receive 
                JOIN receive_order_po_detail ON ReceiveOrderPoDetailPoItemReceiveID = PoItemReceiveID
                SET PoItemReceiveQtyReceived = (PoItemReceiveQtyReceived - ReceiveOrderPoDetailQty), 
                    PoItemReceiveUpdatedUserID = ?
                WHERE PoItemReceiveID IN ($placeholders)
                    AND ReceiveOrderPoDetailID IN ($placeholders_po)";
                $que_up_po_receive = $this->db->query($sql_up_po_receive, $del_param);
                if (!$que_up_po_receive) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update receive po item");
                    exit;
                }
                
                # UPDATE receive order po detail #
                $upd_param = array_merge(array($user['M_UserID']), $deleted_id);
                $sql_update_detail = "UPDATE receive_order_po_detail SET 
                    ReceiveOrderPoDetailIsActive = 'N', 
                    ReceiveOrderPoDetailDeleted = NOW(),
                    ReceiveOrderPoDetailDeletedUserID = ?
                WHERE ReceiveOrderPoDetailID IN ($placeholders)";
                $que_update_detail = $this->db->query($sql_update_detail, $upd_param);
                if (!$que_update_detail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update status receive order detail");
                    exit;
                }

                # update status table form inspeksi #
                $deleted_inspeksi = array_map(function($item) {
                    if (isset($item['inspeksi_item']['ReceiveOrderPoInspeksiID'])) {
                        return $item['inspeksi_item']['ReceiveOrderPoInspeksiID'];
                    }
                    return null;
                }, $delete);
                $placeholders_inspeksi = implode(', ', array_fill(0, count($deleted_inspeksi), '?'));
                $delinspeksi_param = array_merge($user['M_UserID'], $deleted_inspeksi);

                $sql_inspeksi_status = "UPDATE receive_order_po_inspeksi SET 
                    ReceiveOrderPoInspeksiIsActive = 'N',
                    ReceiveOrderPoInspeksiUserID = ?,
                    ReceiveOrderPoInspeksiDeleted = NOW()
                WHERE ReceiveOrderPoInspeksiID IN ($placeholders_inspeksi)";
                $que_inspeksi_status = $this->db->query($sql_inspeksi_status, $delinspeksi_param);
                if (!$que_inspeksi_status) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update status form receive item");
                    exit;
                }
            }

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

    function confirmRO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $this->db->trans_begin();
            $param = $this->sys_input;
            $user = $this->sys_user;
            $ROID = $param['ROID'];

            # GET DATA RO #
            $json_ro_before = $this->getDataROPO($ROID);
            $sql_ro = "SELECT 
                receive_order_po.*,
                DATE_FORMAT(ReceiveOrderPoIDate, '%d-%m-%Y') AS formatedDate,
                CASE
                    WHEN WarehouseType = 'B' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', M_BranchName) 
                    WHEN WarehouseType = 'R' 
                        THEN CONCAT(WarehouseCode,' ', WarehouseName, ' - ', S_RegionalName) 
                    ELSE ''
                END WarehouseName,
                WarehouseID
            FROM receive_order_po
            JOIN warehouse ON ReceiveOrderPoWarehouseID = WarehouseID 
            JOIN s_regional ON WarehouseS_RegionalID = S_RegionalID 
                AND S_RegionalIsActive = 'Y'
            LEFT JOIN m_branch ON WarehouseM_BranchID = M_BranchID 
                AND M_BranchIsActive = 'Y'
            WHERE ReceiveOrderPoID = ?";
            $que_ro = $this->db->query($sql_ro, [$ROID]);
            if (!$que_ro) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get data RO");
                exit;
            }
            $data_ro = $que_ro->row_array();
            if ($data_ro['ReceiveOrderPoConfirmed'] == 'Y') {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] item dari RO{$data_ro['ReceiveOrderPoNumber']} sudah pernah di konfirmasi");
                exit;
            }

            $sql_detail_ro = "SELECT 
                receive_order_po_detail.*,
                PurchaseOrderID, 
                PurchaseOrderNumber,
                PurchaseOrderTaxPercentPph,
                PurchaseOrderTaxPercentPpn,
                PurchaseOrderSummaryDiscountAmount as DiscountPerItem,
                PurchaseOrderSummaryQty as QtyAllPO, 
                PurchaseOrderSummaryTotal as TotalAllPO,
                PurchaseOrderDiscountPercent AS DiscPOPercent,
                PurchaseOrderDiscountAmount AS DiscPORupiah
            FROM receive_order_po_detail
            JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID
            JOIN purchase_order_summary ON ReceiveOrderPoDetailPurchaseOrderSummaryID = PurchaseOrderSummaryID
                AND PurchaseOrderSummaryIsActive = 'Y'
            WHERE ReceiveOrderPoDetailReceiveOrderPoID = ? 
                AND ReceiveOrderPoDetailIsActive='Y'
            GROUP BY ReceiveOrderPoDetailID";
            $que_detail_ro = $this->db->query($sql_detail_ro, [$ROID]);
            if (!$que_detail_ro) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get detail data ro");
                exit;
            }
            $data_detail_ro = $que_detail_ro->result_array();

            # UPDATE STATUS RO #
            $sql_update_ro = "UPDATE receive_order_po SET 
                ReceiveOrderPoConfirmed = 'Y',
                ReceiveOrderPoConfirmedDate = NOW(),
                ReceiveOrderPoConfirmedUserID = ?
            WHERE ReceiveOrderPoID = ?";
            $que_update_ro = $this->db->query($sql_update_ro, [$user['M_UserID'], $ROID]);
            if (!$que_update_ro) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get update status ro");
                exit;
            }

            # PREPARE DATA FOR JURNAL #
            $sql_jrnl_num = "SELECT `fn_numbering`('J') AS numbering";
            $que_jrnl_num = $this->db->query($sql_jrnl_num, []);
            if (!$que_jrnl_num) {
                $this->sys_error_db("[Error] generate jurnal number");
                $this->db->trans_rollback();
                exit;
            }
            $jurnal_number = $que_jrnl_num->row_array()['numbering'];

            $sql_periode = "SELECT periodeID FROM periode
            WHERE DATE(NOW()) BETWEEN periodeStartDate AND periodeEndDate 
                AND periodeIsActive= 'Y' AND periodeIsClosed = 'N'";
            $que_periode = $this->db->query($sql_periode, []);
            if (!$que_periode) {
                $this->sys_error_db("[Error] get periode", $this->db);
                $this->db->trans_rollback();
                exit;
            }
            if ($que_periode->num_rows() === 0) {
                $this->sys_error_db("[Error] waktu periode tidak ditemukan", $this->db);
                $this->db->trans_rollback();
                exit;
            }
            $jurnal_periode = $que_periode->row_array()['periodeID'];

            $sql_jrnl_type = "SELECT JurnalTypeID FROM jurnal_type WHERE JurnalTypeCode = ? AND JurnalTypeIsActive = 'Y'";
            $que_jrnl_type = $this->db->query($sql_jrnl_type, ['AUTOGOODRECEIVE']);
            if (!$que_jrnl_type) {
                $this->sys_error_db("[Error] get jurnal type", $this->db);
                $this->db->trans_rollback();
                exit;
            }
            if ($que_jrnl_type->num_rows() === 0) {
                $this->sys_error_db("[Error] tipe jurnal tidak ditemukan", $this->db);
                $this->db->trans_rollback();
                exit;
            }
            $jurnal_type =  $que_jrnl_type->row_array()['JurnalTypeID'];

            $list_po_in_ro = array_column($data_detail_ro, 'PurchaseOrderNumber');
            $str_po = implode(', ', array_unique($list_po_in_ro));

            $jurnal_title = "Jurnal Penerimaan Barang Dari PO {$str_po} Tanggal {$data_ro['formatedDate']}";
            $jurnal_desc = $jurnal_title . " Dengan Nomor DO {$data_ro['ReceiveOrderPoDONumber']} Di {$data_ro['WarehouseName']}";

            # INSERT JURNAL HEADER #
            $sql_head_jurnal = "INSERT INTO jurnal (
                jurnalM_BranchCompanyID,
                JurnalS_RegionalID,
                jurnalM_BranchCode,
                jurnalperiodeID,
                jurnalNo,
                jurnalTitle,
                jurnalDescription,
                jurnalDate,
                jurnalJurnalTypeID,
                jurnalM_UserID
            ) VALUES (?,?,?,?,?,?,?,NOW(),?,?)";
            $que_head_jurnal = $this->db->query($sql_head_jurnal, [
                $user['M_BranchCompanyID'], $user['S_RegionalID'], $user['M_BranchCode'], 
                $jurnal_periode, $jurnal_number, $jurnal_title, $jurnal_desc, $jurnal_type,  $user['M_UserID'] 
            ]);
            if (!$que_head_jurnal) {
                $this->sys_error_db("[Error] insert header jurnal", $this->db);
                $this->db->trans_rollback();
                exit;
            }
            $jurnal_ID = $this->db->insert_id();

            $total_debet = 0.00;
            $total_discount_item = 0.00;
            $total_discount_po = 0.00;
            $each_desc = [];

            foreach ($data_detail_ro as $key => $detail) {
                # UPDATE PO Summary #
                $sql_posummary = "UPDATE purchase_order_summary SET 
                    PurchaseOrderSummaryQtyReceived = PurchaseOrderSummaryQtyReceived + ? 
                WHERE PurchaseOrderSummaryIsActive = 'Y'
                    AND PurchaseOrderSummaryID = ?";
                $que_posummary = $this->db->query($sql_posummary, [
                    intval($detail['ReceiveOrderPoDetailQty']),
                    $detail['ReceiveOrderPoDetailPurchaseOrderSummaryID']
                ]);
                if (!$que_posummary) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update qty received po summary");
                    exit;
                }

                $sql_cek_item = "SELECT * FROM m_item WHERE M_ItemID = ?";
                $que_cek_item = $this->db->query($sql_cek_item, [$detail['ReceiveOrderPoItemID']]);
                if (!$que_cek_item) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] cek item received");
                    exit;
                }
                $item_cek = $que_cek_item->row_array();
                if (empty($item_cek['M_ItemItem_CategoryID'])) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] kategori item {$item_cek['M_ItemDesc']} tidak ditemukan pada PO
                        {$detail['PurchaseOrderNumber']}");
                    exit;
                }

                # INSERT JURNAL TX #
                if (intval($item_cek['M_ItemItem_CategoryID']) == 1) {
                    if (empty($item_cek['M_ItemNat_SubGroupID'])) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] subgroup item {$item_cek['M_ItemDesc']} tidak ditemukan pada PO
                            {$detail['PurchaseOrderNumber']}");
                        exit;
                    }

                    $sql_item_group = "SELECT * FROM map_nat_subgroup 
                        WHERE MapNatSub_NatGroupID = ? 
                        AND MapNatSub_NatSubGroupID = ?
                        AND MapNatSub_IsActive = 'Y'";
                    $que_item_group = $this->db->query($sql_item_group, [
                        $item_cek['M_ItemNat_GroupID'],
                        $item_cek['M_ItemNat_SubGroupID']
                    ]);
                    if (!$que_item_group) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] get item group/subgroup");
                        exit;
                    }
                    $item_group = $que_item_group->row_array();
                    if (empty($item_group['MapNatSub_PersediaanCoaID']) || 
                        empty($item_group['MapNatSub_PersediaanCoaAccountNo']) || 
                        empty($item_group['MapNatSub_PersediaanCoaDescription'])
                    ) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] Account persediaan item {$item_cek['M_ItemDesc']} tidak ditemukan");
                        exit;
                    }

                    $debet = doubleval($detail['ReceiveOrderPoDetailTotal']);
                    $total_debet = round($total_debet + $debet, 2);

                    # INSERT JURNAL TX DEBET #
                    $sql_tx_jurnal = "INSERT INTO jurnal_tx (
                        jurnalTxJurnalID,
                        jurnalTxCoaID,
                        jurnalTxDescription,
                        jurnalTxDebit,
                        jurnalTxCredit,
                        jurnalTxM_UserID
                    ) VALUES (?,?,?,?,?,?)";
                    $que_tx_jurnal = $this->db->query($sql_tx_jurnal, [
                        $jurnal_ID, $item_group['MapNatSub_PersediaanCoaID'], 
                        $item_group['MapNatSub_PersediaanCoaDescription'],
                        $debet, 0, $user['M_UserID']
                    ]);
                    if (!$que_tx_jurnal) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] failed insert jurnal tx {$item_cek['M_ItemDesc']} 
                            PO {$detail['PurchaseOrderNumber']}");
                        exit;
                    }
                    $juranl_tx_ID = $this->db->insert_id();
                    $each_desc[] = "- Add detail Jurnal debet {$item_cek['M_ItemDesc']} 
                        {$item_group['MapNatSub_PersediaanCoaDescription']} sejumlah {$this->formatRupiah($debet)}";

                    # INSERT JURNAL ADDON  #
                    $sql_addon_jurnal = "INSERT INTO jurnal_addon (
                        jurnalAddOnJurnalID,
                        jurnalAddOnJurnalTxID,
                        jurnalAddOnCode,
                        jurnalAddOnValue,
                        jurnalAddOnCreated,
                        jurnalAddOnCreatedUserID
                    ) VALUES (?,?,'PONUMB',?,NOW(),?)";
                    $que_addon_jurnal = $this->db->query($sql_addon_jurnal, [
                        $jurnal_ID, $juranl_tx_ID, $detail['PurchaseOrderNumber'], $user['M_UserID']
                    ]);
                    if (!$que_addon_jurnal) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] failed insert jurnal addon {$item_cek['M_ItemDesc']} 
                            PO {$detail['PurchaseOrderNumber']}");
                        exit;
                    }

                    # INSERT JURNAL TX KREDIT DISKON #
                    $sql_po = "SELECT 
                        SUM(subquery.PurchaseOrderSummaryQty) as totalAllQtyPO,
                        SUM(subquery.PurchaseOrderSummaryTotal) as totalAllPricePO
                    FROM (
                        SELECT 
                            PurchaseOrderSummaryQty,
                            PurchaseOrderSummaryTotal
                        FROM receive_order_po_detail
                        JOIN purchase_order_summary ON PurchaseOrderSummaryPurchaseOrderID = ReceiveOrderPoDetailPurchaseOrderID
                            AND PurchaseOrderSummaryIsActive = 'Y'
                        WHERE ReceiveOrderPoDetailReceiveOrderPoID = ?
                        GROUP BY PurchaseOrderSummaryID
                    ) as subquery";
                    $que_po = $this->db->query($sql_po, [$ROID]);
                    if (!$que_po) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] get detail total qty and price of item po");
                        exit;
                    }
                    $data_po = $que_po->row_array();

                    // calc diskon per item
                    $diskon = round(intval($detail['ReceiveOrderPoDetailQty'] * doubleval($detail['DiscountPerItem'])), 2);
                    $total_discount_item = round($total_discount_item + $diskon, 2);

                    // calc diskon prorata PO untuk item
                    $diskon_po = doubleval($detail['DiscPORupiah']);
                    if (doubleval($detail['DiscPOPercent']) != 0.00) {
                        $diskon_po = doubleval($data_po['totalAllPricePO']) * doubleval($detail['DiscPOPercent']) / 100;
                    }
                    $diskon_prorata_po = $diskon_po * (doubleval($detail['TotalAllPO'])) / doubleval($data_po['totalAllPricePO']);
                    $diskon_prorata_item = round((intval($detail['ReceiveOrderPoDetailQty']) / intval($detail['QtyAllPO'])) * $diskon_prorata_po, 2);

                    $total_discount_po = round($total_discount_po + $diskon_prorata_item, 2);
                    $input_diskon = [$diskon, $diskon_prorata_item];

                    // get account coa diskon
                    $sql_coa_disc = "SELECT * FROM coa WHERE coaAccountNo = ? LIMIT 1";
                    $que_coa_disc = $this->db->query($sql_coa_disc, ['2190100001']);
                    if (!$que_coa_disc) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] get account diskon");
                        exit;
                    }
                    $coa_diskon = $que_coa_disc->row_array();

                    foreach ($input_diskon as $key => $value) {
                        if (doubleval($value) === 0.00) {
                            continue;
                        }
                        $que_tx_jurnal = $this->db->query($sql_tx_jurnal, [
                            $jurnal_ID, $coa_diskon['coaID'], $coa_diskon['coaDescription'], 
                            0, $value, $user['M_UserID']
                        ]);
                        if (!$que_tx_jurnal) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] failed insert jurnal tx diskon 
                                item {$item_cek['M_ItemDesc']} PO {$detail['PurchaseOrderNumber']}");
                            exit;
                        }
                        $juranl_txdisc_ID = $this->db->insert_id();
                        $que_addon_jurnal = $this->db->query($sql_addon_jurnal, [
                            $jurnal_ID, $juranl_txdisc_ID, $detail['PurchaseOrderNumber'], $user['M_UserID']
                        ]);
                        if (!$que_addon_jurnal) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] failed insert jurnal addon diskon 
                                item {$item_cek['M_ItemDesc']} PO {$detail['PurchaseOrderNumber']}");
                            exit;
                        }
                    }

                    // save diskon prorata
                    $sql_save_diskon = "UPDATE receive_order_po_detail SET
                        ReceiveOrderPoDetailDiskonPoProrata = ?
                    WHERE ReceiveOrderPoDetailID = ?";
                    $que_save_diskon = $this->db->query($sql_save_diskon, [
                        $diskon_prorata_item, $detail['ReceiveOrderPoDetailID']
                    ]);
                    if (!$que_save_diskon) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] save diskon prorata 
                            item {$item_cek['M_ItemDesc']} PO {$detail['PurchaseOrderNumber']}");
                        exit;
                    }
                } else {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] BE Jurnal tipe selain Persediaan belum di implementasi");
                    exit;
                }
                
                # HANDLE STOCK UPDATE #
                // decode batchlist item diterima 
                $json_batch_item = $detail['ReceiveOrderPoDetailBatchList'];
                $batch_list = [];
                if (!empty($json_batch_item)) {
                    $decode = json_decode($json_batch_item, true);
                    if ($decode !== null && json_last_error() === JSON_ERROR_NONE) {
                        $batch_list = $decode;
                    } else {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] decode json for batches number");
                        exit;
                    }
                }

                // update stock based on batch
                foreach ($batch_list as $key => $batch) {
                    $sql_cek_stock = "SELECT * FROM stock 
                        WHERE StockItemID = ? AND StockItemUnitID = ? 
                        AND StockWarehouseID = ? AND StockBatchNo = ?";
                    $que_cek_stock = $this->db->query($sql_cek_stock, [
                        $detail['ReceiveOrderPoItemID'], $detail['ReceiveOrderPoItemUnitID'], 
                        $data_ro['WarehouseID'], $batch['batchno']
                    ]);
                    if (!$que_cek_stock) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] check stock exist or not");
                        exit;
                    }
                    $stock_exist = $que_cek_stock->row_array();
                    $stock_ID = $stock_exist['StockID'];
                    $stock_qty_ori = intval($stock_exist['StockQty']);
                    $stock_price = doubleval($stock_exist['StockItemPrice']);
                    $stock_qty_end = 0;

                    if (!empty($stock_exist)) {
                        // calc new prive average 
                        $stock_qty_end = $stock_qty_ori + intval($batch['qty']);
                        $all_price = ($stock_qty_ori * $stock_price) + (intval($batch['qty']) * doubleval($detail['ReceiveOrderPoDetailPrice']));
                        $avg_prive = round($all_price / $stock_qty_end, 2);

                        $sql_update_stock = "UPDATE stock SET 
                            StockItemPrice = ?,
                            StockQty = ?,
                            StockBatchNo = ?,
                            StockED = ?, 
                            StockLastUpdated = NOW(),
                            StockUserID = ?
                        WHERE StockID = ?";
                        $que_update_stock = $this->db->query($sql_update_stock, [
                            $avg_prive, $stock_qty_end, $batch['batchno'], 
                            $batch['expiredate'], $user['M_UserID'], $stock_ID
                        ]);
                        if (!$que_update_stock) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] insert update stock");
                            exit;
                        }
                    } else {
                        $sql_insert_stock = "INSERT INTO stock (
                            StockWarehouseAlmariID,
                            StockWarehouseRackID,
                            StockWarehouseID,
                            StockStockNumber,
                            StockBatchNo,
                            StockItemID,
                            StockItemUnitID,
                            StockED,
                            StockItemPrice,
                            StockQty,
                            StockLastUpdated,
                            StockUserID
                        ) VALUES (0,0,?,fn_numbering('SN'),?,?,?,?,?,?,NOW(),?)";
                        $que_insert_stock = $this->db->query($sql_insert_stock, [
                            $data_ro['WarehouseID'], $batch['batchno'], $detail['ReceiveOrderPoItemID'], 
                            $detail['ReceiveOrderPoItemUnitID'], $batch['expiredate'], 
                            $detail['ReceiveOrderPoDetailPrice'], $batch['qty'], $user['M_UserID']
                        ]);
                        if (!$que_insert_stock) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] insert new batch stock");
                            exit;
                        }

                        $stock_ID = $this->db->insert_id();
                        $stock_qty_ori = 0;
                        $stock_qty_end = intval($batch['qty']);
                    }

                    // insert stock card
                    $sql_stock_card = "INSERT INTO stockcard (
                        StockCardWarehouseID,
                        StockCardDatetime,
                        StockCardItemID,
                        StockCardItemUnitID,
                        StockCardBatchNo,
                        StockCardED,
                        StockCardReffID,
                        StockCardStatus,
                        StockCardBefore,
                        StockCardIn,
                        StockCardOut,
                        StockCardAfter,
                        StockCardUserID
                    ) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'PRV', ?, ?, 0, ?, ?)";
                    $que_stock_card = $this->db->query($sql_stock_card, [
                        $data_ro['WarehouseID'], $detail['ReceiveOrderPoItemID'], $detail['ReceiveOrderPoItemUnitID'], 
                        $batch['batchno'], $batch['expiredate'], $stock_ID, $stock_qty_ori, $batch['qty'], 
                        $stock_qty_end, $user['M_UserID']
                    ]);
                    if (!$que_stock_card) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] insert stock card");
                        exit;
                    }

                    // insert stock log
                    $sql_stock = "SELECT * FROM stock WHERE StockID = ?";
                    $que_stock = $this->db->query($sql_stock, [$stock_ID]);
                    if (!$que_stock) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] get latest stock");
                        exit;
                    }
                    $stock_latest = $que_stock->row_array();

                    $sql_stock_log = "INSERT INTO stocklog (
                        StockLogWarehouseID,
                        StockLogDateTime,
                        StockLogItemID,
                        StockLogItemUnitID,
                        StockLogStockNumber,
                        StockLogBatchNo,
                        StockLogED,
                        StockLogReffID,
                        StockLogStatus,
                        StockLogQty,
                        StockLogUserID
                    ) VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, 'PRV', ?, ?)";
                    $que_stock_log = $this->db->query($sql_stock_log, [
                        $data_ro['WarehouseID'], $detail['ReceiveOrderPoItemID'], $detail['ReceiveOrderPoItemUnitID'], 
                        $stock_latest['StockStockNumber'], $stock_latest['StockBatchNo'], $stock_latest['StockED'], 
                        $stock_ID, $batch['qty'], $user['M_UserID']
                    ]);
                    if (!$que_stock_log) {
                        $this->db->trans_rollback();
                        $this->sys_error_db("[Error] get latest stock");
                        exit;
                    }
                }
            }

            # insert shipping cost ke jurnal #
            $ship_cost = round(doubleval($data_ro['ReceiveOrderShippingCostAmount']), 2);
            if ($ship_cost > 0.00) {
                $sql_tx_ship = "INSERT INTO jurnal_tx (
                    jurnalTxJurnalID,
                    jurnalTxCoaID,
                    jurnalTxDescription,
                    jurnalTxDebit,
                    jurnalTxCredit,
                    jurnalTxM_UserID
                ) SELECT ?, coaID, coaDescription, ?, 0, ? 
                  FROM coa WHERE coaAccountNo = '5320700001' LIMIT 1";
                $que_tx_ship = $this->db->query($sql_tx_ship, [$jurnal_ID, $ship_cost, $user['M_UserID']]);
                if (!$que_tx_ship) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] insert jurnal tx shipping cost");
                    exit;
                }
            }

            // insert jurnal tx for GRNI
            $total_grni = round($total_debet - ($total_discount_item + $total_discount_po), 2) + $ship_cost;
            $sql_grni = "INSERT INTO jurnal_tx (
                jurnalTxJurnalID,
                jurnalTxCoaID,
                jurnalTxDescription,
                jurnalTxDebit,
                jurnalTxCredit,
                jurnalTxM_UserID
            ) SELECT ?, coaID, coaDescription, 0, ?, ? 
              FROM coa WHERE coaAccountNo = '2110100030' LIMIT 1";
            $que_grni = $this->db->query($sql_grni, [$jurnal_ID, $total_grni, $user['M_UserID']]);
            if (!$que_grni) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert jurnal tx GRNI");
                exit;
            }
            $grni_jurnal_id = $this->db->insert_id();

            $sql_addon_grni = "INSERT INTO jurnal_addon (
                jurnalAddOnJurnalID,
                jurnalAddOnJurnalTxID,
                jurnalAddOnCode,
                jurnalAddOnValue,
                jurnalAddOnCreated,
                jurnalAddOnCreatedUserID
            ) VALUES (?,?,'RONUMB',?,NOW(),?)";
            $que_addon_grni = $this->db->query($sql_addon_grni, [
                $jurnal_ID, $grni_jurnal_id, $data_ro['ReceiveOrderPoNumber'], $user['M_UserID']
            ]);
            if (!$que_addon_grni) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert jurnal addon GRNI");
                exit;
            }

            # insert into shipping table #
            $sql_rec_ship = "INSERT INTO receive_order_po_shipping (
                ReceiveOrderPoShippingPurchaseOrderID,
                ReceiveOrderPoShippingReceiveOrderPoID,
                ReceiveOrderPoShippingAmount,
                ReceiveOrderPoShippingStatus,
                ReceiveOrderPoShippingIsActive,
                ReceiveOrderPoShippingCreatedUserID
            ) SELECT 
                PurchaseOrderID,
                {$ROID},
                PurchaseOrderShippingCost,
                PurchaseOrderShippingCostStatus,
                'Y',
                {$user['M_UserID']}
              FROM purchase_order
              WHERE PurchaseOrderID IN (
                SELECT DISTINCT ReceiveOrderPoDetailPurchaseOrderID 
                FROM receive_order_po
                JOIN receive_order_po_detail ON ReceiveOrderPoDetailReceiveOrderPoID = ReceiveOrderPoID
                WHERE ReceiveOrderPoID = {$user['M_UserID']}
              ) 
              AND PurchaseOrderShippingCost IS NOT NULL
              AND PurchaseOrderShippingCost > 0";
            $que_rec_ship = $this->db->query($sql_rec_ship, []);
            if (!$que_rec_ship) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert table shipping cost");
                exit;
            }

            # LOG #
            $desc = "Konfirmasi penerimaan barang {$data_ro['ReceiveOrderPoNumber']} Tanggal {$data_ro['formatedDate']} 
            di {$data_ro['WarehouseName']} yang meliputi beberapa item dari PO {$str_po}";
            $log_desc = $desc . "\n" . implode("\n", $each_desc);
            
            $json_ro_after = $this->getDataROPO($ROID);
            $this->insertLogReceiveOrderPo($ROID, 'CONFIRM', $json_ro_before, $json_ro_after, $user['M_UserID'], $log_desc);
            $this->insertUserActivty($ROID, 'RO', 'CONFIRM', $user['M_UserID'], $log_desc);

            # GENERATE PURCHASE INVOICE #
            $this->generatePurchaseInvoice($user, $ROID, $total_grni, $total_discount_po, $ship_cost);

            $this->db->trans_commit();
            $this->sys_ok("[Success]  Berhasil konfirmasi terima barang");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function deleteRO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $this->db->trans_begin();
            $param = $this->sys_input;
            $user = $this->sys_user;

            $ROID = $param['ROID'];

            # data before fo logging #
            $json_ro_before = $this->getDataROPO($ROID);

            # update status header RO #
            $sql = "UPDATE receive_order_po SET 
                ReceiveOrderPoIsActive = 'N',
                ReceiveOrderPoDeleted = NOW(),
                ReceiveOrderPoDeletedUserID = ?
            WHERE ReceiveOrderPoID = ?";
            $que = $this->db->query($sql, [$user['M_UserID'], $ROID]);
            if (!$que) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] delete receive oder");
                exit;
            }

            $sql_detail = "SELECT * FROM receive_order_po_detail 
                WHERE ReceiveOrderPoDetailReceiveOrderPoID = ? 
                AND ReceiveOrderPoDetailIsActive = 'Y'";
            $que_detail = $this->db->query($sql_detail, [$ROID]);
            if (!$que_detail) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get detail RO ");
                exit;
            }
            $details = $que_detail->result_array();

            # update status detail RO #
            foreach ($details as $key => $detail) {
                # update receive detail po #
                $sql_update_detail = "UPDATE receive_order_po_detail SET 
                    ReceiveOrderPoDetailIsActive = 'N', 
                    ReceiveOrderPoDetailDeletedUserID = ?, 
                    ReceiveOrderPoDetailDeleted = NOW() 
                WHERE ReceiveOrderPoDetailID = ?";
                $que_update_detail = $this->db->query($sql_update_detail, [
                    $user['M_UserID'],
                    $detail['ReceiveOrderPoDetailID']
                ]);
                if (!$que_update_detail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update status detail RO");
                    exit;
                }

                # update status form inspeksi #
                $sql_inspeksi = "UPDATE receive_order_po_inspeksi SET 
                    ReceiveOrderPoInspeksiIsActive = 'N'
                WHERE ReceiveOrderPoInspeksiReceiveOrderPoDetailID = ? ";
                $que_inspeksi = $this->db->query($sql_inspeksi, [$detail['ReceiveOrderPoDetailID']]);
                if (!$que_inspeksi) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update status form inspeksi");
                    exit;
                }

                # update po item receive #
                $json_po_before = $this->getPOItemReceived($detail['ReceiveOrderPoDetailPoItemReceiveID']);
                $sql_upd_po_received = "UPDATE po_item_receive 
                    JOIN receive_order_po_detail ON PoItemReceiveID = ReceiveOrderPoDetailPoItemReceiveID 
                        AND ReceiveOrderPoDetailReceiveOrderPoID = ? 
                        AND ReceiveOrderPoDetailID = ? 
                    SET PoItemReceiveQtyReceived = PoItemReceiveQtyReceived - ReceiveOrderPoDetailQty, 
                        PoItemReceiveUpdatedUserID = ? 
                    WHERE PoItemReceiveID = ?";
                $que_upd_po_received = $this->db->query($sql_upd_po_received, [
                    $ROID, $detail['ReceiveOrderPoDetailID'], $user['M_UserID'], 
                    $detail['ReceiveOrderPoDetailPoItemReceiveID']
                ]);
                if (!$que_upd_po_received) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] update qty PO item received");
                    exit;
                }
                $json_po_after = $this->getPOItemReceived($detail['ReceiveOrderPoDetailPoItemReceiveID']);
                $descItem = "Dihapus {$detail['M_ItemDesc']} sejumlah {$detail['ReceiveOrderPoDetailQty']} 
                    {$detail['ItemUnitName']} di RO nomor {$json_ro_before['ReceiveOrderPoNumber']} 
                    *RO belum di konfirmasi";

                # log #
                $this->insertLogPoItemReceive(
                    $detail['ReceiveOrderPoDetailPoItemReceiveID'],
                    'UPDATE', $ROID, $json_po_before, $json_po_after, 
                    $user['M_UserID'], $descItem
                );
            }

            $json_ro_after = $this->getDataROPO($ROID);
            $this->insertLogReceiveOrderPo(
                $ROID, 'DELETE', $json_ro_before, 
                $json_ro_after, $user['M_UserID'], "Update data RO ID: {$ROID}"
            );

            $this->insertUserActivty($ROID, 'RO', 'DELETE', $user['M_UserID'], "Update data RO ID: {$ROID}");

            $this->db->trans_commit();
            $this->sys_ok("[Success]");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function saveInspeksi() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

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

            # Prepare Data #
            $inspeksi = $param['form_inspeksi'];
            $ROdetailID = $param['ROdetailID'];

            # Insert Query #
            $this->db->trans_begin();

            $sql_insert = "INSERT INTO receive_order_po_inspeksi (
                ReceiveOrderPoInspeksiReceiveOrderPoDetailID,
                ReceiveOrderPoInspeksiQtyPesan,
                ReceiveOrderPoInspeksiQtyActual,
                ReceiveOrderPoInspeksiPricePesan,
                ReceiveOrderPoInspeksiPriceActual,
                ReceiveOrderPoInspeksiDatePesan,
                ReceiveOrderPoInspeksiDateActual,
                ReceiveOrderPoInspeksiExpireDate,
                ReceiveOrderPoInspeksiKeadaanKemasan,
                ReceiveOrderPoInspeksiKondisiPengiriman,
                ReceiveOrderPoInspeksiSimpulan,
                ReceiveOrderPoInspeksiCatatan,
                ReceiveOrderPoInspeksiStaffPenerima,
                ReceiveOrderPoInspeksiStaffPengirim,
                ReceiveOrderPoInspeksiUserID
            ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
            $que_insert = $this->db->query($sql_insert, [
                $ROdetailID, $inspeksi['qty_po'], $inspeksi['qty_ro'], 
                $inspeksi['price_po'], $inspeksi['price_ro'], $inspeksi['date_po'], 
                $inspeksi['date_ro'], $inspeksi['expire_date'], $inspeksi['condt_kemasan'], 
                $inspeksi['condt_pengiriman'], $inspeksi['summary'], $inspeksi['catatan'], 
                $inspeksi['pemeriksa'], $inspeksi['pengirim'], $user['M_UserID']
            ]);
            if (!$que_insert) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert into table receiec order po inspeksi");
                exit;
            }

            $this->db->trans_commit();
            $this->sys_ok("[Success] save inspeksi item");
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    function generateItemPO() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }

            $param = $this->sys_input;
            $userid = $this->sys_user;
            $POID = $param['id'];

            if (!isset($POID)) {
                $this->sys_error("Missing required parameters");
                exit;
            }

            $this->db->trans_begin();

            $sql_po = "SELECT * FROM purchase_order WHERE PurchaseOrderID = ?";
            $que_po = $this->db->query($sql_po, [$POID]);
            if (!$que_po) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] get data PO");
                exit;
            }
            $data_po = $que_po->row_array();

            if ($data_po['PurchaseOrderStatus'] != 'Approved') {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] status PO belum Approved");
                exit;
            }

            $data_detail = [];

            if (strtolower($data_po['PurchaseOrderWarehouseType']) == 'single') {
                $sql_POdetail = "SELECT 
                    IFNULL(pir.PoItemReceiveID, 0) as receivedID,
                    IFNULL(pir.PoItemReceiveQtyReceived, 0) as qtyReceived, 
                    pos.PurchaseOrderSummaryID as orderSummaryID, 
                    pos.PurchaseOrderSummaryItemID as itemID,
                    pos.PurchaseOrderSummaryItemUnitID as itemUnitID,
                    pos.PurchaseOrderSummaryQty as qty,
                    pos.PurchaseOrderSummaryPrice as price,
                    m_itemDesc as itemName,
                    ItemUnitName as itemUnitName,
                    PurchaseOrderDetailID
                FROM purchase_order_summary pos
                JOIN purchase_order_detail ON PurchaseOrderDetailPurchaseOrderID = pos.PurchaseOrderSummaryPurchaseOrderID 
                    AND PurchaseOrderSummaryItemID = PurchaseOrderDetailItemID 
                    AND PurchaseOrderSummaryItemUnitID = PurchaseOrderDetailItemUnitID 
                JOIN m_item ON pos.PurchaseOrderSummaryItemID = M_ItemID 
                JOIN itemunit ON pos.PurchaseOrderSummaryItemUnitID = ItemUnitID 
                LEFT JOIN po_item_receive pir ON pos.PurchaseOrderSummaryPurchaseOrderID = pir.PoItemReceivePurchaseOrderID 
                    AND pos.PurchaseOrderSummaryItemID = pir.PoItemReceiveItemID 
                WHERE pos.PurchaseOrderSummaryPurchaseOrderID = ? 
                    AND pos.PurchaseOrderSummaryIsActive = 'Y' 
                    AND IFNULL(pir.PoItemReceiveQtyReceived, 0) = 0";
                $que_POdetail = $this->db->query($sql_POdetail, [$POID]);
                if (!$que_POdetail) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] get data detail PO");
                    exit;
                }
                $data_detail = $que_POdetail->result_array();
                
                foreach ($data_detail as $key => $obj) {
                    if (intval($obj['PoItemReceiveID']) == 0) {
                        $sql_ins = "INSERT INTO po_item_receive (
                            PoItemReceivePurchaseOrderID,
                            PoItemReceivePurchaseOrderSummaryID,
                            PoItemReceivePurchaseOrderDetailID,
                            PoItemReceiveWarehouseID,
                            PoItemReceiveItemID,
                            PoItemReceiveItemUnitID,
                            PoItemReceiveQty,
                            PoItemReceiveQtyReceived,
                            PoItemReceiveIsActive,
                            PoItemReceiveCreated,
                            PoItemReceiveCreatedUserID,
                            PoItemReceivePrice
                        ) VALUES (?,?,?,?,?,?,?,0,'Y',NOW(),?,?)";
                        $que_ins = $this->db->query($sql_ins, [
                            $POID, $obj['orderSummaryID'], $obj['PurchaseOrderDetailID'], 
                            $data_po['PurchaseOrderWarehouseID'], $obj['itemID'], $obj['itemUnitID'], 
                            $obj['qty'], $userid, $obj['price']
                        ]);
                        if (!$que_ins) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] insert po item receive");
                            exit;
                        }
                        $insertedID = $this->db->insert_id();

                        $desc = "ADD {$obj['itemName']} {$obj['qty']} {$obj['itemUnitName']} {$data_po['WarehouseName']}";
                        $jsonAfter = $this->getPOItemReceived($insertedID);
                        $this->insertLogPoItemReceive($insertedID, 'ADD', 0, '', $jsonAfter, $userid, $desc);
                    } else {
                        $jsonBefore = $this->getPOItemReceived($obj['PoItemReceiveID']);
                        $sql_upd = "UPDATE po_item_receive SET
                            PoItemReceiveQty = ?,
                            PoItemReceiveIsActive = 'Y',
                            PoItemReceiveUpdate = NOW(),
                            PoItemReceiveUpdatedUserID = ?,
                            PoItemReceiveWarehouseID = ?,
                            PoItemReceiveItemUnitID = ?,
                            PoItemReceivePrice = ?
                        WHERE PoItemReceiveID = ?";
                        $que_upd = $this->db->query($sql_upd, [
                            $obj['qty'], $userid, $data_po['PurchaseOrderWarehouseID'],
                            $obj['itemUnitID'], $obj['price'], $obj['receivedID']
                        ]);
                        if (!$que_upd) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] update po item receive");
                            exit;
                        }

                        $jsonAfter = $this->getPOItemReceived($obj['PoItemReceiveID']);
                        $desc = "UPDATE {$obj['itemName']} {$obj['qty']} {$obj['itemUnitName']} {$data_po['WarehouseName']}";
                        $this->insertLogPoItemReceive($obj['PoItemReceiveID'], 'UPDATE', 0, $jsonBefore, $jsonAfter, $userid, $desc);
                    }
                }
            }

            if (strtolower($data_po['PurchaseOrderWarehouseType']) == 'multiple') {
                $sql_det = "SELECT 
                    IFNULL(PoItemReceiveID, 0) as cekID,
                    IFNULL(PoItemReceiveQtyReceived, 0) as qtyCek,
                    pod.PurchaseOrderDetailPurchaseSummaryID as summaryID,
                    pod.PurchaseOrderDetailID as poDetailID,
                    pod.PurchaseOrderDetailWarehouseID as warehouseID,
                    WarehouseName,
                    pod.PurchaseOrderDetailItemID as itemID,
                    M_ItemDesc as itemName,
                    pos.PurchaseOrderSummaryItemUnitID as itemUnitID,
                    ItemUnitName as itemUnitName,
                    ConvertQuantityToPurchasedItemUnit(
                        pod.PurchaseOrderDetailItemID, 
                        pod.PurchaseOrderDetailItemUnitID, 
                        pod.PurchaseOrderDetailQty
                    ) as poQty,
                    pos.PurchaseOrderSummaryPrice as price
                FROM purchase_order_detail pod
                JOIN purchase_order_summary pos ON pod.PurchaseOrderDetailPurchaseSummaryID = pos.PurchaseOrderSummaryID
                JOIN m_item ON pod.PurchaseOrderDetailItemID  = M_ItemID
                JOIN itemunit ON pos.PurchaseOrderSummaryItemUnitID = ItemUnitID
                JOIN warehouse ON pod.PurchaseOrderDetailWarehouseID = WarehouseID
                LEFT JOIN  po_item_receive p ON p.PoItemReceiveItemID = pod.PurchaseOrderDetailItemID
                    AND  p.PoItemReceivePurchaseOrderID = ?
                    AND p.PoItemReceivePurchaseOrderSummaryID = pos.PurchaseOrderSummaryID
                    AND p.PoItemReceivePurchaseOrderDetailID = pod.PurchaseOrderDetailID
                WHERE pod.PurchaseOrderDetailPurchaseOrderID = ?
                    AND IFNULL(PoItemReceiveQtyReceived, 0) = 0
                    AND pod.PurchaseOrderDetailIsActive = 'Y'";
                $que_det = $this->db->query($sql_det, [$POID, $POID]);
                if (!$que_det) {
                    $this->db->trans_rollback();
                    $this->sys_error_db("[Error] get item");
                    exit;
                }
                $data_detail = $que_det->result_array();

                foreach ($data_detail as $key => $obj) {
                    if (intval($obj['cekID']) == 0) {
                        $sql_ins = "INSERT INTO po_item_receive (
                            PoItemReceivePurchaseOrderID,
                            PoItemReceivePurchaseOrderSummaryID,
                            PoItemReceivePurchaseOrderDetailID,
                            PoItemReceiveWarehouseID,
                            PoItemReceiveItemID,
                            PoItemReceiveItemUnitID,
                            PoItemReceiveQty,
                            PoItemReceiveQtyReceived,
                            PoItemReceiveIsActive,
                            PoItemReceiveCreated,
                            PoItemReceiveCreatedUserID,
                            PoItemReceivePrice
                        ) VALUES (?,?,?,?,?,?,?,0,'Y',NOW(),?,?)";
                        $que_ins = $this->db->query($sql_ins, [
                            $POID, $obj['summaryID'], $obj['poDetailID'], $obj['warehouseID'], 
                            $obj['itemID'], $obj['itemUnitID'], $obj['poQty'], $userid, $obj['price']
                        ]);
                        if (!$que_ins) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] insert po item receive multiple wh");
                            exit;
                        }
                        $insertedID = $this->db->insert_id();

                        $desc = "ADD {$obj['itemName']} {$obj['poQty']} {$obj['itemUnitName']} {$obj['WarehouseName']}";
                        $jsonAfter = $this->getPOItemReceived($insertedID);
                        $this->insertLogPoItemReceive($insertedID, 'ADD', 0, '', $jsonAfter, $userid, $desc);
                    } else {
                        $jsonBefore = $this->getPOItemReceived($obj['cekID']);

                        $sql_upd = "UPDATE po_item_receive SET 
                            PoItemReceiveQty = ?,
                            PoItemReceiveIsActive = 'Y',
                            PoItemReceiveUpdate = NOW(),
                            PoItemReceiveUpdatedUserID = ?,
                            PoItemReceiveWarehouseID = ?,
                            PoItemReceiveItemUnitID = ?,
                            PoItemReceivePrice = ?
                        WHERE PoItemReceiveID = ?";
                        $que_upd = $this->db->query($sql_upd, [
                            $obj['poQty'], $userid, $obj['warehouseID'], 
                            $obj['itemUnitID'], $obj['price'], $obj['cekID']
                        ]);
                        if (!$que_upd) {
                            $this->db->trans_rollback();
                            $this->sys_error_db("[Error] update po item receive multiple wh");
                            exit;
                        }

                        $desc = "ADD {$obj['itemName']} {$obj['poQty']} {$obj['itemUnitName']} {$obj['WarehouseName']}";
                        $jsonAfter = $this->getPOItemReceived($obj['cekID']);
                        $this->insertLogPoItemReceive($obj['cekID'], 'UPDATE', 0, $jsonBefore, $jsonAfter, $userid, $desc);
                    }
                }
            }

            $this->db->trans_commit();
            $this->sys_ok([
                'header' => $data_po,
                'detail' => $data_detail
            ]);
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }

    private function generatePurchaseInvoice($user, $ROID, $grni, $discpo, $shipping) {
        
        # generate nomor purchase invoice #
        $numPI = $this->generateNoLogistik($user, 'PI');

        # prepare data #
        $sql_data_ro = "SELECT * FROM receive_order_po WHERE ReceiveOrderPoID = ?";
        $que_data_ro = $this->db->query($sql_data_ro, [$ROID]);
        if (!$que_data_ro) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] get data RO to generate PI");
            exit;
        }
        $data_ro = $que_data_ro->row_array();

        $sql_detail_ro = "SELECT 
            receive_order_po_detail.*,
            M_ItemDesc,
            PurchaseOrderSummaryDiscountType AS DiscountType,
            PurchaseOrderSummaryDiscountRupiah AS DiscountRupiah,
            PurchaseOrderSummaryDiscountPercent AS DiscountPercent,
            PurchaseOrderSummaryDiscountAmount as DiscountPerItem
            FROM receive_order_po_detail 
            JOIN m_item ON M_ItemID = ReceiveOrderPoItemID AND M_ItemIsActive = 'Y'
            JOIN purchase_order_summary ON ReceiveOrderPoDetailPurchaseOrderSummaryID = PurchaseOrderSummaryID
            WHERE ReceiveOrderPoDetailIsActive = 'Y'
                AND ReceiveOrderPoDetailReceiveOrderPoID = ?";
        $que_detail_ro = $this->db->query($sql_detail_ro, [$ROID]);
        if (!$que_detail_ro) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] get data detail RO to generate PI");
            exit;
        }
        $detail_ro = $que_detail_ro->result_array();

        $due_date = new DateTime($data_ro['ReceiveOrderPoIDate']);
        $due_date->add(new DateInterval('P30D'));
        $due_date = $due_date->format('Y-m-d');

        $subtotal = round(($grni - $shipping) + $discpo, 2);

        # insert header purchase invoice #
        $sql_insert_PI = "INSERT INTO supplier_invoice (
            SupplierInvoiceNumber,
            SupplierInvoiceReceiveOrderPoID,
            SupplierInvoiceDate,
            SupplierInvoiceDueDate,
            SupplierInvoiceSupplierID,
            SupplierInvoiceRefNumber,
            SupplierInvoiceDeliveryOrderNumber,
            SupplierInvoiceSubTotal,
            SupplierInvoiceDiscountPercent,
            SupplierInvoiceDiscountAmount,
            SupplierInvoiceTaxPercentPph,
            SupplierInvoiceTaxAmountPph,
            SupplierInvoiceTaxPercentPpn,
            SupplierInvoiceTaxAmountPpn,
            SupplierInvoiceShippingCost,
            SupplierInvoiceGrandTotal,
            SupplierInvoiceUnpaid,
            SupplierInvoiceNote,
            SupplierInvoiceReceiveDate,
            SupplierInvoiceReceivedBy,
            SupplierInvoiceCreatedUserID
        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?,?)";
        $que_insert_PI = $this->db->query($sql_insert_PI, [
            $numPI, $data_ro['ReceiveOrderPoID'], $data_ro['ReceiveOrderPoIDate'], $due_date,
            $data_ro['ReceiveOrderPoSupplierID'], $data_ro['ReceiveOrderPoRefNumber'], $data_ro['ReceiveOrderPoDONumber'], 
            $subtotal, 0.00, $discpo, 0.00, 0.00, 0.00, 0.00, $shipping, $grni, $grni, 
            $data_ro['ReceiveOrderPoNote'], $user['M_UserID'], $user['M_UserID']
        ]);
        if (!$que_insert_PI) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] insert header purchase invoice");
            exit;
        }
        $Pinvoice_ID = $this->db->insert_id();

        # insert detail purchase invoice #
        $sql_insert_detailPI = "INSERT INTO supplier_invoice_detail (
            SupplierInvoiceDetailSupplierInvoiceID,
            SupplierInvoiceDetailPurchaseOrderID,
            SupplierInvoiceDetailPurchaseOrderSummaryID,
            SupplierInvoiceDetailReceiveOrderPoID,
            SupplierInvoiceDetailReceiveOrderPoDetailID,
            SupplierInvoiceDetailItemID,
            SupplierInvoiceDetailItemUnitID,
            SupplierInvoiceDetailDescription,
            SupplierInvoiceDetailQty,
            SupplierInvoiceDetailPrice,
            SupplierInvoiceDetailDiscountPercent,
            SupplierInvoiceDetailDiscountDiscountRupiah,
            SupplierInvoiceDetailDiscountDiscountType,
            SupplierInvoiceDetailDiscountPoProrata,
            SupplierInvoiceDetailDiscountAmount,
            SupplierInvoiceDetailTotal,
            SupplierInvoiceDetailUnpaid,
            SupplierInvoiceDetailCreatedUserID
        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
        
        foreach ($detail_ro as $key => $item) {
            $itemprice = doubleval($item['ReceiveOrderPoDetailPrice']) - doubleval($item['DiscountPerItem']);
            $total_price = round($itemprice * intval($item['ReceiveOrderPoDetailQty']), 2);

            $que_insert_itemPI = $this->db->query($sql_insert_detailPI, [
                $Pinvoice_ID, $item['ReceiveOrderPoDetailPurchaseOrderID'], 
                $item['ReceiveOrderPoDetailPurchaseOrderSummaryID'], $item['ReceiveOrderPoDetailReceiveOrderPoID'], 
                $item['ReceiveOrderPoDetailID'], $item['ReceiveOrderPoItemID'], $item['ReceiveOrderPoItemUnitID'], 
                $item['M_ItemDesc'], $item['ReceiveOrderPoDetailQty'], $item['ReceiveOrderPoDetailPrice'], 
                $item['DiscountPercent'], $item['DiscountRupiah'], $item['DiscountType'], $item['ReceiveOrderPoDetailDiskonPoProrata'], 
                $item['DiscountPerItem'], $total_price, $total_price, $user['M_UserID']
            ]);
            if (!$que_insert_itemPI) {
                $this->db->trans_rollback();
                $this->sys_error_db("[Error] insert header purchase invoice");
                exit;
            }
        }

        # insert log activity #
        $this->insertUserActivty($Pinvoice_ID, 'INV', 'CREATE', $user['M_UserID'], '');
    }

    private function insertLogReceiveOrderPo($id, $type, $dataBefore, $dataAfter, $userID, $desc = '') {
        $sql = "INSERT INTO acc_one_log.receive_order_po_log (
            ReceiveOrderPoLogReceiveOrderPoID,
            ReceiveOrderPoLogType,
            ReceiveOrderPoLogDesc,
            ReceiveOrderPoLogJsonBefore,
            ReceiveOrderPoLogJsonAfter,
            ReceiveOrderPoLogUserID,
            ReceiveOrderPoLogCreated
        ) VALUES (?, ?, ?, ?, ?, ?, NOW())";
        $que = $this->db->query($sql, [
            $id, $type, $desc, 
            !empty($dataBefore) ? json_encode($dataBefore) : '', 
            !empty($dataAfter) ? json_encode($dataAfter) : '', 
            $userID
        ]);
        if (!$que) {
            $this->db->trans_rollback();
            $this->sys_error_db("[Error] inserting log into insert LogReceiveOrderPo");
            exit;
        }
    }

    private function insertLogPoItemReceive($id, $type, $roID, $dataBefore, $dataAfter, $userID, $desc = '') {
        $sql = "INSERT INTO acc_one_log.po_item_receive_log (
            PoItemReceiveLogPoItemReceiveID,
            PoItemReceiveLogReceiveOrderPoID,
            PoItemReceiveLogType,
            PoItemReceiveLogDesc,
            PoItemReceiveLogJsonBefore,
            PoItemReceiveLogJsonAfter,
            PoItemReceiveLogUserID,
            PoItemReceiveLogCreated
        ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())";
        $que = $this->db->query($sql, [
            $id, $roID, $type, $desc, 
            !empty($dataBefore) ? json_encode($dataBefore) : '',
            !empty($dataAfter) ? json_encode($dataAfter) : '',
            $userID
        ]);
        if (!$que) {
            $this->sys_error_db("[Error] inserting log po item receive");
            $this->db->trans_rollback();
            exit;
        }
    }

    private function insertUserActivty($id, $typePL, $status, $userID, $desc = '') {
        $sql = "INSERT INTO user_activity (
            UserActivityCode,
            UserActivityStatus,
            UserActivityDescription,
            UserActivityRefID,
            UserActivityUserID,
            UserActivityCreated
        ) VALUES (?, ?, ?, ?, ?, NOW())";
        $que = $this->db->query($sql, [$typePL, $status, $desc, $id, $userID]);
        if (!$que) {
            $this->sys_error_db("[Error] inserting user activity");
            $this->db->trans_rollback();
            exit;
        }
    }

    private function formatRupiah($data) {
        $data = str_replace(',', '.', $data);
        if (!is_numeric($data)) {
            return 'Input tidak valid';
        }

        $data = (float)$data;
        $data = round($data, 2);

        if (fmod($data, 1) == 0.0) {
            $formatted = number_format($data, 0, ',', '.');
        } else {
            $formatted = number_format($data, 2, ',', '.');
        }
        return 'Rp ' . $formatted;
    }

    function template() {
        try {
            if (!$this->isLogin) {
                $this->sys_error("invalid token");
                exit;
            }
        } catch (Exception $exc) {
            $message = $exc->getMessage();
            $this->sys_error($message);
        }
    }
}