Files
be-accone/application/controllers/mockup/purchase/transfer/TransferRequest.php

1741 lines
72 KiB
PHP

<?php
class TransferRequest extends MY_Controller
{
var $db;
public function index()
{
echo "Goods Transfer Request/Requester API";
}
public function __construct()
{
parent::__construct();
}
public function getBranches()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$regID = $prm["S_RegionalID"];
$sql = "SELECT M_BranchID, M_BranchCode, M_BranchName
FROM m_branch
WHERE M_BranchIsActive = 'Y'
AND M_BranchS_RegionalID = ?";
$qry = $this->db->query($sql, [$regID]);
if (!$qry) {
$this->sys_error_db("Failed to get Branches from RegionalID: {$regID}", $this->db);
exit;
}
$rst = $qry->result_array();
$this->sys_ok(["records" => $rst]);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function getStatus()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
}
$rst = [
["value" => 0, "title" => "All"],
["value" => 1, "title" => "Draft"],
["value" => 2, "title" => "Pending"],
["value" => 3, "title" => "Approved"],
["value" => 4, "title" => "Rejected"],
["value" => 5, "title" => "Paid"],
["value" => 6, "title" => "Completed"]
];
$this->sys_ok(["records" => $rst]);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
exit;
}
}
public function search()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$payload = $this->sys_input;
$regionalID = $this->sys_user["S_RegionalID"];
$startDate = $payload["startDate"];
$endDate = $payload["endDate"];
$search = $payload['search'];
if ($search == "") {
$search = "%";
}
$status = $payload['StatusName'];
if (strtolower($status) == "all") {
$status = "%";
}
$cabangcode = $payload["M_BranchCode"];
if (strtolower($cabangcode) == "all") {
$cabangcode = "%";
}
$offset = 0;
$limit = 10;
if ($payload['currentPage'] > 0) {
$offset = ($payload['currentPage'] - 1) * $limit;
}
$sql = "SELECT
t_goods_transfer.*,
M_BranchID,
M_BranchCode,
M_BranchName
FROM t_goods_transfer
JOIN m_branch ON T_GoodsTransferToM_BranchCode = M_BranchCode
AND M_BranchIsActive = 'Y'
WHERE T_GoodsTransferIsActive = 'Y'
AND T_GoodsTransferFromS_RegionalID = ?
AND T_GoodsTransferNum LIKE ?
AND T_GoodsTransferToM_BranchCode LIKE ?
AND T_GoodsTransferType = 'P'
AND T_GoodsTransferStatus LIKE ?
AND T_GoodsTransferDate BETWEEN DATE(?) AND DATE(?)
ORDER BY T_GoodsTransferDate DESC";
$sqldata = $sql . " LIMIT ? OFFSET ?";
$quedata = $this->db->query($sqldata, [
$regionalID,
$search,
$cabangcode,
$status,
$startDate,
$endDate,
$limit,
$offset
]);
if (!$quedata) {
$this->sys_error_db("[Error] get data");
exit;
}
$data = $quedata->result_array();
$sqltotal = "SELECT COUNT(*) AS total FROM ($sql) AS x";
$quetotal = $this->db->query($sqltotal, [
$regionalID,
$search,
$cabangcode,
$status,
$startDate,
$endDate
]);
if (!$quetotal) {
$this->sys_error_db("[Error] get total data");
exit;
}
$total = $quetotal->result_array()[0]['total'];
$result = array(
"records" => $data,
"total" => $total
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function getUserLevel()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$userId = $this->sys_user["M_UserID"];
$sqlevel = "SELECT * FROM m_user WHERE M_UserIsActive = 'Y' AND M_UserID = ?";
$qulevel = $this->db->query($sqlevel, [$userId]);
if (!$qulevel) {
$this->sys_error_db("[Error] get approval level user");
exit;
}
$data = $qulevel->row_array();
$this->sys_ok($data);
}
public function createTfRequest()
{
try {
if (!$this->isLogin) {
throw new Exception("invalid token");
}
$user = $this->sys_user;
$userID = $this->sys_user["M_UserID"];
$data = $this->sys_input;
// Check for debug mode
$isDebug = isset($data['isDebug']) && $data['isDebug'] === true;
if ($isDebug) {
$this->db->trans_start(true); // Auto rollback mode
} else {
$this->db->trans_start();
}
// $sqlnum = "SELECT `fn_numbering`('TR') AS GoodsTfNum";
// $quenum = $this->db->query($sqlnum, []);
// if (!$quenum) {
// throw new Exception("[Error] Failed to generate transfer request number");
// }
// $goodsTfNum = $quenum->row()->GoodsTfNum;
$areaid = $user['M_BranchID'];
$areatype = 'B';
if ($user['loginLevel'] == 'regional') {
$areaid = $user['S_RegionalID'];
$areatype = 'R';
}
$sqlusrdivisi = "SELECT M_UserDivisionDivisionID FROM m_userdivision
WHERE M_UserDivisionM_UserID = ? AND M_UserDivisionIsActive = 'Y'";
$queusrdivisi = $this->db->query($sqlusrdivisi, [$user['M_UserID']]);
if (!$queusrdivisi) {
$this->db->trans_rollback();
$this->sys_error_db("[Error] get user divisi", $this->db);
exit;
}
$userDivID = $queusrdivisi->row_array()['M_UserDivisionDivisionID'];
$sqlnum = "SELECT `fn_penomoran`(?, ?, ?, ?, ?, ?) AS numpd;";
$quenum = $this->db->query($sqlnum, ['ST', $userDivID, $areatype, $areaid, 'SM', 'Y']);
if (!$quenum) {
throw new Exception("[Error] Failed generate number for TF request. " . json_encode($this->db->error()));
}
$goodsTfNum = $quenum->row_array()['numpd'];
$sqlTG = "INSERT INTO t_goods_transfer (
T_GoodsTransferFromS_RegionalID,
T_GoodsTransferToM_BranchCode,
T_GoodsTransferNum,
T_GoodsTransferNotes,
T_GoodsTransferDate,
T_GoodsTransferType,
T_GoodsTransferStatus,
T_GoodsTransferIsActive,
T_GoodsTransferCreatedAt,
T_GoodsTransferM_UserID
) VALUES (?,?,?,?,?,?,?,'Y',NOW(),?)";
$queTG = $this->db->query($sqlTG, [
$data["S_RegionalID"],
$data["M_BranchCode"],
$goodsTfNum,
$data["GoodsTfNotes"],
$data["GoodsTfDate"],
'P',
$data["GoodsTfStatus"],
$userID
]);
if (!$queTG) {
throw new Exception("[Error] Failed to insert into table t_goods_transfer. " . json_encode($this->db->error()));
}
$tGoodsTFID = $this->db->insert_id();
$datadetail = $data['detail'];
foreach ($datadetail as $key => $value) {
$flagID = $value['PurchaseRequestFlagID'];
$itemID = $value['PurchaseRequestDetailM_ItemID'];
$unitID = $value['PurchaseRequestDetailItemUnitID'];
$QtyStart = intval($value['PurchaseRequestFlagQty']);
$QtyTfRes = intval($value['PurchaseRequestFlagQtyRest']);
// encode json list batch number
$jsonBatchNo = "[]";
if (isset($value['ItemRequest']) && is_array($value['ItemRequest'])) {
$batches = $value['ItemRequest'];
if (sizeof($batches) > 0) {
$jsonResult = json_encode($batches);
if ($jsonResult === false) {
throw new Exception("[Error] Failed to encode json batch number");
} else {
$jsonBatchNo = $jsonResult;
}
}
}
$sqlTGD = "INSERT INTO t_goods_transfer_detail (
T_GoodsTransferDetailIDT_GoodsTransferID,
T_GoodsTransferDetailT_PurchaseRequestFlagID,
T_GoodsTransferDetailM_ItemID,
T_GoodsTransferDetailItemUnitID,
T_GoodsTransferDetailItemBatchNo,
T_GoodsTransferDetailQty,
T_GoodsTransferDetailQtyTF,
T_GoodsTransferDetailCreatedAt,
T_GoodsTransferDetailM_UserID
) VALUES (?,?,?,?,?,?,?,NOW(),?)";
$queTGD = $this->db->query($sqlTGD, [
$tGoodsTFID,
$flagID,
$itemID,
$unitID,
$jsonBatchNo,
$QtyStart,
$QtyTfRes,
$userID
]);
if (!$queTGD) {
throw new Exception("[Error] Failed to insert TF goods detail. " . json_encode($this->db->error()));
}
$TGoodsTFDetailID = $this->db->insert_id();
$datadetail[$key]['TGoodsTFDetailID'] = $TGoodsTFDetailID;
// jika status verified
if ($data["GoodsTfStatus"] == 'Verified') {
$this->moveStockToTransit($value, $data["S_RegionalID"], $userID, $TGoodsTFDetailID);
$sqlUpdFlag = "UPDATE purchase_request_flag
SET PurchaseRequestFlagQtyRest = ?,
PurchaseRequestFlagQtyProses = PurchaseRequestFlagQtyProses + ?
WHERE PurchaseRequestFlagID = ?
AND PurchaseRequestFlagIsActive = 'Y'";
$queUpdFlag = $this->db->query($sqlUpdFlag, [$QtyTfRes, $QtyTfRes, $flagID]);
if (!$queUpdFlag) {
throw new Exception("[Error] Failed to update quantity in table purchase_request_flag. " . json_encode($this->db->error()));
}
}
}
// get last inserted data
$sqltfgood = "SELECT * FROM t_goods_transfer WHERE T_GoodsTransferIsActive = 'Y' AND T_GoodsTransferID = ?";
$quetfgood = $this->db->query($sqltfgood, [$tGoodsTFID]);
if (!$quetfgood) {
throw new Exception("[Error] Failed to get last inserted transfer data. " . json_encode($this->db->error()));
}
$lastTFData = $quetfgood->row_array();
$sqltfdetail = "SELECT * FROM t_goods_transfer_detail WHERE T_GoodsTransferDetailIsActive = 'Y' AND T_GoodsTransferDetailIDT_GoodsTransferID = ?";
$quetfdetail = $this->db->query($sqltfdetail, [$tGoodsTFID]);
if (!$quetfdetail) {
throw new Exception("[Error] Failed to get last inserted transfer detail data. " . json_encode($this->db->error()));
}
$lastTFDetail = $quetfdetail->result_array();
$lastTFData['detail'] = $lastTFDetail;
$msg = "Transfer Request nomor: {$lastTFData['T_GoodsTransferNum']}, tanggal: {$lastTFData['T_GoodsTransferDate']}, type: {$lastTFData['T_GoodsTransferType']}";
$this->insertUserActivty($tGoodsTFID, "CREATE", $userID, $msg, json_encode($lastTFData));
$this->db->trans_complete();
$result = array(
"total" => 1,
"msg" => $msg,
"records" => $lastTFData
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->db->trans_rollback();
$this->sys_error($message);
}
}
public function updateTfRequest()
{
try {
if (!$this->isLogin) {
throw new Exception("Invalid Token");
}
$user = $this->sys_user;
$userID = $this->sys_user["M_UserID"];
$data = $this->sys_input;
// print_r($data['actverif']);
// print_r($data['editqty']);
// print_r(json_encode($data['detail']));
// exit;
$this->db->trans_begin();
$sqlTG = "UPDATE t_goods_transfer SET
T_GoodsTransferFromS_RegionalID = ?,
T_GoodsTransferToM_BranchCode = ?,
T_GoodsTransferNum = ?,
T_GoodsTransferNotes = ?,
T_GoodsTransferDate = ?,
T_GoodsTransferStatus = ?,
T_GoodsTransferLastUpdated = NOW(),
T_GoodsTransferM_UserID = ?
WHERE T_GoodsTransferID = ?
AND T_GoodsTransferIsActive = 'Y'";
$queTG = $this->db->query($sqlTG, [
$data["S_RegionalID"],
$data["M_BranchCode"],
$data["GoodsTfNum"],
$data["GoodsTfNotes"],
$data["GoodsTfDate"],
$data["GoodsTfStatus"],
$userID,
$data["GoodsTfID"]
]);
if (!$queTG) {
throw new Exception("[Error] Failed to update table t_goods_transfer. " . json_encode($this->db->error()));
}
// approve or verif tf request
if (($data["GoodsTfStatus"] == 'Verified' && $data['actverif'] == 'V') || $data["GoodsTfStatus"] == 'Approved') {
$this->toVerifOrApprove($data["GoodsTfID"], $user);
}
$sqlUpdToN = "UPDATE t_goods_transfer_detail SET T_GoodsTransferDetailIsActive = 'N' WHERE T_GoodsTransferDetailIDT_GoodsTransferID = ?";
$queUpdToN = $this->db->query($sqlUpdToN, [$data["GoodsTfID"]]);
if (!$queUpdToN) {
throw new Exception("[Error] Failed to update all detail records to inactive. " . json_encode($this->db->error()));
}
$datadetail = $data['detail'];
foreach ($datadetail as $key => $value) {
$flagID = $value['PurchaseRequestFlagID'];
$itemID = $value['PurchaseRequestDetailM_ItemID'];
$unitID = $value['PurchaseRequestDetailItemUnitID'];
$QtyStart = intval($value['PurchaseRequestFlagQty']);
$QtyTfRes = intval($value['PurchaseRequestFlagQtyRest']);
// encode json list batch number
$jsonBatchNo = "[]";
if (isset($value['ItemRequest']) && is_array($value['ItemRequest'])) {
$batches = $value['ItemRequest'];
if (sizeof($batches) > 0) {
$jsonResult = json_encode($batches);
if ($jsonResult === false) {
throw new Exception("[Error] Failed to encode json batch number");
} else {
$jsonBatchNo = $jsonResult;
}
}
}
$sqlTGD = "INSERT INTO t_goods_transfer_detail (
T_GoodsTransferDetailIDT_GoodsTransferID,
T_GoodsTransferDetailT_PurchaseRequestFlagID,
T_GoodsTransferDetailM_ItemID,
T_GoodsTransferDetailItemUnitID,
T_GoodsTransferDetailItemBatchNo,
T_GoodsTransferDetailQty,
T_GoodsTransferDetailQtyTF,
T_GoodsTransferDetailCreatedAt,
T_GoodsTransferDetailM_UserID
) VALUES (?,?,?,?,?,?,?,NOW(),?)";
$queTGD = $this->db->query($sqlTGD, [
$data["GoodsTfID"],
$flagID,
$itemID,
$unitID,
$jsonBatchNo,
$QtyStart,
$QtyTfRes,
$userID
]);
if (!$queTGD) {
throw new Exception("[Error] Failed to insert TF goods detail. " . json_encode($this->db->error()));
}
$TGoodsTFDetailID = $this->db->insert_id();
$datadetail[$key]['TGoodsTFDetailID'] = $TGoodsTFDetailID;
// jika status verified
if ($data["GoodsTfStatus"] == 'Verified' && $data['actverif'] == 'V') {
$this->moveStockToTransit($value, $data["S_RegionalID"], $userID, $TGoodsTFDetailID);
$sqlUpdFlag = "UPDATE purchase_request_flag
SET PurchaseRequestFlagQtyRest = ?,
PurchaseRequestFlagQtyProses = PurchaseRequestFlagQtyProses + ?
WHERE PurchaseRequestFlagID = ?
AND PurchaseRequestFlagIsActive = 'Y'";
$queUpdFlag = $this->db->query($sqlUpdFlag, [$QtyTfRes, $QtyTfRes, $flagID]);
if (!$queUpdFlag) {
throw new Exception("[Error] Failed to update quantity in table purchase_request_flag. " . json_encode($this->db->error()));
}
}
}
// get last inserted data
$sqltfgood = "SELECT * FROM t_goods_transfer WHERE T_GoodsTransferIsActive = 'Y' AND T_GoodsTransferID = ?";
$quetfgood = $this->db->query($sqltfgood, [$data["GoodsTfID"]]);
if (!$quetfgood) {
throw new Exception("[Error] Failed to get updated transfer data. " . json_encode($this->db->error()));
}
$lastTFData = $quetfgood->row_array();
$sqltfdetail = "SELECT * FROM t_goods_transfer_detail WHERE T_GoodsTransferDetailIsActive = 'Y' AND T_GoodsTransferDetailIDT_GoodsTransferID = ?";
$quetfdetail = $this->db->query($sqltfdetail, [$data["GoodsTfID"]]);
if (!$quetfdetail) {
throw new Exception("[Error] Failed to get updated transfer detail data. " . json_encode($this->db->error()));
}
$lastTFDetail = $quetfdetail->result_array();
$lastTFData['detail'] = $lastTFDetail;
$msg = "Transfer Request nomor: {$lastTFData['T_GoodsTransferNum']}, tanggal: {$lastTFData['T_GoodsTransferDate']}, type: {$lastTFData['T_GoodsTransferType']}";
$this->insertUserActivty($data["GoodsTfID"], "UPDATE", $userID, $msg, json_encode($lastTFData));
$this->db->trans_commit();
$result = array(
"total" => 1,
"msg" => $msg,
"records" => $lastTFData
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->db->trans_rollback();
$this->sys_error($message);
}
}
/**
* * Pindah Stock ke WH Transit jika Verified
* @param array $detailTf - The detail of the transfer with ItemRequest info
* @param int $regionalID - The regional ID to find the appropriate transit warehouse
* @param int $userID - The user ID performing the operation
* @param int $TGoodsTFDetailID - The ID of the transfer detail
* @return void
* @throws Exception if any validation or database operation fails
*/
private function moveStockToTransit($detailTf, $regionalID, $userID, $TGoodsTFDetailID)
{
//* Validasi StockQty bisa memenuhi
foreach ($detailTf['ItemRequest'] as $itemRequest) {
$stockID = $itemRequest['StockID'];
$qtyReq = intval($itemRequest['QtyReq']);
$sqlCheck = "SELECT StockQty FROM stock WHERE StockID = ?";
$qryCheck = $this->db->query($sqlCheck, [$stockID]);
if (!$qryCheck || $qryCheck->num_rows() === 0) {
$err = json_encode($this->db->error());
throw new Exception($err);
}
if ($qryCheck->row()->StockQty < $qtyReq) {
$sqlItem = "SELECT M_ItemDesc, ItemUnitName
FROM stock
JOIN m_item ON M_ItemID = StockItemID
JOIN itemunit ON ItemUnitID = StockItemUnitID
WHERE StockID = ?";
$qryItem = $this->db->query($sqlItem, [$stockID]);
$itemInfo = $qryItem->row_array();
throw new Exception("Err: Perhatikan Qty Stock dan Request. Tidak bisa verifikasi karena Stock Item {$itemInfo['M_ItemDesc']}
tersisa " . ($qryCheck ? $qryCheck->row()->StockQty : 0) . " {$itemInfo['ItemUnitName']},
sedangkan request {$qtyReq} {$itemInfo['ItemUnitName']}");
}
}
//* Query Warehouse Transit where WHRegionalID == $data[S_RegionalID]
$sqlWh = "SELECT WarehouseID FROM warehouse WHERE WarehouseS_RegionalID = ?
AND WarehouseIsTransit = 'Y' AND WarehouseIsActive = 'Y'
AND WarehouseType = 'R' ORDER BY WarehouseID DESC LIMIT 1";
$qryWh = $this->db->query($sqlWh, [$regionalID]);
if (!$qryWh || $qryWh->num_rows() === 0) {
throw new Exception("Transit warehouse not found for Regional ID: {$regionalID}");
}
$transitWhId = $qryWh->row()->WarehouseID;
foreach ($detailTf['ItemRequest'] as $itemReq) {
$stockID = $itemReq['StockID'];
$qtyReq = intval($itemReq['QtyReq']);
$stockNumber = $itemReq['StockStockNumber'];
$batchNo = $itemReq['StockBatchNo'];
$itemID = $itemReq['StockItemID'];
$unitID = $itemReq['StockItemUnitID'];
$itemPrice = $itemReq['StockItemPrice'];
$expDate = $itemReq['StockED'];
$warehouseID = $itemReq['WarehouseID'];
$lastSourceStockQty = $itemReq['StockQty'];
//* Update Kurangi Existing Stock Qty
$sqlUpdt = "UPDATE stock SET StockQty = StockQty - ?,
StockLastUpdated = NOW(), StockUserID = ?
WHERE StockID = ?";
$qryUpdt = $this->db->query($sqlUpdt, [$qtyReq, $userID, $stockID]);
if (!$qryUpdt) {
throw new Exception("Failed to update source stock quantity (ID: {$stockID})");
}
//* Create / Update Transit Stock
$sqlCekTransit = "SELECT StockID FROM stock
WHERE StockWarehouseID = ?
AND StockItemID = ? AND StockItemUnitID = ?
AND StockBatchNo = ? ORDER BY StockID DESC LIMIT 1";
$qryCekTransit = $this->db->query($sqlCekTransit, [
$transitWhId,
$itemID,
$unitID,
$batchNo
]);
if (!$qryCekTransit) {
$err = json_encode($this->db->error());
throw new Exception("Failed to cek stock on warehouseId (ID: {$transitWhId}). DB Error: $err");
}
// *Insert or Update Row Stock Warehouse Transit
if ($qryCekTransit->num_rows() > 0) {
$transitStockID = $qryCekTransit->row()->StockID;
$sqlUpdTransit = "UPDATE stock SET StockQty = StockQty + ?,
StockLastUpdated = NOW(), StockUserID = ?
WHERE StockID = ?";
$qryUpdTransit = $this->db->query($sqlUpdTransit, [
$qtyReq,
$userID,
$transitStockID
]);
if (!$qryUpdTransit) {
throw new Exception("Failed to update transit stock quantity (ID: {$transitStockID})");
}
} else {
$sqlInsTransit = "INSERT INTO stock (
StockWarehouseID, StockItemID, StockItemUnitID,
StockBatchNo, StockQty, StockStockNumber,
StockItemPrice, StockED,
StockLastUpdated, StockUserID
) VALUES (?, ?, ?, ?, ?, fn_numbering('SN'), ?, ?, NOW(), ?)";
$qryInsTransit = $this->db->query($sqlInsTransit, [
$transitWhId,
$itemID,
$unitID,
$batchNo,
$qtyReq,
$itemPrice,
$expDate,
$userID
]);
if (!$qryInsTransit) {
throw new Exception("Failed to insert new transit stock for Item ID: {$itemID}");
}
$transitStockID = $this->db->insert_id();
}
//* Log Stock Movement (WTT - Warehouse To Transit)
// Log hanya dari sumber ke transit
$sqlLog = "INSERT INTO stocklog (
StockLogWarehouseID, StockLogDateTime, StockLogItemID,
StockLogItemUnitID, StockLogStockNumber, StockLogBatchNo,
StockLogED, StockLogReffID, StockLogStatus, StockLogQty, StockLogUserID)
VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, 'WTT', ?, ?)";
$qryLog = $this->db->query($sqlLog, [
$warehouseID,
$itemID,
$unitID,
$stockNumber,
$batchNo,
$expDate,
$TGoodsTFDetailID,
$qtyReq,
$userID
]);
if (!$qryLog) {
throw new Exception("Failed to log source warehouse movement");
}
// * Query Stockcard where StockCardReffID == $stockID ORDER BY StockCardID DESC LIMIT 1
// * Insert Stockcard WH Sumber ke Transit (OUT)
// StockCardBefore = Previous StockCardAfter, StockCardIn=0, StockCardOut= qtyReg, StockCardAfter = PreviousCardAfter - qtyReq
$sqlLastSourceCard = "SELECT StockCardAfter FROM stockcard
WHERE StockCardReffID = ? ORDER BY StockCardID DESC LIMIT 1";
$qryLastSourceCard = $this->db->query($sqlLastSourceCard, [$stockID]);
if (!$qryLastSourceCard) {
throw new Exception("Failed to query last stockcard entry for source stock ID: {$stockID}");
}
$beforeQty = 0;
if ($qryLastSourceCard->num_rows() > 0) {
$beforeQty = $qryLastSourceCard->row()->StockCardAfter;
} else {
$beforeQty = $lastSourceStockQty;
}
$afterQty = $beforeQty - $qtyReq;
// Insert Stockcard for source warehouse (OUT)
$sqlInsSourceCard = "INSERT INTO stockcard (
StockCardWarehouseID, StockCardDatetime, StockCardItemID,
StockCardItemUnitID, StockCardBatchNo, StockCardED,
StockCardReffID, StockCardStatus,
StockCardBefore, StockCardIn, StockCardOut, StockCardAfter,
StockCardUserID
) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'MWT', ?, 0, ?, ?, ?)";
$qryInsSourceCard = $this->db->query($sqlInsSourceCard, [
$warehouseID,
$itemID,
$unitID,
$batchNo,
$expDate,
$stockID,
$beforeQty,
$qtyReq,
$afterQty,
$userID
]);
if (!$qryInsSourceCard) {
throw new Exception("Failed to insert stockcard for source warehouse ID: {$warehouseID}");
}
// * Query Stockcard where StockCardReffID == $transitStockID ORDER BY StockCardID DESC LIMIT 1
// * Insert Stockcard WH Transit (IN)
// New row with StockReffID = $transitStockID, StockCardIn = qtyReq, StockCardOut = 0, StockCardAfter = PreviousCardAfter + qtyReq
$sqlLastTransitCard = "SELECT StockCardAfter FROM stockcard
WHERE StockCardReffID = ? ORDER BY StockCardID DESC LIMIT 1";
$qryLastTransitCard = $this->db->query($sqlLastTransitCard, [$transitStockID]);
if (!$qryLastTransitCard) {
throw new Exception("Failed to query last stockcard entry for transit stock ID: {$transitStockID}");
}
$transitBeforeQty = 0;
if ($qryLastTransitCard->num_rows() > 0) {
$transitBeforeQty = $qryLastTransitCard->row()->StockCardAfter;
} else {
$sqlTransitQty = "SELECT StockQty FROM stock WHERE StockID = ?";
$qryTransitQty = $this->db->query($sqlTransitQty, [$transitStockID]);
if (!$qryTransitQty || $qryTransitQty->num_rows() === 0) {
throw new Exception("Failed to get current stock quantity for transit stock ID: {$transitStockID}");
}
$transitBeforeQty = $qryTransitQty->row()->StockQty - $qtyReq; // Subtract qtyReq since we've already added it
}
$transitAfterQty = $transitBeforeQty + $qtyReq;
// Insert Stockcard for transit warehouse (IN)
$sqlInsTransitCard = "INSERT INTO stockcard (
StockCardWarehouseID, StockCardDatetime, StockCardItemID,
StockCardItemUnitID, StockCardBatchNo, StockCardED,
StockCardReffID, StockCardStatus,
StockCardBefore, StockCardIn, StockCardOut, StockCardAfter, StockCardUserID
) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'MWF', ?, ?, 0, ?, ?)";
$qryInsTransitCard = $this->db->query($sqlInsTransitCard, [
$transitWhId,
$itemID,
$unitID,
$batchNo,
$expDate,
$transitStockID,
$transitBeforeQty,
$qtyReq,
$transitAfterQty,
$userID
]);
if (!$qryInsTransitCard) {
throw new Exception("Failed to insert stockcard for transit warehouse ID: {$transitWhId}");
}
}
// Return true to indicate success
return true;
}
public function deleteTfRequest()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
}
$userID = $this->sys_user["M_UserID"];
$goodsTfID = $this->sys_input["GoodsTfID"];
$this->db->trans_begin();
$sql = "UPDATE t_goods_transfer SET
T_GoodsTransferIsActive = 'N',
T_GoodsTransferDeleted = NOW(),
T_GoodsTransferM_UserID = ?
WHERE T_GoodsTransferID = ?
AND T_GoodsTransferIsActive = 'Y'";
$qry = $this->db->query($sql, [$userID, $goodsTfID]);
if (!$qry) {
$this->db->trans_rollback();
$this->sys_error_db("Gagal menghapus Transfer Request (ID: {$goodsTfID}). Error:", $this->db);
exit;
}
$sqldet = "UPDATE t_goods_transfer_detail SET
T_GoodsTransferDetailIsActive = 'N',
T_GoodsTransferDetailDeletedAt = NOW(),
T_GoodsTransferDetailM_UserID = ?
WHERE T_GoodsTransferDetailIDT_GoodsTransferID = ? AND T_GoodsTransferDetailIsActive = 'Y'
";
$qdet = $this->db->query($sqldet, [$userID, $goodsTfID]);
if (!$qdet) {
$this->db->trans_rollback();
$this->sys_error_db("failed delete detail transfer request item. Error:", $this->db);
exit;
}
$sql_json = "SELECT t_goods_transfer.*, '' as details
FROM t_goods_transfer
WHERE T_GoodsTransferID = ?";
$qry_json = $this->db->query($sql_json, [$goodsTfID]);
if (!$qry_json) {
$this->db->trans_rollback();
$this->sys_error_db("select json tf error");
exit;
}
$row_json = $qry_json->row_array();
$descActivity = "Hapus transfer request {$row_json['T_GoodsTransferNum']}";
$sql_json_detail = "SELECT t_goods_transfer_detail.*
FROM t_goods_transfer_detail
WHERE T_GoodsTransferDetailIDT_GoodsTransferID = ?";
$qry_json_detail = $this->db->query($sql_json_detail, [$goodsTfID]);
if ($qry_json_detail) {
$row_json["details"] = $qry_json_detail->result_array();
} else {
$this->sys_error_db("select detail tf error", $this->db);
exit;
}
$data_json = $row_json;
$this->insertUserActivty($goodsTfID, 'DELETE', $userID, $descActivity, json_encode($data_json));
$this->db->trans_commit();
$this->sys_ok("Berhasil menghapus Transfer Request (ID: {$goodsTfID})");
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function getListDetail()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$regionalid = $prm['regionalid'];
$branchIDOrigin = $prm['branchIDorigin'];
$branchCodeDestin = $prm['branchcodedestin'];
$currpage = $prm['currpage'];
$page = 0;
$limit = 5;
if ($currpage > 0) {
$page = ($currpage - 1) * $limit;
}
// Get total count (unchanged)
$sqltotal = "SELECT
COUNT(DISTINCT PurchaseRequestDetailID) as total
FROM purchase_request_flag
JOIN purchase_request_detail ON PurchaseRequestDetailID = PurchaseRequestFlagPurchaseRequestDetailID
AND PurchaseRequestDetailIsActive = 'Y'
JOIN purchase_request ON PurchaseRequestID = PurchaseRequestDetailPurchaseRequestID
AND PurchaseRequestIsActive = 'Y'
JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
AND M_ItemItem_CategoryID = 1
WHERE PurchaseRequestFlagIsActive = 'Y'
AND FIND_IN_SET('TF', PurchaseRequestFlagStatus)
AND PurchaseRequestDetailStatus = 'Read'
AND PurchaseRequestFlagIsClosed = 'N'
AND PurchaseRequestS_RegionalID = ?
AND PurchaseRequestM_BranchCode = ?
AND NOT EXISTS (
SELECT 1
FROM t_goods_transfer
JOIN t_goods_transfer_detail ON T_GoodsTransferDetailIDT_GoodsTransferID = T_GoodsTransferID
AND T_GoodsTransferDetailIsActive = 'Y'
WHERE T_GoodsTransferDetailT_PurchaseRequestFlagID = PurchaseRequestFlagID
AND T_GoodsTransferFromS_RegionalID = ?
AND T_GoodsTransferToM_BranchCode = ?
)";
$quetotal = $this->db->query($sqltotal, [
$regionalid,
$branchCodeDestin,
$regionalid,
$branchCodeDestin
]);
if (!$quetotal) {
$this->sys_error_db("error get total listing tf request");
exit;
}
$total = $quetotal->result_array()[0]['total'];
if (intval($total) === 0) {
$this->sys_ok(["records" => [], "total" => $total]);
exit;
}
// Simplified main query - removing stock-related JOINs
$sqlist = "SELECT
PurchaseRequestDate,
PurchaseRequestNumber,
PurchaseRequestDetailM_ItemID,
PurchaseRequestDetailItemUnitID,
M_ItemDesc,
unitreq.ItemUnitID as RequestItemUnitID,
unitreq.ItemUnitName as RequestItemUnitName,
PurchaseRequestFlagID,
PurchaseRequestFlagQty,
PurchaseRequestFlagQtyRest,
PurchaseRequestFlagStatus
FROM purchase_request_flag
JOIN purchase_request_detail ON PurchaseRequestDetailID = PurchaseRequestFlagPurchaseRequestDetailID
AND PurchaseRequestDetailIsActive = 'Y'
JOIN purchase_request ON PurchaseRequestID = PurchaseRequestDetailPurchaseRequestID
AND PurchaseRequestIsActive = 'Y'
JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID AND M_ItemIsActive = 'Y'
AND M_ItemItem_CategoryID = 1
JOIN itemunit unitreq
ON unitreq.ItemUnitID = PurchaseRequestDetailItemUnitID
AND unitreq.ItemUnitIsActive = 'Y'
WHERE PurchaseRequestFlagIsActive = 'Y'
AND FIND_IN_SET('TF', PurchaseRequestFlagStatus)
AND PurchaseRequestDetailStatus = 'Read'
AND PurchaseRequestFlagIsClosed = 'N'
AND PurchaseRequestS_RegionalID = ?
AND PurchaseRequestM_BranchCode = ?
AND NOT EXISTS (
SELECT 1
FROM t_goods_transfer
JOIN t_goods_transfer_detail ON T_GoodsTransferDetailIDT_GoodsTransferID = T_GoodsTransferID
AND T_GoodsTransferDetailIsActive = 'Y'
WHERE T_GoodsTransferDetailT_PurchaseRequestFlagID = PurchaseRequestFlagID
AND T_GoodsTransferFromS_RegionalID = ?
AND T_GoodsTransferToM_BranchCode = ?
)
ORDER BY PurchaseRequestDate ASC, PurchaseRequestNumber ASC
LIMIT ? OFFSET ?";
$quelist = $this->db->query($sqlist, [
$regionalid,
$branchCodeDestin,
$regionalid,
$branchCodeDestin,
$limit,
$page
]);
if (!$quelist) {
$this->sys_error_db("error get listing detail request transfer");
exit;
}
$rows = $quelist->result_array();
foreach ($rows as $key => $value) {
$itemID = $value['PurchaseRequestDetailM_ItemID'];
$reqUnitID = $value['RequestItemUnitID'];
$reqUnitName = $value['RequestItemUnitName'];
// Query semua stock yang tersedia di Gudang
$stockGudang = $this->getStockForItemRequested($itemID, $regionalid);
$rows[$key]['StockGudang'] = $stockGudang;
// Cek dulu apakah salah satu UnitStock ada yang == UnitReq
$hasDirectMatch = $this->checkStockReqUnitMatch($stockGudang, $reqUnitID);
$unpackData = $this->checkConversionPossibility($stockGudang, $reqUnitID, $reqUnitName, $itemID);
$hasUnpackOption = $unpackData['UnpackData']['CanUnpack'] ?? false;
$rows[$key]['CanDirectUse'] = $hasDirectMatch;
$rows[$key]['CanUnpack'] = $hasUnpackOption;
$rows[$key]['UnpackData'] = $unpackData['UnpackData'];
$rows[$key]['ItemRequest'] = [];
}
$result = array(
"records" => $rows,
"total" => $total
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
/**
* Ambil semua stock untuk ItemID di gudang regionalId meskipun beda batch dan UnitItem
* @param : int ItemID, int RegionalID
* @return : array of objects
*/
private function getStockForItemRequested($itemID, $regionalID)
{
$sqlStock = "SELECT
stock.StockID,
stock.StockItemUnitID,
itemunit.ItemUnitName as StockItemUnitName,
stock.StockQty,
stock.StockBatchNo,
stock.StockED,
stock.StockStockNumber,
stock.StockItemPrice
FROM warehouse
JOIN stock ON StockWarehouseID = WarehouseID
AND stock.StockQty > 0
JOIN itemunit ON ItemUnitID = stock.StockItemUnitID
AND itemunit.ItemUnitIsActive = 'Y'
WHERE WarehouseS_RegionalID = ?
AND WarehouseIsTransit = 'N'
AND WarehouseType = 'R'
AND WarehouseIsActive = 'Y'
AND stock.StockItemID = ?
ORDER BY stock.StockED ASC, stock.StockQty DESC";
$qryStock = $this->db->query($sqlStock, [$regionalID, $itemID]);
if (!$qryStock) {
return [];
}
return $qryStock->result_array();
}
/**
* Cek apakah ada Stock memiliki ItemUnit yang sama dengan Unit Request
* @param : array($stockGudang), int(reqUnitID)
* @return : boolean, true jika ada
*/
private function checkStockReqUnitMatch($stockGudang, $reqUnitID)
{
foreach ($stockGudang as $stock) {
if ($stock['StockItemUnitID'] == $reqUnitID) {
return true;
}
}
return false;
}
/**
* Cek semua kemungkinan Konversi Unit
* Misal: UnitReq = BUAH, tapi di stock ada LUSIN, dan KODI. Maka akan ada beberapa ConversionOptions
* @param : array($stockGudang), int($reqUnitID), string($reqUnitName), int($itemID)
* @return : object
*/
private function checkConversionPossibility($stockGudang, $reqUnitID, $reqUnitName, $itemID)
{
$conversionOptions = [];
$uniqueCombinations = [];
foreach ($stockGudang as $stock) {
$stockUnitID = $stock['StockItemUnitID'];
$stockUnitName = $stock['StockItemUnitName'];
if ($stockUnitID != $reqUnitID) {
$sqlConv = "SELECT * FROM unitconvert
WHERE UnitConvertFromItemUnitID = ?
AND UnitConvertToItemUnitID = ?
AND UnitConvertIsActive = 'Y'
LIMIT 1";
$qryConv = $this->db->query($sqlConv, [$stockUnitID, $reqUnitID]);
$conversionKey = $stockUnitID . '_' . $reqUnitID;
// Hanya tambah ke conversionOptions jika FromItemUnitID dan ToItemUnitID berbeda dengan existing
if ($qryConv && $qryConv->num_rows() > 0 && !in_array($conversionKey, $uniqueCombinations)) {
$conversionData = $qryConv->row();
$conversionOptions[] = [
"FromItemUnitID" => $stockUnitID,
"FromItemUnitName" => $stockUnitName,
"ToItemUnitID" => $reqUnitID,
"ToItemUnitName" => $reqUnitName,
"UnitConvertAmount" => $conversionData->UnitConvertAmount
];
$uniqueCombinations[] = $conversionKey;
}
}
}
if (empty($conversionOptions)) {
return [
'MustUnpack' => true,
'UnpackData' => [
"M_ItemID" => $itemID,
"CanUnpack" => false,
"ReasonCannotUnpack" => "Tidak ditemukan konversi dari unit stock yang tersedia ke unit request {$reqUnitName}"
]
];
} else {
return [
'MustUnpack' => true,
'UnpackData' => [
"M_ItemID" => $itemID,
"CanUnpack" => true,
"ConversionOptions" => $conversionOptions
]
];
}
}
public function getDraftDetail()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$regID = $this->sys_user["S_RegionalID"];
$goodTfID = $prm['goodTfID'];
$sql = "SELECT
PurchaseRequestDate,
PurchaseRequestNumber,
PurchaseRequestDetailM_ItemID,
PurchaseRequestDetailItemUnitID,
M_ItemDesc,
ItemUnitName,
COALESCE(SUM(CASE WHEN WarehouseID IS NOT NULL AND StockItemId IS NOT NULL THEN StockQty ELSE 0 END), 0) as StockGudang,
PurchaseRequestFlagID,
T_GoodsTransferDetailQty as PurchaseRequestFlagQty,
T_GoodsTransferDetailQtyTF as PurchaseRequestFlagQtyRest,
T_GoodsTransferDetailItemBatchNo,
PurchaseRequestFlagStatus,
T_GoodsTransferVerifiedBy,
T_GoodsTransferVerifiedDate
FROM t_goods_transfer
LEFT JOIN t_goods_transfer_detail ON T_GoodsTransferDetailIDT_GoodsTransferID = T_GoodsTransferID
AND T_GoodsTransferDetailIsActive = 'Y'
JOIN purchase_request_flag ON PurchaseRequestFlagID = T_GoodsTransferDetailT_PurchaseRequestFlagID
AND PurchaseRequestFlagIsActive = 'Y'
JOIN purchase_request_detail ON PurchaseRequestDetailID = PurchaseRequestFlagPurchaseRequestDetailID
AND PurchaseRequestDetailIsActive = 'Y'
JOIN purchase_request ON PurchaseRequestID = PurchaseRequestDetailPurchaseRequestID
AND PurchaseRequestIsActive = 'Y'
JOIN m_item ON M_ItemID = PurchaseRequestDetailM_ItemID
AND M_ItemItem_CategoryID = 1
AND M_ItemIsActive = 'Y'
JOIN itemunit ON ItemUnitID = PurchaseRequestDetailItemUnitID
AND ItemUnitIsActive = 'Y'
JOIN m_branch ON M_BranchCode = PurchaseRequestM_BranchCode
AND M_BranchIsActive = 'Y'
JOIN warehouse ON WarehouseS_RegionalID = PurchaseRequestS_RegionalID
AND WarehouseIsTransit = 'N'
AND WarehouseM_BranchID = 0
AND WarehouseIsActive = 'Y'
LEFT JOIN stock ON StockItemId = M_ItemID
AND StockWarehouseID = WarehouseID
WHERE PurchaseRequestFlagIsActive = 'Y'
AND T_GoodsTransferID = ?
AND T_GoodsTransferIsActive = 'Y'
GROUP BY PurchaseRequestNumber, PurchaseRequestFlagID";
$qry = $this->db->query($sql, [$goodTfID]);
if (!$qry) {
$this->sys_error_db("error get data draft detail transfer");
exit;
}
$detail = $qry->result_array();
foreach ($detail as $key => $value) {
$jsonStr = $value['T_GoodsTransferDetailItemBatchNo'];
$batches = [];
if (!empty($jsonStr)) {
$decode = json_decode($jsonStr);
if ($decode !== null && json_last_error() === JSON_ERROR_NONE) {
$batches = $decode;
} else {
$this->sys_error_db("[Error] decode json for batches number");
exit;
}
}
$detail[$key]['StockGudang'] = [];
// Foreach batches update StockQty dengan Query Baru
foreach ($batches as &$batch) {
$sqlCekStock = "SELECT stock.StockID,
stock.StockItemUnitID,
itemunit.ItemUnitName as StockItemUnitName,
stock.StockQty,
stock.StockBatchNo,
stock.StockED,
stock.StockStockNumber,
stock.StockItemPrice
FROM stock
JOIN itemunit ON ItemUnitID = stock.StockItemUnitID
AND itemunit.ItemUnitIsActive = 'Y'
WHERE StockID = ?";
$qryCekStock = $this->db->query($sqlCekStock, [$batch->StockID]);
if (!$qryCekStock) {
$this->sys_error_db("[Error] Query StockQty yang terbaru untuk ItemRequest");
exit;
}
$batch->StockQty = $qryCekStock->row()->StockQty;
$detail[$key]['StockGudang'][] = $qryCekStock->row();
}
unset($batch);
$detail[$key]['ItemRequest'] = $batches;
unset($detail[$key]['T_GoodsTransferDetailItemBatchNo']);
}
$result = array(
"records" => $detail,
"total" => sizeof($detail)
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function processTransfer()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$userID = $this->sys_user["M_UserID"];
$data = $this->sys_input;
$tfdetail = $data['detail'];
foreach ($tfdetail as $key => $value) {
$reqFlagID = $data['PurchaseRequestFlagID'];
$reqTFQty = $data['PurchaseRequestFlagQtyRest'];
$sqlgetproc = "SELECT SELECT PurchaseRequestFlagQtyProses FROM purchase_request_flag
WHERE PurchaseRequestFlagID = ? AND PurchaseRequestFlagIsActive = 'Y'";
$qrygetproc = $this->db->query($sqlgetproc, [$reqFlagID]);
if (!$qrygetproc) {
$this->sys_error_db("error get current purchase request flag process quantity");
exit;
}
$qtyProc = $qrygetproc->result_array()[0]['PurchaseRequestFlagQtyProses'];
$this->db->trans_begin();
$procQtyUpdt = intval($qtyProc) + intval($reqTFQty);
$sqlupdtproc = "UPDATE purchase_request_flag
SET
PurchaseRequestFlagQtyProses = ?,
PurchaseRequestFlagLastUpdated = NOW(),
PurchaseRequestFlagUserID = ?
WHERE PurchaseRequestFlagID = ?
AND PurchaseRequestFlagIsActive = 'Y'";
$qryupdtproc = $this->db->query($sqlupdtproc, [$procQtyUpdt, $userID, $reqFlagID]);
if (!$qryupdtproc) {
$this->db->trans_rollback();
$this->sys_error_db('error update flag update process qty');
exit;
}
}
$this->db->trans_commit();
$this->sys_ok("Berhasil mengupdate Qty process (ID: {$data['GoodsTfID']})");
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function getItemBatchListing()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$batchNo = "%";
if ($prm['batchno'] != '') {
$batchNo = $prm['batchno'] . "%";
}
$sqlbatch = "SELECT
WarehouseID,
WarehouseName,
StockID,
StockStockNumber,
StockItemID,
StockItemUnitID,
ItemUnitName,
StockItemPrice,
StockBatchNo,
StockED,
StockQty
FROM warehouse
JOIN stock ON StockWarehouseID = WarehouseID
AND StockQty > 0
AND WarehouseIsActive = 'Y' AND WarehouseIsTransit = 'N'
JOIN itemunit ON ItemUnitID = StockItemUnitID AND ItemUnitIsActive= 'Y'
WHERE WarehouseS_RegionalID = ?
AND WarehouseM_BranchID = 0
AND StockItemID = ?
AND StockItemUnitID = ?
AND StockBatchNo LIKE ?";
$quebatch = $this->db->query($sqlbatch, [
$prm['regionalid'],
$prm['itemid'],
$prm['unitid'],
$batchNo
]);
if (!$quebatch) {
$this->sys_error_db("error get data draft detail transfer");
exit;
}
$batch = $quebatch->result_array();
$result = array(
'records' => $batch,
'total' => sizeof($batch)
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function getUnpackItemBatchListing()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$batchNo = "%";
if ($prm['batchno'] != '') {
$batchNo = $prm['batchno'] . "%";
}
$sqlbatch = "SELECT
WarehouseID,
WarehouseName,
StockID,
StockStockNumber,
StockItemID,
StockItemUnitID,
ItemUnitName,
StockItemPrice,
StockBatchNo,
StockED,
StockQty
FROM warehouse
JOIN stock ON StockWarehouseID = WarehouseID
AND WarehouseIsActive = 'Y' AND WarehouseIsTransit = 'N'
JOIN itemunit ON ItemUnitID = StockItemUnitID AND ItemUnitIsActive= 'Y'
WHERE WarehouseS_RegionalID = ?
AND WarehouseM_BranchID = 0
AND StockItemID = ?
AND StockItemUnitID IN ? -- Bedanya dengan yang getItemBatchListing
AND StockBatchNo LIKE ?";
$quebatch = $this->db->query($sqlbatch, [
$prm['regionalid'],
$prm['itemid'],
$prm['unitid'],
$batchNo
]);
if (!$quebatch) {
$this->sys_error_db("error get data draft detail transfer");
exit;
}
$batch = $quebatch->result_array();
$result = array(
'records' => $batch,
'total' => sizeof($batch)
);
$this->sys_ok($result);
} catch (Exception $exc) {
$message = $exc->getMessage();
$this->sys_error($message);
}
}
public function unpackingStock()
{
try {
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$stockNo = $prm['StockStockNumber'];
$stockBatchNo = $prm['StockBatchNo'];
$unpackQty = intval($prm['UnpackQty']); // Qty yang mau di unpack dalam Unit Stock
$toItemUnitID = $prm['ToItemUnitID'];
$convertAmount = $prm['UnitConvertAmount'];
if ($prm['isDebug'] == true) {
$this->db->trans_start(true);
} else {
$this->db->trans_start();
}
/* *== Input Validation ==* */
// UnitConvertAmount can't 0
if ($convertAmount <= 0) {
throw new Exception("UnitConvertAmount tidak boleh <= 0");
}
// Query Stock by StockNumber and BatchNo
$sqlCekStock = "SELECT stock.*,
ItemUnitName as StockUnitName
FROM stock
JOIN itemunit
ON ItemUnitID = StockItemUnitID
WHERE StockStockNumber = ? AND StockBatchNo = ? ";
$qryCekStock = $this->db->query($sqlCekStock, [$stockNo, $stockBatchNo]);
if (!$qryCekStock || $qryCekStock->num_rows() == 0) {
$error = json_encode($this->db->error(), JSON_PRETTY_PRINT);
throw new Exception("Error Query DB $error atau Tidak Ditemukan Stock dengan StockNumber {$stockNo} dan Batch $stockBatchNo");
}
// Take the StockID
$rstCek = $qryCekStock->row();
$stockID = $rstCek->StockID;
$stockQty = $rstCek->StockQty;
$stockUnitName = $rstCek->StockUnitName;
// Validate StockQty >= UnpackQty
if ($stockQty < $unpackQty) {
throw new Exception("Unpack Qty tidak boleh melebih Stock Qty");
}
$qryLogs = [];
// Update StockQty = StockQty - UnpackQty
$newStockQty = $stockQty - $unpackQty;
$sqlUp = "UPDATE stock SET StockQty = ? WHERE StockID = ?";
$qryUp = $this->db->query($sqlUp, [$newStockQty, $stockID]);
if (!$qryUp) {
throw new Exception("Failed to update StockQty");
} else {
$qryLogs["update_qty_unpack"] = "Berhasil existing stock dari $stockQty $stockUnitName to {$newStockQty} {$stockUnitName}";
$sql = "SELECT stock.*, ItemUnitName as StockUnitName
FROM stock
JOIN itemunit ON ItemUnitID = StockItemUnitID
WHERE StockID = ?";
$qry = $this->db->query($sql, [$stockID]);
$updatedStock = $qry->row();
}
// Insert Log, with StockLogStatus = UP_OUT
$sqlUpLog = "INSERT INTO stocklog (
StockLogWarehouseID, StockLogDateTime, StockLogItemID,
StockLogItemUnitID, StockLogStockNumber, StockLogBatchNo,
StockLogED, StockLogStatus, StockLogQty, StockLogUserID
) VALUES (?,NOW(),?,?,?,?,?,?,?,?)";
$qryUpLog = $this->db->query($sqlUpLog, [
$rstCek->StockWarehouseID,
$rstCek->StockItemID,
$rstCek->StockItemUnitID,
$rstCek->StockStockNumber,
$rstCek->StockBatchNo,
$rstCek->StockED,
'UP_OUT',
$unpackQty, // OUT sesuai UnpackQty
$this->sys_user['M_UserID']
]);
if (!$qryUpLog) {
throw new Exception("Failed to insert stocklog for UP_OUT");
} else {
$qryLogs["insert_log_up_out"] = "Berhasil stocklog UP_OUT {$rstCek->StockQty} {$stockUnitName} for StockNumber {$rstCek->StockStockNumber}";
}
$sqlLastSourceCard = "SELECT StockCardAfter FROM stockcard
WHERE StockCardReffID = ? ORDER BY StockCardID DESC LIMIT 1";
$qryLastSourceCard = $this->db->query($sqlLastSourceCard, [$stockID]);
if (!$qryLastSourceCard) {
throw new Exception("Failed to query last stockcard entry for source StockID: {$stockID}");
}
$beforeQty = 0;
if ($qryLastSourceCard->num_rows() > 0) {
$beforeQty = $qryLastSourceCard->row()->StockCardAfter;
} else {
$beforeQty = $stockQty;
}
$afterQty = $beforeQty - $unpackQty;
// Insert Stockcard for source stock (OUT)
$sqlInsSourceCard = "INSERT INTO stockcard (
StockCardWarehouseID, StockCardDatetime, StockCardItemID,
StockCardItemUnitID, StockCardBatchNo, StockCardED,
StockCardReffID, StockCardStatus,
StockCardBefore, StockCardIn, StockCardOut, StockCardAfter,
StockCardUserID
) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'UP_OUT', ?, 0, ?, ?, ?)";
$qryInsSourceCard = $this->db->query($sqlInsSourceCard, [
$rstCek->StockWarehouseID,
$rstCek->StockItemID,
$rstCek->StockItemUnitID,
$rstCek->StockBatchNo,
$rstCek->StockED,
$stockID,
$beforeQty,
$unpackQty,
$afterQty,
$this->sys_user['M_UserID']
]);
if (!$qryInsSourceCard) {
throw new Exception("Failed to insert stockcard for source stock (UP_OUT)");
} else {
$qryLogs["insert_card_up_out"] = `Berhasil stockcard UP_OUT pada StockCardID: {$this->db->insert_id()}dengan StockCardReffID: {$stockID}`;
}
// Insert new row stock hasil unpack
$sqlIn = "INSERT INTO stock (
StockWarehouseAlmariID, StockWarehouseRackID,
StockWarehouseID,StockStockNumber,
StockBatchNo,StockItemID,StockItemUnitID,
StockED,StockItemPrice,
StockQty,StockLastUpdated,StockUserID
) VALUES (0, 0, ?, fn_numbering('SN'), ?, ?, ?, ?, ?, ?, NOW(), ?)
ON DUPLICATE KEY UPDATE
StockItemPrice = VALUES(StockItemPrice),
StockQty = StockQty + VALUES(StockQty),
StockLastUpdated = NOW(),
StockUserID = VALUES(StockUserID);";
$qtyConverted = $unpackQty * $convertAmount;
$priceConverted = $rstCek->StockItemPrice / $convertAmount;
$qryIn = $this->db->query($sqlIn, [
$rstCek->StockWarehouseID,
$rstCek->StockBatchNo,
$rstCek->StockItemID,
$toItemUnitID,
$rstCek->StockED,
$priceConverted,
$qtyConverted,
$this->sys_user['M_UserID']
]);
if (!$qryIn) {
$error = json_encode($this->db->error(), JSON_PRETTY_PRINT);
throw new Exception("Failed to insert new stock for unpacking. DB Error: $error");
}
// Query Stock Hasil Unpack yang Baru untuk data Stocklog dan Stockcard
$sql = "SELECT stock.*, ItemUnitName as StockUnitName
FROM stock
JOIN itemunit ON ItemUnitID = StockItemUnitID
WHERE StockWarehouseID = ?
AND StockItemID = ? AND StockItemUnitID = ?
AND StockBatchNo = ? AND StockED = ?";
$qry = $this->db->query($sql, [
$rstCek->StockWarehouseID,
$rstCek->StockItemID,
$toItemUnitID,
$rstCek->StockBatchNo,
$rstCek->StockED
]);
if (!$qry) {
$err = json_encode($this->db->error(), JSON_PRETTY_PRINT);
throw new Exception("Error select row stock hasil unpacking");
}
$unpackResultStock = $qry->row();
$qryLogs["insert_new_stock"] = "Berhasil insert Stock No. {$unpackResultStock->StockStockNumber} after unpacking Stock No. {$rstCek->StockStockNumber} dari {$unpackQty} {$stockUnitName} ke {$qtyConverted} {$unpackResultStock->StockUnitName}";
// Log hasil konversi with StockLogStatus = UP_IN
$sqlUpLog = "INSERT INTO stocklog (
StockLogWarehouseID, StockLogDateTime, StockLogItemID,
StockLogItemUnitID, StockLogStockNumber, StockLogBatchNo,
StockLogED, StockLogStatus, StockLogQty, StockLogUserID
) VALUES (?,NOW(),?,?,?,?,?,?,?,?)";
$qryUpLog = $this->db->query($sqlUpLog, [
$unpackResultStock->StockWarehouseID,
$unpackResultStock->StockItemID,
$unpackResultStock->StockItemUnitID,
$unpackResultStock->StockStockNumber,
$unpackResultStock->StockBatchNo,
$unpackResultStock->StockED,
'UP_IN',
$qtyConverted, // IN sesuai stock hasil konversi jangan ambil StockQty jaga2 kalau StockQty=StcokQty+convertedQty
$this->sys_user['M_UserID']
]);
if (!$qryUpLog) {
throw new Exception("Failed to insert stocklog for UP_IN");
} else {
$qryLogs["insert_log_up_in"] = "Berhasil stocklog UP_IN {$qtyConverted} {$unpackResultStock->StockUnitName}";
}
$sqlLastRstCard = "SELECT StockCardAfter FROM stockcard
WHERE StockCardReffID = ? ORDER BY StockCardID DESC LIMIT 1";
$qryLastRstCard = $this->db->query($sqlLastRstCard, [$unpackResultStock->StockID]);
if (!$qryLastRstCard) {
throw new Exception("Failed to query last stockcard entry for destination stock ID: {$unpackResultStock->StockID}");
}
$destBeforeQty = 0;
if ($qryLastRstCard->num_rows() > 0) {
$destBeforeQty = $qryLastRstCard->row()->StockCardAfter;
} else {
// If no previous stockcard for unpack result stock. Ambil StockQty
$destBeforeQty = $unpackResultStock->StockQty - $qtyConverted;
}
$destAfterQty = $destBeforeQty + $qtyConverted;
// Insert Stockcard for destination stock (IN)
$sqlInsDestCard = "INSERT INTO stockcard (
StockCardWarehouseID, StockCardDatetime, StockCardItemID,
StockCardItemUnitID, StockCardBatchNo, StockCardED,
StockCardReffID, StockCardStatus,
StockCardBefore, StockCardIn, StockCardOut, StockCardAfter,
StockCardUserID
) VALUES (?, NOW(), ?, ?, ?, ?, ?, 'UP_IN', ?, ?, 0, ?, ?)";
$qryInsDestCard = $this->db->query($sqlInsDestCard, [
$unpackResultStock->StockWarehouseID,
$unpackResultStock->StockItemID,
$unpackResultStock->StockItemUnitID,
$unpackResultStock->StockBatchNo,
$unpackResultStock->StockED,
$unpackResultStock->StockID,
$destBeforeQty,
$qtyConverted,
$destAfterQty,
$this->sys_user['M_UserID']
]);
if (!$qryInsDestCard) {
throw new Exception("Failed to insert stockcard for destination stock (UP_IN)");
} else {
$qryLogs["insert_card_up_in"] = `Berhasil stockcard UP_OUT pada StockCardID: {$this->db->insert_id()}dengan StockCardReffID: {$stockID}`;
}
$this->db->trans_complete();
$this->sys_ok([
"OldStock" => $rstCek,
"UpdatedStock" => $updatedStock,
"UnpackResultStock" => $unpackResultStock,
"TranssactionLogs" => $qryLogs
]);
} catch (Exception $e) {
$this->db->trans_rollback();
$message = $e->getMessage();
$this->sys_error($message);
}
}
function insertUserActivty($id, $type, $userID, $desc = '', $data = '')
{
$sql = "INSERT INTO user_activity (
UserActivityCode,
UserActivityStatus,
UserActivityDescription,
UserActivityRefID,
UserActivityData,
UserActivityUserID,
UserActivityCreated
) VALUES (
?, ?, ?, ?, ?, ?, NOW()
)";
// Eksekusi query dengan parameter yang diberikan
$exec = $this->db->query($sql, [
'TR',
$type,
$desc,
$id,
$data,
$userID
]);
// Jika query gagal, lempar error
if (!$exec) {
$this->sys_error_db("Error inserting user activity", $this->db);
$this->db->trans_rollback();
exit;
}
}
function toVerifOrApprove($id, $user)
{
$sqlevl = "SELECT * FROM m_user WHERE M_UserIsActive = 'Y' AND M_UserID = ?";
$quelvl = $this->db->query($sqlevl, [$user['M_UserID']]);
if (!$quelvl) {
$this->db->trans_rollback();
$this->sys_error_db("[Error] get user level");
exit;
}
$level = $quelvl->row_array()['M_UserM_ApproveLevelID'];
if ($level == '1') {
$sql = "UPDATE t_goods_transfer SET
T_GoodsTransferVerifiedBy = ?,
T_GoodsTransferVerifiedDate = NOW(),
T_GoodsTransferLastUpdated = NOW()
WHERE T_GoodsTransferID = ?
AND T_GoodsTransferIsActive = 'Y'";
$que = $this->db->query($sql, [$user['M_UserID'], $id]);
if (!$que) {
$this->db->trans_rollback();
$this->sys_error_db("[Error] update verified by manager");
exit;
}
} elseif ($level == '2') {
$sql = "UPDATE t_goods_transfer SET
T_GoodsTransferApprovedBy = ?,
T_GoodsTransferApprovedDate = NOW(),
T_GoodsTransferLastUpdated = NOW()
WHERE T_GoodsTransferID = ?
AND T_GoodsTransferIsActive = 'Y'";
$que = $this->db->query($sql, [$user['M_UserID'], $id]);
if (!$que) {
$this->db->trans_rollback();
$this->sys_error_db("[Error] update approve kareg");
exit;
}
}
}
}