433 lines
17 KiB
PHP
433 lines
17 KiB
PHP
<?php
|
|
|
|
/**
|
|
* JurnalPiCicilanOrderAset
|
|
*
|
|
* Endpoint scheduler untuk membuat jurnal PI cicilan order aset.
|
|
* Controller ini dibuat agar bisa dipanggil via cURL dari proses generator.
|
|
*/
|
|
class JurnalPiCicilanOrderAset extends MY_Controller
|
|
{
|
|
var $db;
|
|
|
|
public function index()
|
|
{
|
|
echo "Jurnal PI Cicilan Order Aset";
|
|
}
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
public function CreateJournal()
|
|
{
|
|
try {
|
|
$para = $this->sys_input;
|
|
|
|
$date = isset($para["date"]) && $para["date"] != "" ? $para["date"] : date("Y-m-d");
|
|
if (!$this->isValidDate($date)) {
|
|
throw new Exception("Format tanggal tidak valid. Gunakan YYYY-MM-DD.");
|
|
}
|
|
|
|
$branchCode = isset($para["branchCode"]) ? trim($para["branchCode"]) : "";
|
|
if ($branchCode == "") {
|
|
throw new Exception("branchCode wajib diisi.");
|
|
}
|
|
|
|
$title = isset($para["title"]) ? trim($para["title"]) : "";
|
|
if ($title == "") {
|
|
throw new Exception("title wajib diisi.");
|
|
}
|
|
|
|
$description = isset($para["description"]) ? trim($para["description"]) : "";
|
|
if ($description == "") {
|
|
throw new Exception("description wajib diisi.");
|
|
}
|
|
|
|
$details = isset($para["details"]) && is_array($para["details"]) ? $para["details"] : [];
|
|
if (count($details) == 0) {
|
|
throw new Exception("details wajib diisi minimal 1 baris.");
|
|
}
|
|
|
|
$periodeID = isset($para["periodeID"]) && (int) $para["periodeID"] > 0
|
|
? (int) $para["periodeID"]
|
|
: $this->getPeriodeIDByDate($date);
|
|
if ($periodeID <= 0) {
|
|
throw new Exception("Periode jurnal tidak ditemukan untuk tanggal {$date}.");
|
|
}
|
|
|
|
$regionalID = isset($para["regionalID"]) && (int) $para["regionalID"] > 0
|
|
? (int) $para["regionalID"]
|
|
: $this->getRegionalIDByBranchCode($branchCode);
|
|
if ($regionalID <= 0) {
|
|
throw new Exception("regionalID wajib diisi atau harus bisa diturunkan dari branchCode.");
|
|
}
|
|
|
|
$branchCompanyID = isset($para["branchCompanyID"]) && (int) $para["branchCompanyID"] > 0
|
|
? (int) $para["branchCompanyID"]
|
|
: $this->getBranchCompanyIDByBranchCode($branchCode);
|
|
if ($branchCompanyID <= 0) {
|
|
throw new Exception("branchCompanyID wajib diisi atau harus bisa diturunkan dari branchCode.");
|
|
}
|
|
|
|
$jurnalTypeID = isset($para["jurnalTypeID"]) && (int) $para["jurnalTypeID"] > 0
|
|
? (int) $para["jurnalTypeID"]
|
|
: $this->getJurnalTypeID(isset($para["jurnalTypeCode"]) && $para["jurnalTypeCode"] != "" ? $para["jurnalTypeCode"] : "PAYMENT");
|
|
if ($jurnalTypeID <= 0) {
|
|
throw new Exception("jurnalTypeID / jurnalTypeCode tidak valid.");
|
|
}
|
|
|
|
$userID = isset($para["userID"]) ? (int) $para["userID"] : 0;
|
|
$jurnalNo = $this->generateJournalNumber();
|
|
if ($jurnalNo === false || $jurnalNo == "") {
|
|
throw new Exception("Gagal generate nomor jurnal.");
|
|
}
|
|
|
|
$totalDebit = 0;
|
|
$totalCredit = 0;
|
|
foreach ($details as $idx => $detail) {
|
|
$debit = isset($detail["debit"]) ? (float) $detail["debit"] : 0;
|
|
$credit = isset($detail["credit"]) ? (float) $detail["credit"] : 0;
|
|
if ($debit < 0 || $credit < 0) {
|
|
throw new Exception("Nilai debit/credit tidak boleh negatif pada detail ke-" . ($idx + 1));
|
|
}
|
|
$totalDebit += $debit;
|
|
$totalCredit += $credit;
|
|
}
|
|
|
|
if (round($totalDebit, 2) !== round($totalCredit, 2)) {
|
|
throw new Exception("Total debit dan credit harus sama. Debit={$totalDebit}, Credit={$totalCredit}");
|
|
}
|
|
|
|
$this->db->trans_begin();
|
|
|
|
$sqlHeader = "INSERT INTO jurnal (
|
|
jurnalM_BranchCompanyID,
|
|
JurnalS_RegionalID,
|
|
jurnalM_BranchCode,
|
|
jurnalperiodeID,
|
|
jurnalNo,
|
|
jurnalTitle,
|
|
jurnalDescription,
|
|
jurnalDate,
|
|
jurnalJurnalTypeID,
|
|
jurnalCreated,
|
|
jurnalM_UserID
|
|
) VALUES (?,?,?,?,?,?,?,?,?,NOW(),?)";
|
|
|
|
$qryHeader = $this->db->query($sqlHeader, [
|
|
$branchCompanyID,
|
|
$regionalID,
|
|
$branchCode,
|
|
$periodeID,
|
|
$jurnalNo,
|
|
$title,
|
|
$description,
|
|
$date,
|
|
$jurnalTypeID,
|
|
$userID
|
|
]);
|
|
if (!$qryHeader) {
|
|
$this->db->trans_rollback();
|
|
$this->sys_error_db("Gagal insert jurnal header.");
|
|
exit;
|
|
}
|
|
|
|
$jurnalID = $this->db->insert_id();
|
|
$createdAddonCount = 0;
|
|
|
|
foreach ($details as $idx => $detail) {
|
|
$coaID = isset($detail["coaID"]) ? (int) $detail["coaID"] : 0;
|
|
$descriptionTx = isset($detail["description"]) ? trim($detail["description"]) : "";
|
|
$debit = isset($detail["debit"]) ? (float) $detail["debit"] : 0;
|
|
$credit = isset($detail["credit"]) ? (float) $detail["credit"] : 0;
|
|
|
|
if ($coaID <= 0) {
|
|
$this->db->trans_rollback();
|
|
$this->sys_error_db("coaID wajib diisi pada detail ke-" . ($idx + 1));
|
|
exit;
|
|
}
|
|
|
|
$sqlTx = "INSERT INTO jurnal_tx (
|
|
jurnalTxJurnalID,
|
|
jurnalTxCoaID,
|
|
jurnalTxDescription,
|
|
jurnalTxDebit,
|
|
jurnalTxCredit,
|
|
jurnalTxCreated,
|
|
jurnalTxM_UserID
|
|
) VALUES (?,?,?,?,?,NOW(),?)";
|
|
|
|
$qryTx = $this->db->query($sqlTx, [
|
|
$jurnalID,
|
|
$coaID,
|
|
$descriptionTx,
|
|
$debit,
|
|
$credit,
|
|
$userID
|
|
]);
|
|
if (!$qryTx) {
|
|
$this->db->trans_rollback();
|
|
$this->sys_error_db("Gagal insert jurnal_tx pada detail ke-" . ($idx + 1));
|
|
exit;
|
|
}
|
|
|
|
$jurnalTxID = $this->db->insert_id();
|
|
|
|
$addonValue = isset($detail["addonValue"]) ? trim($detail["addonValue"]) : "";
|
|
$mItemID = isset($detail["mItemID"]) ? (int) $detail["mItemID"] : 0;
|
|
$addonCode = isset($detail["addonCode"]) && $detail["addonCode"] != "" ? trim($detail["addonCode"]) : "PICICIL";
|
|
|
|
if ($addonValue != "" || $mItemID > 0) {
|
|
$sqlAddon = "INSERT INTO jurnal_addon (
|
|
jurnalAddOnJurnalID,
|
|
jurnalAddOnJurnalTxID,
|
|
jurnalAddOnCode,
|
|
jurnalAddOnValue,
|
|
jurnalAddOnM_ItemID,
|
|
jurnalAddOnCreated,
|
|
jurnalAddOnCreatedUserID
|
|
) VALUES (?,?,?,?,?,NOW(),?)";
|
|
|
|
$qryAddon = $this->db->query($sqlAddon, [
|
|
$jurnalID,
|
|
$jurnalTxID,
|
|
$addonCode,
|
|
$addonValue,
|
|
$mItemID > 0 ? $mItemID : null,
|
|
$userID
|
|
]);
|
|
if (!$qryAddon) {
|
|
$this->db->trans_rollback();
|
|
$this->sys_error_db("Gagal insert jurnal_addon pada detail ke-" . ($idx + 1));
|
|
exit;
|
|
}
|
|
$createdAddonCount++;
|
|
}
|
|
}
|
|
|
|
if ($this->db->trans_status() === false) {
|
|
$this->db->trans_rollback();
|
|
$this->sys_error_db("Transaksi jurnal gagal.");
|
|
exit;
|
|
}
|
|
|
|
$this->db->trans_commit();
|
|
|
|
$this->sys_ok([
|
|
"jurnalID" => $jurnalID,
|
|
"jurnalNo" => $jurnalNo,
|
|
"detailCount" => count($details),
|
|
"addonCount" => $createdAddonCount
|
|
]);
|
|
} catch (Exception $exc) {
|
|
$this->sys_error($exc->getMessage());
|
|
}
|
|
}
|
|
|
|
public function CreatePiInstallmentJournal()
|
|
{
|
|
$this->CreateJournal();
|
|
}
|
|
|
|
public function ListEligiblePurchaseOrderAssetContracts()
|
|
{
|
|
try {
|
|
$para = $this->sys_input;
|
|
|
|
$startDate = isset($para["startDate"]) && $para["startDate"] != ""
|
|
? $para["startDate"]
|
|
: date("Y-m-01");
|
|
$endDate = isset($para["endDate"]) && $para["endDate"] != ""
|
|
? $para["endDate"]
|
|
: date("Y-m-t");
|
|
|
|
if (!$this->isValidDate($startDate) || !$this->isValidDate($endDate)) {
|
|
throw new Exception("Format tanggal tidak valid. Gunakan YYYY-MM-DD.");
|
|
}
|
|
|
|
if (strtotime($startDate) > strtotime($endDate)) {
|
|
throw new Exception("startDate tidak boleh lebih besar dari endDate");
|
|
}
|
|
|
|
$records = $this->getEligiblePurchaseOrderAssetContracts($startDate, $endDate);
|
|
|
|
$this->sys_ok([
|
|
"startDate" => $startDate,
|
|
"endDate" => $endDate,
|
|
"total" => count($records),
|
|
"records" => $records
|
|
]);
|
|
} catch (Exception $exc) {
|
|
$this->sys_error($exc->getMessage());
|
|
}
|
|
}
|
|
|
|
private function generateJournalNumber()
|
|
{
|
|
$qry = $this->db->query("SELECT fn_numbering('J') AS JNumber");
|
|
if (!$qry || $qry->num_rows() == 0) {
|
|
return false;
|
|
}
|
|
return $qry->row_array()["JNumber"];
|
|
}
|
|
|
|
private function getPeriodeIDByDate($date)
|
|
{
|
|
$sql = "SELECT periodeID
|
|
FROM periode
|
|
WHERE periodeIsActive = 'Y'
|
|
AND DATE(?) BETWEEN periodeStartDate AND periodeEndDate
|
|
LIMIT 1";
|
|
$qry = $this->db->query($sql, [$date]);
|
|
if (!$qry || $qry->num_rows() == 0) {
|
|
return 0;
|
|
}
|
|
return (int) $qry->row_array()["periodeID"];
|
|
}
|
|
|
|
private function getBranchCompanyIDByBranchCode($branchCode)
|
|
{
|
|
$sql = "SELECT M_BranchCompanyDetailM_BranchCompanyID AS branchCompanyID
|
|
FROM m_branch_companydetail
|
|
WHERE M_BranchCompanyDetailM_BranchCode = ?
|
|
AND M_BranchCompanyDetailIsActive = 'Y'
|
|
LIMIT 1";
|
|
$qry = $this->db->query($sql, [$branchCode]);
|
|
if (!$qry || $qry->num_rows() == 0) {
|
|
return 0;
|
|
}
|
|
return (int) $qry->row_array()["branchCompanyID"];
|
|
}
|
|
|
|
private function getRegionalIDByBranchCode($branchCode)
|
|
{
|
|
$sql = "SELECT M_BranchS_RegionalID AS regionalID
|
|
FROM m_branch
|
|
WHERE M_BranchCode = ?
|
|
AND M_BranchIsActive = 'Y'
|
|
LIMIT 1";
|
|
$qry = $this->db->query($sql, [$branchCode]);
|
|
if (!$qry || $qry->num_rows() == 0) {
|
|
return 0;
|
|
}
|
|
return (int) $qry->row_array()["regionalID"];
|
|
}
|
|
|
|
private function getJurnalTypeID($jurnalTypeCode)
|
|
{
|
|
$sql = "SELECT JurnalTypeID
|
|
FROM jurnal_type
|
|
WHERE JurnalTypeCode = ?
|
|
AND JurnalTypeIsActive = 'Y'
|
|
LIMIT 1";
|
|
$qry = $this->db->query($sql, [$jurnalTypeCode]);
|
|
if (!$qry || $qry->num_rows() == 0) {
|
|
return 0;
|
|
}
|
|
return (int) $qry->row_array()["JurnalTypeID"];
|
|
}
|
|
|
|
private function getEligiblePurchaseOrderAssetContracts($startDate, $endDate)
|
|
{
|
|
$sql = "SELECT
|
|
c.PurchaseOrderAssetContractID,
|
|
c.PurchaseOrderAssetContractPurchaseOrderID,
|
|
c.PurchaseOrderAssetContractName,
|
|
c.PurchaseOrderAssetContractStartDate,
|
|
c.PurchaseOrderAssetContractEndDate,
|
|
c.PurchaseOrderAssetContractInstallmentNumber,
|
|
c.PurchaseOrderAssetContractInstallmentPaid,
|
|
c.PurchaseOrderAssetContractInstallmentDate,
|
|
c.PurchaseOrderAssetContractInstallmentPayAmount,
|
|
c.PurchaseOrderAssetContractReceiveOrderPoID,
|
|
po.PurchaseOrderID,
|
|
po.PurchaseOrderNumber,
|
|
po.PurchaseOrderSupplierID,
|
|
po.PurchaseOrderPaymentTerm,
|
|
po.PurchaseOrderWarehouseType,
|
|
po.PurchaseOrderWarehouseID,
|
|
wh.WarehouseCode,
|
|
wh.WarehouseName,
|
|
wh.WarehouseType,
|
|
wh.WarehouseS_RegionalID AS WarehouseS_RegionalID,
|
|
wh.WarehouseM_BranchID AS WarehouseM_BranchID,
|
|
ro.ReceiveOrderPoID,
|
|
ro.ReceiveOrderPoConfirmed,
|
|
m_branch.M_BranchCode,
|
|
m_branch.M_BranchName,
|
|
m_branch.M_BranchS_RegionalID,
|
|
m_branch_companydetail.M_BranchCompanyDetailM_BranchCompanyID AS M_BranchCompanyID,
|
|
ps.PurchaseOrderSummaryID,
|
|
ps.PurchaseOrderSummaryItemID,
|
|
ps.PurchaseOrderSummaryItemUnitID
|
|
FROM purchase_order_asset_contract c
|
|
JOIN purchase_order po
|
|
ON po.PurchaseOrderID = c.PurchaseOrderAssetContractPurchaseOrderID
|
|
AND po.PurchaseOrderIsActive = 'Y'
|
|
AND po.PurchaseOrderStatus = 'Approved'
|
|
LEFT JOIN warehouse wh
|
|
ON wh.WarehouseID = po.PurchaseOrderWarehouseID
|
|
JOIN (
|
|
SELECT
|
|
rd.ReceiveOrderPoDetailPurchaseOrderID,
|
|
MIN(ro0.ReceiveOrderPoID) AS ReceiveOrderPoID,
|
|
MAX(ro0.ReceiveOrderPoConfirmed) AS ReceiveOrderPoConfirmed
|
|
FROM receive_order_po ro0
|
|
JOIN receive_order_po_detail rd
|
|
ON rd.ReceiveOrderPoDetailReceiveOrderPoID = ro0.ReceiveOrderPoID
|
|
AND rd.ReceiveOrderPoDetailIsActive = 'Y'
|
|
WHERE ro0.ReceiveOrderPoIsActive = 'Y'
|
|
AND ro0.ReceiveOrderPoConfirmed = 'Y'
|
|
GROUP BY rd.ReceiveOrderPoDetailPurchaseOrderID
|
|
) ro
|
|
ON ro.ReceiveOrderPoDetailPurchaseOrderID = po.PurchaseOrderID
|
|
LEFT JOIN m_branch
|
|
ON m_branch.M_BranchID = wh.WarehouseM_BranchID
|
|
AND m_branch.M_BranchIsActive = 'Y'
|
|
LEFT JOIN m_branch_companydetail
|
|
ON m_branch_companydetail.M_BranchCompanyDetailM_BranchCode = m_branch.M_BranchCode
|
|
AND m_branch_companydetail.M_BranchCompanyDetailIsActive = 'Y'
|
|
LEFT JOIN (
|
|
SELECT ps0.*
|
|
FROM purchase_order_summary ps0
|
|
JOIN (
|
|
SELECT
|
|
PurchaseOrderSummaryPurchaseOrderID,
|
|
MIN(PurchaseOrderSummaryID) AS PurchaseOrderSummaryID
|
|
FROM purchase_order_summary
|
|
WHERE PurchaseOrderSummaryIsActive = 'Y'
|
|
GROUP BY PurchaseOrderSummaryPurchaseOrderID
|
|
) psx
|
|
ON psx.PurchaseOrderSummaryID = ps0.PurchaseOrderSummaryID
|
|
) ps
|
|
ON ps.PurchaseOrderSummaryPurchaseOrderID = po.PurchaseOrderID
|
|
WHERE c.PurchaseOrderAssetContractIsActive = 'Y'
|
|
AND c.PurchaseOrderAssetContractStatus = 'belum lunas'
|
|
AND IFNULL(c.PurchaseOrderAssetContractInstallmentPayAmount, 0) > 0
|
|
AND IFNULL(c.PurchaseOrderAssetContractInstallmentPaid, 0) < IFNULL(c.PurchaseOrderAssetContractInstallmentNumber, 0)
|
|
AND DATE(c.PurchaseOrderAssetContractStartDate) <= DATE(?)
|
|
AND (
|
|
c.PurchaseOrderAssetContractEndDate IS NULL
|
|
OR DATE(c.PurchaseOrderAssetContractEndDate) >= DATE(?)
|
|
)
|
|
AND IFNULL(c.PurchaseOrderAssetContractInstallmentDate, 1) <= ?
|
|
ORDER BY c.PurchaseOrderAssetContractID ASC";
|
|
|
|
$qry = $this->db->query($sql, [$endDate, $startDate, (int) date("d", strtotime($endDate))]);
|
|
if (!$qry) {
|
|
$this->sys_error_db("Gagal mengambil data purchase_order_asset_contract.");
|
|
exit;
|
|
}
|
|
|
|
return $qry->result_array();
|
|
}
|
|
|
|
private function isValidDate($tanggal)
|
|
{
|
|
$d = DateTime::createFromFormat("Y-m-d", $tanggal);
|
|
return $d && $d->format("Y-m-d") === $tanggal;
|
|
}
|
|
}
|