Files
BE_CPONE/application/controllers/mockup/fo/cashiernewpayment-cpone-v3/Payment.php
2026-05-28 14:35:21 +07:00

1508 lines
41 KiB
PHP

<?php
class Payment extends MY_Controller
{
var $db_onedev;
public function index()
{
echo "API";
}
public function __construct()
{
parent::__construct();
$this->db_onedev = $this->load->database("onedev", true);
}
/**
** FUNCTIONS FITUR KIRIM WA KWITANSI START HERE
*/
public function kirim_bukti_tx_via_wa()
{
try {
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$this->db_onedev->trans_begin();
$prm = $this->sys_input;
$url = isset($prm['urlX']) ? $prm['urlX'] : "";
$concat_PID = "&PID=";
$T_OrderHeaderID = isset($prm['T_OrderHeaderID']) ? $prm['T_OrderHeaderID'] : 0;
$M_PatientHp = isset($prm['M_PatientHp']) ? $prm['M_PatientHp'] : "";
$M_PatientID = isset($prm['M_PatientID']) ? $prm['M_PatientID'] : 0;
$T_OrderHeaderLabNumber = isset($prm['T_OrderHeaderLabNumber']) ? $prm['T_OrderHeaderLabNumber'] : "";
$urlPrint = "";
$sql_pid = "SELECT F_PaymentID, T_OrderHeaderDate
FROM f_payment
LEFT JOIN t_orderheader ON T_OrderHeaderID = F_PaymentT_OrderHeaderID
WHERE F_PaymentT_OrderHeaderID = ?
ORDER BY F_PaymentID DESC
LIMIT 1";
$qpid = $this->db_onedev->query($sql_pid, [$T_OrderHeaderID]);
if (!$qpid || !$qpid->row_array()) {
$this->db_onedev->trans_rollback();
$this->sys_error_db("error select f_payment", $this->db_onedev);
exit;
}
$PID = $qpid->row_array()['F_PaymentID'];
$T_OrderHeaderDate = $qpid->row_array()['T_OrderHeaderDate'];
$hostname = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'];
$urlPrint .= $hostname . $url . $concat_PID . $PID;
// x_wa_outbox
$XWaOutboxSubject = "Kwitansi WA";
$XWaOutboxRecipientsNumber = $M_PatientHp;
$XWaOutboxRecipientsM_PatientID = $M_PatientID;
$XWaOutboxResultFileName = "kwitansi_" . $T_OrderHeaderLabNumber . ".pdf";
$XWaOutboxRefID = $T_OrderHeaderID;
$XWaOutboxBody = "";
$XWaOutboxLocalUrl = $urlPrint;
$XWaOutboxType = "KWITANSI";
// Kalau sudah pernah di kirim jangan di INSERT lagi
// Sebenarnya ketika XWaOutboxIsSent != '' sudah tidak bisa dikirim lagi dari FE
$check_sql = "SELECT XWaOutboxID FROM x_wa_outbox
WHERE XWaOutboxRefID = ?
OR XWaOutboxResultFileName = ?
OR XWaOutboxLocalUrl = ?
LIMIT 1";
$check_query = $this->db_onedev->query($check_sql, array(
$XWaOutboxRefID,
$XWaOutboxResultFileName,
$XWaOutboxLocalUrl
));
if ($check_query && $check_query->num_rows() > 0) {
$existing_record = $check_query->row();
$s_update = "UPDATE x_wa_outbox SET
XWaOutboxSubject = ?,
XWaOutboxRecipientsNumber = ?,
XWaOutboxRecipientsM_PatientID = ?,
XWaOutboxResultFileName = ?,
XWaOutboxResultDate = ?,
XWaOutboxBody = ?,
XWaOutboxLocalUrl = ?,
XWaOutboxType = ?,
XWaOutboxRefID = ?,
XWaOutboxLastUpdated = NOW(),
XWaOutboxIsSent = ?
WHERE XWaOutboxID = ?";
$qinsert = $this->db_onedev->query($s_update, array(
$XWaOutboxSubject,
$XWaOutboxRecipientsNumber,
$XWaOutboxRecipientsM_PatientID,
$XWaOutboxResultFileName,
$T_OrderHeaderDate,
$XWaOutboxBody,
$XWaOutboxLocalUrl,
$XWaOutboxType,
$XWaOutboxRefID,
'N', // XWaOutboxIsSent
$existing_record->XWaOutboxID
));
if (!$qinsert) {
$this->db_onedev->trans_rollback();
$this->sys_error_db("error update wa outbox", $this->db_onedev);
exit;
}
} else {
$s_insert = "INSERT INTO x_wa_outbox(
XWaOutboxSubject,
XWaOutboxRecipientsNumber,
XWaOutboxRecipientsM_PatientID,
XWaOutboxResultFileName,
XWaOutboxResultDate,
XWaOutboxBody,
XWaOutboxLocalUrl,
XWaOutboxType,
XWaOutboxRefID,
XWaOutboxIsSent
) VALUES (?,?,?,?,?,?,?,?,?, 'N')";
$qinsert = $this->db_onedev->query($s_insert, [
$XWaOutboxSubject,
$XWaOutboxRecipientsNumber,
$XWaOutboxRecipientsM_PatientID,
$XWaOutboxResultFileName,
$T_OrderHeaderDate,
$XWaOutboxBody,
$XWaOutboxLocalUrl,
$XWaOutboxType,
$XWaOutboxRefID
]);
if (!$qinsert) {
$this->db_onedev->trans_rollback();
$this->sys_error_db("error insert wa outbox", $this->db_onedev);
exit;
}
}
$this->db_onedev->trans_commit();
$result = array(
"message" => "Sukses Proses Insert Data",
"sql" => $this->db_onedev->last_query()
);
$this->sys_ok($result);
} catch (Exception $exc) {
$this->sys_error($exc->getMessage());
}
}
// * List Outbox yang mau dikirim
public function listOutbox()
{
try {
$prm = $this->sys_input;
$status = $prm["statusOutbox"];
$startDate = $prm["startDate"];
$endDate = $prm["endDate"];
$query = "SELECT
T_OrderHeaderID as orderID,
T_OrderHeaderLabNumber as orderNumber,
DATE_FORMAT(T_OrderHeaderDate, '%d-%m-%Y') as orderDate,
T_OrderHeaderM_PatientID as patientID,
DATE_FORMAT(M_PatientDOB, '%d%m%Y') as patientDOB,
M_PatientDOB,
CONCAT(IF(ISNULL(M_TitleName),'',CONCAT(M_TitleName,'.')),
' ',
IFNULL(M_PatientPrefix,''),
' ',
M_PatientName,
' ',
IFNULL(M_PatientSuffix,'')) as patientName,
M_PatientHp as patientHp,
M_PatientHp as patientHpOld,
CorporateName,
XWaOutboxID as sendWaID,
XWaOutboxIsSent,
IFNULL(XWaOutboxRetry , 0) as XWaOutboxIsRetry,
XWaOutboxCdnUrl as fileUrl,
XWaOutboxLocalUrl as localUrl,
XWaOutboxResultFilename as fileName,
DATE_FORMAT(XWaOutboxSentDate, '%d-%m-%Y %H:%i') as sentDate,
XWaOutboxType as sentType
FROM t_orderheader
JOIN x_wa_outbox
ON T_OrderHeaderID = XWaOutboxRefID
AND XWaOutboxID IS NOT NULL
AND XWaOutboxIsSent = ?
AND XWaOutboxIsActive = 'Y'
AND XWaOutboxType = 'KWITANSI'
JOIN m_patient
ON T_OrderHeaderM_PatientID = M_PatientID
LEFT JOIN m_title
ON M_PatientM_TitleID = M_TitleID
JOIN corporate ON T_OrderHeaderCorporateID = CorporateID
WHERE T_OrderHeaderIsActive = 'Y'
AND DATE(T_OrderHeaderDate) BETWEEN ? AND ? ";
$query = $this->db_onedev->query($query, [$status, $startDate, $endDate]);
if (!$query) {
$message = json_encode($this->db_onedev->error(), JSON_PRETTY_PRINT);
throw new Exception("Error executing query: " . $message);
}
$result = $query->result_array();
$this->sys_ok($result);
} catch (Exception $e) {
$msg = $e->getMessage();
$this->sys_error($msg);
exit;
}
}
// ** Upload File Kwitansi dari Birt ke CDN Qontak
public function uploadFile()
{
try {
$url = "https://service-chat.qontak.com/api/open/v1/file_uploader";
$fileName = $this->sys_input["fileName"];
$rpt_url_raw = $this->sys_input["rptUrl"];
$mimeType = $this->sys_input["mime"]; //application/pdf
// Breakdown rpt_url_raw ke scheme:https, host:devcpone, path:/birt/run, query:__report=...dst
$url_parts = parse_url($rpt_url_raw);
// Base url
$base_url = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'];
// Parse query ke array
$query_params = [];
if (isset($url_parts['query'])) {
parse_str($url_parts['query'], $query_params);
}
// Encode setiap query url dari array agar jadi url valid. Misal ada spasi atau escape character
$encoded_url = $base_url . '?' . http_build_query($query_params);
$fileContents = file_get_contents($encoded_url);
$this->db_onedev->trans_start();
// Jika file tidak ditemukan atau kosong
if ($fileContents === false || strlen($fileContents) === 0) {
// Return an error or handle it as needed
$resp = "Error: Gagal upload file ke CDN karena file local kosong atau tidak bisa diakses. Cek file di URL File Local: " . $rpt_url;
$sql = "UPDATE x_wa_outbox SET
XWaOutboxLastUpdated = NOW(),
XWaOutboxJsonQontak = ?
WHERE
XWaOutboxLocalUrl = ?
";
$query = $this->db_onedev->query($sql, [$resp, $rpt_url]);
if (!$query) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();
$this->sys_error([
"msg" => "Error change JSONQontak when upload file",
"error" => $message
]);
$this->db_onedev->trans_rollback();
exit;
}
$this->db_onedev->trans_complete();
$this->sys_error($resp);
exit;
}
$boundary = uniqid();
$body = "--$boundary\r\n" .
"Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"\r\n" .
"Content-Type: $mimeType\r\n\r\n" .
$fileContents . "\r\n" .
"--$boundary--\r\n";
// TODO: Need to be refactor to differentiate between Kwitansi dan Result WA
$query = "SELECT * FROM x_qontak_api
WHERE XQontakApiType = 'KWITANSI'
ORDER BY XQontakApiLastUpdated DESC LIMIT 1";
$configwa = $this->db_onedev->query($query)->result_array();
$token = $configwa[0]["XQontakApiToken"];
// Set cURL options
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$token}",
"Content-Type: multipart/form-data; boundary=$boundary"
],
CURLOPT_POSTFIELDS => $body
]);
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
$respArray = json_decode($response, true);
if ($respArray['status'] == "success") {
// Check if decoding was successful and access the "url"
if (isset($respArray['data']['url'])) {
$url = $respArray['data']['url'];
$sql = "UPDATE x_wa_outbox SET
XWaOutboxCdnUrl = ?,
XWaOutboxLastUpdated = NOW()
WHERE
XWaOutboxID = ? ";
$query = $this->db_onedev->query($sql, [$url, $this->sys_input["XWaOutboxID"]]);
if (!$query) {
$message = json_encode($this->db_onedev->error());
throw new Exception("Error updating CDN URL: " . $message);
}
$this->sys_ok([
"msg" => "Berhasil upload file dan update CDN",
"url" => $url
]);
} else {
throw new Exception("URL not found in response.");
}
}
if ($error) {
$err = json_encode(["status" => "ERR", "message" => $error]);
throw new Exception("cURL Error: " . $err);
}
$this->db_onedev->trans_complete();
} catch (Exception $e) {
$msg = $e->getMessage();
$this->db_onedev->trans_rollback();
$this->sys_error($msg);
exit;
}
}
// * Send WA Msg Using Qontak
public function qontakSendMsg()
{
try {
$url = "https://service-chat.qontak.com/api/open/v1/broadcasts/whatsapp/direct";
$query = "SELECT * FROM x_qontak_api
WHERE XQontakApiType = 'KWITANSI'
ORDER BY XQontakApiLastUpdated DESC LIMIT 1";
$configwa = $this->db_onedev->query($query)->result_array();
if (!$configwa) {
$err = json_encode($this->db_onedev->error());
throw new Exception("Error fetching Qontak API config: " . $err);
}
$token = $configwa[0]["XQontakApiToken"];
$wa_integration_id = $configwa[0]["XQontakApiWaIntegrationID"];
$template_id = $configwa[0]["XQontakApiTemplateID"];
$prm = $this->sys_input;
$orderID = $prm["orderID"];
$orderDate = $prm["orderDate"];
$patientName = $prm["patientName"];
$patientHp = $prm["patientHp"];
if (substr($patientHp, 0, 1) === "0") {
$patientHp = "62" . substr($patientHp, 1);
}
$corpName = $prm["corpName"];
$fileName = $prm["fileName"];
$statusOutbox = $prm["statusOutbox"];
$retryOutbox = $prm["retryOutbox"];
$outboxID = $prm["sendWaID"];
/* Ambil CDN Url */
$sql = "SELECT XWaOutboxCdnUrl as fileUrl FROM x_wa_outbox WHERE XWaOutboxID = ?";
$query = $this->db_onedev->query($sql, [$outboxID]);
if (!$query) {
$err = json_encode($this->db_onedev->error());
throw new Exception("Error fetching CDN URL: " . $err);
}
$sqlPayDate = "SELECT DATE_FORMAT(F_PaymentDate, '%d-%m-%Y') as F_PaymentDate FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
AND F_PaymentIsActive = 'Y' ORDER BY F_PaymentID DESC LIMIT 1";
$queryPayDate = $this->db_onedev->query($sqlPayDate, [$orderID]);
if (!$queryPayDate) {
$err = json_encode($this->db_onedev->error());
throw new Exception("Error fetching payment date: " . $err);
}
$tglBayar = $queryPayDate->row()->F_PaymentDate;
$uploaded_url_doc = $query->row_array()['fileUrl'];
// Kirim WA
$param = [
"to_name" => $patientName,
"to_number" => $patientHp,
"message_template_id" => $template_id,
"channel_integration_id" => $wa_integration_id,
"language" => [
"code" => "id"
],
"parameters" => [
"header" => [
"format" => "DOCUMENT",
"params" => [
[
"key" => "url",
"value" => $uploaded_url_doc
],
[
"key" => "filename",
"value" => $fileName
]
]
],
"body" => [
[
"key" => 1,
"value" => "nama_pasien",
"value_text" => $patientName
],
[
"key" => 2,
"value" => "tanggal",
"value_text" => $tglBayar
]
]
]
];
$json_param = json_encode($param);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $json_param,
CURLOPT_HTTPHEADER => array(
"Authorization: Bearer {$token}",
"Content-Type: application/json"
),
));
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
$respArray = json_decode($response, true);
if ($respArray['status'] == "success") {
$sql = "UPDATE x_wa_outbox SET
XWaOutboxIsSent = 'Y',
XWaOutboxRetry = 0,
XWaOutboxSentDate = NOW(),
XWaOutboxLastUpdated = NOW(),
XWaOutboxJsonQontak = ?
WHERE
XWaOutboxID = ?
";
$query = $this->db_onedev->query($sql, [json_encode($respArray), $outboxID]);
if (!$query) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();
$this->sys_error([
"msg" => "Error update outbox",
"error" => $message
]);
exit;
}
$this->sys_ok("Berhasil kirim wa dan update outbox");
exit;
} else {
$sql = "UPDATE x_wa_outbox SET
XWaOutboxIsSent = 'E',
XWaOutboxRetry = ?,
XWaOutboxSentDate = NOW(),
XWaOutboxLastUpdated = NOW(),
XWaOutboxJsonQontak = ?
WHERE
XWaOutboxID = ?
";
$query = $this->db_onedev->query($sql, [$retryOutbox, json_encode($respArray), $outboxID]);
if (!$query) {
$message = $this->db_onedev->error();
$message['qry'] = $this->db_onedev->last_query();
$this->sys_error([
"msg" => "Error update outbox",
"error" => $message
]);
exit;
}
$this->sys_error($respArray);
}
} catch (Exception $e) {
$msg = $e->getMessage();
// $this->db_onedev->trans_rollback(); // tidak perlu transaction karena hanya 1 update
$this->sys_error($msg);
exit;
}
}
public function changeStatusOutbox()
{
try {
$this->db_onedev->trans_start();
$prm = $this->sys_input;
$sql = "UPDATE x_wa_outbox
SET XWaOutboxIsSent = ?,
XWaOutboxRetry = ?,
XWaOutboxLastUpdated = NOW()
WHERE XWaOutboxID = ? ";
$query = $this->db_onedev->query($sql, [$prm["toStatus"], $prm["retry"], $prm["XWaOutboxID"]]);
if (!$query) {
$msg = $this->db_onedev->error();
throw new Exception($msg);
}
$this->db_onedev->trans_complete();
$this->sys_ok("Berhasil update status outbox");
} catch (Exceptions $e) {
$msg = $e->getMessage();
$this->db_onedev->trans_rollback();
$this->sys_error($msg);
exit;
}
}
/**
** FUNCTIONS FITUR KIRIM WA KWITANSI END HERE
*/
function getlanguages()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$query = "SELECT Nat_LangID as id,
Nat_LangCode as code,
Nat_LangName as name
FROM nat_lang WHERE Nat_LangIsActive = 'Y'";
$rows = $this->db_onedev->query($query)->result_array();
$this->sys_ok($rows);
exit;
}
function lookup_type()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$query = "SELECT M_PaymentTypeID as id,
M_PaymentTypeCode as code,
'N' as chex,
M_PaymentTypeName as chexlabel,
'Jumlah' as leftlabel,
'' as selected_card,
'' as selected_edc,
'' as selected_account,
'' as selected_promo,
CASE
WHEN M_PaymentTypeCode = 'CASH' THEN 'Kembali'
WHEN M_PaymentTypeCode = 'DEBIT' THEN 'Nomor Kartu'
WHEN M_PaymentTypeCode = 'CREDIT' THEN 'Nomor Kartu'
WHEN M_PaymentTypeCode = 'TRANSFER' THEN 'No. Rekening'
ELSE 'Nomor Voucher'
END as rightlabel,
0 as leftvalue,
CASE
WHEN M_PaymentTypeCode = 'VOUCHER' THEN ''
ELSE 0
END as rightvalue
FROM m_paymenttype
WHERE M_PaymentTypeIsActive = 'Y'";
$rows = $this->db_onedev->query($query)->result_array();
foreach ($rows as $k => $v) {
$rows[$k]['selected_card'] = array('id' => 0, 'name' => '');
$rows[$k]['selected_edc'] = array('id' => 0, 'name' => '');
$rows[$k]['selected_promo'] = array('id' => 0, 'name' => '', 'type' => '', 'value' => 0);
if ($v['chex'] == 'N')
$rows[$k]['chex'] = false;
else
$rows[$k]['chex'] = true;
}
$result = array(
"total" => count($rows),
"records" => $rows,
);
$this->sys_ok($result);
exit;
}
function lookup_banks()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$query = "SELECT Nat_BankID as id, Nat_BankCode as name
FROM nat_bank
WHERE
Nat_BankIsActive = 'Y'
ORDER BY Nat_BankCode DESC";
$rows = $this->db_onedev->query($query)->result_array();
$result = array(
"total" => count($rows),
"records" => $rows,
);
$this->sys_ok($result);
exit;
}
function lookup_accounts()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$query = "SELECT M_BankAccountID as id, CONCAT(Nat_BankCode,' (',M_BankAccountNo,')') as name
FROM m_bank_account
JOIN nat_bank ON M_BankAccountNat_BankID = Nat_BankID
WHERE
M_BankAccountIsActive = 'Y'
ORDER BY Nat_BankCode DESC";
$rows = $this->db_onedev->query($query)->result_array();
$result = array(
"total" => count($rows),
"records" => $rows,
);
$this->sys_ok($result);
exit;
}
function lookup_promos()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$bank_id = $prm['bank_id'];
$query = "SELECT DISTINCT
M_PromoID as id,
M_PromoName as name,
M_PromoDiscountType as type,
M_PromoValue as value
FROM m_promo
JOIN m_promo_bank ON M_PromoBankM_PromoID = M_PromoID
WHERE
M_PromoIsActive = 'Y'
AND M_PromoBankIsActive = 'Y'
AND M_PromoBankNat_BankID = ?
AND CURDATE() BETWEEN M_PromoStartDate AND M_PromoEndDate
ORDER BY M_PromoName DESC";
$qry = $this->db_onedev->query($query, array($bank_id));
if (!$qry) {
$this->sys_error_db("List Promo", $this->db_onedev);
exit;
}
$rows = $qry->result_array();
$result = array(
"total" => count($rows),
"records" => $rows,
);
$this->sys_ok($result);
exit;
}
function searchcard()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$max_rst = 12;
$tot_count = 0;
$q = [
'search' => '%'
];
if ($prm['search'] != '') {
$q['search'] = "%{$prm['search']}%";
}
// QUERY TOTAL
if ($prm['search'] != '') {
$sql = "
SELECT count(*) as total
FROM nat_bank
WHERE
Nat_BankName like ?
AND Nat_BankIsActive = 'Y'
ORDER BY Nat_BankName DESC
";
} else {
$sql = "
SELECT count(*) as total
FROM nat_bank
WHERE
Nat_BankIsActive = 'Y'
ORDER BY Nat_BankName DESC
";
}
$query = $this->db_onedev->query($sql, $q['search']);
//echo $query;
if ($query) {
$tot_count = $query->result_array()[0]["total"];
} else {
$this->sys_error_db("m_city count", $this->db_onedev);
exit;
}
if ($prm['search'] != '') {
$sql = "
SELECT Nat_BankID as id, Nat_BankName as name
FROM nat_bank
WHERE
Nat_BankName like ?
AND Nat_BankIsActive = 'Y'
ORDER BY Nat_BankName DESC
";
} else {
$sql = "
SELECT Nat_BankID as id, Nat_BankName as name
FROM nat_bank
WHERE
Nat_BankIsActive = 'Y'
ORDER BY Nat_BankName DESC
";
}
$query = $this->db_onedev->query($sql, array($q['search']));
if ($query) {
$rows = $query->result_array();
//echo $this->db_onedev->last_query();
$result = array("total" => $tot_count, "records" => $rows, "total_display" => sizeof($rows));
$this->sys_ok($result);
} else {
$this->sys_error_db("m_city rows", $this->db_onedev);
exit;
}
}
private function _getVoucherNumbersFromPayments($payments)
{
$numbers = [];
foreach ($payments as $v) {
if (empty($v['chex'])) continue;
$code = $v['code'] ?? '';
if ($code !== 'VOUCHER') continue;
$vn = trim((string)($v['rightvalue'] ?? ''));
if ($vn !== '') $numbers[] = $vn;
}
$numbers = array_values(array_unique($numbers));
return $numbers;
}
public function pay()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$this->db_onedev->trans_begin();
try {
$xuserid = (int)($this->sys_user['M_UserID'] ?? 0);
$prm = $this->sys_input;
$orderid = (int)($prm['orderid'] ?? 0);
$payments = (array)($prm['payments'] ?? []);
$voucherNumbersFromReq = $this->_getVoucherNumbersFromPayments($payments);
if (count($voucherNumbersFromReq) > 1) {
throw new Exception("Tidak bisa menggunakan lebih dari 1 voucher dalam 1 order.");
}
$hasVoucherUsed = $this->db_onedev->query(
"SELECT VoucherDetailID
FROM voucher_detail
WHERE VoucherDetailT_OrderHeaderID = ?
AND VoucherDetailIsActive = 'Y'
AND VoucherDetailIsUsed = 'Y'
LIMIT 1",
[$orderid]
)->row_array();
if ($hasVoucherUsed) {
throw new Exception("Tidak bisa menggunakan Voucher lagi.");
}
if ($orderid <= 0) {
throw new Exception("OrderID tidak valid");
}
if ($xuserid <= 0) {
throw new Exception("User tidak valid");
}
$rowOrder = $this->db_onedev->query(
"SELECT T_OrderHeaderTotal AS total
FROM t_orderheader
WHERE T_OrderHeaderID = ?
LIMIT 1",
[$orderid]
)->row_array();
if (! $rowOrder) {
throw new Exception("Order tidak ditemukan");
}
$bill_total = (int)$rowOrder['total'];
$rowPaidBefore = $this->db_onedev->query(
"SELECT IFNULL(SUM(F_PaymentTotal),0) AS total_paid
FROM f_payment
WHERE F_PaymentT_OrderHeaderID = ?
AND F_PaymentIsActive = 'Y'",
[$orderid]
)->row_array();
$paid_before = (int)($rowPaidBefore['total_paid'] ?? 0);
$requested_paid = 0;
$promo_bill_total = null;
foreach ($payments as $v) {
if (empty($v['chex'])) continue;
$code = (string)($v['code'] ?? '');
$left = (int)($v['leftvalue'] ?? 0);
$right = (int)($v['rightvalue'] ?? 0);
if ($left < 0) $left = 0;
if ($code === 'CASH') {
$net = $left;
if ($left > 0) {
$net = $left - max(0, $right);
}
if ($net < 0) $net = 0;
$requested_paid += $net;
} else {
$requested_paid += $left;
}
if ($code === 'PROMO') {
$promo_bill_total = $left;
}
}
if ($promo_bill_total !== null) {
$bill_total = (int)$promo_bill_total;
}
$remaining_before_tx = $bill_total - $paid_before;
if ($remaining_before_tx < 0) $remaining_before_tx = 0;
$paid_to_record = min($requested_paid, $remaining_before_tx);
$qHeader = $this->db_onedev->query(
"INSERT INTO f_payment(
F_PaymentT_OrderHeaderID,
F_PaymentDate,
F_PaymentTotal,
F_PaymentCreated,
F_PaymentM_UserID
) VALUES (?, CURDATE(), ?, NOW(), ?)",
[$orderid, $paid_to_record, $xuserid]
);
if (! $qHeader) {
$this->sys_error_db("f_payment insert", $this->db_onedev);
exit;
}
$headerid = (int)$this->db_onedev->insert_id();
$remaining_alloc = $paid_to_record;
$voucherUsedNumbers = [];
$voucherRestRows = [];
foreach ($payments as $v) {
if (empty($v['chex'])) continue;
//if ($remaining_alloc <= 0) break;
$code = (string)($v['code'] ?? '');
$typeId = (int)($v['id'] ?? 0);
if ($typeId <= 0) continue;
if ($code === 'CASH') {
$actual = (int)($v['leftvalue'] ?? 0);
$change = (int)($v['rightvalue'] ?? 0);
if($actual > 0){
$amount = intval($v['leftvalue']) - intval($v['rightvalue']);
}
else{
$amount = $actual;
}
$q = $this->db_onedev->query(
"INSERT INTO f_paymentdetail(
F_PaymentDetailF_PaymentID,
F_PaymentDetailM_PaymentTypeID,
F_PaymentDetailAmount,
F_PaymentDetailActual,
F_PaymentDetailChange,
F_PaymentDetailCreated,
F_PaymentDetailLastUpdated,
F_PaymentDetailUserID
) VALUES (?,?,?,?,?,NOW(),NOW(),?)",
[$headerid, $typeId, $amount, $actual, $change, $xuserid]
);
if (! $q) {
$this->sys_error_db("f_paymentdetail cash insert", $this->db_onedev);
exit;
}
$remaining_alloc -= $amount;
continue;
}
$amount_req = (int)($v['leftvalue'] ?? 0);
if ($amount_req < 0) $amount_req = 0;
$amount = min($amount_req, $remaining_alloc);
$selected_card = (int)($v['selected_card']['id'] ?? 0);
$selected_edc = (int)($v['selected_edc']['id'] ?? 0);
$selected_account = (int)($v['selected_account']['id'] ?? 0);
$selected_promo = (int)($v['selected_promo']['id'] ?? 0);
$bankAccountId = 0;
if ($code === 'TRANSFER' || $code === 'QRIS') {
$bankAccountId = $selected_account;
} else {
$bankAccountId = $selected_edc;
}
$q = $this->db_onedev->query(
"INSERT INTO f_paymentdetail(
F_PaymentDetailF_PaymentID,
F_PaymentDetailM_PaymentTypeID,
F_PaymentDetailAmount,
F_PaymentDetailActual,
F_PaymentDetailChange,
F_PaymentDetailCardNat_BankID,
F_PaymentDetailEDCNat_BankID,
F_PaymentDetailM_BankAccountID,
F_PaymentDetailCreated,
F_PaymentDetailLastUpdated,
F_PaymentDetailUserID
) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW(),?)",
[$headerid, $typeId, $amount, 0, 0, $selected_card, 0, $bankAccountId, $xuserid]
);
if (! $q) {
$this->sys_error_db("f_paymentdetail non cash insert", $this->db_onedev);
exit;
}
$paymentDetailId = (int)$this->db_onedev->insert_id();
if ($code === 'PROMO') {
if ($selected_promo <= 0) {
throw new Exception("Promo tidak valid.");
}
$qPromo = $this->db_onedev->query(
"INSERT INTO t_promo(
T_PromoT_OrderHeaderID,
T_PromoF_PaymentDetailID,
T_PromoF_PaymentID,
T_PromoM_PromoID,
T_PromoIsActive,
T_PromoCreated,
T_PromoCreatedUserID
) VALUES (?,?,?,?, 'Y', NOW(), ?)",
[$orderid, $paymentDetailId, $headerid, $selected_promo, $xuserid]
);
if (! $qPromo) {
$this->sys_error_db("t_promo insert", $this->db_onedev);
exit;
}
}
if ($code === 'VOUCHER') {
$vn = trim((string)($v['rightvalue'] ?? ''));
if ($vn !== '' && $amount > 0) {
$voucherUsedNumbers[] = $vn;
$rest = $amount_req - $amount;
if ($rest > 0) {
$voucherRestRows[] = [
'voucher_number' => $vn,
'rest_value' => $rest,
'payment_detail_id' => $paymentDetailId,
];
}
}
}
$remaining_alloc -= $amount;
}
$paid_all = 0;
$unpaid = 0;
$sql = "SELECT SUM(F_PaymentTotal) AS total_paid, T_OrderHeaderTotal AS total_bill
FROM
f_payment
JOIN t_orderheader ON T_OrderHeaderID = F_PaymentT_OrderHeaderID AND T_OrderHeaderIsActive = 'Y'
WHERE F_PaymentT_OrderHeaderID = ? AND F_PaymentIsActive = 'Y'
GROUP BY F_PaymentT_OrderHeaderID";
$row = $this->db_onedev->query($sql, [$orderid])->row_array();
$paid_all = (int)($row['total_paid'] ?? 0);
$unpaid = (int)($row['total_bill'] ?? 0) - $paid_all;
$lunas = ($unpaid <= 0) ? 'Y' : 'N';
$last = $this->db_onedev->query(
"SELECT Last_StatusPaymentID
FROM last_statuspayment
WHERE Last_StatusPaymentT_OrderHeaderID = ?
AND Last_StatusPaymentIsActive = 'Y'
LIMIT 1",
[$orderid]
)->row_array();
if ($last) {
$q = $this->db_onedev->query(
"UPDATE last_statuspayment SET
Last_StatusPaymentBillTotal = ?,
Last_StatusPaymentPaid = ?,
Last_StatusPaymentUnpaid = ?,
Last_StatusPaymentIsLunas = ?,
Last_StatusPaymentLastUpdated = NOW()
WHERE Last_StatusPaymentID = ?",
[$row['total_bill'], $paid_all, $unpaid, $lunas, (int)$last['Last_StatusPaymentID']]
);
if (! $q) {
$this->sys_error_db("last_statuspayment update", $this->db_onedev);
exit;
}
} else {
$q = $this->db_onedev->query(
"INSERT INTO last_statuspayment (
Last_StatusPaymentT_OrderHeaderID,
Last_StatusPaymentBillTotal,
Last_StatusPaymentPaid,
Last_StatusPaymentUnpaid,
Last_StatusPaymentIsLunas,
Last_StatusPaymentCreated,
Last_StatusPaymentUserID
) VALUES (?,?,?,?,?,NOW(),?)",
[$orderid, $row['total_bill'], $paid_all, $unpaid, $lunas, $xuserid]
);
if (! $q) {
$this->sys_error_db("last_statuspayment insert", $this->db_onedev);
exit;
}
}
$voucherUsedNumbers = array_values(array_unique($voucherUsedNumbers));
foreach ($voucherUsedNumbers as $vn) {
$q = $this->db_onedev->query(
"UPDATE voucher_detail
SET VoucherDetailIsUsed = 'Y',
VoucherDetailT_OrderHeaderID = ?,
VoucherDetailUserID = ?,
VoucherDetailLastUpdated = NOW()
WHERE VoucherDetailNumber = ?
AND VoucherDetailIsUsed = 'N'
AND VoucherDetailIsActive = 'Y'
LIMIT 1",
[$orderid, $xuserid, $vn]
);
if (! $q) {
$this->sys_error_db("gagal update voucher_detail", $this->db_onedev);
exit;
}
if ($this->db_onedev->affected_rows() === 0) {
throw new Exception("Voucher {$vn} sudah digunakan / tidak valid");
}
}
if (!empty($voucherRestRows)) {
foreach ($voucherRestRows as $vr) {
$vn = $vr['voucher_number'];
$restValue = (int)$vr['rest_value'];
$paymentDetailId = (int)$vr['payment_detail_id'];
if ($restValue <= 0) continue;
$rowVd = $this->db_onedev->query(
"SELECT VoucherDetailID
FROM voucher_detail
WHERE VoucherDetailNumber = ?
LIMIT 1",
[$vn]
)->row_array();
if (!$rowVd) {
throw new Exception("Voucher detail tidak ditemukan untuk nomor {$vn}");
}
$voucherDetailId = (int)$rowVd['VoucherDetailID'];
$qRest = $this->db_onedev->query(
"INSERT INTO t_voucher_rest(
T_VoucherRestT_OrderHeaderID,
T_VoucherRestF_PaymentDetailID,
T_VoucherRestF_PaymentID,
T_VoucherRestVoucherDetailID,
T_VoucherRestValue,
T_VoucherRestIsActive,
T_VoucherRestCreated,
T_VoucherRestCreatedUserID
) VALUES (?,?,?,?,?,'Y',NOW(),?)",
[
$orderid,
$paymentDetailId,
$headerid,
$voucherDetailId,
$restValue,
$xuserid
]
);
if (!$qRest) {
$this->sys_error_db("gagal insert t_voucher_rest", $this->db_onedev);
exit;
}
}
}
$row_menu = $this->db_onedev->query(
"SELECT * FROM s_menu
WHERE S_MenuIsActive = 'Y'
AND S_MenuName = 'Registration (Walk In)'
LIMIT 1"
)->row_array();
$rows = $this->db_onedev->query("
SELECT
M_PaymentTypeID as id,
M_PaymentTypeCode as code,
IF(M_PaymentTypeCode = 'CASH','Y','N') as chex,
M_PaymentTypeName as chexlabel,
'Jumlah' as leftlabel,
CASE
WHEN M_PaymentTypeCode = 'CASH' THEN 'Kembali'
WHEN M_PaymentTypeCode = 'DEBIT' THEN 'Nomor Kartu'
WHEN M_PaymentTypeCode = 'CREDIT' THEN 'Nomor Kartu'
WHEN M_PaymentTypeCode = 'TRANSFER' THEN 'Nomor Rekening'
WHEN M_PaymentTypeCode = 'QRIS' THEN 'Nomor Rekening'
ELSE 'Nomor Voucher'
END as rightlabel,
0 as leftvalue,
CASE WHEN M_PaymentTypeCode = 'VOUCHER' THEN '' ELSE 0 END as rightvalue
FROM m_paymenttype
WHERE M_PaymentTypeIsActive = 'Y'
")->result_array();
foreach ($rows as $k => $v) {
$rows[$k]['chex'] = ($v['chex'] === 'Y');
}
$xdata = $this->db_onedev->query(
"SELECT F_PaymentID as idx, F_PaymentNumber as numberx
FROM f_payment
WHERE F_PaymentID = ?",
[$headerid]
)->row();
$this->db_onedev->trans_commit();
$this->sys_ok([
"total" => count($rows),
"records" => [
"payments" => $payments,
"types" => $rows,
"data" => $xdata
],
"menu_walk_in" => $row_menu ? $row_menu['S_MenuUrl'] : ''
]);
exit;
} catch (Exception $e) {
$this->db_onedev->trans_rollback();
$this->sys_error($e->getMessage());
exit;
}
}
function delete_note()
{
//# cek token valid
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
//# ambil parameter input
$xuserid = $this->sys_user['M_UserID'];
$prm = $this->sys_input;
$prmnota = $prm['nota'];
$catatan = $prm['catatan'];
$sql = "UPDATE f_payment SET F_PaymentIsActive = 'N', F_PaymentNote = '{$catatan}' WHERE F_PaymentID = {$prmnota['note_id']}";
//echo $sql;
$query = $this->db_onedev->query($sql);
if (!$query) {
$this->sys_error_db("f_payment delete");
exit;
}
$sql = "UPDATE f_paymentdetail SET F_PaymentDetailIsActive = 'N' WHERE F_PaymentDetailF_PaymentID = {$prmnota['note_id']}";
//echo $sql;
$query = $this->db_onedev->query($sql);
if (!$query) {
$this->sys_error_db("f_paymentdetail delete");
exit;
}
$result = array(
"total" => 1,
"records" => array('prm' => $prm)
);
$this->sys_ok($result);
exit;
}
function getLocations()
{
$prm = $this->sys_input;
$station_location = [];
$locations = [];
$sql = "SELECT T_OrderDetailT_OrderHeaderID as order_id, T_SampleStationID as station_id, T_SampleStationName as station_name,
fn_get_location(T_SampleStationID,T_OrderDetailT_OrderHeaderID) as location_id, '' locations
FROM (
SELECT distinct T_OrderDetailT_OrderHeaderID,T_SampleStationID, T_SampleStationName
FROM t_orderdetail
JOIN t_test ON T_OrderDetailT_TestID = T_TestID
JOIN t_sampletype ON T_SampleTypeID = T_TestT_SampleTypeID
JOIN t_bahan ON T_SampleTypeT_BahanID = T_BahanID
JOIN t_samplestation ON T_BahanT_SampleStationID = T_SampleStationID
WHERE
T_OrderDetailT_OrderHeaderID = ? AND T_OrderDetailIsActive = 'Y'
) x";
$query = $this->db_onedev->query($sql, array($prm['order_id']));
//echo $this->db_onedev->last_query();
if ($query) {
$datas = $query->result_array();
foreach ($datas as $key => $value) {
$sql = "SELECT M_LocationID as location_id, M_LocationName as location_name FROM m_location WHERE M_LocationT_SampleStationID = ? AND M_LocationIsActive = 'Y' ";
$query = $this->db_onedev->query($sql, array($value['station_id']));
if ($query) {
$datas[$key]['locations'] = $query->result_array();
} else {
$datas[$key]['locations'] = [];
}
}
$this->sys_ok(["datas" => $datas]);
} else {
echo $this->db_onedev->last_query();
$this->sys_error_db("gagal ambil data", $this->db_onedev);
exit;
}
}
function save_control()
{
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$userid = $this->sys_user['M_UserID'];
if ($prm['data'] && count($prm['data']) > 0) {
foreach ($prm['data'] as $key => $value) {
$sql = "INSERT INTO t_order_location (
T_OrderLocationT_OrderHeaderID,
T_OrderLocationM_LocationID,
T_OrderLocationT_SampleStationID,
T_OrderLocationCreated,
T_OrderLocationLastUpdated,
T_OrderLocationUserID
)
VALUES (?,?,?,NOW(),NOW(),?)
ON DUPLICATE KEY
UPDATE T_OrderLocationT_OrderHeaderID = ?,
T_OrderLocationM_LocationID = ?,
T_OrderLocationT_SampleStationID = ?,
T_OrderLocationLastUpdated = NOW(),
T_OrderLocationUserID = ?";
$query = $this->db_onedev->query($sql, array($value['order_id'], $value['location_id'], $value['station_id'], $userid, $value['order_id'], $value['location_id'], $value['station_id'], $userid));
}
$this->sys_ok(["datas" => '']);
} else {
$this->sys_error_db("data not valid", $this->db_onedev);
exit;
}
//echo $sql;
}
public function validate_voucher()
{
try {
if (! $this->isLogin) {
$this->sys_error("Invalid Token");
exit;
}
$prm = $this->sys_input;
$voucherNumber = isset($prm['voucherNumber']) ? trim($prm['voucherNumber']) : "";
$orderid = isset($prm['orderid']) ? (int)$prm['orderid'] : 0;
if ($voucherNumber == "") {
$this->sys_ok([
"code" => "N",
"message" => "Kode voucher kosong",
"amount" => 0
]);
exit;
}
if ($orderid > 0) {
$sqlOrderVoucher = "
SELECT VoucherDetailID
FROM voucher_detail
WHERE VoucherDetailT_OrderHeaderID = ?
AND VoucherDetailIsActive = 'Y'
AND VoucherDetailIsUsed = 'Y'
LIMIT 1
";
$qOrderVoucher = $this->db_onedev->query($sqlOrderVoucher, [$orderid]);
if ($qOrderVoucher && $qOrderVoucher->num_rows() > 0) {
$this->sys_ok([
"code" => "X",
"message" => "Tidak bisa menggunakan Voucher lagi",
"amount" => 0
]);
exit;
}
}
$sqlDetail = "
SELECT
vd.VoucherDetailID,
vd.VoucherDetailVoucherHeaderID,
vd.VoucherDetailNumber,
vd.VoucherDetailIsUsed,
vd.VoucherDetailIsActive
FROM voucher_detail vd
WHERE vd.VoucherDetailNumber = ?
LIMIT 1
";
$qDetail = $this->db_onedev->query($sqlDetail, [$voucherNumber]);
if (!$qDetail || !$qDetail->row_array()) {
$this->sys_ok([
"code" => "N",
"message" => "Voucher tidak ditemukan",
"amount" => 0
]);
exit;
}
$detail = $qDetail->row_array();
if (($detail['VoucherDetailIsActive'] ?? 'N') != 'Y') {
$this->sys_ok([
"code" => "N",
"message" => "Voucher tidak aktif",
"amount" => 0
]);
exit;
}
if (($detail['VoucherDetailIsUsed'] ?? 'N') == 'Y') {
$this->sys_ok([
"code" => "N",
"message" => "Voucher sudah digunakan",
"amount" => 0
]);
exit;
}
$sqlHeader = "
SELECT
vh.VoucherHeaderID,
vh.VoucherHeaderAmount,
vh.VoucherHeaderStartDate,
vh.VoucherHeaderEndDate,
vh.VoucherHeaderIsActive
FROM voucher_header vh
WHERE vh.VoucherHeaderID = ?
LIMIT 1
";
$qHeader = $this->db_onedev->query($sqlHeader, [$detail['VoucherDetailVoucherHeaderID']]);
if (!$qHeader || !$qHeader->row_array()) {
$this->sys_ok([
"code" => "N",
"message" => "Voucher header tidak ditemukan",
"amount" => 0
]);
exit;
}
$header = $qHeader->row_array();
if (($header['VoucherHeaderIsActive'] ?? 'N') != 'Y') {
$this->sys_ok([
"code" => "N",
"message" => "Voucher header tidak aktif",
"amount" => 0
]);
exit;
}
$sqlPeriod = "
SELECT 1
WHERE CURDATE() BETWEEN ? AND ?
";
$qPeriod = $this->db_onedev->query($sqlPeriod, [$header['VoucherHeaderStartDate'], $header['VoucherHeaderEndDate']]);
if (!$qPeriod || $qPeriod->num_rows() === 0) {
$this->sys_ok([
"code" => "X",
"message" => "Voucher tidak dalam periode aktif (expired / belum mulai)",
"amount" => 0
]);
exit;
}
$this->sys_ok([
"code" => "Y",
"message" => "Voucher bisa digunakan",
"voucherNumber" => $detail['VoucherDetailNumber'],
"amount" => (int)$header['VoucherHeaderAmount'],
"headerId" => (int)$header['VoucherHeaderID']
]);
exit;
} catch (Exception $e) {
$this->sys_error($e->getMessage());
exit;
}
}
}