Files
BE_IBL/application/libraries/Ibl_merge_report_gateway.php
2026-05-29 16:02:46 +07:00

752 lines
27 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Ibl_merge_report_gateway
{
protected $CI;
protected $db_onedev;
public function __construct()
{
$this->CI = &get_instance();
$this->db_onedev = $this->CI->load->database('onedev', true);
}
public function create_merge_request_from_lab_number($labNumber, $creatorUserId, $customName = '')
{
$order = $this->resolve_order_by_lab_number($labNumber);
if (!$order) {
return $this->error('ORDER_NOT_FOUND', 'Nomor lab tidak ditemukan.');
}
return $this->create_merge_request_from_order($order['T_OrderHeaderID'], $creatorUserId, $customName);
}
public function create_merge_request_from_order($orderHeaderId, $creatorUserId, $customName = '')
{
$order = $this->get_order_header($orderHeaderId);
if (!$order) {
return $this->error('ORDER_NOT_FOUND', 'Order tidak ditemukan.');
}
$composition = $this->compose_merge_request_payload($orderHeaderId, $customName);
if ($composition['status'] !== 'OK') {
return $composition;
}
$payloadJson = json_encode($composition['data']['snapshot'], JSON_UNESCAPED_SLASHES);
$insert = $this->db_onedev->query(
"INSERT INTO merge_request (
mergeRequestT_OrderHeaderID,
mergeRequestPayload,
mergeRequestCreatedUserID,
mergeRequestCreated
) VALUES (?, ?, ?, NOW())",
array(
$orderHeaderId,
$payloadJson,
$creatorUserId
)
);
if (!$insert) {
return $this->error('MERGE_REQUEST_INSERT_FAILED', 'Gagal menyimpan merge request.');
}
$mergeRequestId = (int) $this->db_onedev->insert_id();
$previewUrl = $this->build_preview_url($mergeRequestId);
$goPayload = $this->build_go_payload_from_snapshot(
$mergeRequestId,
$orderHeaderId,
$composition['data']['snapshot']
);
return array(
'status' => 'OK',
'data' => array(
'mergeRequestID' => $mergeRequestId,
'T_OrderHeaderID' => (int) $orderHeaderId,
'T_OrderHeaderLabNumber' => $order['T_OrderHeaderLabNumber'],
'previewUrl' => $previewUrl,
'snapshot' => $composition['data']['snapshot'],
'goPayloadPreview' => $goPayload
)
);
}
public function compose_merge_request_payload($orderHeaderId, $customName = '')
{
$order = $this->get_order_header($orderHeaderId);
if (!$order) {
return $this->error('ORDER_NOT_FOUND', 'Order tidak ditemukan.');
}
$summary = $this->get_merge_summary_by_order_id($orderHeaderId);
if ($summary['status'] !== 'OK') {
return $summary;
}
if ($summary['data']['available_merge'] !== 'Y') {
return $this->error('MERGE_NOT_READY', 'Order belum siap untuk merge report.');
}
$sources = array();
$sourceDedup = array();
foreach ($summary['data']['records'] as $group) {
if (empty($group['group_result_ids'])) {
continue;
}
foreach ($group['group_result_ids'] as $groupResultId) {
$groupSources = $this->get_group_sources(
$order,
(int) $groupResultId
);
if ($groupSources['status'] !== 'OK') {
return $groupSources;
}
foreach ($groupSources['data'] as $source) {
if ($source['relativeUrl'] === '') {
return $this->error('MERGE_SOURCE_MISSING', 'Salah satu sumber report belum tersedia.');
}
if (isset($sourceDedup[$source['absoluteUrl']])) {
continue;
}
$sourceDedup[$source['absoluteUrl']] = true;
$sources[] = $source;
}
}
}
if (count($sources) === 0) {
return $this->error('MERGE_SOURCE_EMPTY', 'Tidak ada sumber report yang bisa digabung.');
}
$fileName = $this->build_merge_filename($order, $customName);
$snapshot = array(
'name' => $fileName,
'T_OrderHeaderID' => (int) $order['T_OrderHeaderID'],
'T_OrderHeaderLabNumber' => $order['T_OrderHeaderLabNumber'],
'createdAt' => date('Y-m-d H:i:s'),
'sources' => $sources
);
return array(
'status' => 'OK',
'data' => array(
'snapshot' => $snapshot,
'summary' => $summary['data']
)
);
}
public function get_merge_summary_by_lab_number($labNumber)
{
$order = $this->resolve_order_by_lab_number($labNumber);
if (!$order) {
return $this->error('ORDER_NOT_FOUND', 'Nomor lab tidak ditemukan.');
}
return $this->get_merge_summary_by_order_id($order['T_OrderHeaderID']);
}
public function get_merge_summary_by_order_id($orderHeaderId)
{
$sql = "SELECT
T_OrderHeaderID,
T_OrderDetailID,
Group_ResultID,
Nat_GroupName,
Group_ResultName,
T_OrderDetailT_TestName,
T_OrderDetailT_TestIsResult,
So_ResultEntryID,
IF(T_OrderPromiseDateTime > NOW(), 'N', 'Y') AS statuspromise,
DATE_FORMAT(T_OrderPromiseDateTime,'%d-%m-%Y %H:%i') as promise_date
FROM t_orderheader
JOIN t_orderdetail ON T_OrderHeaderID = T_OrderDetailT_OrderHeaderID
AND T_OrderDetailIsActive = 'Y'
AND T_OrderHeaderID = ?
AND T_OrderHeaderIsActive = 'Y'
AND T_OrderDetailT_TestIsResult = 'Y'
JOIN group_resultdetail ON T_OrderDetailT_TestID = Group_ResultDetailT_TestID
AND Group_ResultDetailIsActive = 'Y'
JOIN group_result ON Group_ResultDetailGroup_ResultID = Group_ResultID
AND Group_ResultIsActive = 'Y'
JOIN t_orderpromise ON T_OrderHeaderID = T_OrderPromiseT_OrderHeaderID
AND T_OrderPromiseIsActive = 'Y'
AND T_OrderDetailT_OrderPromiseID = T_OrderPromiseID
JOIN t_test ON T_OrderDetailT_TestID = T_TestID
AND T_TestIsActive = 'Y'
JOIN nat_group ON T_TestNat_GroupID = Nat_GroupID
AND Nat_GroupIsActive = 'Y'
JOIN t_orderdelivery ON T_OrderDeliveryT_OrderHeaderID = T_OrderHeaderID
AND T_OrderDeliveryM_DeliveryTypeID = 3
AND T_OrderDeliveryIsActive = 'Y'
LEFT JOIN so_resultentry ON T_OrderHeaderID = So_ResultEntryT_OrderHeaderID
AND T_OrderDetailID = So_ResultEntryT_OrderDetailID
AND So_ResultEntryIsActive = 'Y'
ORDER BY Nat_GroupID, promise_date";
$query = $this->db_onedev->query($sql, array($orderHeaderId));
if (!$query) {
return $this->error('MERGE_SUMMARY_FAILED', 'Gagal membaca komposisi merge order.');
}
$rows = $query->result_array();
if (count($rows) === 0) {
return $this->error('MERGE_SUMMARY_EMPTY', 'Order tidak memiliki komposisi merge.');
}
$grouped = array();
foreach ($rows as $row) {
$groupName = $row['Nat_GroupName'];
if (!isset($grouped[$groupName])) {
$grouped[$groupName] = array(
'group' => $groupName,
'ohid' => (int) $row['T_OrderHeaderID'],
'statuspromise' => true,
'promises' => array(),
'orderdetailid' => array(),
'resultnames' => array(),
'details' => array(),
'group_result_ids' => array()
);
}
if ($row['statuspromise'] !== 'Y') {
$grouped[$groupName]['statuspromise'] = false;
}
if (!in_array($row['promise_date'], $grouped[$groupName]['promises'])) {
$grouped[$groupName]['promises'][] = $row['promise_date'];
}
$grouped[$groupName]['orderdetailid'][] = (int) $row['T_OrderDetailID'];
if (!in_array($row['Group_ResultName'], $grouped[$groupName]['resultnames'])) {
$grouped[$groupName]['resultnames'][] = $row['Group_ResultName'];
}
if (!in_array((int) $row['Group_ResultID'], $grouped[$groupName]['group_result_ids'])) {
$grouped[$groupName]['group_result_ids'][] = (int) $row['Group_ResultID'];
}
if (!isset($grouped[$groupName]['details'][$row['Group_ResultName']])) {
$grouped[$groupName]['details'][$row['Group_ResultName']] = array(
'name' => $row['Group_ResultName'],
'resultentryid' => array()
);
}
if (!is_null($row['So_ResultEntryID'])) {
$grouped[$groupName]['details'][$row['Group_ResultName']]['resultentryid'][] = (int) $row['So_ResultEntryID'];
}
}
$records = array();
$countTotalGroup = count($grouped);
$countDoneGroup = 0;
foreach ($grouped as $group) {
if ($group['statuspromise']) {
$countDoneGroup++;
}
$detailList = array();
foreach ($group['details'] as $detail) {
$detailList[] = $detail;
}
$group['details'] = $detailList;
$records[] = $group;
}
$availableMerge = 'Y';
if ($countTotalGroup === 1) {
if ($countDoneGroup < 1) {
$availableMerge = 'N';
}
} elseif ($countTotalGroup >= 2) {
if ($countDoneGroup < 2 || $countDoneGroup < $countTotalGroup) {
$availableMerge = 'N';
}
}
return array(
'status' => 'OK',
'data' => array(
'total' => 0,
'records' => $records,
'available_merge' => $availableMerge
)
);
}
public function stream_merge_request($mergeRequestId, $labNumber = '')
{
$mergeRequest = $this->get_merge_request($mergeRequestId);
if (!$mergeRequest) {
return $this->error('MERGE_REQUEST_NOT_FOUND', 'Merge request tidak ditemukan.');
}
$payload = json_decode($mergeRequest['mergeRequestPayload'], true);
if (!is_array($payload)) {
return $this->error('MERGE_REQUEST_INVALID', 'Payload merge request tidak valid.');
}
$order = $this->get_order_header($mergeRequest['mergeRequestT_OrderHeaderID']);
if (!$order) {
return $this->error('ORDER_NOT_FOUND', 'Order untuk merge request tidak ditemukan.');
}
if ($labNumber !== '') {
$resolvedOrder = $this->resolve_order_by_lab_number($labNumber);
if (!$resolvedOrder || (int) $resolvedOrder['T_OrderHeaderID'] !== (int) $mergeRequest['mergeRequestT_OrderHeaderID']) {
return $this->error('MERGE_REQUEST_ORDER_MISMATCH', 'Merge request tidak terkait dengan nomor lab tersebut.');
}
}
$goPayload = $this->build_go_payload_from_snapshot(
(int) $mergeRequest['mergeRequestID'],
(int) $mergeRequest['mergeRequestT_OrderHeaderID'],
$payload
);
return $this->call_merge_service($goPayload);
}
public function build_go_payload_from_snapshot($mergeRequestId, $orderHeaderId, array $snapshot)
{
$urls = array();
foreach ($snapshot['sources'] as $source) {
$urls[] = $this->make_absolute_url($source['relativeUrl']);
}
return array(
'name' => $snapshot['name'],
'urls' => $urls,
'mergeRequestID' => (int) $mergeRequestId,
'T_OrderHeaderID' => (int) $orderHeaderId
);
}
public function get_current_user_group_id($userId)
{
$query = $this->db_onedev->query(
"SELECT M_UserM_UserGroupID FROM m_user WHERE M_UserID = ? AND M_UserIsActive = 'Y' LIMIT 1",
array($userId)
);
if (!$query || $query->num_rows() === 0) {
return 0;
}
return (int) $query->row()->M_UserM_UserGroupID;
}
public function is_admin_group_allowed($userId)
{
$config = $this->get_system_config();
$allowed = array();
if (!empty($config['S_SystemsMergeReportAdminGroupIDs'])) {
foreach (explode(',', $config['S_SystemsMergeReportAdminGroupIDs']) as $value) {
$value = (int) trim($value);
if ($value > 0) {
$allowed[] = $value;
}
}
}
if (count($allowed) === 0) {
return array(
'status' => 'ERR',
'code' => 'MERGE_ADMIN_GROUP_NOT_CONFIGURED',
'message' => 'Akses admin merge report belum dikonfigurasi.'
);
}
$groupId = $this->get_current_user_group_id($userId);
if (!in_array($groupId, $allowed)) {
return array(
'status' => 'ERR',
'code' => 'MERGE_ADMIN_FORBIDDEN',
'message' => 'User group tidak diizinkan mengakses tools merge report.'
);
}
return array(
'status' => 'OK',
'data' => array(
'groupId' => $groupId,
'allowedGroupIds' => $allowed
)
);
}
public function get_system_config()
{
static $config = null;
if ($config !== null) {
return $config;
}
$query = $this->db_onedev->query(
"SELECT
S_SystemsMergeReportGateway,
S_SystemIPAddressRegional,
S_SystemsMergeReportServiceBaseUrl,
S_SystemsMergeReportServiceSecret,
S_SystemsMergeReportAdminGroupIDs
FROM conf_systems
WHERE S_SystemsIsActive = 'Y'
LIMIT 1"
);
if ($query && $query->num_rows() > 0) {
$config = $query->row_array();
} else {
$config = array();
}
return $config;
}
protected function get_merge_request($mergeRequestId)
{
$query = $this->db_onedev->query(
"SELECT
mergeRequestID,
mergeRequestT_OrderHeaderID,
mergeRequestPayload,
mergeRequestCreatedUserID,
mergeRequestCreated
FROM merge_request
WHERE mergeRequestID = ?
LIMIT 1",
array($mergeRequestId)
);
if (!$query || $query->num_rows() === 0) {
return null;
}
return $query->row_array();
}
protected function get_order_header($orderHeaderId)
{
$sql = "SELECT
T_OrderHeaderID,
T_OrderHeaderLabNumber,
T_OrderHeaderLabNumberExt,
DATE(T_OrderHeaderDate) AS order_date,
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CONCAT(IFNULL(M_TitleName,''),' ',M_PatientName), ' ', '-'), '.', '_'), '(', ''), ')', ''), '\\'', '') AS patient_name
FROM t_orderheader
JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID
LEFT JOIN m_title ON M_PatientM_TitleID = M_TitleID
WHERE T_OrderHeaderID = ?
AND T_OrderHeaderIsActive = 'Y'
LIMIT 1";
$query = $this->db_onedev->query($sql, array($orderHeaderId));
if (!$query || $query->num_rows() === 0) {
return null;
}
return $query->row_array();
}
protected function resolve_order_by_lab_number($labNumber)
{
$query = $this->db_onedev->query(
"SELECT T_OrderHeaderID, T_OrderHeaderLabNumber
FROM t_orderheader
WHERE T_OrderHeaderLabNumber = ?
AND T_OrderHeaderIsActive = 'Y'
ORDER BY T_OrderHeaderID DESC
LIMIT 1",
array($labNumber)
);
if (!$query || $query->num_rows() === 0) {
return null;
}
return $query->row_array();
}
protected function get_group_sources(array $order, $groupResultId)
{
$query = $this->db_onedev->query(
"SELECT DISTINCT
Group_ResultID,
Group_ResultName,
Group_ResultFlagNonLab,
IFNULL(T_EmailNonLabUrl,'-') AS EmailNonLabUrl,
IF(T_EmailNonLabUrl IS NULL AND Group_ResultFlagNonLab = 'Y',' [Belum Pilih Format Hasil]','') AS temail
FROM t_orderdetail
JOIN group_resultdetail
ON Group_ResultDetailT_TestID = T_OrderDetailT_TestID
AND T_OrderDetailIsActive = 'Y'
AND Group_ResultDetailIsActive = 'Y'
AND T_OrderDetailT_OrderHeaderID = ?
JOIN group_result
ON Group_ResultDetailGroup_ResultID = Group_ResultID
AND Group_ResultIsActive = 'Y'
AND Group_ResultID = ?
LEFT JOIN t_email_nonlab
ON T_EmailNonLabT_OrderHeaderID = T_OrderDetailT_OrderHeaderID
AND T_EmailNonLabType LIKE CONCAT('%', REPLACE(Group_ResultName, 'Elektromedik', 'electromedis'), '%')",
array($order['T_OrderHeaderID'], $groupResultId)
);
if (!$query) {
return $this->error('MERGE_SOURCE_QUERY_FAILED', 'Gagal menyusun sumber report.');
}
$rows = $query->result_array();
$result = array();
$ts = '&ts=' . time();
foreach ($rows as $row) {
$relativeUrl = '';
$emailNonLabUrl = str_replace(' ', '', $row['EmailNonLabUrl']);
if (strpos($emailNonLabUrl, 'fisik') !== false) {
continue;
}
switch ((int) $row['Group_ResultID']) {
case 1:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_test_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
case 2:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_hasil_papsmear_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
case 3:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_hasil_fna_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
case 12:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_hasil_lcprep_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
case 13:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_test_mikro_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
case 14:
$relativeUrl = '/birt/frameset?__report=report/onelab/lab/rpt_hasil_cytologi_email.rptdesign&__format=pdf&username=admin&PID=' . $order['T_OrderHeaderID'] . $ts;
break;
default:
$relativeUrl = $emailNonLabUrl;
break;
}
if ($relativeUrl === '-' || $relativeUrl === '') {
continue;
}
$result[] = array(
'groupResultID' => (int) $row['Group_ResultID'],
'name' => $row['Group_ResultName'],
'relativeUrl' => $relativeUrl,
'absoluteUrl' => $this->make_absolute_url($relativeUrl)
);
}
return array(
'status' => 'OK',
'data' => $result
);
}
public function stream_from_qr_printout($orderHeaderId)
{
$query = $this->db_onedev->query(
"SELECT QR_PrintOutReportURLElectronic
FROM qr_printout
WHERE QR_PrintOutT_OrderHeaderID = ?
AND QR_PrintOutReportURLElectronic != ''
AND QR_PrintOutIsActive = 1
ORDER BY QR_PrintOutGroup_ResultID ASC",
array((int) $orderHeaderId)
);
if (!$query || $query->num_rows() === 0) {
return $this->error('QR_PRINTOUT_NOT_FOUND', 'Tidak ada URL report di qr_printout untuk order ini.');
}
$urls = array();
$seen = array();
foreach ($query->result_array() as $row) {
$url = trim($row['QR_PrintOutReportURLElectronic']);
if ($url === '' || isset($seen[$url])) {
continue;
}
$seen[$url] = true;
$url = str_replace('http://localhost/', 'http://127.0.0.1/', $url);
$urls[] = $url;
}
if (count($urls) === 0) {
return $this->error('QR_PRINTOUT_EMPTY', 'URL report kosong setelah normalisasi.');
}
$payload = array(
'name' => 'merge-' . (int) $orderHeaderId . '.pdf',
'urls' => $urls,
'mergeRequestID' => (int) $orderHeaderId,
'T_OrderHeaderID' => (int) $orderHeaderId,
);
return $this->call_merge_service($payload);
}
protected function call_merge_service(array $payload)
{
$config = $this->get_system_config();
$baseUrl = isset($config['S_SystemsMergeReportServiceBaseUrl']) ? trim($config['S_SystemsMergeReportServiceBaseUrl']) : '';
$secret = isset($config['S_SystemsMergeReportServiceSecret']) ? trim($config['S_SystemsMergeReportServiceSecret']) : '';
if ($baseUrl === '' || $secret === '') {
return $this->error('MERGE_SERVICE_NOT_CONFIGURED', 'Konfigurasi merge report service belum lengkap.');
}
$url = rtrim($baseUrl, '/') . '/merge';
$jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES);
$lastError = array(
'status' => 'ERR',
'code' => 'MERGE_SERVICE_FAILED',
'message' => 'Layanan merge internal gagal memproses permintaan.'
);
for ($attempt = 1; $attempt <= 3; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, array(
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $jsonPayload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'X-Internal-Secret: ' . $secret
),
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 30
));
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$contentType = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if ($response === false || $curlError !== '') {
$lastError = $this->error('MERGE_SERVICE_TIMEOUT', 'Layanan merge internal melebihi batas waktu.');
continue;
}
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
if ($httpCode >= 200 && $httpCode < 300 && stripos($contentType, 'application/pdf') !== false) {
return array(
'status' => 'OK',
'data' => array(
'headers' => $headers,
'contentType' => $contentType,
'body' => $body,
'payload' => $payload
)
);
}
$lastError = $this->map_service_error($httpCode);
}
return $lastError;
}
protected function map_service_error($httpCode)
{
if ($httpCode === 400 || $httpCode === 422) {
return $this->error('MERGE_SERVICE_REJECTED', 'Komposisi merge report tidak valid.');
}
if ($httpCode === 401 || $httpCode === 403) {
return $this->error('MERGE_SERVICE_UNAUTHORIZED', 'Layanan merge internal menolak permintaan.');
}
if ($httpCode === 404) {
return $this->error('MERGE_SOURCE_NOT_FOUND', 'Salah satu sumber report tidak ditemukan.');
}
if ($httpCode === 408 || $httpCode === 504) {
return $this->error('MERGE_SERVICE_TIMEOUT', 'Layanan merge internal melebihi batas waktu.');
}
return $this->error('MERGE_SERVICE_FAILED', 'Layanan merge internal gagal memproses permintaan.');
}
protected function build_preview_url($mergeRequestId)
{
return rtrim($this->get_public_base_url(), '/') . '/report/' . (int) $mergeRequestId;
}
protected function make_absolute_url($url)
{
if ($url === '') {
return '';
}
if (preg_match('/^https?:\/\//i', $url)) {
return $url;
}
return rtrim($this->get_public_base_url(), '/') . '/' . ltrim($url, '/');
}
protected function get_public_base_url()
{
$config = $this->get_system_config();
if (!empty($config['S_SystemsMergeReportGateway'])) {
return rtrim($config['S_SystemsMergeReportGateway'], '/');
}
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
$script = isset($_SERVER['SCRIPT_NAME']) ? dirname($_SERVER['SCRIPT_NAME']) : '';
$script = trim(str_replace('\\', '/', $script), '/');
if ($script === '' || $script === '.') {
return $https . '://' . $host;
}
return $https . '://' . $host . '/' . $script;
}
protected function build_merge_filename(array $order, $customName)
{
$name = trim($customName);
if ($name === '') {
$name = $order['T_OrderHeaderLabNumber'] . '-' . $order['patient_name'] . '-merge-report.pdf';
}
if (strtolower(substr($name, -4)) !== '.pdf') {
$name .= '.pdf';
}
return preg_replace('/[^A-Za-z0-9._-]/', '-', $name);
}
protected function error($code, $message)
{
return array(
'status' => 'ERR',
'code' => $code,
'message' => $message
);
}
}