gabungkan fungsi get data dengan create jurnal

This commit is contained in:
Hanan Askarim
2026-07-14 13:25:33 +07:00
committed by sasadib
parent aef86b7acf
commit 2d072fad16
2 changed files with 518 additions and 195 deletions

View File

@@ -1,42 +1,92 @@
@host = https://accone.aplikasi.web.id/one-api/ @host = https://accone.aplikasi.web.id/one-api
@date = 2026-07-14
@coaBayarID = 1110100001
@coaHutangID = 2110100030
### Create jurnal PI cicilan order aset # =============================================================================
POST {{host}}/mockup/scheduler/JurnalPiCicilanOrderAset/CreateJournal # JurnalPiCicilanOrderAset — Contoh Pemanggilan Endpoint
Content-Type: application/json # =============================================================================
# Urutan penggunaan yang disarankan:
# 1. ListEligiblePurchaseOrderAssetContracts → lihat kontrak yang akan diproses
# 2. GenerateJournalsFromContracts (dryRun=Y) → simulasi tanpa simpan ke DB
# 3. GenerateJournalsFromContracts (dryRun=N) → generate jurnal sesungguhnya
# 4. CreateJournal → buat jurnal manual 1 per 1
# =============================================================================
{
"date": "2026-07-14",
"branchCode": "BA",
"regionalID": 8,
"branchCompanyID": 1,
"jurnalTypeID": 11,
"title": "Jurnal PI Cicilan Order Aset",
"description": "Pembuatan jurnal PI cicilan order aset",
"userID": 0,
"details": [
{
"coaID": 2110100030,
"description": "Hutang cicilan",
"debit": 15000000,
"credit": 0,
"addonCode": "PICICIL",
"addonValue": "PI-INS-202607140001",
"mItemID": 123
},
{
"coaID": 1110100001,
"description": "Cicilan",
"debit": 0,
"credit": 15000000
}
]
}
### List eligible purchase order asset contracts ### 1. Lihat kontrak aset yang eligible untuk dibuatkan jurnal bulan ini
POST {{host}}/mockup/scheduler/JurnalPiCicilanOrderAset/ListEligiblePurchaseOrderAssetContracts GET {{host}}/index.php/mockup/scheduler/JurnalPiCicilanOrderAset/ListEligiblePurchaseOrderAssetContracts
Content-Type: application/json Content-Type: application/json
{ {
"startDate": "2026-07-01", "startDate": "2026-07-01",
"endDate": "2026-07-31" "endDate": "2026-07-31"
} }
### 2. Simulasi generate jurnal (dryRun=Y) — tidak menyimpan ke database
POST {{host}}/index.php/mockup/scheduler/JurnalPiCicilanOrderAset/GenerateJournalsFromContracts
Content-Type: application/json
{
"date" : "{{date}}",
"dryRun" : "Y"
}
### 3. Generate jurnal untuk semua kontrak eligible bulan ini (sesungguhnya)
POST {{host}}/index.php/mockup/scheduler/JurnalPiCicilanOrderAset/GenerateJournalsFromContracts
Content-Type: application/json
{
"date" : "{{date}}",
"coaBayarID" : {{coaBayarID}},
"dryRun" : "N"
}
### 4. Generate jurnal untuk 1 kontrak tertentu saja (contractID=99)
POST {{host}}/index.php/mockup/scheduler/JurnalPiCicilanOrderAset/GenerateJournalsFromContracts
Content-Type: application/json
{
"date" : "{{date}}",
"coaBayarID" : {{coaBayarID}},
"dryRun" : "N",
"contractID" : 99
}
### 5. Buat jurnal manual (1 jurnal, input detail sendiri)
POST {{host}}/index.php/mockup/scheduler/JurnalPiCicilanOrderAset/CreateJournal
Content-Type: application/json
{
"date" : "{{date}}",
"branchCode" : "BA",
"regionalID" : 8,
"branchCompanyID": 1,
"jurnalTypeID" : 11,
"title" : "Jurnal PI Cicilan Order Aset",
"description" : "Cicilan ke-1 dari 12 | Kontrak Kendaraan Operasional | Periode 2026-07",
"details" : [
{
"coaID" : {{coaHutangID}},
"description": "Hutang cicilan aset - Kendaraan Operasional",
"debit" : 15000000,
"credit" : 0,
"addonCode" : "PIINSTALMENT",
"addonValue" : "42",
"mItemID" : 123
},
{
"coaID" : {{coaBayarID}},
"description": "Pembayaran cicilan aset - Kendaraan Operasional",
"debit" : 0,
"credit" : 15000000,
"addonCode" : "PIINSTALMENT",
"addonValue" : "42",
"mItemID" : 123
}
]
}

View File

@@ -15,15 +15,15 @@
* 3. Insert baris transaksi ke tabel `jurnal_tx` (untuk setiap baris cicilan) * 3. Insert baris transaksi ke tabel `jurnal_tx` (untuk setiap baris cicilan)
* - Referensi ke jurnalID header * - Referensi ke jurnalID header
* - COA (Chart of Account) dan deskripsinya: * - COA (Chart of Account) dan deskripsinya:
* * Sisi hutang cicilan → nilai DEBIT * * Sisi hutang cicilan → nilai DEBIT (diambil dari HutangCoaID di fa_class)
* * Sisi pembayaran cicilan → nilai KREDIT * * Sisi pembayaran cicilan → nilai KREDIT (diambil dari ItemCoaID di fa_class)
* 4. Insert data tambahan ke tabel `jurnal_addon` (jika ada addonValue atau mItemID) * 4. Insert data tambahan ke tabel `jurnal_addon` (jika ada addonValue atau mItemID)
* - Referensi ke jurnalID dan jurnalTxID * - Referensi ke jurnalID dan jurnalTxID
* - Berisi: addonCode (default: "PICICIL"), addonValue, mItemID * - Berisi: addonCode (default: "PIINSTALMENT"), addonValue, mItemID
* *
* Endpoint: * Endpoint:
* POST /mockup/scheduler/JurnalPiCicilanOrderAset/CreateJournal * POST /mockup/scheduler/JurnalPiCicilanOrderAset/CreateJournal
* POST /mockup/scheduler/JurnalPiCicilanOrderAset/CreatePiInstallmentJournal * POST /mockup/scheduler/JurnalPiCicilanOrderAset/GenerateJournalsFromContracts
* GET /mockup/scheduler/JurnalPiCicilanOrderAset/ListEligiblePurchaseOrderAssetContracts * GET /mockup/scheduler/JurnalPiCicilanOrderAset/ListEligiblePurchaseOrderAssetContracts
*/ */
class JurnalPiCicilanOrderAset extends MY_Controller class JurnalPiCicilanOrderAset extends MY_Controller
@@ -74,7 +74,7 @@ class JurnalPiCicilanOrderAset extends MY_Controller
* 10. Jika ada addonValue atau mItemID, INSERT ke `jurnal_addon`: * 10. Jika ada addonValue atau mItemID, INSERT ke `jurnal_addon`:
* - jurnalAddOnJurnalID : referensi ke header jurnal * - jurnalAddOnJurnalID : referensi ke header jurnal
* - jurnalAddOnJurnalTxID: referensi ke baris jurnal_tx * - jurnalAddOnJurnalTxID: referensi ke baris jurnal_tx
* - jurnalAddOnCode : kode addon (default: "PICICIL") * - jurnalAddOnCode : kode addon (default: "PIINSTALMENT")
* - jurnalAddOnValue : nilai tambahan (misal: nomor PI/kontrak) * - jurnalAddOnValue : nilai tambahan (misal: nomor PI/kontrak)
* - jurnalAddOnM_ItemID : ID item aset yang terkait * - jurnalAddOnM_ItemID : ID item aset yang terkait
* *
@@ -90,7 +90,7 @@ class JurnalPiCicilanOrderAset extends MY_Controller
* - credit : Nilai kredit (pembayaran cicilan). * - credit : Nilai kredit (pembayaran cicilan).
* - addonValue : (opsional) Nilai addon, misal nomor PI. * - addonValue : (opsional) Nilai addon, misal nomor PI.
* - mItemID : (opsional) ID item aset. * - mItemID : (opsional) ID item aset.
* - addonCode : (opsional) Kode addon. Default: "PICICIL". * - addonCode : (opsional) Kode addon. Default: "PIINSTALMENT".
* - periodeID : (opsional) ID periode. Default: diambil dari tanggal. * - periodeID : (opsional) ID periode. Default: diambil dari tanggal.
* - regionalID : (opsional) ID regional. Default: diambil dari branchCode. * - regionalID : (opsional) ID regional. Default: diambil dari branchCode.
* - branchCompanyID: (opsional) ID branch company. Default: diambil dari branchCode. * - branchCompanyID: (opsional) ID branch company. Default: diambil dari branchCode.
@@ -158,180 +158,253 @@ class JurnalPiCicilanOrderAset extends MY_Controller
throw new Exception("jurnalTypeID / jurnalTypeCode tidak valid."); throw new Exception("jurnalTypeID / jurnalTypeCode tidak valid.");
} }
$userID = isset($para["userID"]) ? (int) $para["userID"] : 0; $userID = 0;
$jurnalNo = $this->generateJournalNumber();
if ($jurnalNo === false || $jurnalNo == "") {
throw new Exception("Gagal generate nomor jurnal.");
}
// Validasi balance: total debit harus sama dengan total credit $result = $this->insertJournal(
$totalDebit = 0; $date,
$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();
// -----------------------------------------------------------------
// INSERT header jurnal ke tabel `jurnal`
// -----------------------------------------------------------------
$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, $branchCode,
$periodeID,
$jurnalNo,
$title, $title,
$description, $description,
$date, $details,
$periodeID,
$regionalID,
$branchCompanyID,
$jurnalTypeID, $jurnalTypeID,
$userID $userID
]); );
if (!$qryHeader) {
$this->db->trans_rollback(); if ($result === false) {
$this->sys_error_db("Gagal insert jurnal header.");
exit; exit;
} }
$jurnalID = $this->db->insert_id(); $this->sys_ok($result);
$createdAddonCount = 0;
// -----------------------------------------------------------------
// INSERT baris transaksi ke tabel `jurnal_tx` (per baris cicilan)
// -----------------------------------------------------------------
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;
}
// Sisi debit = hutang cicilan (COA hutang)
// Sisi kredit = pembayaran cicilan (COA kas/bank)
$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();
// -----------------------------------------------------------------
// INSERT data tambahan ke tabel `jurnal_addon` (jika ada)
// Diisi jika ada addonValue (misal nomor PI) atau mItemID (ID aset)
// -----------------------------------------------------------------
$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) { } catch (Exception $exc) {
$this->sys_error($exc->getMessage()); $this->sys_error($exc->getMessage());
} }
} }
/** /**
* CreatePiInstallmentJournal — Alias dari CreateJournal. * GenerateJournalsFromContracts — Generate jurnal otomatis dari seluruh kontrak aset eligible.
* *
* Endpoint alternatif dengan nama yang lebih spesifik untuk konteks * Fungsi ini mengintegrasikan data dari getEligiblePurchaseOrderAssetContracts()
* pembuatan jurnal PI cicilan order aset. Memanggil CreateJournal secara langsung. * dengan proses pembuatan jurnal (insertJournal()) secara otomatis.
* *
* @see CreateJournal() * Untuk setiap kontrak yang eligible, dibuat 1 jurnal dengan 2 baris transaksi:
* - Baris DEBIT : COA hutang cicilan (HutangCoaID dari fa_class) — mencatat hutang cicilan
* - Baris KREDIT : COA kas/bank/aset (ItemCoaID dari fa_class) — mencatat pembayaran cicilan
* Addon (jurnal_addon): menyimpan ID kontrak sebagai addonValue dan ItemID sebagai mItemID
*
* Parameter (POST JSON):
* - date : Tanggal jurnal (YYYY-MM-DD). Default: hari ini.
* - userID : ID user yang membuat jurnal.
* - dryRun : "Y" = simulasi tanpa simpan ke DB. Default: "N".
* - contractID : (opsional) Filter hanya 1 kontrak tertentu.
*
* Syarat kontrak dilewati (skipped):
* - branchCode kosong (data cabang belum lengkap)
* - COA hutang (HutangCoaID) tidak ditemukan di fa_class
* - COA bayar/aset (ItemCoaID) tidak ditemukan di fa_class
*
* @return JSON { tanggal, totalDibuat, totalDilewati, daftarDibuat[], daftarDilewati[] }
*/ */
public function CreatePiInstallmentJournal() public function GenerateJournalsFromContracts()
{ {
$this->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.");
}
$userID = 0;
$dryRun = isset($para["dryRun"]) && ($para["dryRun"] === true || $para["dryRun"] == "Y" || $para["dryRun"] == "1");
$filterContractID = isset($para["contractID"]) && (int) $para["contractID"] > 0
? (int) $para["contractID"]
: null;
$monthStart = date("Y-m-01", strtotime($date));
$monthEnd = date("Y-m-t", strtotime($date));
// Ambil semua kontrak yang eligible untuk periode bulan ini
$contracts = $this->getEligiblePurchaseOrderAssetContracts($monthStart, $monthEnd);
$dibuat = [];
$dilewati = [];
foreach ($contracts as $kontrak) {
$contractID = (int) $kontrak["PurchaseOrderAssetContractID"];
// Filter 1 kontrak jika diminta
if ($filterContractID !== null && $contractID !== $filterContractID) {
continue;
}
$branchCode = $kontrak["M_BranchCode"] ?? "";
$regionalID = (int) ($kontrak["M_BranchS_RegionalID"] ?? 0);
$branchCompanyID = (int) ($kontrak["M_BranchCompanyID"] ?? 0);
$coaHutangID = (int) ($kontrak["HutangCoaID"] ?? 0); // COA debit dari fa_class (Fa_ClassHutangCoaID)
$coaBayarID = (int) ($kontrak["ItemCoaID"] ?? 0); // COA kredit dari fa_class (Fa_ClassCoaID)
$jumlah = (float) $kontrak["PurchaseOrderAssetContractInstallmentPayAmount"];
$itemID = (int) ($kontrak["PurchaseOrderSummaryItemID"] ?? 0);
$namaKontrak = $kontrak["PurchaseOrderAssetContractName"] != ""
? $kontrak["PurchaseOrderAssetContractName"]
: "Kontrak ID {$contractID}";
$nomorPO = $kontrak["PurchaseOrderNumber"] ?? "";
$cicilanKe = ((int) $kontrak["PurchaseOrderAssetContractInstallmentPaid"]) + 1;
$totalCicilan = (int) $kontrak["PurchaseOrderAssetContractInstallmentNumber"];
$periode = date("Y-m", strtotime($date));
// Validasi: branchCode harus ada
if ($branchCode == "") {
$dilewati[] = [
"kontrakID" => $contractID,
"alasan" => "Kode cabang (branchCode) tidak ditemukan. Pastikan warehouse PO sudah terhubung ke data cabang."
];
continue;
}
// Validasi: COA hutang harus ada (untuk sisi DEBIT)
if ($coaHutangID <= 0) {
$dilewati[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"alasan" => "COA hutang tidak ditemukan. Pastikan item PO memiliki Fa_Class dengan Fa_ClassHutangCoaID."
];
continue;
}
// Validasi: COA bayar/aset harus ada (untuk sisi KREDIT)
if ($coaBayarID <= 0) {
$dilewati[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"alasan" => "COA bayar/aset tidak ditemukan. Pastikan item PO memiliki Fa_Class dengan Fa_ClassCoaID."
];
continue;
}
// Mode simulasi: catat tanpa simpan ke DB
if ($dryRun) {
$dibuat[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"namaKontrak" => $namaKontrak,
"jumlah" => $jumlah,
"coaHutangID" => $coaHutangID,
"coaBayarID" => $coaBayarID,
"keterangan" => "Mode simulasi (dryRun), data tidak disimpan."
];
continue;
}
// Susun title dan description jurnal
$title = "PI Cicilan Aset - {$nomorPO}";
$description = "Jurnal cicilan ke-{$cicilanKe} dari {$totalCicilan} | {$namaKontrak} | Periode {$periode}";
// Ambil periodeID dari tanggal
$periodeID = $this->getPeriodeIDByDate($date);
if ($periodeID <= 0) {
$dilewati[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"alasan" => "Periode jurnal tidak ditemukan untuk tanggal {$date}."
];
continue;
}
// Ambil jurnalTypeID untuk tipe PAYMENT
$jurnalTypeID = $this->getJurnalTypeID("PAYMENT");
if ($jurnalTypeID <= 0) {
$dilewati[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"alasan" => "Tipe jurnal PAYMENT tidak ditemukan di tabel jurnal_type."
];
continue;
}
// Ambil description detail dari database fa_class dengan fallback jika kosong
$descHutang = isset($kontrak["HutangCoaDesc"]) && $kontrak["HutangCoaDesc"] != ""
? $kontrak["HutangCoaDesc"]
: "Hutang cicilan aset - {$namaKontrak}";
$descBayar = isset($kontrak["ItemCoaDesc"]) && $kontrak["ItemCoaDesc"] != ""
? $kontrak["ItemCoaDesc"]
: "Pembayaran cicilan aset - {$namaKontrak}";
// Susun 2 baris detail jurnal:
// Baris 1 — DEBIT : hutang cicilan aset (COA dari fa_class)
// Baris 2 — KREDIT : pembayaran cicilan (COA kas/bank dari parameter)
$details = [
[
"coaID" => $coaHutangID,
"description" => $descHutang,
"debit" => $jumlah,
"credit" => 0,
"addonValue" => (string) $contractID,
"mItemID" => $itemID,
"addonCode" => "PIINSTALMENT"
],
[
"coaID" => $coaBayarID,
"description" => $descBayar,
"debit" => 0,
"credit" => $jumlah,
"addonValue" => (string) $contractID,
"mItemID" => $itemID,
"addonCode" => "PIINSTALMENT"
]
];
// Panggil insertJournal() untuk simpan ke DB
$result = $this->insertJournal(
$date,
$branchCode,
$title,
$description,
$details,
$periodeID,
$regionalID,
$branchCompanyID,
$jurnalTypeID,
$userID
);
if ($result === false) {
$dilewati[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"alasan" => "Gagal menyimpan jurnal ke database."
];
continue;
}
$dibuat[] = [
"kontrakID" => $contractID,
"branchCode" => $branchCode,
"namaKontrak" => $namaKontrak,
"jurnalID" => $result["jurnalID"],
"jurnalNo" => $result["jurnalNo"],
"jumlah" => $jumlah
];
}
$this->sys_ok([
"tanggal" => $date,
"periodeAwal" => $monthStart,
"periodeAkhir" => $monthEnd,
"totalDibuat" => count($dibuat),
"totalDilewati" => count($dilewati),
"daftarDibuat" => $dibuat,
"daftarDilewati" => $dilewati
]);
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
} }
/** /**
* ListEligiblePurchaseOrderAssetContracts — Menampilkan daftar kontrak aset * ListEligiblePurchaseOrderAssetContracts — Menampilkan daftar kontrak aset
* yang memenuhi syarat untuk dibuatkan jurnal PI cicilan pada periode tertentu. * yang memenuhi syarat untuk dibuatkan jurnal PI cicilan pada periode tertentu.
@@ -390,6 +463,195 @@ class JurnalPiCicilanOrderAset extends MY_Controller
// FUNGSI PRIVATE / HELPER // FUNGSI PRIVATE / HELPER
// ========================================================================= // =========================================================================
/**
* insertJournal — Menyimpan 1 jurnal lengkap ke database (header + tx + addon).
*
* Fungsi internal yang dipanggil oleh CreateJournal() dan GenerateJournalsFromContracts().
* Mengelola transaksi DB secara penuh (begin → commit/rollback).
*
* Alur:
* 1. Validasi balance debit == kredit
* 2. Generate nomor jurnal via fn_numbering('J')
* 3. INSERT header ke tabel `jurnal`
* 4. INSERT baris transaksi ke `jurnal_tx` (per detail)
* 5. INSERT addon ke `jurnal_addon` (jika addonValue atau mItemID ada)
*
* @param string $date Tanggal jurnal (YYYY-MM-DD)
* @param string $branchCode Kode cabang
* @param string $title Judul jurnal
* @param string $description Keterangan jurnal
* @param array $details Baris transaksi [ coaID, description, debit, credit, addonValue?, mItemID?, addonCode? ]
* @param int $periodeID ID periode akuntansi
* @param int $regionalID ID regional
* @param int $branchCompanyID ID branch company
* @param int $jurnalTypeID ID tipe jurnal
* @param int $userID ID user pembuat jurnal
*
* @return array|false Array { jurnalID, jurnalNo, detailCount, addonCount }, atau false jika gagal
*/
private function insertJournal($date, $branchCode, $title, $description, $details, $periodeID, $regionalID, $branchCompanyID, $jurnalTypeID, $userID)
{
// Validasi balance sebelum menyimpan
$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) {
$this->sys_error("Nilai debit/credit tidak boleh negatif pada detail ke-" . ($idx + 1));
return false;
}
$totalDebit += $debit;
$totalCredit += $credit;
}
if (round($totalDebit, 2) !== round($totalCredit, 2)) {
$this->sys_error("Total debit dan credit harus sama. Debit={$totalDebit}, Credit={$totalCredit}");
return false;
}
$jurnalNo = $this->generateJournalNumber();
if ($jurnalNo === false || $jurnalNo == "") {
$this->sys_error("Gagal generate nomor jurnal.");
return false;
}
$this->db->trans_begin();
// -----------------------------------------------------------------
// INSERT header jurnal ke tabel `jurnal`
// -----------------------------------------------------------------
$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.");
return false;
}
$jurnalID = $this->db->insert_id();
$createdAddonCount = 0;
// -----------------------------------------------------------------
// INSERT baris transaksi ke tabel `jurnal_tx` (per baris cicilan)
// -----------------------------------------------------------------
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));
return false;
}
// Sisi debit = hutang cicilan (COA hutang aset dari fa_class)
// Sisi kredit = pembayaran cicilan (COA kas/bank)
$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));
return false;
}
$jurnalTxID = $this->db->insert_id();
// -----------------------------------------------------------------
// INSERT addon ke `jurnal_addon` (jika addonValue atau mItemID ada)
// addonValue = ID kontrak aset (untuk tracing)
// mItemID = ID item aset dari purchase_order_summary
// -----------------------------------------------------------------
$addonValue = isset($detail["addonValue"]) ? trim($detail["addonValue"]) : "";
$mItemID = isset($detail["mItemID"]) ? (int) $detail["mItemID"] : 0;
$addonCode = isset($detail["addonCode"]) && $detail["addonCode"] != "" ? trim($detail["addonCode"]) : "PIINSTALMENT";
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));
return false;
}
$createdAddonCount++;
}
}
if ($this->db->trans_status() === false) {
$this->db->trans_rollback();
$this->sys_error_db("Transaksi jurnal gagal.");
return false;
}
$this->db->trans_commit();
return [
"jurnalID" => $jurnalID,
"jurnalNo" => $jurnalNo,
"detailCount" => count($details),
"addonCount" => $createdAddonCount
];
}
/** /**
* generateJournalNumber — Generate nomor jurnal otomatis. * generateJournalNumber — Generate nomor jurnal otomatis.
* *
@@ -553,9 +815,12 @@ class JurnalPiCicilanOrderAset extends MY_Controller
mi.M_ItemFa_ClassID, mi.M_ItemFa_ClassID,
fc.Fa_ClassID AS ItemFaClassID, fc.Fa_ClassID AS ItemFaClassID,
fc.Fa_ClassName AS ItemFaClassName, fc.Fa_ClassName AS ItemFaClassName,
fc.Fa_ClassCoaID AS ItemCoaID, coa_bayar.coaID AS ItemCoaID,
fc.Fa_ClassCoaAccountNo AS ItemCoaAccountNo, coa_bayar.coaAccountNo AS ItemCoaAccountNo,
fc.Fa_ClassCoaDesc AS ItemCoaDesc, coa_bayar.coaDescription AS ItemCoaDesc,
coa_hutang.coaID AS HutangCoaID,
coa_hutang.coaAccountNo AS HutangCoaAccountNo,
coa_hutang.coaDescription AS HutangCoaDesc,
m_branch.M_BranchCode, m_branch.M_BranchCode,
m_branch.M_BranchName, m_branch.M_BranchName,
m_branch.M_BranchS_RegionalID, m_branch.M_BranchS_RegionalID,
@@ -625,6 +890,14 @@ class JurnalPiCicilanOrderAset extends MY_Controller
ON fc.Fa_ClassID = mi.M_ItemFa_ClassID ON fc.Fa_ClassID = mi.M_ItemFa_ClassID
AND fc.Fa_ClassIsActive = 'Y' AND fc.Fa_ClassIsActive = 'Y'
-- Join langsung ke tabel coa untuk memastikan kevalidan akun dan deskripsi
LEFT JOIN coa coa_hutang
ON coa_hutang.coaID = fc.Fa_ClassHutangCoaID
AND coa_hutang.coaIsActive = 'Y'
LEFT JOIN coa coa_bayar
ON coa_bayar.coaID = fc.Fa_ClassCoaID
AND coa_bayar.coaIsActive = 'Y'
WHERE c.PurchaseOrderAssetContractIsActive = 'Y' WHERE c.PurchaseOrderAssetContractIsActive = 'Y'
AND c.PurchaseOrderAssetContractStatus = 'belum lunas' AND c.PurchaseOrderAssetContractStatus = 'belum lunas'
-- Hanya kontrak yang ada nilai cicilannya -- Hanya kontrak yang ada nilai cicilannya