feature: payment instruction & cashier case cicilan dan downpayment

This commit is contained in:
2026-07-21 15:21:24 +07:00
parent 64f434cd78
commit e1feaa68a8
9 changed files with 1930 additions and 1388 deletions

View File

@@ -1127,11 +1127,14 @@ class ReceiveItemPOAsset extends MY_Controller
PurchaseOrderSummaryQty as QtyAllPO, PurchaseOrderSummaryQty as QtyAllPO,
PurchaseOrderSummaryTotal as TotalAllPO, PurchaseOrderSummaryTotal as TotalAllPO,
PurchaseOrderDiscountPercent AS DiscPOPercent, PurchaseOrderDiscountPercent AS DiscPOPercent,
PurchaseOrderDiscountAmount AS DiscPORupiah PurchaseOrderDiscountAmount AS DiscPORupiah,
M_ItemItem_CategoryID
FROM receive_order_po_detail FROM receive_order_po_detail
JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID
JOIN purchase_order_summary ON ReceiveOrderPoDetailPurchaseOrderSummaryID = PurchaseOrderSummaryID JOIN purchase_order_summary ON ReceiveOrderPoDetailPurchaseOrderSummaryID = PurchaseOrderSummaryID
AND PurchaseOrderSummaryIsActive = 'Y' AND PurchaseOrderSummaryIsActive = 'Y'
JOIN m_item ON M_ItemID = ReceiveOrderPoItemID
AND M_ItemIsActive = 'Y'
WHERE ReceiveOrderPoDetailReceiveOrderPoID = ? WHERE ReceiveOrderPoDetailReceiveOrderPoID = ?
AND ReceiveOrderPoDetailIsActive='Y' AND ReceiveOrderPoDetailIsActive='Y'
GROUP BY ReceiveOrderPoDetailID"; GROUP BY ReceiveOrderPoDetailID";
@@ -1354,7 +1357,7 @@ class ReceiveItemPOAsset extends MY_Controller
throw new Exception("[Error] failed get account diskon", 1); throw new Exception("[Error] failed get account diskon", 1);
} }
$coa_diskon = $que_coadiskon->row_array(); $coa_diskon = $que_coadiskon->row_array();
foreach ($input_diskon as $key => $val) { foreach ($input_diskon as $idx => $val) {
if (doubleval($val) <= 0.00) { if (doubleval($val) <= 0.00) {
continue; continue;
} }
@@ -1397,10 +1400,199 @@ class ReceiveItemPOAsset extends MY_Controller
$this->db->trans_rollback(); $this->db->trans_rollback();
throw new Exception("[Error] save diskon prorate", 1); throw new Exception("[Error] save diskon prorate", 1);
} }
/* handling table stock */
$sql_cekstock = "SELECT *
FROM stock s
JOIN m_item i ON s.StockItemID = i.M_ItemID
WHERE s.StockItemID = ?
AND s.StockItemUnitID = ?
AND s.StockWarehouseID = ?
AND i.M_ItemItem_CategoryID = ?";
$que_cekstock = $this->db->query($sql_cekstock, [
$elem['ReceiveOrderPoItemID'],
$elem['ReceiveOrderPoItemUnitID'],
$data_ro['ReceiveOrderPoWarehouseID'],
$elem['M_ItemItem_CategoryID']
]);
if (!$que_cekstock) {
$this->db->trans_rollback();
throw new Exception("[Error] failed to check item exist in stock", 1);
}
$stock_exist = $que_cekstock->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)) {
$stock_qty_end = $stock_qty_ori + intval($elem['ReceiveOrderPoDetailQty']);
$all_price = ($stock_qty_ori * $stock_price) + (intval($elem['ReceiveOrderPoDetailQty']) * doubleval($elem['ReceiveOrderPoDetailPrice']));
$avg_price = round($all_price / $stock_qty_end, 2);
$sql_update_stock = "UPDATE stock SET
StockItemPrice = ?,
StockQty = ?,
StockLastUpdated = NOW(),
StockUserID = ?
WHERE StockID = ?";
$que_update_stock = $this->db->query($sql_update_stock, [
$avg_price,
$stock_qty_end,
$user['M_UserID'],
$stock_ID
]);
if (!$que_update_stock) {
$this->db->trans_rollback();
throw new Exception("[Error] failed to update stock", 1);
}
} else {
$sql_insert_stock = "INSERT INTO stock (
StockWarehouseAlmariID,
StockWarehouseRackID,
StockWarehouseID,
StockStockNumber,
StockItemID,
StockItemUnitID,
StockItemPrice,
StockQty,
StockLastUpdated,
StockUserID
) VALUES (0,0,?,fn_numbering('SN'),?,?,?,?,NOW(),?)";
$que_insert_stock = $this->db->query($sql_insert_stock, [
$data_ro['ReceiveOrderPoWarehouseID'],
$elem['ReceiveOrderPoItemID'],
$elem['ReceiveOrderPoItemUnitID'],
$elem['ReceiveOrderPoDetailPrice'],
$elem['ReceiveOrderPoDetailQty'],
$user['M_UserID']
]);
if (!$que_insert_stock) {
$this->db->trans_rollback();
throw new Exception("[Error] failed to insert stock", 1);
}
$stock_ID = $this->db->insert_id();
$stock_qty_ori = 0;
$stock_qty_end = intval($elem['ReceiveOrderPoDetailQty']);
}
/* insert stock card */
$sql_stock_card = "INSERT INTO stockcard (
StockCardWarehouseID,
StockCardDatetime,
StockCardItemID,
StockCardItemUnitID,
StockCardReffID,
StockCardStatus,
StockCardBefore,
StockCardIn,
StockCardOut,
StockCardAfter,
StockCardUserID
) VALUES (?, NOW(), ?, ?, ?, 'PRV', ?, ?, 0, ?, ?)";
$que_stock_card = $this->db->query($sql_stock_card, [
$data_ro['ReceiveOrderPoWarehouseID'],
$elem['ReceiveOrderPoItemID'],
$elem['ReceiveOrderPoItemUnitID'],
$stock_ID,
$stock_qty_ori,
$elem['ReceiveOrderPoDetailQty'],
$stock_qty_end,
$user['M_UserID']
]);
if (!$que_stock_card) {
$this->db->trans_rollback();
throw new Exception("[Error] failed to insert stock card", 1);
}
/* 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();
throw new Exception("[Error] failed to cek latest stock", 1);
}
$stock_latest = $que_stock->row_array();
$sql_stock_log = "INSERT INTO stocklog (
StockLogWarehouseID,
StockLogDateTime,
StockLogItemID,
StockLogItemUnitID,
StockLogStockNumber,
StockLogReffID,
StockLogStatus,
StockLogQty,
StockLogUserID
) VALUES (?, NOW(), ?, ?, ?, ?, 'PRV', ?, ?)";
$que_stock_log = $this->db->query($sql_stock_log, [
$data_ro['WarehouseID'],
$elem['ReceiveOrderPoItemID'],
$elem['ReceiveOrderPoItemUnitID'],
$stock_latest['StockStockNumber'],
$stock_ID,
$elem['ReceiveOrderPoDetailQty'],
$user['M_UserID']
]);
if (!$que_stock_log) {
$this->db->trans_rollback();
throw new Exception("[Error] failed insert stock log", 1);
}
}
/* handle shipping */
$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, [$jurnalID, $ship_cost, $user['M_UserID']]);
if (!$que_tx_ship) {
$this->db->trans_rollback();
throw new Exception("[Error] failed insert jurnal shipping", 1);
}
$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();
throw new Exception("[Error] failed insert table shipping", 1);
}
} }
# INSERT jurnal tx for GRNI # # INSERT jurnal tx for GRNI #
$total_grni = round($total_debet - ($total_diskonitem + $total_diskonpo), 2); $total_grni = round($total_debet - ($total_diskonitem + $total_diskonpo), 2) + $ship_cost;
$sql_grni = "INSERT INTO jurnal_tx ( $sql_grni = "INSERT INTO jurnal_tx (
jurnalTxJurnalID, jurnalTxJurnalID,
jurnalTxCoaID, jurnalTxCoaID,
@@ -1533,10 +1725,11 @@ class ReceiveItemPOAsset extends MY_Controller
SupplierInvoiceGrandTotal, SupplierInvoiceGrandTotal,
SupplierInvoiceUnpaid, SupplierInvoiceUnpaid,
SupplierInvoiceNote, SupplierInvoiceNote,
SupplierInvoiceIsInstallment,
SupplierInvoiceReceiveDate, SupplierInvoiceReceiveDate,
SupplierInvoiceReceivedBy, SupplierInvoiceReceivedBy,
SupplierInvoiceCreatedUserID SupplierInvoiceCreatedUserID
) VALUES (?,?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?,?)"; ) VALUES (?,?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,'Y',NOW(),?,?)";
$que_insert_pi = $this->db->query($sql_insert_pi, [ $que_insert_pi = $this->db->query($sql_insert_pi, [
$num_pi, $num_pi,
$data_ro['ReceiveOrderPoID'], $data_ro['ReceiveOrderPoID'],

View File

@@ -1705,22 +1705,38 @@ class ReceiveItemPoInventaris extends MY_Controller
exit; exit;
} }
$sql_coa_inv = "SELECT $sql_invcoa = "SELECT
CoaMapInventarisCoaID, COALESCE(
CoaMapInventarisCoaNo, i.M_InventarisItemCoaMappingID,
CoaMapInventarisCoaDesc g.M_InventarisCoaMappingID
FROM coa_map_inventaris ) AS MappingID,
WHERE CoaMapInventarisM_InventarisGolID = ? COALESCE(
AND CoaMapInventarisIsActive = 'Y'"; i.M_InventarisItemCoaMappingCoaInventarisID,
$que_coa_inv = $this->db->query($sql_coa_inv, [$item_cek['M_ItemM_InventarisGolID']]); g.M_InventarisCoaMappingCoaInventarisID
if (!$que_coa_inv) { ) AS CoaInventarisID,
c.coaDescription AS CoaInventarisDesc
FROM (SELECT 1) AS inventory
LEFT JOIN m_inventaris_item_coa_mapping i
ON i.M_InventarisItemCoaMappingM_ItemID = ?
AND i.M_InventarisItemCoaMappingIsActive = 'Y'
LEFT JOIN m_inventaris_coa_mapping g
ON g.M_InventarisCoaMappingM_InventarisGolID = ?
AND g.M_InventarisCoaMappingIsActive = 'Y'
LEFT JOIN coa c
ON c.coaID = COALESCE(i.M_InventarisItemCoaMappingCoaInventarisID, g.M_InventarisCoaMappingCoaInventarisID)
AND c.coaIsActive = 'Y'";
$que_invcoa = $this->db->query($sql_invcoa, [
$item_cek['M_ItemID'],
$item_cek['M_ItemM_InventarisGolID']
]);
if (!$que_invcoa) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] failed to get coa biaya item inventaris"); $this->sys_error_db("[Error] failed to get coa biaya item inventaris");
exit; exit;
} }
$iteminv_coa = $que_coa_inv->row_array(); $iteminv_coa = $que_invcoa->row_array();
if (empty($iteminv_coa['CoaMapInventarisCoaID'])) { if (empty($iteminv_coa['CoaInventarisID'])) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] coa gol inventaris not found"); $this->sys_error_db("[Error] coa gol inventaris not found");
exit; exit;
@@ -1740,8 +1756,8 @@ class ReceiveItemPoInventaris extends MY_Controller
) VALUES (?,?,?,?,?,?)"; ) VALUES (?,?,?,?,?,?)";
$que_tx_jurnal = $this->db->query($sql_tx_jurnal, [ $que_tx_jurnal = $this->db->query($sql_tx_jurnal, [
$jurnal_ID, $jurnal_ID,
$iteminv_coa['CoaMapInventarisCoaID'], $iteminv_coa['CoaInventarisID'],
$iteminv_coa['CoaMapInventarisCoaDesc'], $iteminv_coa['CoaInventarisDesc'],
$debet, $debet,
0, 0,
$user['M_UserID'] $user['M_UserID']
@@ -1754,7 +1770,7 @@ class ReceiveItemPoInventaris extends MY_Controller
} }
$juranl_tx_ID = $this->db->insert_id(); $juranl_tx_ID = $this->db->insert_id();
$each_desc[] = "- Add detail Jurnal debet {$item_cek['M_ItemDesc']} $each_desc[] = "- Add detail Jurnal debet {$item_cek['M_ItemDesc']}
{$iteminv_coa['CoaMapInventarisCoaDesc']} sejumlah {$this->formatRupiah($debet)}"; {$iteminv_coa['CoaInventarisDesc']} sejumlah {$this->formatRupiah($debet)}";
# INSERT JURNAL ADDON # # INSERT JURNAL ADDON #
$sql_addon_jurnal = "INSERT INTO jurnal_addon ( $sql_addon_jurnal = "INSERT INTO jurnal_addon (
@@ -1944,20 +1960,6 @@ class ReceiveItemPoInventaris extends MY_Controller
$this->sys_error_db("[Error] insert update stock"); $this->sys_error_db("[Error] insert update stock");
exit; exit;
} }
// insert stockid barcode barang
// $sql_update_barcode = "UPDATE t_barcode_barang SET
// T_BarcodeBarangStockID = ?
// WHERE T_BarcodeBarangReceiveOrderPoDetailID = ?";
// $qry_update_barcode = $this->db->query($sql_update_barcode, [
// $stock_ID,
// $detail['ReceiveOrderPoDetailID']
// ]);
// if (!$qry_update_barcode) {
// $this->db->trans_rollback();
// $this->sys_error_db("[Error] update barcode stockID", $this->db);
// exit;
// }
} else { } else {
$sql_insert_stock = "INSERT INTO stock ( $sql_insert_stock = "INSERT INTO stock (
StockWarehouseAlmariID, StockWarehouseAlmariID,
@@ -1985,23 +1987,9 @@ class ReceiveItemPoInventaris extends MY_Controller
exit; exit;
} }
$stockID = $this->db->insert_id(); $stock_ID = $this->db->insert_id();
$stock_qty_ori = 0; $stock_qty_ori = 0;
$stock_qty_end = intval($batch['qty']); $stock_qty_end = intval($batch['qty']);
// insert stockid barcode barang
// $sql_update_barcode = "UPDATE t_barcode_barang SET
// T_BarcodeBarangStockID = ?
// WHERE T_BarcodeBarangReceiveOrderPoDetailID = ?";
// $qry_update_barcode = $this->db->query($sql_update_barcode, [
// $stockID,
// $detail['ReceiveOrderPoDetailID']
// ]);
// if (!$qry_update_barcode) {
// $this->db->trans_rollback();
// $this->sys_error_db("[Error] update barcode stockID", $this->db);
// exit;
// }
} }
// insert stock card // insert stock card

View File

@@ -1,78 +1,55 @@
# ============================================================ # ============================================================
# FILE : PurchaseInvoiceInstallment.http # FILE : PurchaseInvoiceInstallment.http
# MODUL : Scheduler — Generate Purchase Invoice Cicilan Aset # MODUL : Scheduler — Generate Supplier Installment Cicilan Aset
# ============================================================ # ============================================================
# #
# DESKRIPSI # DESKRIPSI
# --------- # ---------
# File ini berisi kumpulan HTTP request untuk menguji endpoint # File ini berisi HTTP request untuk menguji endpoint scheduler
# scheduler yang secara otomatis membuat Purchase Invoice (PI) # yang secara otomatis membuat baris cicilan ke tabel
# cicilan bulanan berdasarkan kontrak aset aktif. # supplier_installment setiap bulan.
# #
# CARA PAKAI # CARA PAKAI
# ---------- # ----------
# 1. Gunakan ekstensi "REST Client" di VS Code (humao.rest-client) # 1. Gunakan ekstensi "REST Client" di VS Code (humao.rest-client)
# atau tool sejenis (IntelliJ HTTP Client, Postman, dll). # atau tool sejenis (IntelliJ HTTP Client, Postman, dll).
# 2. Ganti nilai @host sesuai environment (local / staging / prod). # 2. Ganti nilai @host sesuai environment.
# 3. Klik "Send Request" di atas tiap blok ### untuk menjalankan # 3. Klik "Send Request" di atas tiap blok ###
# request tersebut secara individual.
# 4. Jalankan request secara BERURUTAN sesuai urutan yang disarankan
# (lihat keterangan tiap endpoint di bawah).
# #
# ALUR PENGGUNAAN YANG DISARANKAN # ALUR
# -------------------------------- # ----
# [1] ListEligibleContracts → Lihat kontrak mana saja yang akan diproses # [1] GenerateMonthlyInvoices → Jalankan proses generate installment
# [2] GenerateMonthlyInvoices → Jalankan proses generate PI cicilan sekaligus
# [3] InsertSupplierInvoice → (Opsional) Insert manual 1 PI jika diperlukan
# #
# CATATAN # CATATAN
# ------- # -------
# - startDate & endDate menentukan periode bulan yang diproses. # - Default periode: bulan berjalan (tanggal 1 s/d akhir bulan)
# Biasanya diisi dengan tanggal awal dan akhir bulan berjalan. # - Aman dijalankan ulang — dedup berdasarkan PO + bulan mencegah
# - Scheduler ini AMAN dijalankan ulang (idempotent). Kontrak yang # duplikasi
# sudah punya PI di bulan yang sama akan dilewati otomatis. # - Tidak membuat jurnal — jurnal dibuat saat pembayaran cashier
# - userID pada InsertSupplierInvoice akan diabaikan; sistem # - Tidak update kontrak — InstallmentPaid diupdate saat pembayaran
# mengambil userID dari PurchaseOrderAssetContractCreatedUserID.
# ============================================================ # ============================================================
@host = https://accone.aplikasi.web.id/one-api @host = https://accone.aplikasi.web.id/one-api
# ============================================================ # ============================================================
# [1] LIST KONTRAK ELIGIBLE # [1] GENERATE INSTALLMENT BULANAN (MAIN ENDPOINT)
# ============================================================ # ============================================================
# Gunakan endpoint ini SEBELUM generate untuk melihat preview # Satu request ini akan:
# kontrak mana saja yang akan dibuatkan PI pada periode ini. # Step 1: Validasi kontrak (aktif, belum lunas, dalam rentang)
# Step 2: Validasi PO (Approved, Active)
# Step 3: Validasi RO (Confirmed, Active)
# Step 4: Validasi parent invoice (IsInstallment = 'Y')
# Step 5: Cek dedup (belum ada installment bulan ini)
# Step 6: INSERT ke supplier_installment (Status = 'Pending')
# #
# Response berisi daftar kontrak beserta detail cicilan, # Response:
# status GRNI, dan apakah PI bulan ini sudah ada atau belum. # - totalEligible : jumlah kontrak lolos validasi
# - totalInserted : jumlah baris berhasil diinsert
# - created[] : detail tiap installment yang dibuat
# ============================================================ # ============================================================
### [1] List kontrak cicilan eligible yang belum diproses bulan ini ### [1] Generate installment untuk periode tertentu
POST {{host}}/mockup/scheduler/PurchaseInvoiceInstallment/ListEligibleContracts
Content-Type: application/json
{
"startDate": "2026-07-01",
"endDate": "2026-07-31"
}
# ============================================================
# [2] GENERATE PI CICILAN BULANAN (MAIN ENDPOINT)
# ============================================================
# Endpoint utama scheduler. Satu request ini akan:
# 1. Mengambil semua kontrak aset aktif yang eligible.
# 2. Mengecek duplikasi — melewati kontrak yang sudah ada PI-nya.
# 3. Membuat nomor PI via fn_penomoran() berdasarkan user
# pembuat kontrak (PurchaseOrderAssetContractCreatedUserID).
# 4. Insert supplier_invoice + supplier_invoice_detail.
#
# Response berisi:
# - berhasil : daftar PI yang berhasil dibuat (dengan nomorPI, supplierInvoiceID, dll)
# - dilewati : daftar kontrak yang dilewati beserta alasannya
# ============================================================
### [2] Generate PI cicilan untuk semua kontrak pada periode ini
POST {{host}}/mockup/scheduler/PurchaseInvoiceInstallment/GenerateMonthlyInvoices POST {{host}}/mockup/scheduler/PurchaseInvoiceInstallment/GenerateMonthlyInvoices
Content-Type: application/json Content-Type: application/json
@@ -81,47 +58,8 @@ Content-Type: application/json
"endDate": "2026-07-31" "endDate": "2026-07-31"
} }
# ============================================================ ### [1a] Generate installment bulan berjalan (tanpa parameter)
# [3] INSERT MANUAL SATU PI (INTERNAL / DEBUG) POST {{host}}/mockup/scheduler/PurchaseInvoiceInstallment/GenerateMonthlyInvoices
# ============================================================
# Endpoint ini dipanggil INTERNAL oleh GenerateMonthlyInvoices
# via cURL. Gunakan hanya untuk debugging atau insert manual
# satu PI tertentu tanpa menjalankan proses batch.
#
# FIELD WAJIB:
# - nomorPI : Nomor PI (format dari fn_penomoran)
# - tanggalPI : Tanggal invoice (YYYY-MM-DD)
# - tanggalJatuhTempo : Tanggal jatuh tempo (YYYY-MM-DD)
# - jumlahCicilan : Nominal cicilan (angka, dalam Rupiah)
# - catatan : Catatan singkat pada header invoice
# - deskripsi : Deskripsi baris detail invoice
# - userID : ID user pembuat (ambil dari kontrak)
# - purchaseOrderAssetContractID : ID kontrak aset
# - purchaseOrderID : ID Purchase Order
# - receiveOrderPoID : ID Receive Order PO (GRNI)
# - supplierID : ID Supplier
# - purchaseOrderSummaryID : ID summary item PO
# - purchaseOrderSummaryItemID : ID item (barang/aset)
# - purchaseOrderSummaryItemUnitID: ID satuan item
# ============================================================
### [3] Insert satu supplier_invoice cicilan secara manual (debug)
POST {{host}}/mockup/scheduler/PurchaseInvoiceInstallmentInsert/InsertSupplierInvoice
Content-Type: application/json Content-Type: application/json
{ {}
"nomorPI": "PI-2026-07-0001",
"tanggalPI": "2026-07-31",
"tanggalJatuhTempo": "2026-08-14",
"jumlahCicilan": 15000000,
"catatan": "PI Cicilan Otomatis — Kontrak Aset ID 123 periode 2026-07",
"deskripsi": "Cicilan Kontrak Aset — Kendaraan Operasional periode 2026-07",
"userID": 1,
"purchaseOrderAssetContractID": 123,
"purchaseOrderID": 456,
"receiveOrderPoID": 789,
"supplierID": 12,
"purchaseOrderSummaryID": 111,
"purchaseOrderSummaryItemID": 222,
"purchaseOrderSummaryItemUnitID": 333
}

View File

@@ -3,20 +3,19 @@
/** /**
* PurchaseInvoiceInstallment * PurchaseInvoiceInstallment
* *
* Digunakan oleh CRON untuk membuat Purchase Invoice (PI) cicilan aset * CRON scheduler untuk membuat baris cicilan otomatis ke tabel supplier_installment
* secara otomatis setiap bulan berdasarkan kontrak yang aktif. * setiap bulan berdasarkan kontrak aset yang aktif dan belum lunas.
* *
* Endpoint utama: * Endpoint:
* POST /scheduler/PurchaseInvoiceInstallment/GenerateMonthlyInvoices * POST /scheduler/PurchaseInvoiceInstallment/GenerateMonthlyInvoices
* POST /scheduler/PurchaseInvoiceInstallment/CurlGenerateMonthlyInvoices
*/ */
class PurchaseInvoiceInstallment extends MY_Controller class PurchaseInvoiceInstallment extends MY_Controller
{ {
var $db; var $db;
var $baseUrl = "https://accone.aplikasi.web.id/one-api/";
public function index() public function index()
{ {
echo "Purchase Invoice Installment — Auto Generate PI Cicilan Aset"; echo "Purchase Invoice Installment — Auto Generate Installment Cicilan Aset";
} }
public function __construct() public function __construct()
@@ -25,327 +24,121 @@ class PurchaseInvoiceInstallment extends MY_Controller
} }
/** /**
* GenerateMonthlyInvoices * GenerateMonthlyInvoices — Step 6
* *
* Membuat Purchase Invoice cicilan untuk semua kontrak aset yang aktif * Creates supplier_installment rows for all eligible contracts this month.
* dan belum lunas pada bulan yang ditentukan. * No journal is created here — that happens at cashier payment time.
* Contract InstallmentPaid is NOT updated here — that happens at payment time.
* *
* Parameter (POST JSON): * Called by CRON daily at 1:00 AM.
* - startDate : Tanggal awal periode (format YYYY-MM-DD). Default: awal bulan ini. * Defaults to current month if no startDate/endDate provided.
* - endDate : Tanggal akhir periode (format YYYY-MM-DD). Default: akhir bulan ini.
* userID diambil otomatis dari token (sys_user["M_UserID"]).
* Jika CRON berjalan tanpa token, fallback ke user ID 0.
*
* Syarat kontrak diproses:
* 1. Kontrak aktif dan berstatus "belum lunas"
* 2. Nilai cicilan > 0
* 3. Jumlah cicilan terbayar < total cicilan
* 4. Tanggal kontrak masuk dalam bulan yang dituju
* 5. RO sudah confirmed
* 6. PO sudah berstatus Approved
*/ */
public function GenerateMonthlyInvoices() public function GenerateMonthlyInvoices()
{ {
try { try {
$para = $this->sys_input; $para = $this->sys_input;
// Ambil userID dari token JWT (sys_user). Fallback ke 0 jika CRON berjalan tanpa token.
$userID = !empty($this->sys_user["M_UserID"]) ? (int) $this->sys_user["M_UserID"] : 0;
$user = $this->getCronUser($userID);
$startDate = isset($para["startDate"]) && $para["startDate"] != "" $userID = !empty($this->sys_user["M_UserID"])
? (int) $this->sys_user["M_UserID"]
: 0;
$startDate = !empty($para["startDate"])
? $para["startDate"] ? $para["startDate"]
: (isset($para["date"]) && $para["date"] != "" ? date("Y-m-01", strtotime($para["date"])) : date("Y-m-01")); : date("Y-m-01");
$endDate = isset($para["endDate"]) && $para["endDate"] != ""
$endDate = !empty($para["endDate"])
? $para["endDate"] ? $para["endDate"]
: (isset($para["date"]) && $para["date"] != "" ? date("Y-m-t", strtotime($para["date"])) : date("Y-m-t")); : date("Y-m-t");
// Validasi format tanggal
if (!$this->isValidDate($startDate) || !$this->isValidDate($endDate)) { if (!$this->isValidDate($startDate) || !$this->isValidDate($endDate)) {
throw new Exception("Format tanggal tidak valid. Gunakan format YYYY-MM-DD, contoh: 2025-07-01"); throw new Exception("Format tanggal tidak valid.");
} }
if (strtotime($startDate) > strtotime($endDate)) { if (strtotime($startDate) > strtotime($endDate)) {
throw new Exception("startDate tidak boleh lebih besar dari endDate"); throw new Exception("startDate > endDate.");
} }
// Tentukan rentang bulan berdasarkan tanggal acuan // ── Steps 15: Get eligible contracts ────────────────────
$monthStart = $startDate; $eligible = $this->getEligibleInstallments($startDate, $endDate);
$monthEnd = $endDate;
$dayOfMonth = (int) date("d", strtotime($endDate));
// ------------------------------------------------------------------------- $created = [];
// Ambil semua kontrak cicilan yang memenuhi syarat pada bulan ini $inserted = 0;
// -------------------------------------------------------------------------
$sqlKontrak = "SELECT
c.PurchaseOrderAssetContractID,
c.PurchaseOrderAssetContractPurchaseOrderID,
ro.ReceiveOrderPoID AS PurchaseOrderAssetContractReceiveOrderPoID,
c.PurchaseOrderAssetContractName,
c.PurchaseOrderAssetContractStartDate,
c.PurchaseOrderAssetContractEndDate,
c.PurchaseOrderAssetContractInstallmentNumber,
c.PurchaseOrderAssetContractInstallmentPaid,
c.PurchaseOrderAssetContractInstallmentDate,
c.PurchaseOrderAssetContractInstallmentPayAmount,
c.PurchaseOrderAssetContractCreatedUserID,
po.PurchaseOrderID,
po.PurchaseOrderNumber,
po.PurchaseOrderSupplierID,
po.PurchaseOrderPaymentTerm,
po.PurchaseOrderWarehouseType,
po.PurchaseOrderWarehouseID,
ro.ReceiveOrderPoConfirmed,
ps.PurchaseOrderSummaryID,
ps.PurchaseOrderSummaryItemID,
ps.PurchaseOrderSummaryItemUnitID
FROM purchase_order_asset_contract c
-- Pastikan PO sudah Approved dan aktif
JOIN purchase_order po
ON po.PurchaseOrderID = c.PurchaseOrderAssetContractPurchaseOrderID
AND po.PurchaseOrderIsActive = 'Y'
AND po.PurchaseOrderStatus = 'Approved'
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
-- Ambil 1 item PO pertama sebagai referensi baris detail PI
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'
-- Hanya kontrak yang ada nilai cicilannya
AND IFNULL(c.PurchaseOrderAssetContractInstallmentPayAmount, 0) > 0
-- Hanya kontrak yang belum selesai seluruh cicilannya
AND IFNULL(c.PurchaseOrderAssetContractInstallmentPaid, 0) < IFNULL(c.PurchaseOrderAssetContractInstallmentNumber, 0)
-- Kontrak sudah mulai sebelum atau pada akhir bulan ini
AND DATE(c.PurchaseOrderAssetContractStartDate) <= DATE(?)
-- Kontrak belum berakhir (atau tidak ada tanggal akhir)
AND (
c.PurchaseOrderAssetContractEndDate IS NULL
OR DATE(c.PurchaseOrderAssetContractEndDate) >= DATE(?)
)
-- Tanggal jatuh tempo cicilan sudah melewati atau sama dengan hari ini
AND IFNULL(c.PurchaseOrderAssetContractInstallmentDate, 1) <= ?";
$params = [$monthEnd, $monthStart, $dayOfMonth];
$qryKontrak = $this->db->query($sqlKontrak, $params);
if (!$qryKontrak) {
$this->sys_error_db("Gagal mengambil daftar kontrak cicilan dari database.");
exit;
}
$berhasil = []; // PI yang berhasil dibuat
$dilewati = []; // PI yang dilewati beserta alasannya
// -------------------------------------------------------------------------
// Proses tiap kontrak satu per satu
// -------------------------------------------------------------------------
$kontraks = $qryKontrak->result_array();
foreach ($kontraks as $kontrak) {
// Lewati jika item/satuan PO tidak ditemukan (tidak bisa buat baris detail)
if (empty($kontrak["PurchaseOrderSummaryItemID"]) || empty($kontrak["PurchaseOrderSummaryItemUnitID"])) {
$dilewati[] = [
"kontrakID" => $kontrak["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $kontrak["PurchaseOrderID"],
"alasan" => "Item atau satuan pada Purchase Order tidak ditemukan, tidak bisa membuat baris detail PI."
];
continue;
}
// -------------------------------------------------------
// Cek apakah PI cicilan bulan ini sudah pernah dibuat
// -------------------------------------------------------
$sqlCekDuplikat = "SELECT SupplierInvoiceID, SupplierInvoiceNumber
FROM supplier_invoice
WHERE SupplierInvoiceIsActive = 'Y'
AND SupplierInvoiceDate >= DATE(?)
AND SupplierInvoiceDate <= DATE(?)
AND SupplierInvoiceStatus = 'Draft'
AND (
SupplierInvoiceReceiveOrderPoID = ?
OR EXISTS (
SELECT 1
FROM supplier_invoice_detail sid
WHERE sid.SupplierInvoiceDetailSupplierInvoiceID = SupplierInvoiceID
AND sid.SupplierInvoiceDetailReceiveOrderPoID = ?
AND sid.SupplierInvoiceDetailIsActive = 'Y'
)
)
LIMIT 1";
$qryCekDuplikat = $this->db->query($sqlCekDuplikat, [
$monthStart,
$monthEnd,
$kontrak["PurchaseOrderAssetContractReceiveOrderPoID"],
$kontrak["PurchaseOrderAssetContractReceiveOrderPoID"]
]);
if (!$qryCekDuplikat) {
$this->sys_error_db("Gagal memeriksa duplikasi PI cicilan untuk kontrak ID " . $kontrak["PurchaseOrderAssetContractID"] . ".");
exit;
}
if ($qryCekDuplikat->num_rows() > 0) {
$piExisting = $qryCekDuplikat->row_array();
$dilewati[] = [
"kontrakID" => $kontrak["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $kontrak["PurchaseOrderID"],
"receiveOrderPoID" => $kontrak["PurchaseOrderAssetContractReceiveOrderPoID"],
"supplierInvoiceID" => $piExisting["SupplierInvoiceID"],
"nomorInvoice" => $piExisting["SupplierInvoiceNumber"],
"alasan" => "PI cicilan untuk bulan ini sudah dibuat sebelumnya, tidak perlu dibuat ulang."
];
continue;
}
$contractUserID = (int) ($kontrak["PurchaseOrderAssetContractCreatedUserID"] ?? 0);
$contractUser = $this->getCronUser($contractUserID);
// Generate nomor PI otomatis via stored function fn_penomoran
$nomorPI = $this->generateNomorPI($kontrak, $contractUser);
$jumlahCicilan = (float) $kontrak["PurchaseOrderAssetContractInstallmentPayAmount"];
$paymentTerm = isset($kontrak["PurchaseOrderPaymentTerm"]) && $kontrak["PurchaseOrderPaymentTerm"] !== null
? (int) $kontrak["PurchaseOrderPaymentTerm"]
: 0;
$tanggalJatuhTempo = $this->hitungJatuhTempo($endDate, $paymentTerm);
$catatan = "PI Cicilan Otomatis — Kontrak Aset ID " . $kontrak["PurchaseOrderAssetContractID"] . " periode " . date("Y-m", strtotime($startDate));
// -------------------------------------------------------
// INSERT header + detail Purchase Invoice (supplier_invoice & supplier_invoice_detail)
// -------------------------------------------------------
$deskripsi = $kontrak["PurchaseOrderAssetContractName"] != ""
? $kontrak["PurchaseOrderAssetContractName"]
: "Cicilan Kontrak Aset ID " . $kontrak["PurchaseOrderAssetContractID"];
$deskripsi .= " periode " . date("Y-m", strtotime($startDate));
$payloadInsert = [
"nomorPI" => $nomorPI,
"tanggalPI" => $endDate,
"tanggalJatuhTempo" => $tanggalJatuhTempo,
"jumlahCicilan" => $jumlahCicilan,
"catatan" => $catatan,
"deskripsi" => $deskripsi,
"userID" => $contractUserID,
"purchaseOrderAssetContractID" => $kontrak["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $kontrak["PurchaseOrderID"],
"receiveOrderPoID" => $kontrak["PurchaseOrderAssetContractReceiveOrderPoID"],
"supplierID" => $kontrak["PurchaseOrderSupplierID"],
"purchaseOrderSummaryID" => $kontrak["PurchaseOrderSummaryID"],
"purchaseOrderSummaryItemID" => $kontrak["PurchaseOrderSummaryItemID"],
"purchaseOrderSummaryItemUnitID" => $kontrak["PurchaseOrderSummaryItemUnitID"]
];
$hasilInsert = $this->curlInsertSupplierInvoice($payloadInsert);
if ($hasilInsert === false) {
$this->sys_error_db("Gagal menyimpan Purchase Invoice cicilan untuk kontrak ID " . $kontrak["PurchaseOrderAssetContractID"] . ".");
exit;
}
if (!empty($hasilInsert["duplicate"])) {
$dilewati[] = [
"kontrakID" => $kontrak["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $kontrak["PurchaseOrderID"],
"receiveOrderPoID" => $kontrak["PurchaseOrderAssetContractReceiveOrderPoID"],
"supplierInvoiceID" => $hasilInsert["supplierInvoiceID"],
"nomorInvoice" => $hasilInsert["supplierInvoiceNumber"],
"alasan" => "PI cicilan untuk bulan ini sudah dibuat sebelumnya, tidak perlu dibuat ulang."
];
continue;
}
$supplierInvoiceID = $hasilInsert["supplierInvoiceID"];
// -------------------------------------------------------
// UPDATE jumlah cicilan terbayar pada kontrak
// -------------------------------------------------------
$this->db->trans_begin(); $this->db->trans_begin();
$cicilanTerbayarBaru = ((int) $kontrak["PurchaseOrderAssetContractInstallmentPaid"]) + 1; foreach ($eligible as $row) {
$statusKontrakBaru = $cicilanTerbayarBaru >= (int) $kontrak["PurchaseOrderAssetContractInstallmentNumber"]
? "lunas"
: "belum lunas";
$sqlUpdateKontrak = "UPDATE purchase_order_asset_contract $amount = (float) $row["PurchaseOrderAssetContractInstallmentPayAmount"];
SET PurchaseOrderAssetContractReceiveOrderPoID = ?, $dayOfMonth = (int) ($row["PurchaseOrderAssetContractInstallmentDate"] ?? 1);
PurchaseOrderAssetContractInstallmentPaid = ?, $lastDay = (int) date("t", strtotime($endDate));
PurchaseOrderAssetContractStatus = ?, $dayOfMonth = min($dayOfMonth, $lastDay);
PurchaseOrderAssetContractLastUpdated = NOW() $installDate = date("Y-m", strtotime($endDate)) . "-" . str_pad($dayOfMonth, 2, "0", STR_PAD_LEFT);
WHERE PurchaseOrderAssetContractID = ? $dueDate = date("Y-m-d", strtotime($installDate . " +7 days"));
AND PurchaseOrderAssetContractIsActive = 'Y'"; $createdBy = (int) ($row["PurchaseOrderAssetContractCreatedUserID"] ?? $userID);
$qryUpdateKontrak = $this->db->query($sqlUpdateKontrak, [ // ── Step 6: INSERT supplier_installment ──────────────
$kontrak["PurchaseOrderAssetContractReceiveOrderPoID"], $sql = "INSERT INTO supplier_installment (
$cicilanTerbayarBaru, SupplierInstallmentPurchaseOrderID,
$statusKontrakBaru, SupplierInstallmentSupplierID,
$kontrak["PurchaseOrderAssetContractID"] SupplierInstallmentSupplierInvoiceID,
SupplierInstallmentAmount,
SupplierInstallmentDate,
SupplierInstallmentDueDate,
SupplierInstallmentPaymentID,
SupplierInstallmentStatus,
SupplierInstallmentIsLunas,
SupplierInstallmentIsActive,
SupplierInstallmentCreated,
SupplierInstallmentCreatedUserID
) VALUES (?, ?, ?, ?, ?, ?, 0, 'Pending', 'N', 'Y', NOW(), ?)";
$que = $this->db->query($sql, [
$row["PurchaseOrderID"],
$row["PurchaseOrderSupplierID"],
$row["SupplierInvoiceID"],
$amount,
$installDate,
$dueDate,
$createdBy
]); ]);
if (!$qryUpdateKontrak) { if (!$que) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("Gagal memperbarui data cicilan terbayar pada kontrak ID " . $kontrak["PurchaseOrderAssetContractID"] . "."); $this->sys_error_db("Gagal insert supplier_installment.");
exit; exit;
} }
// Pastikan tidak ada error di dalam transaksi sebelum commit $installmentID = $this->db->insert_id();
$inserted++;
$created[] = [
"installmentID" => $installmentID,
"contractID" => $row["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $row["PurchaseOrderID"],
"parentInvoiceID" => $row["SupplierInvoiceID"],
"parentInvoiceNumber" => $row["SupplierInvoiceNumber"],
"amount" => $amount,
"installDate" => $installDate,
"dueDate" => $dueDate
];
}
if ($this->db->trans_status() === false) { if ($this->db->trans_status() === false) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("Transaksi database gagal saat memproses kontrak ID " . $kontrak["PurchaseOrderAssetContractID"] . ". Semua perubahan dibatalkan."); $this->sys_error_db("Transaksi gagal.");
exit; exit;
} }
$this->db->trans_commit(); $this->db->trans_commit();
// Catat PI yang berhasil dibuat
$berhasil[] = [
"kontrakID" => $kontrak["PurchaseOrderAssetContractID"],
"purchaseOrderID" => $kontrak["PurchaseOrderID"],
"supplierInvoiceID" => $supplierInvoiceID,
"nomorInvoice" => $nomorPI,
"jumlahCicilan" => $jumlahCicilan
];
}
// -------------------------------------------------------
// Response sukses — ringkasan hasil proses
// -------------------------------------------------------
$this->sys_ok([ $this->sys_ok([
"startDate" => $startDate, "startDate" => $startDate,
"endDate" => $endDate, "endDate" => $endDate,
"periodeAwal" => $monthStart, "totalEligible" => count($eligible),
"periodeAkhir" => $monthEnd, "totalInserted" => $inserted,
"totalDibuat" => count($berhasil), "created" => $created
"totalDilewati" => count($dilewati),
"daftarDibuat" => $berhasil,
"daftarDilewati" => $dilewati
]); ]);
} catch (Exception $exc) { } catch (Exception $exc) {
if ($this->db->trans_status() === false) { if ($this->db->trans_status() === false) {
$this->db->trans_rollback(); $this->db->trans_rollback();
@@ -354,229 +147,6 @@ class PurchaseInvoiceInstallment extends MY_Controller
} }
} }
/**
* Kirim payload insert supplier_invoice ke controller terpisah via cURL.
*
* @param array $payload
* @return array|false
*/
private function curlInsertSupplierInvoice($payload)
{
$endpoint = rtrim($this->baseUrl, "/") . "/mockup/scheduler/PurchaseInvoiceInstallmentInsert/InsertSupplierInvoice";
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Accept: application/json"
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 120
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$pesanError = curl_error($ch);
curl_close($ch);
$this->sys_error("Gagal menghubungi endpoint insert supplier_invoice. Detail: " . $pesanError);
return false;
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
$this->sys_error("Endpoint insert supplier_invoice mengembalikan HTTP " . $httpCode . ".");
return false;
}
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
$this->sys_error("Response insert supplier_invoice tidak valid JSON.");
return false;
}
if (!isset($decoded["status"]) || strtoupper($decoded["status"]) !== "OK") {
$pesan = isset($decoded["message"]) ? $decoded["message"] : "Insert supplier_invoice gagal.";
$this->sys_error($pesan);
return false;
}
return isset($decoded["data"]) && is_array($decoded["data"]) ? $decoded["data"] : $decoded;
}
public function ListEligibleContracts()
{
try {
$para = $this->sys_input;
$startDate = isset($para["startDate"]) && $para["startDate"] != ""
? $para["startDate"]
: (isset($para["date"]) && $para["date"] != "" ? date("Y-m-01", strtotime($para["date"])) : date("Y-m-01"));
$endDate = isset($para["endDate"]) && $para["endDate"] != ""
? $para["endDate"]
: (isset($para["date"]) && $para["date"] != "" ? date("Y-m-t", strtotime($para["date"])) : date("Y-m-t"));
if (!$this->isValidDate($startDate) || !$this->isValidDate($endDate)) {
throw new Exception("Format tanggal tidak valid. Gunakan format YYYY-MM-DD, contoh: 2025-07-01");
}
if (strtotime($startDate) > strtotime($endDate)) {
throw new Exception("startDate tidak boleh lebih besar dari endDate");
}
$monthStart = $startDate;
$monthEnd = $endDate;
$dayOfMonth = (int) date("d", strtotime($endDate));
$sqlKontrak = "SELECT
c.PurchaseOrderAssetContractID,
c.PurchaseOrderAssetContractPurchaseOrderID,
ro.ReceiveOrderPoID AS PurchaseOrderAssetContractReceiveOrderPoID,
c.PurchaseOrderAssetContractName,
c.PurchaseOrderAssetContractStartDate,
c.PurchaseOrderAssetContractEndDate,
c.PurchaseOrderAssetContractInstallmentNumber,
c.PurchaseOrderAssetContractInstallmentPaid,
c.PurchaseOrderAssetContractInstallmentDate,
c.PurchaseOrderAssetContractInstallmentPayAmount,
po.PurchaseOrderID,
po.PurchaseOrderNumber,
po.PurchaseOrderSupplierID,
po.PurchaseOrderPaymentTerm,
po.PurchaseOrderWarehouseType,
po.PurchaseOrderWarehouseID,
ro.ReceiveOrderPoConfirmed,
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'
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 (
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";
$qryKontrak = $this->db->query($sqlKontrak, [$monthEnd, $monthStart, $dayOfMonth]);
if (!$qryKontrak) {
$this->sys_error_db("Gagal mengambil daftar kontrak cicilan eligible.");
exit;
}
$this->sys_ok([
"startDate" => $startDate,
"endDate" => $endDate,
"periodeAwal" => $monthStart,
"periodeAkhir" => $monthEnd,
"total" => $qryKontrak->num_rows(),
"records" => $qryKontrak->result_array()
]);
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
/**
* CurlGenerateMonthlyInvoices
*
* Wrapper untuk memanggil GenerateMonthlyInvoices melalui HTTP request (cURL).
* Cocok digunakan oleh CRON eksternal yang tidak bisa memanggil function PHP langsung.
*
* Parameter (POST JSON):
* - baseUrl : Base URL server tujuan. Default: URL server ini sendiri.
* - startDate : Tanggal awal periode (YYYY-MM-DD). Default: awal bulan ini.
* - endDate : Tanggal akhir periode (YYYY-MM-DD). Default: akhir bulan ini.
* - userID : ID user yang menjalankan. Default: 0.
*/
// =========================================================================
// FUNGSI PRIVATE / HELPER
// =========================================================================
/**
* Generate nomor Purchase Invoice.
*
* @param array $kontrak Data baris kontrak dari query
* @param array $user Data user CRON
* @return string Nomor PI yang dihasilkan
*/
private function generateNomorPI($kontrak, $user)
{
$userID = isset($user["M_UserID"]) ? (int) $user["M_UserID"] : 0;
// Tentukan area ID dan type
$areaid = isset($user["M_BranchID"]) && (int) $user["M_BranchID"] > 0 ? (int) $user["M_BranchID"] : 0;
$areatype = 'B';
if (isset($user["loginLevel"]) && $user["loginLevel"] == 'regional') {
$areaid = isset($user["S_RegionalID"]) && (int) $user["S_RegionalID"] > 0 ? (int) $user["S_RegionalID"] : 0;
$areatype = 'R';
}
// Ambil divisi user
$userDivID = 0;
if ($userID > 0) {
$sqlusrdivisi = "SELECT M_UserDivisionDivisionID FROM m_userdivision
WHERE M_UserDivisionM_UserID = ? AND M_UserDivisionIsActive = 'Y' LIMIT 1";
$queusrdivisi = $this->db->query($sqlusrdivisi, [$userID]);
if ($queusrdivisi && $queusrdivisi->num_rows() > 0) {
$userDivID = (int) $queusrdivisi->row_array()['M_UserDivisionDivisionID'];
}
}
// Jalankan stored function fn_penomoran
$sqlnum = "SELECT `fn_penomoran`(?, ?, ?, ?, ?, ?) AS numpd;";
$quenum = $this->db->query($sqlnum, ['PI', $userDivID, $areatype, $areaid, 'SM', 'N']);
if ($quenum && $quenum->num_rows() > 0) {
return $quenum->row_array()['numpd'];
}
// Fallback jika stored function gagal
$contractID = isset($kontrak["PurchaseOrderAssetContractID"]) ? (int) $kontrak["PurchaseOrderAssetContractID"] : 0;
return "PI-INS-FALLBACK-" . $contractID . "-" . date("Ymd");
}
/** /**
* Ambil data user berdasarkan userID untuk keperluan CRON. * Ambil data user berdasarkan userID untuk keperluan CRON.
* Jika user tidak ditemukan, kembalikan data default (tanpa branch/regional). * Jika user tidak ditemukan, kembalikan data default (tanpa branch/regional).
@@ -638,4 +208,95 @@ class PurchaseInvoiceInstallment extends MY_Controller
$d = DateTime::createFromFormat("Y-m-d", $tanggal); $d = DateTime::createFromFormat("Y-m-d", $tanggal);
return $d && $d->format("Y-m-d") === $tanggal; return $d && $d->format("Y-m-d") === $tanggal;
} }
/**
* getEligibleInstallments — Steps 15 combined
*
* Returns contracts that:
* Step 1: Are active, not paid off, within date range, due date reached
* Step 2: Have approved & active PO
* Step 3: Have confirmed & active RO
* Step 4: Have a parent supplier_invoice with IsInstallment = 'Y'
* Step 5: Do NOT already have a supplier_installment for this month
*
* @param string $startDate YYYY-MM-DD
* @param string $endDate YYYY-MM-DD
* @return array
*/
private function getEligibleInstallments($startDate, $endDate)
{
$dayOfMonth = (int) date("d", strtotime($endDate));
$sql = "SELECT
c.PurchaseOrderAssetContractID,
c.PurchaseOrderAssetContractInstallmentPayAmount,
c.PurchaseOrderAssetContractInstallmentDate,
c.PurchaseOrderAssetContractCreatedUserID,
po.PurchaseOrderID,
po.PurchaseOrderSupplierID,
si.SupplierInvoiceID,
si.SupplierInvoiceNumber
FROM purchase_order_asset_contract c
-- Step 2: PO must be Approved and Active
JOIN purchase_order po
ON po.PurchaseOrderID = c.PurchaseOrderAssetContractPurchaseOrderID
AND po.PurchaseOrderIsActive = 'Y'
AND po.PurchaseOrderStatus = 'Approved'
-- Step 3: RO must be Confirmed and Active
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
-- Step 4: Parent invoice must exist with IsInstallment = 'Y'
JOIN supplier_invoice si
ON si.SupplierInvoiceReceiveOrderPoID = ro.ReceiveOrderPoID
AND si.SupplierInvoiceIsActive = 'Y'
AND si.SupplierInvoiceIsInstallment = 'Y'
-- Step 1: Contract eligibility
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) <= ?
-- Step 5: Dedup — no existing supplier_installment this month (any status)
AND NOT EXISTS (
SELECT 1
FROM supplier_installment inst
WHERE inst.SupplierInstallmentIsActive = 'Y'
AND inst.SupplierInstallmentPurchaseOrderID = po.PurchaseOrderID
AND DATE_FORMAT(inst.SupplierInstallmentDate, '%Y-%m') = DATE_FORMAT(?, '%Y-%m')
)
ORDER BY c.PurchaseOrderAssetContractID ASC";
$params = [$endDate, $startDate, $dayOfMonth, $endDate];
$qry = $this->db->query($sql, $params);
if (!$qry) {
$this->sys_error_db("Gagal mengambil daftar installment eligible.");
exit;
}
return $qry->result_array();
}
} }

View File

@@ -28,7 +28,7 @@ class Billv2 extends MY_Controller {
$offset = ($params['currentpage'] - 1) * $limit; $offset = ($params['currentpage'] - 1) * $limit;
} }
// ── UNION base — invoice branch + downpayment branch ──── // ── UNION base — invoice + downpayment + installment ───
$sql_base = " $sql_base = "
SELECT SELECT
sp.SupplierPaymentID, sp.SupplierPaymentID,
@@ -76,7 +76,33 @@ class Billv2 extends MY_Controller {
ON dp_sup.SupplierID = dp.SupplierDownpaymentSupplierID ON dp_sup.SupplierID = dp.SupplierDownpaymentSupplierID
JOIN purchase_order po JOIN purchase_order po
ON po.PurchaseOrderID = dp.SupplierDownpaymentPurchasOrderID ON po.PurchaseOrderID = dp.SupplierDownpaymentPurchasOrderID
WHERE sp.SupplierPaymentSupplierDownpaymentID IS NOT NULL"; WHERE sp.SupplierPaymentSupplierDownpaymentID IS NOT NULL
UNION ALL
SELECT
sp.SupplierPaymentID,
sp.SupplierPaymentDate,
sp.SupplierPaymentNumber,
sp.SupplierPaymentAmount,
sp.SupplierPaymentStatus,
sp.SupplierPaymentIsVerif,
sp.SupplierPaymentIsApproved,
sp.SupplierPaymentIsActive,
0 AS SupplierInvoiceID,
CONCAT('INST-', si_inst.SupplierInvoiceNumber) AS SupplierInvoiceNumber,
inst.SupplierInstallmentDueDate AS SupplierInvoiceDraftPaymentDate,
inst_sup.SupplierCode AS SupplierCode,
inst_sup.SupplierName AS SupplierName,
'INSTALLMENT' AS type
FROM supplier_payment sp
JOIN supplier_installment inst
ON inst.SupplierInstallmentID = sp.SupplierPaymentSupplierInstallmentID
JOIN supplier_invoice si_inst
ON si_inst.SupplierInvoiceID = inst.SupplierInstallmentSupplierInvoiceID
JOIN supplier inst_sup
ON inst_sup.SupplierID = inst.SupplierInstallmentSupplierID
WHERE sp.SupplierPaymentSupplierInstallmentID IS NOT NULL";
// ── Outer: common filters + ordering + pagination ─────── // ── Outer: common filters + ordering + pagination ───────
$sql_data = " $sql_data = "
@@ -97,8 +123,7 @@ class Billv2 extends MY_Controller {
} }
// ── COUNT — wrap UNION in outer filter ────────────────── // ── COUNT — wrap UNION in outer filter ──────────────────
$sql_total = " $sql_total = "SELECT COUNT(*) AS total FROM ($sql_base) AS combined
SELECT COUNT(*) AS total FROM ($sql_base) AS combined
WHERE SupplierPaymentIsActive = 'Y' WHERE SupplierPaymentIsActive = 'Y'
AND SupplierPaymentNumber LIKE ? AND SupplierPaymentNumber LIKE ?
AND (SupplierPaymentDate BETWEEN DATE(?) AND DATE(?)) AND (SupplierPaymentDate BETWEEN DATE(?) AND DATE(?))
@@ -118,7 +143,6 @@ class Billv2 extends MY_Controller {
]; ];
$this->sys_ok($output); $this->sys_ok($output);
exit;
} catch (Exception $exc) { } catch (Exception $exc) {
$message = $exc->getMessage(); $message = $exc->getMessage();
$code = $exc->getCode(); $code = $exc->getCode();
@@ -143,7 +167,8 @@ class Billv2 extends MY_Controller {
// ── Detect payment type ────────────────────────────────── // ── Detect payment type ──────────────────────────────────
$sql_type = "SELECT $sql_type = "SELECT
SupplierPaymentSupplierInvoiceID, SupplierPaymentSupplierInvoiceID,
SupplierPaymentSupplierDownpaymentID SupplierPaymentSupplierDownpaymentID,
SupplierPaymentSupplierInstallmentID
FROM supplier_payment FROM supplier_payment
WHERE SupplierPaymentID = ? AND SupplierPaymentIsActive = 'Y'"; WHERE SupplierPaymentID = ? AND SupplierPaymentIsActive = 'Y'";
@@ -206,7 +231,7 @@ class Billv2 extends MY_Controller {
$data['detail'] = $que_detail->result_array(); $data['detail'] = $que_detail->result_array();
// ── DOWNPAYMENT branch ────────────────────────────────── // ── DOWNPAYMENT branch ──────────────────────────────────
} else { } elseif ($payment['SupplierPaymentSupplierDownpaymentID'] > 0) {
$sql = "SELECT $sql = "SELECT
dp.SupplierDownpaymentPurchasOrderID, dp.SupplierDownpaymentPurchasOrderID,
dp.SupplierDownpaymentAmount AS SupplierInvoiceSubTotal, dp.SupplierDownpaymentAmount AS SupplierInvoiceSubTotal,
@@ -255,6 +280,51 @@ class Billv2 extends MY_Controller {
throw new Exception('failed to get dp detail', 2); throw new Exception('failed to get dp detail', 2);
} }
$data['detail'] = $que_detail->result_array();
} else {
$sql = "SELECT
inst.SupplierInstallmentAmount AS SupplierInvoiceSubTotal,
0 AS SupplierInvoiceShippingCost,
0 AS SupplierInvoiceDiscountPercent,
0 AS SupplierInvoiceDiscountAmount,
0 AS SupplierInvoiceTaxPercentPpn,
0 AS SupplierInvoiceTaxAmountPpn,
inst.SupplierInstallmentAmount AS SupplierInvoiceGrandTotal,
si.SupplierInvoiceID,
'INSTALLMENT' AS type
FROM supplier_payment sp
JOIN supplier_installment inst
ON inst.SupplierInstallmentID = sp.SupplierPaymentSupplierInstallmentID
JOIN supplier_invoice si
ON si.SupplierInvoiceID = inst.SupplierInstallmentSupplierInvoiceID
WHERE sp.SupplierPaymentID = ? AND sp.SupplierPaymentIsActive = 'Y'";
$que = $this->db->query($sql, [$para['paymentID']]);
if (!$que) {
throw new Exception("[Error] failed get Installment row data", 2);
}
$data = $que->row_array();
$sql_detail = "SELECT
M_ItemDesc,
SupplierInvoiceDetailQty,
SupplierInvoiceDetailPrice,
SupplierInvoiceDetailDiscountAmount,
(SupplierInvoiceDetailPrice - SupplierInvoiceDetailDiscountAmount) AS DiscountedPrice,
SupplierInvoiceDetailTotal
FROM supplier_invoice_detail
JOIN m_item
ON M_ItemID = SupplierInvoiceDetailItemID AND M_ItemIsActive = 'Y'
WHERE SupplierInvoiceDetailSupplierInvoiceID = ?
AND SupplierInvoiceDetailIsActive = 'Y'
GROUP BY SupplierInvoiceDetailID";
$que_detail = $this->db->query($sql_detail, [$data['SupplierInvoiceID']]);
if (!$que_detail) {
throw new Exception("[Error] failed to get installment item payments", 2);
}
unset($data['SupplierInvoiceID']);
$data['detail'] = $que_detail->result_array(); $data['detail'] = $que_detail->result_array();
} }

View File

@@ -121,9 +121,86 @@ class Bill extends MY_Controller
} }
} }
public function add_notes_installment($orderid)
{
$sql = "SELECT SupplierPaymentSupplierInstallmentID as note_order_id,
SupplierPaymentID as note_id,
SupplierInstallmentID as detail_id,
SupplierPaymentDate as note_date,
SupplierPaymentNumber as note_number,
GROUP_CONCAT(DISTINCT coaDescription separator ' , ') as paymenttypes_name,
SUM(SupplierInstallmentAmount) as note_amount,
n.M_UserUsername as note_user,
SupplierInstallmentIsActive as note_active,
'N' as show_detail,
SupplierPaymentNote as keterangan,
SupplierPaymentCoaID,
coaID,
coaDescription,
SupplierPaymentIsConfirm,
CONCAT(
'Confirmed by : ',c.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentConfirmDate,'%d-%m-%Y %H:%i')
) AS d_confirm,
SupplierPaymentIsApproved,
CONCAT(
'Approved by : ',a.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentApprovedDate,'%d-%m-%Y %H:%i')
) AS d_approved,
CONCAT(
'Verified by : ',b.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentVerifDate,'%d-%m-%Y %H:%i')
) AS d_verif
FROM supplier_payment
JOIN supplier_installment
ON SupplierInstallmentID = SupplierPaymentSupplierInstallmentID
AND SupplierInstallmentIsActive = 'Y'
LEFT JOIN coa ON SupplierPaymentCoaID = coaID
LEFT JOIN m_user n ON SupplierPaymentUserID = n.M_UserID
LEFT JOIN m_user c ON SupplierPaymentConfirmUserID = c.M_UserID
LEFT JOIN m_user a ON SupplierPaymentApprovedUserID = a.M_UserID
LEFT JOIN m_user b ON SupplierPaymentVerifUserID = b.M_UserID
WHERE SupplierPaymentSupplierInstallmentID = {$orderid}
AND SupplierPaymentIsActive = 'Y'
GROUP BY SupplierPaymentID";
$query = $this->db_onedev->query($sql);
if ($query) {
$rows = $query->result_array();
return $rows ?: [];
} else {
$this->sys_error_db("get Installment notes", $this->db_onedev);
exit;
}
}
public function add_tagihans($orderid) public function add_tagihans($orderid)
{ {
$sql = "SELECT SupplierInvoiceID as tagihan_id, $sql_dp = "SELECT
Fa_ClassDownPaymentCoaID
FROM supplier_invoice_detail
JOIN m_item
ON M_ItemID = SupplierInvoiceDetailItemID
AND SupplierInvoiceDetailSupplierInvoiceID = ?
AND SupplierInvoiceDetailIsActive = 'Y'
AND SupplierInvoiceDetailIsVerified = 'Y'
JOIN fa_class
ON Fa_ClassID = M_ItemFa_ClassID
AND Fa_ClassIsActive = 'Y'";
$que_dp = $this->db_onedev->query($sql_dp, [$orderid]);
if (!$que_dp) {
$this->sys_error_db("failed to get coa id dp tagihans", $this->db_onedev);
exit;
}
$dataDP = $que_dp->result_array();
$excludeIds = array_column($dataDP, 'Fa_ClassDownPaymentCoaID');
$excludeIds[] = 563; // always excluded
$excludeIds = array_unique($excludeIds);
$excludeIds = array_values($excludeIds); // re-index
$notInPlaceholders = implode(',', array_fill(0, count($excludeIds), '?'));
$sql = "SELECT
SupplierInvoiceID as tagihan_id,
PurchaseOrderNumber as tagihan_number, PurchaseOrderNumber as tagihan_number,
jurnalTxDescription as pasien, jurnalTxDescription as pasien,
jurnalTxCredit as tagihan_total, jurnalTxCredit as tagihan_total,
@@ -135,14 +212,20 @@ class Bill extends MY_Controller
jurnalTxID SupplierInvoiceDetailID, jurnalTxID SupplierInvoiceDetailID,
PurchaseOrderID SupplierInvoiceDetailPurchaseOrderID PurchaseOrderID SupplierInvoiceDetailPurchaseOrderID
FROM supplier_invoice FROM supplier_invoice
JOIN receive_order_po
ON ReceiveOrderPoID = SupplierInvoiceReceiveOrderPoID
JOIN receive_order_po_detail
ON ReceiveOrderPoDetailReceiveOrderPoID = ReceiveOrderPoID
JOIN purchase_order JOIN purchase_order
ON SupplierInvoicePurchaseOrderID = PurchaseOrderID ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID
JOIN jurnal_addon JOIN jurnal_addon
ON jurnalAddOnValue = SupplierInvoiceNumber ON jurnalAddOnValue = SupplierInvoiceNumber
AND jurnalAddOnIsActive = 'Y'
JOIN jurnal_tx JOIN jurnal_tx
ON jurnalTxJurnalID = jurnalAddOnJurnalID ON jurnalTxJurnalID = jurnalAddOnJurnalID
AND jurnalTxIsActive = 'Y'
AND jurnalTxCredit <> 0 AND jurnalTxCredit <> 0
AND jurnalTxCoaID <> 563 AND jurnalTxCoaID NOT IN ($notInPlaceholders)
LEFT JOIN supplier_payment LEFT JOIN supplier_payment
ON SupplierPaymentSupplierInvoiceID = SupplierInvoiceID ON SupplierPaymentSupplierInvoiceID = SupplierInvoiceID
AND SupplierInvoiceIsActive = 'Y' AND SupplierInvoiceIsActive = 'Y'
@@ -152,7 +235,7 @@ class Bill extends MY_Controller
AND SupplierPaymentDetailIsActive = 'Y' AND SupplierPaymentDetailIsActive = 'Y'
WHERE SupplierInvoiceID = ? WHERE SupplierInvoiceID = ?
GROUP BY jurnalTxID"; GROUP BY jurnalTxID";
$query = $this->db_onedev->query($sql, [$orderid]); $query = $this->db_onedev->query($sql, array_merge($excludeIds, [$orderid]));
if ($query) { if ($query) {
$rows = $query->result_array(); $rows = $query->result_array();
return $rows; return $rows;
@@ -395,10 +478,19 @@ class Bill extends MY_Controller
$dp_params = [$regionalid]; $dp_params = [$regionalid];
// ── INSTALLMENT branch WHERE ────────────────────────────
$inst_where = " inst.SupplierInstallmentIsActive = 'Y'
AND rop.ReceiveOrderPoS_RegionalID = ? ";
$inst_params = [$regionalid];
// ── UNION base ─────────────────────────────────────────── // ── UNION base ───────────────────────────────────────────
$sql_base = " $sql_base = "
SELECT SELECT
si.SupplierInvoiceID, si.SupplierInvoiceID,
0 AS DownpaymentID,
0 AS InstallmentID,
0 AS parent_invoice_id,
si.SupplierInvoiceNumber, si.SupplierInvoiceNumber,
si.SupplierInvoiceDraftPaymentDate, si.SupplierInvoiceDraftPaymentDate,
si.SupplierInvoiceDraftPaymentDate AS filter_date, si.SupplierInvoiceDraftPaymentDate AS filter_date,
@@ -441,7 +533,10 @@ class Bill extends MY_Controller
UNION ALL UNION ALL
SELECT SELECT
dp.SupplierDownpaymentID * -1 AS SupplierInvoiceID, 0 AS SupplierInvoiceID,
dp.SupplierDownpaymentID AS DownpaymentID,
0 AS InstallmentID,
0 AS parent_invoice_id,
CONCAT('DP-', po.PurchaseOrderNumber) AS SupplierInvoiceNumber, CONCAT('DP-', po.PurchaseOrderNumber) AS SupplierInvoiceNumber,
dp.SupplierDownpaymentDueDate AS SupplierInvoiceDraftPaymentDate, dp.SupplierDownpaymentDueDate AS SupplierInvoiceDraftPaymentDate,
dp.SupplierDownpaymentDueDate AS filter_date, dp.SupplierDownpaymentDueDate AS filter_date,
@@ -480,7 +575,59 @@ class Bill extends MY_Controller
ON dp_sup.SupplierID = dp.SupplierDownpaymentSupplierID ON dp_sup.SupplierID = dp.SupplierDownpaymentSupplierID
JOIN purchase_order po JOIN purchase_order po
ON po.PurchaseOrderID = dp.SupplierDownpaymentPurchasOrderID ON po.PurchaseOrderID = dp.SupplierDownpaymentPurchasOrderID
WHERE $dp_where"; WHERE $dp_where
UNION ALL
SELECT
0 AS SupplierInvoiceID,
0 AS DownpaymentID,
inst.SupplierInstallmentID AS InstallmentID,
si_inst.SupplierInvoiceID AS parent_invoice_id,
CONCAT('INST-', si_inst.SupplierInvoiceNumber) AS SupplierInvoiceNumber,
inst.SupplierInstallmentDueDate AS SupplierInvoiceDraftPaymentDate,
inst.SupplierInstallmentDueDate AS filter_date,
inst.SupplierInstallmentIsLunas AS flaglunas,
si_inst.SupplierInvoiceSupplierInvoiceNumber AS SupplierInvoiceSupplierInvoiceNumber,
DATE_FORMAT(
IFNULL(si_inst.SupplierInvoiceSupplierInvoiceDate,''),'%d-%m-%Y'
) AS tanggalinvoice,
inst_sup.SupplierName AS SupplierName,
IFNULL(sp.SupplierPaymentID,0) AS SupplierPaymentID,
IFNULL(sp.SupplierPaymentIsConfirm,'N') AS SupplierPaymentIsConfirm,
IFNULL(sp.SupplierPaymentIsApproved,'N') AS SupplierPaymentIsApproved,
IFNULL(sp.SupplierPaymentIsVerif,'N') AS SupplierPaymentIsVerif,
IFNULL(sp.SupplierPaymentIsActive,'Y') AS SupplierPaymentIsActive,
IFNULL(sp.SupplierPaymentCashierNumber,'') AS SupplierPaymentCashierNumber,
DATE_FORMAT(
inst.SupplierInstallmentDueDate,'%d-%m-%Y'
) AS tanggalbayar,
inst.SupplierInstallmentAmount AS totalbill,
CASE WHEN inst.SupplierInstallmentStatus = 'Paid'
THEN inst.SupplierInstallmentAmount
ELSE 0 END AS paid,
CASE WHEN inst.SupplierInstallmentStatus = 'Paid'
THEN 0
ELSE inst.SupplierInstallmentAmount END AS unpaid,
'' AS SupplierPaymentNumber,
0 AS SupplierPaymentAmount,
'' AS SupplierPaymentDate,
'' AS notes,
'' AS tagihans,
0 AS xrounding,
'' AS chex,
'INSTALLMENT' AS type
FROM supplier_installment inst
LEFT JOIN supplier_payment sp
ON sp.SupplierPaymentSupplierInstallmentID = inst.SupplierInstallmentID
AND sp.SupplierPaymentIsActive = 'Y'
JOIN supplier_invoice si_inst
ON si_inst.SupplierInvoiceID = inst.SupplierInstallmentSupplierInvoiceID
JOIN receive_order_po rop
ON rop.ReceiveOrderPoID = si_inst.SupplierInvoiceReceiveOrderPoID
JOIN supplier inst_sup
ON inst_sup.SupplierID = inst.SupplierInstallmentSupplierID
WHERE $inst_where";
// ── Outer common filters ───────────────────────────────── // ── Outer common filters ─────────────────────────────────
$outer_where = " SupplierPaymentIsActive = 'Y' $outer_where = " SupplierPaymentIsActive = 'Y'
@@ -506,7 +653,7 @@ class Bill extends MY_Controller
FROM ($sql_base) AS combined FROM ($sql_base) AS combined
WHERE $outer_where"; WHERE $outer_where";
$count_params = array_merge($inv_params, $dp_params, $outer_params); $count_params = array_merge($inv_params, $dp_params, $inst_params, $outer_params);
$query = $this->db_onedev->query($sql_count, $count_params); $query = $this->db_onedev->query($sql_count, $count_params);
$tot_count = 0; $tot_count = 0;
@@ -526,7 +673,7 @@ class Bill extends MY_Controller
ORDER BY SupplierInvoiceID ASC ORDER BY SupplierInvoiceID ASC
LIMIT ? OFFSET ?"; LIMIT ? OFFSET ?";
$data_params = array_merge($inv_params, $dp_params, $outer_params, [$number_limit, $number_offset]); $data_params = array_merge($inv_params, $dp_params, $inst_params, $outer_params, [$number_limit, $number_offset]);
$query = $this->db_onedev->query($sql_data, $data_params); $query = $this->db_onedev->query($sql_data, $data_params);
$rows = $query->result_array(); $rows = $query->result_array();
@@ -537,7 +684,7 @@ class Bill extends MY_Controller
// ── Downpayment branch ────────────────────────── // ── Downpayment branch ──────────────────────────
if ($v['type'] === 'DP') { if ($v['type'] === 'DP') {
$rows[$k]['chex'] = false; $rows[$k]['chex'] = false;
$dp_id = abs($v['SupplierInvoiceID']); $dp_id = $v['DownpaymentID'];
$s_payment = $this->db_onedev->query(" $s_payment = $this->db_onedev->query("
SELECT SELECT
@@ -566,6 +713,39 @@ class Bill extends MY_Controller
continue; continue;
} }
// ── Installment branch ─────────────────────────
if ($v['type'] === 'INSTALLMENT') {
$rows[$k]['chex'] = false;
$inst_id = $v['InstallmentID'];
$parent_invoice_id = $v['parent_invoice_id'];
$s_payment = $this->db_onedev->query("
SELECT
GROUP_CONCAT(SupplierPaymentNumber SEPARATOR ', ') as SupplierPaymentNumber,
SUM(IFNULL(SupplierPaymentAmount,0)) as SupplierPaymentAmount,
GROUP_CONCAT(
DATE_FORMAT(SupplierPaymentDate,'%d-%m-%Y')
SEPARATOR ', '
) as SupplierPaymentDate
FROM supplier_payment
WHERE SupplierPaymentIsActive = 'Y'
AND SupplierPaymentSupplierInstallmentID = ?",
[$inst_id]
)->row();
$amount = $s_payment->SupplierPaymentAmount
? $s_payment->SupplierPaymentAmount
: "0.00";
$rows[$k]['SupplierPaymentNumber'] = $s_payment->SupplierPaymentNumber;
$rows[$k]['SupplierPaymentAmount'] = $amount;
$rows[$k]['SupplierPaymentDate'] = $s_payment->SupplierPaymentDate;
$rows[$k]['paid'] = $amount;
$rows[$k]['notes'] = $this->add_notes_installment($inst_id);
$rows[$k]['tagihans'] = $this->add_tagihans($parent_invoice_id);
continue;
}
// ── Invoice branch ────────────────────────────── // ── Invoice branch ──────────────────────────────
$rows[$k]['chex'] = false; $rows[$k]['chex'] = false;
$inv_id = $v['SupplierInvoiceID']; $inv_id = $v['SupplierInvoiceID'];

View File

@@ -3,9 +3,10 @@
class PaymentV2 extends MY_Controller class PaymentV2 extends MY_Controller
{ {
var $db; var $db;
public function index() public function index()
{ {
echo "API"; echo 'API';
} }
public function __construct() public function __construct()
@@ -17,8 +18,8 @@ class PaymentV2 extends MY_Controller
{ {
try { try {
if (!$this->isLogin) { if (!$this->isLogin) {
$this->sys_error("invalid token"); $this->sys_error('invalid token');
exit; exit();
} }
$this->db->trans_begin(); $this->db->trans_begin();
@@ -26,15 +27,15 @@ class PaymentV2 extends MY_Controller
$param = $this->sys_input; $param = $this->sys_input;
$users = $this->sys_user; $users = $this->sys_user;
# Generate number supplier payment # // Generate number supplier payment #
$sql_gennumber = "SELECT `fn_numbering`(?) as numberx"; $sql_gennumber = 'SELECT `fn_numbering`(?) as numberx';
$paygroupnum = $this->db->query($sql_gennumber, ['PC'])->row()->numberx; $paygroupnum = $this->db->query($sql_gennumber, ['PC'])->row()->numberx;
$paynumber = $this->db->query($sql_gennumber, ['PN'])->row()->numberx; $paynumber = $this->db->query($sql_gennumber, ['PN'])->row()->numberx;
if ($paygroupnum == '' || $paynumber == '') { if ($paygroupnum == '' || $paynumber == '') {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] generate number supplier payment"); $this->sys_error_db('[Error] generate number supplier payment');
exit; exit();
} }
$rounding = 0; $rounding = 0;
@@ -43,7 +44,7 @@ class PaymentV2 extends MY_Controller
$rounding = round($sisa, 2); $rounding = round($sisa, 2);
} }
# UPDATE header supplier payment # // UPDATE header supplier payment #
$sql_updateheader = "UPDATE supplier_payment SET $sql_updateheader = "UPDATE supplier_payment SET
SupplierPaymentIsConfirm = 'Y', SupplierPaymentIsConfirm = 'Y',
SupplierPaymentConfirmUserID = ?, SupplierPaymentConfirmUserID = ?,
@@ -61,51 +62,93 @@ class PaymentV2 extends MY_Controller
$rounding, $rounding,
$param['keterangan'], $param['keterangan'],
$param['paymenttype'], $param['paymenttype'],
$param['orderid'] $param['orderid'],
]); ]);
if (!$que_updateheader) { if (!$que_updateheader) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] update supplier payment header"); $this->sys_error_db('[Error] update supplier payment header');
exit; exit();
} }
# UPDATE status lunas supplier invoice # // UPDATE status lunas supplier invoice #
if ($param['type'] == 'DP') { switch ($param['type']) {
case 'DP':
$sql_updatedp = "UPDATE supplier_downpayment $sql_updatedp = "UPDATE supplier_downpayment
SET SupplierDownpaymentIsLunas = 'Y' SET SupplierDownpaymentIsLunas = 'Y'
WHERE SupplierDownpaymentID = ?"; WHERE SupplierDownpaymentID = ?";
$que_updatedp = $this->db->query($sql_updatedp, [ $que_updatedp = $this->db->query($sql_updatedp, [
abs($param['SupplierInvoiceID']) abs($param['DownpaymentID']),
]); ]);
if (!$que_updatedp) { if (!$que_updatedp) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] update status lunas downpayment"); $this->sys_error_db('[Error] update status lunas downpayment');
exit; exit();
} }
} else { break;
case 'INSTALLMENT':
$sql_updinstlment = "UPDATE supplier_installment
SET SupplierInstallmentIsLunas = 'Y'
WHERE SupplierInstallmentID = ?";
$que_updinstlment = $this->db->query($sql_updinstlment, [
$param['InstallmentID'],
]);
if (!$que_updinstlment) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] update status lunas installment');
exit();
}
$sql_updKontrak = "UPDATE purchase_order_asset_contract c
JOIN supplier_installment inst
ON inst.SupplierInstallmentPurchaseOrderID = c.PurchaseOrderAssetContractPurchaseOrderID
SET
c.PurchaseOrderAssetContractInstallmentPaid = c.PurchaseOrderAssetContractInstallmentPaid + 1,
c.PurchaseOrderAssetContractStatus = IF(
c.PurchaseOrderAssetContractInstallmentPaid + 1 >= c.PurchaseOrderAssetContractInstallmentNumber,
'lunas',
'belum lunas'
),
c.PurchaseOrderAssetContractLastUpdated = NOW()
WHERE inst.SupplierInstallmentID = ?
AND inst.SupplierInstallmentIsActive = 'Y'
AND c.PurchaseOrderAssetContractIsActive = 'Y'";
$que_updKontrak = $this->db->query($sql_updKontrak, [
$param['InstallmentID'],
]);
if (!$que_updKontrak) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] update kontrak cicilan');
exit();
}
break;
default:
$sql_suppinvoice = "UPDATE supplier_invoice $sql_suppinvoice = "UPDATE supplier_invoice
SET SupplierInvoiceIsLunas = 'Y' SET SupplierInvoiceIsLunas = 'Y'
WHERE SupplierInvoiceID = ?"; WHERE SupplierInvoiceID = ?";
$que_suppinvoice = $this->db->query($sql_suppinvoice, [$param['SupplierInvoiceID']]); $que_suppinvoice = $this->db->query($sql_suppinvoice, [
$param['SupplierInvoiceID'],
]);
if (!$que_suppinvoice) { if (!$que_suppinvoice) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] update status lunas invoice"); $this->sys_error_db('[Error] update status lunas invoice');
exit; exit();
} }
break;
} }
# GET Latest data supplier payment # // GET Latest data supplier payment for logging #
$sql_suppayment = "SELECT * FROM supplier_payment WHERE SupplierPaymentID = ?"; $sql_suppayment = 'SELECT * FROM supplier_payment WHERE SupplierPaymentID = ?';
$que_suppayment = $this->db->query($sql_suppayment, [$param['orderid']]); $que_suppayment = $this->db->query($sql_suppayment, [$param['orderid']]);
if (!$que_suppayment) { if (!$que_suppayment) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data latest supplier payment"); $this->sys_error_db('[Error] get data latest supplier payment');
exit; exit();
} }
$suppayment_header = $que_suppayment->row_array(); $suppayment_header = $que_suppayment->row_array();
$suppayment_detail = []; $suppayment_detail = [];
if ($param['type'] == 'DP') { switch ($param['type']) {
case 'DP':
$sql_suppaymentdetail = "SELECT supplier_downpayment.* $sql_suppaymentdetail = "SELECT supplier_downpayment.*
FROM supplier_payment FROM supplier_payment
JOIN supplier_downpayment JOIN supplier_downpayment
@@ -115,44 +158,60 @@ class PaymentV2 extends MY_Controller
$que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$param['orderid']]); $que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$param['orderid']]);
if (!$que_suppaymentdetail) { if (!$que_suppaymentdetail) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data latest supplier downpayment"); $this->sys_error_db('[Error] get data latest supplier downpayment');
exit; exit();
} }
$suppayment_detail = $que_suppaymentdetail->result_array(); $suppayment_detail = $que_suppaymentdetail->result_array();
} else { break;
$sql_suppaymentdetail = "SELECT * FROM supplier_payment_detail case 'INSTALLMENT':
WHERE SupplierPaymentDetailSupplierPaymentID = ?"; $sql_suppaymentdetail = "SELECT
supplier_installment.*,
PurchaseOrderAssetContractInstallmentNumber AS qtyPay,
PurchaseOrderAssetContractInstallmentPaid AS qtyPaid
FROM supplier_payment
JOIN supplier_installment
ON SupplierInstallmentID = SupplierPaymentSupplierInstallmentID
AND SupplierInstallmentIsActive = 'Y'
JOIN purchase_order_asset_contract
ON PurchaseOrderAssetContractPurchaseOrderID = SupplierInstallmentPurchaseOrderID
AND PurchaseOrderAssetContractIsActive = 'Y'
WHERE SupplierPaymentID = ?";
$que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$param['orderid']]); $que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$param['orderid']]);
if (!$que_suppaymentdetail) { if (!$que_suppaymentdetail) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data latest supplier payment detail"); $this->sys_error_db('[Error] get data latest supplier installment');
exit; exit();
} }
$suppayment_detail = $que_suppaymentdetail->result_array(); $suppayment_detail = $que_suppaymentdetail->result_array();
break;
default:
$sql_suppaymentdetail = 'SELECT * FROM supplier_payment_detail
WHERE SupplierPaymentDetailSupplierPaymentID = ?';
$que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$param['orderid']]);
if (!$que_suppaymentdetail) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] get data latest supplier payment detail');
exit();
}
$suppayment_detail = $que_suppaymentdetail->result_array();
break;
} }
$data_log = [ $data_log = [
"header" => $suppayment_header, 'header' => $suppayment_header,
"detail" => $suppayment_detail 'detail' => $suppayment_detail,
]; ];
# LOG activity confirm payment # // LOG activity confirm payment #
$messg = "Pembayaran Faktur No: {$suppayment_header['SupplierPaymentNumber']}"; $messg = "Pembayaran Faktur No: {$suppayment_header['SupplierPaymentNumber']}";
$messg .= " telah dikonfirmasi oleh {$users['M_UserUsername']} dengan nomor"; $messg .= " telah dikonfirmasi oleh {$users['M_UserUsername']} dengan nomor";
$messg .= " pembayaran kasir: {$suppayment_header['SupplierPaymentCashierNumber']}"; $messg .= " pembayaran kasir: {$suppayment_header['SupplierPaymentCashierNumber']}";
$this->insert_activity_log( $this->insert_activity_log('PF', 'CONFIRM', $messg, $param['orderid'], $data_log, $users['M_UserID']);
"PF",
"CONFIRM",
$messg,
$param['orderid'],
$data_log,
$users['M_UserID']
);
# INSERT JURNAL # // INSERT JURNAL #
$detail_transac = []; $detail_transac = [];
switch ($param['type']) {
if ($param['type'] == "DP") { case 'DP':
$sql_coaDP = "SELECT $sql_coaDP = "SELECT
coaID, coaID,
coaDescription, coaDescription,
@@ -178,27 +237,84 @@ class PaymentV2 extends MY_Controller
AND coaIsActive = 'Y' AND coaIsActive = 'Y'
WHERE SupplierPaymentID = ?"; WHERE SupplierPaymentID = ?";
$que_coaDP = $this->db->query($sql_coaDP, [ $que_coaDP = $this->db->query($sql_coaDP, [
$param['orderid'] $param['orderid'],
]); ]);
if (!$que_coaDP) { if (!$que_coaDP) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data hutang item"); $this->sys_error_db('[Error] get data hutang item dp');
exit; exit();
} }
$data_dp = $que_coaDP->result_array(); $data_dp = $que_coaDP->result_array();
if (empty($data_dp)) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] no COA found for uang muka item');
exit();
}
foreach ($data_dp as $key => $dp) { foreach ($data_dp as $key => $dp) {
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $dp['coaID'], 'coaID' => $dp['coaID'],
"coaDescription" => $dp['coaDescription'], 'coaDescription' => $dp['coaDescription'],
"debit" => $dp['SupplierDownpaymentAmount'], 'debit' => $dp['SupplierDownpaymentAmount'],
"credit" => 0, 'credit' => 0,
"addoncode" => "DP-JFA", 'addoncode' => 'DP-JFA',
"addonvalue" => $dp['SupplierPaymentNumber'], 'addonvalue' => $dp['SupplierPaymentNumber'],
"addonitemid" => $dp['M_ItemID'] 'addonitemid' => $dp['M_ItemID'],
]; ];
} }
} else { break;
# GET data hutang # case 'INSTALLMENT':
$sql_datahutang = "SELECT
coaID,
coaDescription,
SupplierInstallmentAmount,
M_ItemID,
SupplierPaymentNumber,
M_ItemID
FROM supplier_payment
JOIN supplier_installment
ON SupplierInstallmentID = SupplierPaymentSupplierInstallmentID
AND SupplierInstallmentIsActive = 'Y'
JOIN purchase_order_detail
ON SupplierInstallmentPurchaseOrderID = PurchaseOrderDetailPurchaseOrderID
AND PurchaseOrderDetailIsActive = 'Y'
JOIN m_item
ON M_ItemID = PurchaseOrderDetailItemID
AND M_ItemIsActive = 'Y'
JOIN fa_class
ON Fa_ClassID = M_ItemFa_ClassID
AND M_ItemIsActive = 'Y'
JOIN coa
ON coaID = Fa_ClassHutangCoaID
AND coaIsActive = 'Y'
WHERE SupplierPaymentID = ?";
$que_datahutang = $this->db->query($sql_datahutang, [
$param['orderid'],
]);
if (!$que_datahutang) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] get data hutang item cicilan');
exit();
}
$data_cicil = $que_datahutang->result_array();
if (empty($data_cicil)) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] no COA found for installment item');
exit();
}
foreach ($data_cicil as $key => $cicil) {
$detail_transac[] = [
'coaID' => $cicil['coaID'],
'coaDescription' => $cicil['coaDescription'],
'debit' => $cicil['SupplierInstallmentAmount'],
'credit' => 0,
'addoncode' => 'INST-JFA',
'addonvalue' => $cicil['SupplierPaymentNumber'],
'addonitemid' => $cicil['M_ItemID'],
];
}
break;
default:
// GET data hutang #
$sql_datahutang = "SELECT $sql_datahutang = "SELECT
SupplierPaymentNumber AS addonvalue, SupplierPaymentNumber AS addonvalue,
SupplierPaymentDetailID, SupplierPaymentDetailID,
@@ -207,33 +323,43 @@ class PaymentV2 extends MY_Controller
SupplierPaymentDetailAmount, SupplierPaymentDetailAmount,
SupplierInvoiceDetailItemID SupplierInvoiceDetailItemID
FROM supplier_payment FROM supplier_payment
JOIN supplier_payment_detail ON SupplierPaymentDetailSupplierPaymentID = SupplierPaymentID JOIN supplier_payment_detail
JOIN supplier_invoice_detail ON SupplierInvoiceDetailSupplierInvoiceID = SupplierPaymentSupplierInvoiceID ON SupplierPaymentDetailSupplierPaymentID = SupplierPaymentID
JOIN supplier_invoice_detail
ON SupplierInvoiceDetailSupplierInvoiceID = SupplierPaymentSupplierInvoiceID
AND SupplierInvoiceDetailIsActive = 'Y' AND SupplierInvoiceDetailIsActive = 'Y'
JOIN jurnal_tx ON SupplierPaymentDetailSupplierInvoiceDetailID = jurnalTxID JOIN jurnal_tx
ON SupplierPaymentDetailSupplierInvoiceDetailID = jurnalTxID
AND jurnalTxIsActive = 'Y'
WHERE SupplierPaymentID = ? WHERE SupplierPaymentID = ?
AND SupplierPaymentDetailIsActive = 'Y'"; AND SupplierPaymentDetailIsActive = 'Y'";
$que_datahutang = $this->db->query($sql_datahutang, [$param['orderid']]); $que_datahutang = $this->db->query($sql_datahutang, [$param['orderid']]);
if (!$que_datahutang) { if (!$que_datahutang) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data hutang item"); $this->sys_error_db('[Error] get data hutang item');
exit; exit();
} }
$data_hutang = $que_datahutang->result_array(); $data_hutang = $que_datahutang->result_array();
if (empty($data_hutang)) {
$this->db->trans_rollback();
$this->sys_error_db('[Error] no COA found for hutang item');
exit();
}
foreach ($data_hutang as $key => $debt) { foreach ($data_hutang as $key => $debt) {
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $debt['coaID'], 'coaID' => $debt['coaID'],
"coaDescription" => $debt['coaDescription'], 'coaDescription' => $debt['coaDescription'],
"debit" => $debt['SupplierPaymentDetailAmount'], 'debit' => $debt['SupplierPaymentDetailAmount'],
"credit" => 0, 'credit' => 0,
"addoncode" => "JFA", 'addoncode' => 'JFA',
"addonvalue" => $debt['addonvalue'], 'addonvalue' => $debt['addonvalue'],
"addonitemid" => $debt['SupplierInvoiceDetailItemID'] 'addonitemid' => $debt['SupplierInvoiceDetailItemID'],
]; ];
} }
break;
} }
# GET data bayar # // GET data bayar #
$sql_databayar = "SELECT $sql_databayar = "SELECT
CONCAT('Jurnal Payment Invoice Nomor : ', CONCAT('Jurnal Payment Invoice Nomor : ',
SupplierPaymentNumber, DATE_FORMAT(now(), SupplierPaymentNumber, DATE_FORMAT(now(),
@@ -257,68 +383,86 @@ class PaymentV2 extends MY_Controller
$que_databayar = $this->db->query($sql_databayar, [$param['orderid']]); $que_databayar = $this->db->query($sql_databayar, [$param['orderid']]);
if (!$que_databayar) { if (!$que_databayar) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data bayar"); $this->sys_error_db('[Error] get data bayar');
exit; exit();
} }
$data_bayar = $que_databayar->row_array(); $data_bayar = $que_databayar->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $data_bayar['coaID'], 'coaID' => $data_bayar['coaID'],
"coaDescription" => $data_bayar['coaDescription'], 'coaDescription' => $data_bayar['coaDescription'],
"debit" => 0, 'debit' => 0,
"credit" => abs($data_bayar['bayar']), 'credit' => abs($data_bayar['bayar']),
"addoncode" => "PAYINVSG", 'addoncode' => 'PAYINVSG',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
# rugi / untung dari rounding # // rugi / untung dari rounding #
$sql_lossprofit = "SELECT coaID, coaDescription $sql_lossprofit = "SELECT coaID, coaDescription
FROM coa WHERE coaAccountNo = ? AND coaIsActive = 'Y'"; FROM coa WHERE coaAccountNo = ? AND coaIsActive = 'Y'";
$data_round = doubleval($data_bayar['rounding']); $data_round = doubleval($data_bayar['rounding']);
if ($data_round > 0) { if ($data_round > 0) {
# loss # // loss #
$que_lossprofit = $this->db->query($sql_lossprofit, ['6120402001']); $que_lossprofit = $this->db->query($sql_lossprofit, ['6120402001']);
if (!$que_lossprofit) { if (!$que_lossprofit) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data coa loss sisa kas"); $this->sys_error_db('[Error] get data coa loss sisa kas');
exit; exit();
} }
$loss_coa = $que_lossprofit->row_array(); $loss_coa = $que_lossprofit->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $loss_coa['coaID'], 'coaID' => $loss_coa['coaID'],
"coaDescription" => $loss_coa['coaDescription'], 'coaDescription' => $loss_coa['coaDescription'],
"debit" => abs($data_round), 'debit' => abs($data_round),
"credit" => 0, 'credit' => 0,
"addoncode" => "PAYINVSG", 'addoncode' => 'PAYINVSG',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
} elseif ($data_round < 0) { } elseif ($data_round < 0) {
# profit # // profit #
$que_lossprofit = $this->db->query($sql_lossprofit, ['6110500001']); $que_lossprofit = $this->db->query($sql_lossprofit, ['6110500001']);
if (!$que_lossprofit) { if (!$que_lossprofit) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data coa profit sisa kas"); $this->sys_error_db('[Error] get data coa profit sisa kas');
exit; exit();
} }
$profit_coa = $que_lossprofit->row_array(); $profit_coa = $que_lossprofit->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $profit_coa['coaID'], 'coaID' => $profit_coa['coaID'],
"coaDescription" => $profit_coa['coaDescription'], 'coaDescription' => $profit_coa['coaDescription'],
"debit" => 0, 'debit' => 0,
"credit" => abs($data_round), 'credit' => abs($data_round),
"addoncode" => "PAYINVSG", 'addoncode' => 'PAYINVSG',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
} }
// "PI Cicilan Aset - {$invoiceNumber}";
$description = '';
// "Jurnal cicilan ke-{$cicilanKe} dari {$totalCicilan} | {$namaKontrak} | PO: {$nomorPO} | Periode {$periode}";
switch ($param['type']) {
case 'DP':
$data_bayar['jurnaltitle'] .= " | Pembayaran Uang Muka Aset - {$param['dpkode']}";
break;
case 'INSTALLMENT':
$cicil = $suppayment_detail[0];
$data_bayar['jurnaltitle'] .= " | Pembayaran Cicilan Aset - {$param['installmentkode']}";
$data_bayar['jurnaldesc'] .= " | Jurnal cicilan ke-{$cicil['qtyPaid']} dari {$cicil['qtyPay']}";
break;
default:
// code
break;
}
$this->insertJurnal($users, $data_bayar, $detail_transac); $this->insertJurnal($users, $data_bayar, $detail_transac);
$this->db->trans_commit(); $this->db->trans_commit();
$this->sys_ok("success update v2 test"); $this->sys_ok('success update v2 test');
} catch (Exception $exc) { } catch (Exception $exc) {
$message = $exc->getMessage(); $message = $exc->getMessage();
$this->sys_error($message); $this->sys_error($message);
@@ -329,22 +473,22 @@ class PaymentV2 extends MY_Controller
{ {
try { try {
if (!$this->isLogin) { if (!$this->isLogin) {
$this->sys_error("Invalid Token"); $this->sys_error('Invalid Token');
exit; exit();
} }
$this->db->trans_begin(); $this->db->trans_begin();
$param = $this->sys_input; $param = $this->sys_input;
$users = $this->sys_user; $users = $this->sys_user;
# Generate number supplier payment # // Generate number supplier payment #
$sql_gennumber = "SELECT `fn_numbering`(?) as numberx"; $sql_gennumber = 'SELECT `fn_numbering`(?) as numberx';
$paygroupnum = $this->db->query($sql_gennumber, ['PC'])->row()->numberx; $paygroupnum = $this->db->query($sql_gennumber, ['PC'])->row()->numberx;
if ($paygroupnum == '') { if ($paygroupnum == '') {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] generate number group supplier payment"); $this->sys_error_db('[Error] generate number group supplier payment');
exit; exit();
} }
$rounding = 0; $rounding = 0;
@@ -353,27 +497,26 @@ class PaymentV2 extends MY_Controller
$rounding = round($sisa, 2); $rounding = round($sisa, 2);
} }
$lastIndex = count($param['details']) - 1; $lastIndex = count($param['details']) - 1;
# LOOP details invoice payment # // LOOP details invoice payment #
foreach ($param['details'] as $idx => $obj) { foreach ($param['details'] as $idx => $obj) {
$paynumber = $this->db->query($sql_gennumber, ['PN'])->row()->numberx; $paynumber = $this->db->query($sql_gennumber, ['PN'])->row()->numberx;
if ($paynumber == '') { if ($paynumber == '') {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] generate number kasir supplier payment"); $this->sys_error_db('[Error] generate number kasir supplier payment');
exit; exit();
} }
$SupplierPaymentID = $obj['SupplierPaymentID']; $SupplierPaymentID = $obj['SupplierPaymentID'];
$SupplierInvoiceID = $obj['SupplierInvoiceID']; $SupplierInvoiceID = $obj['SupplierInvoiceID'];
# CEK index invoice terakhir # // CEK index invoice terakhir #
$roundvalue = 0; $roundvalue = 0;
if ($idx == $lastIndex) { if ($idx == $lastIndex) {
$roundvalue = $rounding; $roundvalue = $rounding;
} }
# UPDATE header supplier payment # // UPDATE header supplier payment #
$sql_updateheader = "UPDATE supplier_payment SET $sql_updateheader = "UPDATE supplier_payment SET
SupplierPaymentIsConfirm = 'Y', SupplierPaymentIsConfirm = 'Y',
SupplierPaymentConfirmUserID = ?, SupplierPaymentConfirmUserID = ?,
@@ -391,67 +534,60 @@ class PaymentV2 extends MY_Controller
$roundvalue, $roundvalue,
$param['keterangan'], $param['keterangan'],
$param['paymenttype'], $param['paymenttype'],
$SupplierPaymentID $SupplierPaymentID,
]); ]);
if (!$que_updateheader) { if (!$que_updateheader) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] update supplier payment header"); $this->sys_error_db('[Error] update supplier payment header');
exit; exit();
} }
# UPDATE status lunas supplier invoice # // UPDATE status lunas supplier invoice #
$sql_suppinvoice = "UPDATE supplier_invoice $sql_suppinvoice = "UPDATE supplier_invoice
SET SupplierInvoiceIsLunas = 'Y' SET SupplierInvoiceIsLunas = 'Y'
WHERE SupplierInvoiceID = ?"; WHERE SupplierInvoiceID = ?";
$que_suppinvoice = $this->db->query($sql_suppinvoice, [$SupplierInvoiceID]); $que_suppinvoice = $this->db->query($sql_suppinvoice, [$SupplierInvoiceID]);
if (!$que_suppinvoice) { if (!$que_suppinvoice) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] update status lunas invoice"); $this->sys_error_db('[Error] update status lunas invoice');
exit; exit();
} }
# GET Latest data supplier payment # // GET Latest data supplier payment #
$sql_suppayment = "SELECT * FROM supplier_payment WHERE SupplierPaymentID = ?"; $sql_suppayment = 'SELECT * FROM supplier_payment WHERE SupplierPaymentID = ?';
$que_suppayment = $this->db->query($sql_suppayment, [$SupplierPaymentID]); $que_suppayment = $this->db->query($sql_suppayment, [$SupplierPaymentID]);
if (!$que_suppayment) { if (!$que_suppayment) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data latest supplier payment"); $this->sys_error_db('[Error] get data latest supplier payment');
exit; exit();
} }
$suppayment_header = $que_suppayment->row_array(); $suppayment_header = $que_suppayment->row_array();
$sql_suppaymentdetail = "SELECT * FROM supplier_payment_detail $sql_suppaymentdetail = 'SELECT * FROM supplier_payment_detail
WHERE SupplierPaymentDetailSupplierPaymentID = ?"; WHERE SupplierPaymentDetailSupplierPaymentID = ?';
$que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$SupplierPaymentID]); $que_suppaymentdetail = $this->db->query($sql_suppaymentdetail, [$SupplierPaymentID]);
if (!$que_suppaymentdetail) { if (!$que_suppaymentdetail) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data latest supplier payment detail"); $this->sys_error_db('[Error] get data latest supplier payment detail');
exit; exit();
} }
$suppayment_detail = $que_suppaymentdetail->result_array(); $suppayment_detail = $que_suppaymentdetail->result_array();
$data_log = [ $data_log = [
"header" => $suppayment_header, 'header' => $suppayment_header,
"detail" => $suppayment_detail 'detail' => $suppayment_detail,
]; ];
# LOG activity confirm payment # // LOG activity confirm payment #
$messg = "Pembayaran Faktur No: {$suppayment_header['SupplierPaymentNumber']}"; $messg = "Pembayaran Faktur No: {$suppayment_header['SupplierPaymentNumber']}";
$messg .= " telah dikonfirmasi oleh {$users['M_UserUsername']} dengan nomor"; $messg .= " telah dikonfirmasi oleh {$users['M_UserUsername']} dengan nomor";
$messg .= " pembayaran kasir: {$suppayment_header['SupplierPaymentCashierNumber']}"; $messg .= " pembayaran kasir: {$suppayment_header['SupplierPaymentCashierNumber']}";
$this->insert_activity_log( $this->insert_activity_log('PF', 'CONFIRM', $messg, $SupplierPaymentID, $data_log, $users['M_UserID']);
"PF",
"CONFIRM",
$messg,
$SupplierPaymentID,
$data_log,
$users['M_UserID']
);
## INSERT jurnal ## // # INSERT jurnal ##
$detail_transac = []; $detail_transac = [];
# GET data hutang # // GET data hutang #
$sql_datahutang = "SELECT $sql_datahutang = "SELECT
SupplierPaymentNumber AS addonvalue, SupplierPaymentNumber AS addonvalue,
SupplierPaymentDetailID, SupplierPaymentDetailID,
@@ -469,23 +605,23 @@ class PaymentV2 extends MY_Controller
$que_datahutang = $this->db->query($sql_datahutang, [$SupplierPaymentID]); $que_datahutang = $this->db->query($sql_datahutang, [$SupplierPaymentID]);
if (!$que_datahutang) { if (!$que_datahutang) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data hutang item"); $this->sys_error_db('[Error] get data hutang item');
exit; exit();
} }
$data_hutang = $que_datahutang->result_array(); $data_hutang = $que_datahutang->result_array();
foreach ($data_hutang as $key => $debt) { foreach ($data_hutang as $key => $debt) {
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $debt['coaID'], 'coaID' => $debt['coaID'],
"coaDescription" => $debt['coaDescription'], 'coaDescription' => $debt['coaDescription'],
"debit" => $debt['SupplierPaymentDetailAmount'], 'debit' => $debt['SupplierPaymentDetailAmount'],
"credit" => 0, 'credit' => 0,
"addoncode" => "JFA", 'addoncode' => 'JFA',
"addonvalue" => $debt['addonvalue'], 'addonvalue' => $debt['addonvalue'],
"addonitemid" => $debt['SupplierInvoiceDetailItemID'] 'addonitemid' => $debt['SupplierInvoiceDetailItemID'],
]; ];
} }
# GET data bayar # // GET data bayar #
$sql_databayar = "SELECT $sql_databayar = "SELECT
CONCAT('Jurnal Payment Invoice Nomor : ', CONCAT('Jurnal Payment Invoice Nomor : ',
SupplierPaymentNumber, DATE_FORMAT(now(), SupplierPaymentNumber, DATE_FORMAT(now(),
@@ -509,61 +645,61 @@ class PaymentV2 extends MY_Controller
$que_databayar = $this->db->query($sql_databayar, [$SupplierPaymentID]); $que_databayar = $this->db->query($sql_databayar, [$SupplierPaymentID]);
if (!$que_databayar) { if (!$que_databayar) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data bayar"); $this->sys_error_db('[Error] get data bayar');
exit; exit();
} }
$data_bayar = $que_databayar->row_array(); $data_bayar = $que_databayar->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $data_bayar['coaID'], 'coaID' => $data_bayar['coaID'],
"coaDescription" => $data_bayar['coaDescription'], 'coaDescription' => $data_bayar['coaDescription'],
"debit" => 0, 'debit' => 0,
"credit" => abs($data_bayar['bayar']), 'credit' => abs($data_bayar['bayar']),
"addoncode" => "PAYINVGR", 'addoncode' => 'PAYINVGR',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
# rugi / untung dari rounding # // rugi / untung dari rounding #
$sql_lossprofit = "SELECT coaID, coaDescription $sql_lossprofit = "SELECT coaID, coaDescription
FROM coa WHERE coaAccountNo = ? AND coaIsActive = 'Y'"; FROM coa WHERE coaAccountNo = ? AND coaIsActive = 'Y'";
$data_round = doubleval($data_bayar['rounding']); $data_round = doubleval($data_bayar['rounding']);
if ($data_round > 0) { if ($data_round > 0) {
# loss # // loss #
$que_lossprofit = $this->db->query($sql_lossprofit, ['6120402001']); $que_lossprofit = $this->db->query($sql_lossprofit, ['6120402001']);
if (!$que_lossprofit) { if (!$que_lossprofit) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data coa loss sisa kas"); $this->sys_error_db('[Error] get data coa loss sisa kas');
exit; exit();
} }
$loss_coa = $que_lossprofit->row_array(); $loss_coa = $que_lossprofit->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $loss_coa['coaID'], 'coaID' => $loss_coa['coaID'],
"coaDescription" => $loss_coa['coaDescription'], 'coaDescription' => $loss_coa['coaDescription'],
"debit" => abs($data_round), 'debit' => abs($data_round),
"credit" => 0, 'credit' => 0,
"addoncode" => "PAYINVGR", 'addoncode' => 'PAYINVGR',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
} elseif ($data_round < 0) { } elseif ($data_round < 0) {
# profit # // profit #
$que_lossprofit = $this->db->query($sql_lossprofit, ['6110500001']); $que_lossprofit = $this->db->query($sql_lossprofit, ['6110500001']);
if (!$que_lossprofit) { if (!$que_lossprofit) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get data coa profit sisa kas"); $this->sys_error_db('[Error] get data coa profit sisa kas');
exit; exit();
} }
$profit_coa = $que_lossprofit->row_array(); $profit_coa = $que_lossprofit->row_array();
$detail_transac[] = [ $detail_transac[] = [
"coaID" => $profit_coa['coaID'], 'coaID' => $profit_coa['coaID'],
"coaDescription" => $profit_coa['coaDescription'], 'coaDescription' => $profit_coa['coaDescription'],
"debit" => 0, 'debit' => 0,
"credit" => abs($data_round), 'credit' => abs($data_round),
"addoncode" => "PAYINVGR", 'addoncode' => 'PAYINVGR',
"addonvalue" => $data_bayar['addonvalue'], 'addonvalue' => $data_bayar['addonvalue'],
"addonitemid" => 0 'addonitemid' => 0,
]; ];
} }
@@ -571,7 +707,7 @@ class PaymentV2 extends MY_Controller
} }
$this->db->trans_commit(); $this->db->trans_commit();
$this->sys_ok("[Success] success multi payment invoice"); $this->sys_ok('[Success] success multi payment invoice');
} catch (Exception $exc) { } catch (Exception $exc) {
$message = $exc->getMessage(); $message = $exc->getMessage();
$this->sys_error($message); $this->sys_error($message);
@@ -580,49 +716,49 @@ class PaymentV2 extends MY_Controller
private function insertJurnal($users, $databayar, $detailtrx) private function insertJurnal($users, $databayar, $detailtrx)
{ {
# GET periode jurnal # // GET periode jurnal #
$sql_periode = "SELECT periodeID FROM periode $sql_periode = "SELECT periodeID FROM periode
WHERE DATE(NOW()) BETWEEN periodeStartDate AND periodeEndDate WHERE DATE(NOW()) BETWEEN periodeStartDate AND periodeEndDate
AND periodeIsActive = 'Y' AND periodeIsClosed = 'N'"; AND periodeIsActive = 'Y' AND periodeIsClosed = 'N'";
$que_periode = $this->db->query($sql_periode, []); $que_periode = $this->db->query($sql_periode, []);
if (!$que_periode) { if (!$que_periode) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] find periode id"); $this->sys_error_db('[Error] find periode id');
exit; exit();
} }
if ($que_periode->num_rows() === 0) { if ($que_periode->num_rows() === 0) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] Waktu periode tidak ditemukan"); $this->sys_error_db('[Error] Waktu periode tidak ditemukan');
exit; exit();
} }
$JurnalPeriode = $que_periode->row_array()['periodeID']; $JurnalPeriode = $que_periode->row_array()['periodeID'];
# GENERATE nomor jurnal # // GENERATE nomor jurnal #
$sql_jurnalno = "SELECT `fn_numbering`('J') AS jnumber"; $sql_jurnalno = "SELECT `fn_numbering`('J') AS jnumber";
$que_jurnalno = $this->db->query($sql_jurnalno, []); $que_jurnalno = $this->db->query($sql_jurnalno, []);
if (!$que_jurnalno) { if (!$que_jurnalno) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] generate jurnal number"); $this->sys_error_db('[Error] generate jurnal number');
exit; exit();
} }
$JurnalNumber = $que_jurnalno->row_array()['jnumber']; $JurnalNumber = $que_jurnalno->row_array()['jnumber'];
$JurnalTitle = $databayar['jurnaltitle']; $JurnalTitle = $databayar['jurnaltitle'];
$JurnalDescp = $databayar['jurnaldesc']; $JurnalDescp = $databayar['jurnaldesc'];
# GET jurnal type # // GET jurnal type #
$sql_jurnaltype = "SELECT JurnalTypeID FROM jurnal_type $sql_jurnaltype = "SELECT JurnalTypeID FROM jurnal_type
WHERE JurnalTypeCode = 'PAYMENTINV' AND JurnalTypeIsActive = 'Y'"; WHERE JurnalTypeCode = 'PAYMENTINV' AND JurnalTypeIsActive = 'Y'";
$que_jurnaltype = $this->db->query($sql_jurnaltype, []); $que_jurnaltype = $this->db->query($sql_jurnaltype, []);
if (!$que_jurnaltype) { if (!$que_jurnaltype) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] get jurnal type"); $this->sys_error_db('[Error] get jurnal type');
exit; exit();
} }
$JurnalType = $que_jurnaltype->row_array()['JurnalTypeID']; $JurnalType = $que_jurnaltype->row_array()['JurnalTypeID'];
# INSERT jurnal header # // INSERT jurnal header #
$sql_insheader = "INSERT INTO jurnal ( $sql_insheader = 'INSERT INTO jurnal (
jurnalM_BranchCompanyID, jurnalM_BranchCompanyID,
JurnalS_RegionalID, JurnalS_RegionalID,
jurnalM_BranchCode, jurnalM_BranchCode,
@@ -633,7 +769,7 @@ class PaymentV2 extends MY_Controller
jurnalDate, jurnalDate,
jurnalJurnalTypeID, jurnalJurnalTypeID,
jurnalM_UserID jurnalM_UserID
) VALUES (?,?,?,?,?,?,?,NOW(),?,?)"; ) VALUES (?,?,?,?,?,?,?,NOW(),?,?)';
$que_inserjurnal = $this->db->query($sql_insheader, [ $que_inserjurnal = $this->db->query($sql_insheader, [
$users['M_BranchCompanyID'], $users['M_BranchCompanyID'],
$users['S_RegionalID'], $users['S_RegionalID'],
@@ -643,42 +779,42 @@ class PaymentV2 extends MY_Controller
$JurnalTitle, $JurnalTitle,
$JurnalDescp, $JurnalDescp,
$JurnalType, $JurnalType,
$users['M_UserID'] $users['M_UserID'],
]); ]);
if (!$que_inserjurnal) { if (!$que_inserjurnal) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] insert jurnal header"); $this->sys_error_db('[Error] insert jurnal header');
exit; exit();
} }
$JurnalID = $this->db->insert_id(); $JurnalID = $this->db->insert_id();
# INSERT jurnal tx # // INSERT jurnal tx #
foreach ($detailtrx as $key => $trax) { foreach ($detailtrx as $key => $trax) {
$sql_trax = "INSERT INTO jurnal_tx ( $sql_trax = 'INSERT INTO jurnal_tx (
jurnalTxJurnalID, jurnalTxJurnalID,
jurnalTxCoaID, jurnalTxCoaID,
jurnalTxDescription, jurnalTxDescription,
jurnalTxDebit, jurnalTxDebit,
jurnalTxCredit, jurnalTxCredit,
jurnalTxM_UserID jurnalTxM_UserID
) VALUES (?,?,?,?,?,?)"; ) VALUES (?,?,?,?,?,?)';
$que_trax = $this->db->query($sql_trax, [ $que_trax = $this->db->query($sql_trax, [
$JurnalID, $JurnalID,
$trax['coaID'], $trax['coaID'],
$trax['coaDescription'], $trax['coaDescription'],
$trax['debit'], $trax['debit'],
$trax['credit'], $trax['credit'],
$users['M_UserID'] $users['M_UserID'],
]); ]);
if (!$que_trax) { if (!$que_trax) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] insert jurnal tx"); $this->sys_error_db('[Error] insert jurnal tx');
exit; exit();
} }
$JurnalTxID = $this->db->insert_id(); $JurnalTxID = $this->db->insert_id();
# INSERT jurnal addon # // INSERT jurnal addon #
$sql_addon = "INSERT INTO jurnal_addon ( $sql_addon = 'INSERT INTO jurnal_addon (
jurnalAddOnJurnalID, jurnalAddOnJurnalID,
jurnalAddOnJurnalTxID, jurnalAddOnJurnalTxID,
jurnalAddOnCode, jurnalAddOnCode,
@@ -686,19 +822,19 @@ class PaymentV2 extends MY_Controller
jurnalAddOnM_ItemID, jurnalAddOnM_ItemID,
jurnalAddOnCreated, jurnalAddOnCreated,
jurnalAddOnCreatedUserID jurnalAddOnCreatedUserID
) VALUES (?,?,?,?,?,NOW(),?)"; ) VALUES (?,?,?,?,?,NOW(),?)';
$que_addon = $this->db->query($sql_addon, [ $que_addon = $this->db->query($sql_addon, [
$JurnalID, $JurnalID,
$JurnalTxID, $JurnalTxID,
$trax['addoncode'], $trax['addoncode'],
$trax['addonvalue'], $trax['addonvalue'],
$trax['addonitemid'], $trax['addonitemid'],
$users['M_UserID'] $users['M_UserID'],
]); ]);
if (!$que_addon) { if (!$que_addon) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] insert jurnal addon"); $this->sys_error_db('[Error] insert jurnal addon');
exit; exit();
} }
} }
} }
@@ -708,11 +844,11 @@ class PaymentV2 extends MY_Controller
$json = json_encode($data); $json = json_encode($data);
if (json_last_error() !== JSON_ERROR_NONE) { if (json_last_error() !== JSON_ERROR_NONE) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error("[Error] encode data into json for log activity"); $this->sys_error('[Error] encode data into json for log activity');
exit; exit();
} }
$sql = "INSERT INTO user_activity( $sql = 'INSERT INTO user_activity(
UserActivityCode, UserActivityCode,
UserActivityStatus, UserActivityStatus,
UserActivityDescription, UserActivityDescription,
@@ -720,19 +856,19 @@ class PaymentV2 extends MY_Controller
UserActivityData, UserActivityData,
UserActivityUserID, UserActivityUserID,
UserActivityCreated UserActivityCreated
) VALUES (?,?,?,?,?,?,NOW())"; ) VALUES (?,?,?,?,?,?,NOW())';
$que = $this->db->query($sql, [ $que = $this->db->query($sql, [
$actcode, $actcode,
$status, $status,
$desc, $desc,
$refID, $refID,
$json, $json,
$userID $userID,
]); ]);
if (!$que) { if (!$que) {
$this->db->trans_rollback(); $this->db->trans_rollback();
$this->sys_error_db("[Error] insert into table log activity"); $this->sys_error_db('[Error] insert into table log activity');
exit; exit();
} }
} }
} }

View File

@@ -114,9 +114,86 @@ class Bill extends MY_Controller
} }
} }
public function add_notes_installment($orderid)
{
$sql = "SELECT SupplierPaymentSupplierInstallmentID as note_order_id,
SupplierPaymentID as note_id,
SupplierInstallmentID as detail_id,
SupplierPaymentDate as note_date,
SupplierPaymentNumber as note_number,
GROUP_CONCAT(DISTINCT coaDescription separator ' , ') as paymenttypes_name,
SUM(SupplierInstallmentAmount) as note_amount,
n.M_UserUsername as note_user,
SupplierInstallmentIsActive as note_active,
'N' as show_detail,
SupplierPaymentNote as keterangan,
SupplierPaymentCoaID,
coaID,
coaDescription,
SupplierPaymentIsConfirm,
CONCAT(
'Confirmed by : ',c.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentConfirmDate,'%d-%m-%Y %H:%i')
) AS d_confirm,
SupplierPaymentIsApproved,
CONCAT(
'Approved by : ',a.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentApprovedDate,'%d-%m-%Y %H:%i')
) AS d_approved,
CONCAT(
'Verified by : ',b.M_UserUsername, ' ',
DATE_FORMAT(SupplierPaymentVerifDate,'%d-%m-%Y %H:%i')
) AS d_verif
FROM supplier_payment
JOIN supplier_installment
ON SupplierInstallmentID = SupplierPaymentSupplierInstallmentID
AND SupplierInstallmentIsActive = 'Y'
LEFT JOIN coa ON SupplierPaymentCoaID = coaID
LEFT JOIN m_user n ON SupplierPaymentUserID = n.M_UserID
LEFT JOIN m_user c ON SupplierPaymentConfirmUserID = c.M_UserID
LEFT JOIN m_user a ON SupplierPaymentApprovedUserID = a.M_UserID
LEFT JOIN m_user b ON SupplierPaymentVerifUserID = b.M_UserID
WHERE SupplierPaymentSupplierInstallmentID = {$orderid}
AND SupplierPaymentIsActive = 'Y'
GROUP BY SupplierPaymentID";
$query = $this->db_onedev->query($sql);
if ($query) {
$rows = $query->result_array();
return $rows ?: [];
} else {
$this->sys_error_db("get Installment notes", $this->db_onedev);
exit;
}
}
public function add_tagihans($orderid) public function add_tagihans($orderid)
{ {
$sql = "SELECT SupplierInvoiceID as tagihan_id, $sql_dp = "SELECT
Fa_ClassDownPaymentCoaID
FROM supplier_invoice_detail
JOIN m_item
ON M_ItemID = SupplierInvoiceDetailItemID
AND SupplierInvoiceDetailSupplierInvoiceID = ?
AND SupplierInvoiceDetailIsActive = 'Y'
AND SupplierInvoiceDetailIsVerified = 'Y'
JOIN fa_class
ON Fa_ClassID = M_ItemFa_ClassID
AND Fa_ClassIsActive = 'Y'";
$que_dp = $this->db_onedev->query($sql_dp, [$orderid]);
if (!$que_dp) {
$this->sys_error_db("failed to get coa id dp tagihans", $this->db_onedev);
exit;
}
$dataDP = $que_dp->result_array();
$excludeIds = array_column($dataDP, 'Fa_ClassDownPaymentCoaID');
$excludeIds[] = 563; // always excluded
$excludeIds = array_unique($excludeIds);
$excludeIds = array_values($excludeIds); // re-index
$notInPlaceholders = implode(',', array_fill(0, count($excludeIds), '?'));
$sql = "SELECT
SupplierInvoiceID as tagihan_id,
PurchaseOrderNumber as tagihan_number, PurchaseOrderNumber as tagihan_number,
jurnalTxDescription as pasien, jurnalTxDescription as pasien,
jurnalTxCredit as tagihan_total, jurnalTxCredit as tagihan_total,
@@ -127,25 +204,36 @@ class Bill extends MY_Controller
'N' as show_detail, 'N' as show_detail,
jurnalTxID SupplierInvoiceDetailID, jurnalTxID SupplierInvoiceDetailID,
PurchaseOrderID SupplierInvoiceDetailPurchaseOrderID PurchaseOrderID SupplierInvoiceDetailPurchaseOrderID
FROM supplier_invoice FROM supplier_invoice
JOIN receive_order_po ON ReceiveOrderPoID = SupplierInvoiceReceiveOrderPoID JOIN receive_order_po
JOIN receive_order_po_detail ON ReceiveOrderPoDetailReceiveOrderPoID = ReceiveOrderPoID ON ReceiveOrderPoID = SupplierInvoiceReceiveOrderPoID
JOIN purchase_order ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID JOIN receive_order_po_detail
JOIN jurnal_addon ON jurnalAddOnValue = SupplierInvoiceNumber ON ReceiveOrderPoDetailReceiveOrderPoID = ReceiveOrderPoID
JOIN jurnal_tx ON jurnalTxJurnalID = jurnalAddOnJurnalID AND jurnalTxCredit <> 0 AND jurnalTxCoaID <> 563 JOIN purchase_order
LEFT JOIN supplier_payment ON SupplierPaymentSupplierInvoiceID = SupplierInvoiceID AND SupplierInvoiceIsActive = 'Y' ON ReceiveOrderPoDetailPurchaseOrderID = PurchaseOrderID
LEFT JOIN supplier_payment_detail ON SupplierPaymentDetailSupplierPaymentID = SupplierPaymentID AND SupplierPaymentDetailSupplierInvoiceDetailID = jurnalTxID AND SupplierPaymentDetailIsActive = 'Y' JOIN jurnal_addon
WHERE ON jurnalAddOnValue = SupplierInvoiceNumber
SupplierInvoiceID = {$orderid} AND jurnalAddOnIsActive = 'Y'
GROUP BY jurnalTxID JOIN jurnal_tx
"; ON jurnalTxJurnalID = jurnalAddOnJurnalID
$query = $this->db_onedev->query($sql); AND jurnalTxIsActive = 'Y'
AND jurnalTxCredit <> 0
AND jurnalTxCoaID NOT IN ($notInPlaceholders)
LEFT JOIN supplier_payment
ON SupplierPaymentSupplierInvoiceID = SupplierInvoiceID
AND SupplierInvoiceIsActive = 'Y'
LEFT JOIN supplier_payment_detail
ON SupplierPaymentDetailSupplierPaymentID = SupplierPaymentID
AND SupplierPaymentDetailSupplierInvoiceDetailID = jurnalTxID
AND SupplierPaymentDetailIsActive = 'Y'
WHERE SupplierInvoiceID = ?
GROUP BY jurnalTxID";
$query = $this->db_onedev->query($sql, array_merge($excludeIds, [$orderid]));
if ($query) { if ($query) {
$rows = $query->result_array(); $rows = $query->result_array();
return $rows; return $rows;
} else { } else {
$this->sys_error_db("get notes", $this->db_onedev); $this->sys_error_db("get tagihans", $this->db_onedev);
exit; exit;
} }
} }
@@ -371,7 +459,24 @@ class Bill extends MY_Controller
$enddate, $enddate,
]; ];
// ── COUNT query — UNION of both sources ──────────────────── // ── INSTALLMENT WHERE ───────────────────────────────────
$inst_where = " inst.SupplierInstallmentIsActive = 'Y'
AND IF(sp.SupplierPaymentID IS NULL,'N','Y') = ?
AND CONCAT('INST-', si.SupplierInvoiceNumber) LIKE ?
AND sup.SupplierName LIKE ?
AND rop.ReceiveOrderPoS_RegionalID = ?
AND inst.SupplierInstallmentDueDate BETWEEN ? AND ? ";
$inst_params = [
$status,
'%' . $search . '%',
'%' . $supplier . '%',
$regionalid,
$startdate,
$enddate,
];
// ── COUNT query — UNION of all three sources ────────────
$sql_count = "SELECT SUM(cnt) as total FROM ( $sql_count = "SELECT SUM(cnt) as total FROM (
SELECT count(*) as cnt SELECT count(*) as cnt
FROM supplier_invoice FROM supplier_invoice
@@ -390,9 +495,24 @@ class Bill extends MY_Controller
JOIN purchase_order ON PurchaseOrderID = SupplierDownpaymentPurchasOrderID JOIN purchase_order ON PurchaseOrderID = SupplierDownpaymentPurchasOrderID
JOIN supplier ON SupplierID = SupplierDownpaymentSupplierID JOIN supplier ON SupplierID = SupplierDownpaymentSupplierID
WHERE $dp_where WHERE $dp_where
UNION ALL
SELECT count(*) as cnt
FROM supplier_installment inst
LEFT JOIN supplier_payment sp
ON sp.SupplierPaymentSupplierInstallmentID = inst.SupplierInstallmentID
AND sp.SupplierPaymentIsActive = 'Y'
JOIN supplier_invoice si
ON si.SupplierInvoiceID = inst.SupplierInstallmentSupplierInvoiceID
JOIN receive_order_po rop
ON rop.ReceiveOrderPoID = si.SupplierInvoiceReceiveOrderPoID
JOIN supplier sup
ON sup.SupplierID = inst.SupplierInstallmentSupplierID
WHERE $inst_where
) AS combined"; ) AS combined";
$count_params = array_merge($inv_params, $dp_params); $count_params = array_merge($inv_params, $dp_params, $inst_params);
$query = $this->db_onedev->query($sql_count, $count_params); $query = $this->db_onedev->query($sql_count, $count_params);
$tot_count = 0; $tot_count = 0;
@@ -410,6 +530,8 @@ class Bill extends MY_Controller
SELECT * FROM ( SELECT * FROM (
SELECT SELECT
SupplierInvoiceID, SupplierInvoiceID,
0 as DownpaymentID,
0 as InstallmentID,
SupplierInvoiceNumber, SupplierInvoiceNumber,
SupplierInvoiceDraftPaymentDate, SupplierInvoiceDraftPaymentDate,
SupplierInvoiceIsLunas as flaglunas, SupplierInvoiceIsLunas as flaglunas,
@@ -426,6 +548,7 @@ class Bill extends MY_Controller
'' as SupplierPaymentDate, '' as SupplierPaymentDate,
'' as notes, '' as notes,
'' as tagihans, '' as tagihans,
0 as parent_invoice_id,
'INVOICE' as type 'INVOICE' as type
FROM supplier_invoice FROM supplier_invoice
LEFT JOIN supplier ON SupplierInvoiceSupplierID = SupplierID LEFT JOIN supplier ON SupplierInvoiceSupplierID = SupplierID
@@ -440,14 +563,16 @@ class Bill extends MY_Controller
UNION ALL UNION ALL
SELECT SELECT
SupplierDownpaymentID * -1 as SupplierInvoiceID, 0 as SupplierInvoiceID,
SupplierDownpaymentID as DownpaymentID,
0 as InstallmentID,
CONCAT('DP-', PurchaseOrderNumber) as SupplierInvoiceNumber, CONCAT('DP-', PurchaseOrderNumber) as SupplierInvoiceNumber,
SupplierDownpaymentDueDate as SupplierInvoiceDraftPaymentDate, SupplierDownpaymentDueDate as SupplierInvoiceDraftPaymentDate,
SupplierDownpaymentIsLunas as flaglunas, SupplierDownpaymentIsLunas as flaglunas,
SupplierName, SupplierName,
DATE_FORMAT(SupplierDownpaymentDueDate,'%d-%m-%Y') as tanggalbayar, DATE_FORMAT(SupplierDownpaymentDueDate,'%d-%m-%Y') as tanggalbayar,
'N' as SupplierPaymentIsApproved, IFNULL(SupplierPaymentIsApproved,'N') as SupplierPaymentIsApproved,
'' as SupplierPaymentCashierNumber, IFNULL(SupplierPaymentCashierNumber,'') as SupplierPaymentCashierNumber,
SupplierDownpaymentAmount as totalbill, SupplierDownpaymentAmount as totalbill,
CASE WHEN SupplierDownpaymentStatus = 'Paid' CASE WHEN SupplierDownpaymentStatus = 'Paid'
THEN SupplierDownpaymentAmount THEN SupplierDownpaymentAmount
@@ -461,17 +586,65 @@ class Bill extends MY_Controller
'' as SupplierPaymentDate, '' as SupplierPaymentDate,
'' as notes, '' as notes,
'' as tagihans, '' as tagihans,
0 as parent_invoice_id,
'DP' as type 'DP' as type
FROM supplier_downpayment FROM supplier_downpayment
JOIN purchase_order ON PurchaseOrderID = SupplierDownpaymentPurchasOrderID JOIN purchase_order ON PurchaseOrderID = SupplierDownpaymentPurchasOrderID
JOIN supplier ON SupplierID = SupplierDownpaymentSupplierID JOIN supplier ON SupplierID = SupplierDownpaymentSupplierID
LEFT JOIN supplier_payment
ON SupplierPaymentSupplierDownpaymentID = SupplierDownpaymentID
AND SupplierPaymentIsActive = 'Y'
WHERE $dp_where WHERE $dp_where
UNION ALL
SELECT
0 as SupplierInvoiceID,
0 as DownpaymentID,
inst.SupplierInstallmentID as InstallmentID,
CONCAT('INST-', si.SupplierInvoiceNumber) as SupplierInvoiceNumber,
inst.SupplierInstallmentDueDate as SupplierInvoiceDraftPaymentDate,
inst.SupplierInstallmentIsLunas as flaglunas,
sup.SupplierName,
DATE_FORMAT(inst.SupplierInstallmentDueDate,'%d-%m-%Y') as tanggalbayar,
IFNULL(sp.SupplierPaymentIsApproved,'N') as SupplierPaymentIsApproved,
IFNULL(sp.SupplierPaymentCashierNumber,'') as SupplierPaymentCashierNumber,
inst.SupplierInstallmentAmount as totalbill,
CASE WHEN inst.SupplierInstallmentStatus = 'Paid'
THEN inst.SupplierInstallmentAmount
ELSE 0 END as paid,
CASE WHEN inst.SupplierInstallmentStatus = 'Paid'
THEN 0
ELSE inst.SupplierInstallmentAmount END as unpaid,
0 as SupplierPaymentID,
'' as SupplierPaymentNumber,
0 as SupplierPaymentAmount,
'' as SupplierPaymentDate,
'' as notes,
'' as tagihans,
si.SupplierInvoiceID as parent_invoice_id,
'INSTALLMENT' as type
FROM supplier_installment inst
LEFT JOIN supplier_payment sp
ON sp.SupplierPaymentSupplierInstallmentID = inst.SupplierInstallmentID
AND sp.SupplierPaymentIsActive = 'Y'
JOIN supplier_invoice si
ON si.SupplierInvoiceID = inst.SupplierInstallmentSupplierInvoiceID
JOIN receive_order_po rop
ON rop.ReceiveOrderPoID = si.SupplierInvoiceReceiveOrderPoID
JOIN supplier sup
ON sup.SupplierID = inst.SupplierInstallmentSupplierID
WHERE $inst_where
) AS combined ) AS combined
ORDER BY SupplierInvoiceID ASC ORDER BY SupplierInvoiceID ASC
LIMIT ? OFFSET ?"; LIMIT ? OFFSET ?";
$data_params = array_merge($inv_params, $dp_params, [$number_limit, $number_offset]); $data_params = array_merge($inv_params, $dp_params, $inst_params, [$number_limit, $number_offset]);
$query = $this->db_onedev->query($sql_data, $data_params); $query = $this->db_onedev->query($sql_data, $data_params);
if (!$query) {
$this->sys_error_db("payment instructions data", $this->db_onedev);
exit;
}
$rows = $query->result_array(); $rows = $query->result_array();
// ── ENRICH — per-row sub-queries ─────────────────────────── // ── ENRICH — per-row sub-queries ───────────────────────────
@@ -480,7 +653,7 @@ class Bill extends MY_Controller
// ── Downpayment branch ────────────────────────── // ── Downpayment branch ──────────────────────────
if ($v['type'] === 'DP') { if ($v['type'] === 'DP') {
$dp_id = abs($v['SupplierInvoiceID']); $dp_id = $v['DownpaymentID'];
$s_payment = $this->db_onedev->query(" $s_payment = $this->db_onedev->query("
SELECT SELECT
@@ -515,6 +688,44 @@ class Bill extends MY_Controller
continue; continue;
} }
// ── Installment branch ─────────────────────────
if ($v['type'] === 'INSTALLMENT') {
$inst_id = $v['InstallmentID'];
$parent_invoice_id = $v['parent_invoice_id'];
$s_payment = $this->db_onedev->query("
SELECT
GROUP_CONCAT(SupplierPaymentNumber SEPARATOR ', ') as SupplierPaymentNumber,
SUM(IFNULL(SupplierPaymentAmount,0)) as SupplierPaymentAmount,
IFNULL(SupplierPaymentID,0) as SupplierPaymentID,
GROUP_CONCAT(
DATE_FORMAT(SupplierPaymentDate,'%d-%m-%Y')
SEPARATOR ', '
) as SupplierPaymentDate
FROM supplier_payment
WHERE SupplierPaymentIsActive = 'Y'
AND SupplierPaymentSupplierInstallmentID = ?",
[$inst_id]
)->row();
$amount = $s_payment->SupplierPaymentAmount
? $s_payment->SupplierPaymentAmount
: "0.00";
$rows[$k]['SupplierPaymentID'] = $s_payment->SupplierPaymentID
? $s_payment->SupplierPaymentID
: '0';
$rows[$k]['SupplierPaymentNumber'] = $s_payment->SupplierPaymentNumber
? $s_payment->SupplierPaymentNumber
: '';
$rows[$k]['SupplierPaymentAmount'] = $amount;
$rows[$k]['SupplierPaymentDate'] = $s_payment->SupplierPaymentDate;
$rows[$k]['paid'] = $amount;
$rows[$k]['notes'] = $this->add_notes_installment($inst_id);
$rows[$k]['tagihans'] = $this->add_tagihans($parent_invoice_id);
// totalbill, unpaid already correct from UNION CASE
continue;
}
// ── Invoice branch ─────────────────────────────── // ── Invoice branch ───────────────────────────────
$inv_id = $v['SupplierInvoiceID']; $inv_id = $v['SupplierInvoiceID'];

File diff suppressed because it is too large Load Diff