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; } } }