From 38f7e705d09920c48bd7694fbb759d67967767ee Mon Sep 17 00:00:00 2001 From: Manop Kongoon Date: Sun, 11 Jan 2026 21:53:29 +0700 Subject: [PATCH] update console --- console/components/Pathology.php | 682 +++++----- console/components/WebSocketServer.php | 90 +- console/config/.gitignore | 2 +- console/config/bootstrap.php | 14 +- console/config/main-local.php | 14 +- console/config/main.php | 110 +- console/config/params-local.php | 6 +- console/config/params.php | 8 +- console/controllers/FinanceController.php | 72 +- .../controllers/LisSendBackUpController.php | 1116 ++++++++-------- console/controllers/LisSendController.php | 1146 ++++++++--------- .../controllers/LisSendPathoController.php | 862 ++++++------- console/controllers/LisSendTestController.php | 288 ++--- console/controllers/MlController.php | 22 +- console/controllers/PatientCaseApprove.php | 154 +++ console/controllers/ReportController.php | 292 ++--- console/controllers/SetPriceController.php | 60 +- console/controllers/WebSocketController.php | 545 +------- console/models/.gitkeep | 2 +- console/models/ApiToken.php | 78 +- console/models/HanumanResultSikarinCheck.php | 216 ++-- console/models/HanumanResultSikarinManual.php | 226 ++-- console/models/HisRequest.php | 288 ++--- console/models/HisRequestTestList.php | 140 +- console/models/LisApiD.php | 176 +-- console/models/LisApiSend.php | 174 +-- console/models/LisResult.php | 148 +-- console/models/PatientCase.php | 620 ++++----- console/models/PatientCaseApprove.php | 254 ++-- console/models/ResultCodeMapping.php | 130 +- console/models/ResultPathoSikarinCheck.php | 120 +- console/models/SendHis.php | 146 +-- console/models/User.php | 652 +++++----- console/runtime/.gitignore | 2 +- 34 files changed, 4256 insertions(+), 4599 deletions(-) create mode 100644 console/controllers/PatientCaseApprove.php diff --git a/console/components/Pathology.php b/console/components/Pathology.php index 560c65c7..ff03887e 100755 --- a/console/components/Pathology.php +++ b/console/components/Pathology.php @@ -1,341 +1,341 @@ -getCaseType($id_case); - $params = [':id_case' => $id_case]; - if ($case_type === 'surgical') { - // CaseSurgical::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_surgical WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'non-gyn') { - //return CaseNonGyn::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_non_gyn WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'pap') { - //return CasePap::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_pap WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'frozen') { - //return CaseFrozen::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_frozen WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'fish') { - //return CaseFish::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_fish WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'dish') { - //return CaseDish::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_dish WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'autopsy') { - //return CaseAutopsy::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_autopsy WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'flow') { - //return CaseFlow::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_fish WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'molecular') { - //return CaseMolecular::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_molecular WHERE id_case = :id_case", $params)->queryOne(); - } - if ($case_type === 'necropsy') { - //return CaseNecropsy::findOne(['id_case' => $id_case]); - return Yii::$app->db->createCommand("SELECT * FROM case_necropsy WHERE id_case = :id_case", $params)->queryOne(); - } - } - - /** - * ส่งข้อมูลผลการตรวจไปยัง HIS - * - * @param integer $approve_id - * @param string $id_case - * @param integer $diagnosis_id - * @return boolean - */ - public function requestHisReport($approve_id, $id_case, $diagnosis_id) - { - $case_type = $this->getCaseType($id_case); - $case = $this->getCase($id_case); - - //$release = CenterApprove::findOne($approve_id); - $release = Yii::$app->db->createCommand("SELECT * FROM center_approve WHERE id = :id", [':id' => $approve_id])->queryOne(); - //$hospital_import = HospitalImport::find()->where(['id_case' => $case->id_case])->orderBy(['id' => SORT_DESC])->one(); - $hospital_import = Yii::$app->db->createCommand("SELECT * FROM hospital_import WHERE his_ln = :his_ln", [':his_ln' => $case['his_ln']])->queryOne(); - - error_log($case['id_case']); - //error_log(is_array($hospital_import) ? reset($hospital_import) : $hospital_import); - ///var_dump($hospital_import); - error_log($case['his_ln']); - - if ($case && isset($case['his_ln']) && isset($case['id_case']) && !empty($case['o_n'])) { - $cytotech1 = null; - $cytotech2 = null; - - if ($case_type == 'surgical') { - //$diagnosis = SurgicalDiagnosis::findOne($diagnosis_id); - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM surgical_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - //$diagnosis_count = SurgicalDiagnosis::find()->where(['id_case' => $case->id_case])->count(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM surgical_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'non-gyn') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM cyto_non_gyn_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM cyto_non_gyn_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - - if ($case_type == 'pap') { - //$diagnosis = CytoPapDiagnosis::findOne($diagnosis_id); - //$diagnosis_count = CytoPapDiagnosis::find()->where(['id_case' => $case->id_case])->count(); - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM cyto_pap_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM cyto_pap_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - $cytotech1 = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $case['cytotech1_id']])->queryOne(); - $cytotech2 = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $case['cytotech2_id']])->queryOne(); - } - if ($case_type == 'frozen') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM frozen_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM frozen_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'dish') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM dish_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM dish_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'fish') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM fish_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM fish_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'flow') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM flow_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM flow_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'molecular') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM molecular_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM molecular_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'autopsy') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM autopsy_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM autopsy_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - if ($case_type == 'necropsy') { - $diagnosis = Yii::$app->db->createCommand("SELECT * FROM necropsy_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); - $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM necropsy_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); - } - - $title = Yii::$app->db->createCommand("SELECT * FROM patient_title WHERE id = :id", [':id' => $case['title_id']])->queryOne(); - - if(isset($diagnosis['pathologist_id'])) { - $appP = $diagnosis['pathologist_id']; - }else if(isset($diagnosis['cytotech2_id'])){ - $appP = $diagnosis['cytotech2_id']; - }else if(isset($diagnosis['cytotech1_id'])) { - $appP = $diagnosis['cytotech1_id']; - } - if($appP){ - $approveBy = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $appP])->queryOne(); - } - $releaseBy = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $release['release_by']])->queryOne(); - - - // Target URL for the POST request - $targetUrl = 'http://172.16.64.22:3439/patho/pathoReport/result'; - - // Data to be sent in the POST request - $postData = [ - 'cid' => str_replace('-', '', $case['id_card']), - 'title_code' => $title['code'], - 'first_name' => $case['given_name'], - 'last_name' => $case['surname'], - 'birthdate' => $case['birthdate'], - 'age' => $case['age'], - 'age_unit' => $case['age_unit'], - 'report_type' => $diagnosis['report_type'], - 'id_case' => $id_case, - 'ln' => $case['his_ln'], - 'o_n' => $case['o_n'], - 'approve_at' => isset($diagnosis['pathologist_at']) ? $diagnosis['pathologist_at'] : (isset($diagnosis['cytotech1_at']) ? $diagnosis['cytotech1_at'] : (isset($diagnosis['cytotech2_at']) ? $diagnosis['cytotech2_at'] : '')), - 'approve_by' => isset($diagnosis['pathologist_id']) ? $approveBy['code'] : (isset($diagnosis['cytotech1_id']) ? $cytotech1['code'] : (isset($diagnosis['cytotech2_id']) ? $cytotech2['code'] : '')), - 'release_at' => $release['release_at'], - 'release_by' => $releaseBy['code'], - 'sequence' => $diagnosis_count == 0 ? 1 : $diagnosis_count + 1, - 'link_all' => 'http://10.10.20.111/api/his/report-all?id_case=' . $case['id_case'], - 'link_report' => 'http://10.10.20.111/api/his/report-case?id_case=' . $case['id_case'] . '&report_type=' . $diagnosis['report_type'] . '&diagnosis_id=' . $diagnosis['id'] - ]; - - // Custom headers for the request - $headers = [ - 'Content-Type: application/json', // Adjust the content type accordingly - 'Authorization: ' . Yii::$app->params['HISSecret'] . '' // Include any other headers you need - ]; - - $postDataJson = json_encode($postData); - - error_log('Send HIS Report'); - error_log($postDataJson); - - // Initialize cURL session - $ch = curl_init(); - - // Set cURL options - curl_setopt($ch, CURLOPT_URL, $targetUrl); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $postDataJson); //http_build_query($postData)); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute cURL session and get the response - $response = curl_exec($ch); - - // Check for cURL errors - if (curl_errno($ch)) { - ///echo 'Curl error: ' . curl_error($ch); - error_log('Curl error: ' . curl_error($ch)); - return false; - } - - // Close cURL session - curl_close($ch); - - // Process the response - //echo $response; - error_log($response); - // Send HIS Report Log - /*Yii::$app->db->createCommand("INSERT INTO send_his (approve_id, id_case, report_type, diagnosis_id, is_send, send_at, response) - VALUES (:approve_id, :id_case, :report_type, :diagnosis_id, :is_send, :send_at, :response)", [ - ':approve_id' => $approve_id, - ':id_case' => $id_case, - ':report_type' => $diagnosis['report_type'], - ':diagnosis_id' => $diagnosis_id, - ':is_send' => 1, - ':send_at' => date('Y-m-d H:i:s'), - ':response' => $response - ])->execute();*/ - - // Define your parameters - $params = [ - ':approve_id' => $approve_id, - ':id_case' => $id_case, - ':report_type' => $diagnosis['report_type'], - ':diagnosis_id' => $diagnosis_id - ]; - - // Additional parameters for the update/insert - $updateParams = array_merge($params, [ - ':is_send' => 1, - ':send_at' => date('Y-m-d H:i:s'), - ':response' => $response, - ]); - - // Check if the record exists - $exists = Yii::$app->db->createCommand(" - SELECT COUNT(*) - FROM send_his - WHERE approve_id = :approve_id - AND id_case = :id_case - AND report_type = :report_type - AND diagnosis_id = :diagnosis_id - AND is_send IS NULL", $params) - ->queryScalar(); - - if ($exists) { - // Perform the update - Yii::$app->db->createCommand(" - UPDATE send_his - SET - is_send = :is_send, - send_at = :send_at, - response = :response - WHERE approve_id = :approve_id - AND id_case = :id_case - AND report_type = :report_type - AND diagnosis_id = :diagnosis_id - AND is_send IS NULL", $updateParams) - ->execute(); - } else { - // Perform the insert - Yii::$app->db->createCommand(" - INSERT INTO send_his - (approve_id, id_case, report_type, diagnosis_id, is_send, send_at, response) - VALUES - (:approve_id, :id_case, :report_type, :diagnosis_id, :is_send, :send_at, :response)", $updateParams) - ->execute(); - } - - - - // Return true if the request was successful - return true; - } - - return false; - } -} +getCaseType($id_case); + $params = [':id_case' => $id_case]; + if ($case_type === 'surgical') { + // CaseSurgical::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_surgical WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'non-gyn') { + //return CaseNonGyn::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_non_gyn WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'pap') { + //return CasePap::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_pap WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'frozen') { + //return CaseFrozen::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_frozen WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'fish') { + //return CaseFish::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_fish WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'dish') { + //return CaseDish::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_dish WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'autopsy') { + //return CaseAutopsy::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_autopsy WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'flow') { + //return CaseFlow::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_fish WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'molecular') { + //return CaseMolecular::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_molecular WHERE id_case = :id_case", $params)->queryOne(); + } + if ($case_type === 'necropsy') { + //return CaseNecropsy::findOne(['id_case' => $id_case]); + return Yii::$app->db->createCommand("SELECT * FROM case_necropsy WHERE id_case = :id_case", $params)->queryOne(); + } + } + + /** + * ส่งข้อมูลผลการตรวจไปยัง HIS + * + * @param integer $approve_id + * @param string $id_case + * @param integer $diagnosis_id + * @return boolean + */ + public function requestHisReport($approve_id, $id_case, $diagnosis_id) + { + $case_type = $this->getCaseType($id_case); + $case = $this->getCase($id_case); + + //$release = CenterApprove::findOne($approve_id); + $release = Yii::$app->db->createCommand("SELECT * FROM center_approve WHERE id = :id", [':id' => $approve_id])->queryOne(); + //$hospital_import = HospitalImport::find()->where(['id_case' => $case->id_case])->orderBy(['id' => SORT_DESC])->one(); + $hospital_import = Yii::$app->db->createCommand("SELECT * FROM hospital_import WHERE his_ln = :his_ln", [':his_ln' => $case['his_ln']])->queryOne(); + + error_log($case['id_case']); + //error_log(is_array($hospital_import) ? reset($hospital_import) : $hospital_import); + ///var_dump($hospital_import); + error_log($case['his_ln']); + + if ($case && isset($case['his_ln']) && isset($case['id_case']) && !empty($case['o_n'])) { + $cytotech1 = null; + $cytotech2 = null; + + if ($case_type == 'surgical') { + //$diagnosis = SurgicalDiagnosis::findOne($diagnosis_id); + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM surgical_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + //$diagnosis_count = SurgicalDiagnosis::find()->where(['id_case' => $case->id_case])->count(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM surgical_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'non-gyn') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM cyto_non_gyn_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM cyto_non_gyn_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + + if ($case_type == 'pap') { + //$diagnosis = CytoPapDiagnosis::findOne($diagnosis_id); + //$diagnosis_count = CytoPapDiagnosis::find()->where(['id_case' => $case->id_case])->count(); + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM cyto_pap_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM cyto_pap_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + $cytotech1 = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $case['cytotech1_id']])->queryOne(); + $cytotech2 = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $case['cytotech2_id']])->queryOne(); + } + if ($case_type == 'frozen') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM frozen_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM frozen_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'dish') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM dish_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM dish_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'fish') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM fish_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM fish_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'flow') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM flow_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM flow_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'molecular') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM molecular_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM molecular_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'autopsy') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM autopsy_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM autopsy_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + if ($case_type == 'necropsy') { + $diagnosis = Yii::$app->db->createCommand("SELECT * FROM necropsy_diagnosis WHERE id = :id", [':id' => $diagnosis_id])->queryOne(); + $diagnosis_count = Yii::$app->db->createCommand("SELECT count(*) FROM necropsy_diagnosis WHERE id_case = :id_case", [':id_case' => $case['id_case']])->queryScalar(); + } + + $title = Yii::$app->db->createCommand("SELECT * FROM patient_title WHERE id = :id", [':id' => $case['title_id']])->queryOne(); + + if(isset($diagnosis['pathologist_id'])) { + $appP = $diagnosis['pathologist_id']; + }else if(isset($diagnosis['cytotech2_id'])){ + $appP = $diagnosis['cytotech2_id']; + }else if(isset($diagnosis['cytotech1_id'])) { + $appP = $diagnosis['cytotech1_id']; + } + if($appP){ + $approveBy = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $appP])->queryOne(); + } + $releaseBy = Yii::$app->db->createCommand("SELECT * FROM user_table WHERE id = :id", [':id' => $release['release_by']])->queryOne(); + + + // Target URL for the POST request + $targetUrl = 'http://172.16.64.22:3439/patho/pathoReport/result'; + + // Data to be sent in the POST request + $postData = [ + 'cid' => str_replace('-', '', $case['id_card']), + 'title_code' => $title['code'], + 'first_name' => $case['given_name'], + 'last_name' => $case['surname'], + 'birthdate' => $case['birthdate'], + 'age' => $case['age'], + 'age_unit' => $case['age_unit'], + 'report_type' => $diagnosis['report_type'], + 'id_case' => $id_case, + 'ln' => $case['his_ln'], + 'o_n' => $case['o_n'], + 'approve_at' => isset($diagnosis['pathologist_at']) ? $diagnosis['pathologist_at'] : (isset($diagnosis['cytotech1_at']) ? $diagnosis['cytotech1_at'] : (isset($diagnosis['cytotech2_at']) ? $diagnosis['cytotech2_at'] : '')), + 'approve_by' => isset($diagnosis['pathologist_id']) ? $approveBy['code'] : (isset($diagnosis['cytotech1_id']) ? $cytotech1['code'] : (isset($diagnosis['cytotech2_id']) ? $cytotech2['code'] : '')), + 'release_at' => $release['release_at'], + 'release_by' => $releaseBy['code'], + 'sequence' => $diagnosis_count == 0 ? 1 : $diagnosis_count + 1, + 'link_all' => 'http://10.10.20.111/api/his/report-all?id_case=' . $case['id_case'], + 'link_report' => 'http://10.10.20.111/api/his/report-case?id_case=' . $case['id_case'] . '&report_type=' . $diagnosis['report_type'] . '&diagnosis_id=' . $diagnosis['id'] + ]; + + // Custom headers for the request + $headers = [ + 'Content-Type: application/json', // Adjust the content type accordingly + 'Authorization: ' . Yii::$app->params['HISSecret'] . '' // Include any other headers you need + ]; + + $postDataJson = json_encode($postData); + + error_log('Send HIS Report'); + error_log($postDataJson); + + // Initialize cURL session + $ch = curl_init(); + + // Set cURL options + curl_setopt($ch, CURLOPT_URL, $targetUrl); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postDataJson); //http_build_query($postData)); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + // Execute cURL session and get the response + $response = curl_exec($ch); + + // Check for cURL errors + if (curl_errno($ch)) { + ///echo 'Curl error: ' . curl_error($ch); + error_log('Curl error: ' . curl_error($ch)); + return false; + } + + // Close cURL session + curl_close($ch); + + // Process the response + //echo $response; + error_log($response); + // Send HIS Report Log + /*Yii::$app->db->createCommand("INSERT INTO send_his (approve_id, id_case, report_type, diagnosis_id, is_send, send_at, response) + VALUES (:approve_id, :id_case, :report_type, :diagnosis_id, :is_send, :send_at, :response)", [ + ':approve_id' => $approve_id, + ':id_case' => $id_case, + ':report_type' => $diagnosis['report_type'], + ':diagnosis_id' => $diagnosis_id, + ':is_send' => 1, + ':send_at' => date('Y-m-d H:i:s'), + ':response' => $response + ])->execute();*/ + + // Define your parameters + $params = [ + ':approve_id' => $approve_id, + ':id_case' => $id_case, + ':report_type' => $diagnosis['report_type'], + ':diagnosis_id' => $diagnosis_id + ]; + + // Additional parameters for the update/insert + $updateParams = array_merge($params, [ + ':is_send' => 1, + ':send_at' => date('Y-m-d H:i:s'), + ':response' => $response, + ]); + + // Check if the record exists + $exists = Yii::$app->db->createCommand(" + SELECT COUNT(*) + FROM send_his + WHERE approve_id = :approve_id + AND id_case = :id_case + AND report_type = :report_type + AND diagnosis_id = :diagnosis_id + AND is_send IS NULL", $params) + ->queryScalar(); + + if ($exists) { + // Perform the update + Yii::$app->db->createCommand(" + UPDATE send_his + SET + is_send = :is_send, + send_at = :send_at, + response = :response + WHERE approve_id = :approve_id + AND id_case = :id_case + AND report_type = :report_type + AND diagnosis_id = :diagnosis_id + AND is_send IS NULL", $updateParams) + ->execute(); + } else { + // Perform the insert + Yii::$app->db->createCommand(" + INSERT INTO send_his + (approve_id, id_case, report_type, diagnosis_id, is_send, send_at, response) + VALUES + (:approve_id, :id_case, :report_type, :diagnosis_id, :is_send, :send_at, :response)", $updateParams) + ->execute(); + } + + + + // Return true if the request was successful + return true; + } + + return false; + } +} diff --git a/console/components/WebSocketServer.php b/console/components/WebSocketServer.php index 5521a3e1..bbc708a0 100755 --- a/console/components/WebSocketServer.php +++ b/console/components/WebSocketServer.php @@ -1,46 +1,46 @@ -clients = new \SplObjectStorage; - } - - public function onOpen(ConnectionInterface $conn) - { - // เมื่อมีการเชื่อมต่อใหม่เข้ามา - $this->clients->attach($conn); - echo "New connection! ({$conn->resourceId})\n"; - } - - public function onMessage(ConnectionInterface $from, $msg) - { - // ส่งข้อความแจ้งเตือนให้กับทุก client - foreach ($this->clients as $client) { - if ($from !== $client) { - $client->send($msg); - } - } - } - - public function onClose(ConnectionInterface $conn) - { - // เมื่อการเชื่อมต่อถูกปิด - $this->clients->detach($conn); - echo "Connection {$conn->resourceId} has disconnected\n"; - } - - public function onError(ConnectionInterface $conn, \Exception $e) - { - echo "An error has occurred: {$e->getMessage()}\n"; - $conn->close(); - } +clients = new \SplObjectStorage; + } + + public function onOpen(ConnectionInterface $conn) + { + // เมื่อมีการเชื่อมต่อใหม่เข้ามา + $this->clients->attach($conn); + echo "New connection! ({$conn->resourceId})\n"; + } + + public function onMessage(ConnectionInterface $from, $msg) + { + // ส่งข้อความแจ้งเตือนให้กับทุก client + foreach ($this->clients as $client) { + if ($from !== $client) { + $client->send($msg); + } + } + } + + public function onClose(ConnectionInterface $conn) + { + // เมื่อการเชื่อมต่อถูกปิด + $this->clients->detach($conn); + echo "Connection {$conn->resourceId} has disconnected\n"; + } + + public function onError(ConnectionInterface $conn, \Exception $e) + { + echo "An error has occurred: {$e->getMessage()}\n"; + $conn->close(); + } } \ No newline at end of file diff --git a/console/config/.gitignore b/console/config/.gitignore index 0ab1e532..17787988 100755 --- a/console/config/.gitignore +++ b/console/config/.gitignore @@ -1,2 +1,2 @@ -#main-local.php +#main-local.php #params-local.php \ No newline at end of file diff --git a/console/config/bootstrap.php b/console/config/bootstrap.php index 73d44a1f..dbabb115 100755 --- a/console/config/bootstrap.php +++ b/console/config/bootstrap.php @@ -1,7 +1,7 @@ - ['gii'], - 'modules' => [ - 'gii' => 'yii\gii\Module', - ], -]; + ['gii'], + 'modules' => [ + 'gii' => 'yii\gii\Module', + ], +]; diff --git a/console/config/main.php b/console/config/main.php index e05b1267..bdd1698e 100755 --- a/console/config/main.php +++ b/console/config/main.php @@ -1,55 +1,55 @@ - 'app-console', - 'basePath' => dirname(__DIR__), - 'bootstrap' => ['log'],//, 'queue' - 'controllerNamespace' => 'console\controllers', - 'aliases' => [ - '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset', - ], - 'controllerMap' => [ - 'fixture' => [ - 'class' => 'yii\console\controllers\FixtureController', - 'namespace' => 'common\fixtures', - ], - 'migrate' => [ - 'class' => 'yii\console\controllers\MigrateController', - 'migrationPath' => null, - 'migrationNamespaces' => [ - // ... - 'yii\queue\db\migrations', - ], - ], - ], - 'components' => [ - 'pathology' => [ - 'class' => 'console\components\Pathology' - ], - 'log' => [ - 'targets' => [ - [ - 'class' => 'yii\log\FileTarget', - 'levels' => ['error', 'warning',], - ], - ], - ], - /*'queue' => [ - 'class' => \yii\queue\db\Queue::class, - 'db' => 'db', // DB connection component or its config - 'tableName' => '{{%queue}}', // Table name - 'channel' => 'default', // Queue channel key - 'mutex' => \yii\mutex\MysqlMutex::class, // Mutex that used to sync queries - 'as log' => \yii\queue\LogBehavior::class, - - ],*/ - ], - 'params' => $params, -]; + 'app-console', + 'basePath' => dirname(__DIR__), + 'bootstrap' => ['log'],//, 'queue' + 'controllerNamespace' => 'console\controllers', + 'aliases' => [ + '@bower' => '@vendor/bower-asset', + '@npm' => '@vendor/npm-asset', + ], + 'controllerMap' => [ + 'fixture' => [ + 'class' => 'yii\console\controllers\FixtureController', + 'namespace' => 'common\fixtures', + ], + 'migrate' => [ + 'class' => 'yii\console\controllers\MigrateController', + 'migrationPath' => null, + 'migrationNamespaces' => [ + // ... + 'yii\queue\db\migrations', + ], + ], + ], + 'components' => [ + 'pathology' => [ + 'class' => 'console\components\Pathology' + ], + 'log' => [ + 'targets' => [ + [ + 'class' => 'yii\log\FileTarget', + 'levels' => ['error', 'warning',], + ], + ], + ], + /*'queue' => [ + 'class' => \yii\queue\db\Queue::class, + 'db' => 'db', // DB connection component or its config + 'tableName' => '{{%queue}}', // Table name + 'channel' => 'default', // Queue channel key + 'mutex' => \yii\mutex\MysqlMutex::class, // Mutex that used to sync queries + 'as log' => \yii\queue\LogBehavior::class, + + ],*/ + ], + 'params' => $params, +]; diff --git a/console/config/params-local.php b/console/config/params-local.php index d0b9c34f..c1d22989 100755 --- a/console/config/params-local.php +++ b/console/config/params-local.php @@ -1,3 +1,3 @@ - 'admin@example.com', -]; + 'admin@example.com', +]; diff --git a/console/controllers/FinanceController.php b/console/controllers/FinanceController.php index 0d675825..0e53c5ff 100755 --- a/console/controllers/FinanceController.php +++ b/console/controllers/FinanceController.php @@ -1,36 +1,36 @@ -pathology->financeApiSend(); - $this->financeApiSend(); - } - - public function financeApiSend() - { - - - } - -} - - +pathology->financeApiSend(); + $this->financeApiSend(); + } + + public function financeApiSend() + { + + + } + +} + + diff --git a/console/controllers/LisSendBackUpController.php b/console/controllers/LisSendBackUpController.php index a350c445..ec2ff6f3 100755 --- a/console/controllers/LisSendBackUpController.php +++ b/console/controllers/LisSendBackUpController.php @@ -1,559 +1,559 @@ -params['external_api'] ?? null; - if (!$cfg || empty($cfg['receiveUrl'])) { - error_log("Missing external_api config or receiveUrl"); - return 1; - } - - // get token - $tokenError = null; - $token = $this->getValidApiToken($cfg, $tokenError); - if (empty($token)) { - error_log("Cannot obtain valid API token: " . - (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown'))); - return 1; - } - - $url = $cfg['receiveUrl']; - error_log("Using API token (ready to send)"); - - // ---------- build grouped payloads ---------- - $groupedByLabNo = []; - $groupedRows = []; // เก็บแถวต้นฉบับต่อ labNo เพื่อใช้บันทึก LisResult ทีหลัง - - $query_lab = HanumanResultSikarinManual::find() - ->where(['not', ['RiaHDocID' => null]]) - ->andWhere(['not', ['PatHN' => null]]) - ->andWhere(['<>', 'RiaHDocID', '']) - ->andWhere(['<>', 'PatHN', '']) - ->asArray() - ->orderBy(['RiaHDocDate' => SORT_DESC]) - ->all(); - - /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ - $fallback = [ - 'report_name' => '', - 'report_dt' => '', - 'approve_name' => '', - 'approve_dt' => '', - ]; - - foreach ($query_lab as $r) { - - if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { - $fallback['report_name'] = $r['RiaLDUserNameEntry']; - } - - if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { - $fallback['report_dt'] = $r['RiaLDUserDateEntry']; - } - - if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { - $fallback['approve_name'] = $r['RiaLDUserNameAppr']; - } - - if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { - $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; - } - } - - foreach ($query_lab as $rows) { - $filesData = []; - $timestamp = date('dmYHis'); - $labNo = $rows['LabNo'] ?? ''; - $docIdParam = $rows['RiaHDocID'] ?? ''; - $docDate = !empty($rows['RiaHDocDate']) ? date('Y-m-d', strtotime($rows['RiaHDocDate'])) : ''; - - if (!empty($docIdParam)) { - $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}"; - $pdfContent = $this->fetchUrl($LisResultUrl, 20); - if ($pdfContent !== false && strlen($pdfContent) > 0) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}.pdf", - 'Filebase64' => base64_encode($pdfContent), - ]; - } - - $localDir = "/var/www/html/pdflink/{$docIdParam}"; - if (is_dir($localDir)) { - foreach (scandir($localDir) as $fn) { - if ($fn === '.' || $fn === '..') continue; - if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue; - $path = "{$localDir}/{$fn}"; - if (is_file($path) && is_readable($path)) { - $content = @file_get_contents($path); - if ($content !== false) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}_{$fn}", - 'Filebase64' => base64_encode($content), - ]; - } - } - } - } - } - - // build test item - $testItem = [ - 'TestCode' => (string)($rows['TestCode'] ?? ''), - 'TestName' => (string)($rows['TestName'] ?? ''), - 'TestRemark' => $rows['TestRemark'] ?? '', - 'InformCriticalByCode' => '', - 'InformCriticalBy' => '', - 'InformCriticalTo' => '', - 'InformCriticalDateTime' => '', - 'CriticalFlag' => '', - 'ReportResultByCode' => '', - 'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), - 'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), - 'ApproveResultByCode' => '', - 'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), - 'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), - 'Filedata' => $filesData, - 'ResultList' => [[ - 'ResultCode' => (string)($rows['ResultCode'] ?? ''), - 'ResultName' => (string)($rows['ResultName'] ?? ''), - 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), - 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), - 'ResultFlag' => '', - 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), - 'ResultRemark' => '', - ]], - ]; - - if (!isset($groupedByLabNo[$labNo])) { - $groupedByLabNo[$labNo] = [ - 'LabNo' => $labNo, - 'RequestRemark' => '', - 'ResultStatus' => '', - 'TestList' => [], - ]; - } - - $groupedByLabNo[$labNo]['TestList'][] = $testItem; - $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab - $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; - } // end foreach build - - // ---------- ส่งแต่ละ payload ที่ grouped ---------- - foreach ($groupedByLabNo as $labNo => $payload) { - if (empty($payload) || !isset($payload['LabNo'])) { - $labKey = $payload['LabNo'] ?? ($labNo ?? '(unknown)'); - error_log("Skipping send: payload missing LabNo for key={$labKey}"); - continue; - } - - $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - $payloadJson = ($payloadJson === false) ? var_export($payload, true) : $payloadJson; - error_log("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - - $tests = array_map(function ($t) { - $code = $t['TestCode'] ?? '(noCode)'; - $name = $t['TestName'] ?? '(noName)'; - return $code . ':' . $name; - }, $payload['TestList'] ?? []); - - error_log("Sending payload for LabNo={$labNo} (tests=" . json_encode($tests, JSON_UNESCAPED_UNICODE) . ")"); - - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = (int)($response['http_code'] ?? 0); - $curlErr = $response['curl_error'] ?? null; - - if (!empty($curlErr)) { - error_log("WARNING: cURL error: {$curlErr}"); - } - - if ($httpCode === 401) { - error_log("401 Unauthorized received — refreshing token and retrying"); - $token = $this->getValidApiToken($cfg, $tokenError); - if (!empty($token)) { - error_log("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = (int)($response['http_code'] ?? 0); - $curlErr = $response['curl_error'] ?? null; - if (!empty($curlErr)) { - error_log("WARNING: cURL error on retry: {$curlErr}"); - } - } else { - error_log("ERROR: Failed to refresh token after 401"); - } - } - - error_log("Response (http={$httpCode}): " . $this->truncateForLog((string)$rawResp, 8192)); - - // decode and check success - $decoded = json_decode($rawResp, true); - $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null; - $messageVal = $decoded['Message'] ?? $decoded['message'] ?? ''; - - $success = false; - if (is_numeric($statusVal)) { - $success = (intval($statusVal) === 0); - } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') { - $success = true; - } elseif (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) { - $success = true; - } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) { - $success = true; - } - - if ($success) { - error_log("Success LabNo={$labNo}"); - - // บันทึก LisResult สำหรับทุก TestList ใน lab นี้ - $testList = $payload['TestList'] ?? []; - foreach ($testList as $testItem) { - $testCode = (string)($testItem['TestCode'] ?? ''); - $testName = (string)($testItem['TestName'] ?? ''); - $resultValue = $testItem['ResultList'][0]['ResultValue'] ?? ''; - $resultUnit = $testItem['ResultList'][0]['ResultUnit'] ?? ''; - $referenceRange = $testItem['ResultList'][0]['ReferenceRange'] ?? ''; - - // หาแถวต้นฉบับที่ตรงกับ testCode เพื่อดึง CustID/HN/RiaHDocID ถ้ามี - $matchRow = null; - if (!empty($groupedRows[$labNo])) { - foreach ($groupedRows[$labNo] as $r) { - if ((string)($r['TestCode'] ?? '') === $testCode) { - $matchRow = $r; - break; - } - } - } - // ถ้าไม่พบ ให้ใช้แถวแรกเป็น fallback (ถ้ามี) - if ($matchRow === null && !empty($groupedRows[$labNo])) { - $matchRow = $groupedRows[$labNo][0]; - } - - $custId = $matchRow['CustID'] ?? null; - $hn = $matchRow['PatHN'] ?? null; - $riahDocId = $matchRow['RiaHDocID'] ?? null; - $riahDocDate = $matchRow['RiaHDocDate'] ?? null; - - // ตรวจสอบว่ามีอยู่แล้วหรือยัง (ใช้ lab_no + test_code + cust_id + hn) - $existsQuery = [ - 'lab_no' => $labNo, - 'test_code' => $testCode, - ]; - if ($custId !== null) $existsQuery['cust_id'] = $custId; - if ($hn !== null) $existsQuery['hn'] = $hn; - - $exists = LisResult::find()->where($existsQuery)->exists(); - - if (!$exists) { - $LisResult = new LisResult([ - 'lab_no' => $labNo, - 'riah_doc_id' => $riahDocId, - 'riah_doc_date' => $riahDocDate, - 'test_code' => $testCode, - 'test_name' => $testName, - 'cust_id' => $custId, - 'hn' => $hn, - 'result_value' => $resultValue ?? '', - 'result_unit' => $resultUnit ?? '', - 'reference_range' => trim((string)$referenceRange), - 'status' => 1, - ]); - $LisResult->save(false); - } else { - error_log( - "Record already exists in LisResult, skipping insert: " - . "LabNo={$labNo}, TestCode={$testCode}, CustID={$custId}, HN={$hn}" - ); - } - } // end foreach testList - } else { - $msg = $lastResponse['response'] ?? ($lastResponse['curl_error'] ?? 'ไม่พบข้อมูลตอบกลับ'); - error_log("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024)); - } - - // optional small delay: usleep(200000); - } // end foreach groupedByLabNo - - error_log("=== Hanuman Result Sikarin END ==="); - return 0; - } - - - /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ - protected function postJsonCurl($url, $payload, $headers = []) - { - $ch = curl_init($url); - $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); - - // security: allow config to control SSL verify - $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); - - curl_setopt($ch, CURLOPT_TIMEOUT, 60); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - - $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; - foreach ($headers as $k => $v) { - if (is_int($k)) { - $curlHeaders[] = $v; - } else { - $curlHeaders[] = "{$k}: {$v}"; - } - } - curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErrno = curl_errno($ch); - $curlError = $curlErrno ? curl_error($ch) : null; - curl_close($ch); - - return [ - 'http_code' => $httpCode, - 'response' => $response, - 'curl_error' => $curlError, - 'curl_errno' => $curlErrno, - 'url' => $url, - 'payload_size' => strlen($jsonPayload), - ]; - } - - /** - * helper: fetch URL via cURL (robust) - */ - protected function fetchUrl($url, $timeout = 10) - { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - $data = curl_exec($ch); - $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $err = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - if ($data === false || $http >= 400) { - //$this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - error_log("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - return false; - } - return $data; - } - - /** - * Log helper — writes to STDOUT with timestamp and also to Yii log. - * $level: 'info'|'warning'|'error' - */ - protected function logConsole($message, $level = 'info') - { - $time = date('Y-m-d H:i:s'); - $line = "[{$time}] {$message}" . PHP_EOL; - // write to console - $this->stdout($line); - // write to Yii logger with appropriate level - switch (strtolower($level)) { - case 'error': - Yii::error($message, __METHOD__); - break; - case 'warning': - Yii::warning($message, __METHOD__); - break; - default: - Yii::info($message, __METHOD__); - } - } - - /** - * Truncate long strings for logging. $maxBytes = null => no truncation. - */ - protected function truncateForLog($text, $maxBytes = 4096) - { - if ($text === null) return ''; - if ($maxBytes === null) return $text; - $len = strlen($text); - if ($len <= $maxBytes) return $text; - $prefix = substr($text, 0, $maxBytes); - return $prefix . "\n...TRUNCATED... (original_length={$len})"; - } - - /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ - public function dateTimeFormat($value) - { - if (empty($value)) return ''; - try { - $dt = new \DateTime($value); - return $dt->format('d/m/Y H:i:s'); - } catch (\Throwable $e) { - return ''; - } - } - - /** - * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) - */ - protected function getApiToken($cfg) - { - $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; - $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; - $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; - - $payload = ['username' => $username, 'password' => $password]; - - $ch = curl_init($tokenUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - - $raw = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErr = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; - - if ($raw === false || $curlErr) { - $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); - return $result; - } - - $decoded = json_decode($raw, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $result['error'] = 'Invalid JSON response from token endpoint'; - return $result; - } - - if (!empty($decoded['access_token'])) { - $result['ok'] = true; - $result['token'] = $decoded['access_token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } elseif (!empty($decoded['token'])) { - $result['ok'] = true; - $result['token'] = $decoded['token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } else { - $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); - } - - return $result; - } - - /** - * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) - * พร้อม debug log วันที่หมดอายุของ Token - */ - protected function getValidApiToken($cfg, &$tokenError = null) - { - $apiName = $cfg['apiName'] ?? 'gems_api'; - $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); - $token = null; - $nowTs = time(); - $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที - - $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); - - if ($tokenModel) { - //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - } else { - error_log("No token found in DB for {$apiName}, will request new token"); - //$this->logConsole("No token found in DB for {$apiName}, will request new token"); - } - - if ($needRequest) { - $tokenResp = $this->getApiToken($cfg); - - if (empty($tokenResp) || empty($tokenResp['ok'])) { - $tokenError = [ - 'message' => 'Failed to get token from API', - 'tokenUrl' => $cfg['tokenUrl'] ?? null, - 'error' => $tokenResp['error'] ?? 'Unknown error', - 'http_code' => $tokenResp['http_code'] ?? 0, - 'raw' => $tokenResp['raw'] ?? null, - ]; - - if ($tokenModel && !empty($tokenModel->token)) { - error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - return $tokenModel->token; // fallback - } - - return null; - } - - $token = $tokenResp['token']; - $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); - - if (!$tokenModel) { - $tokenModel = new ApiToken(); - $tokenModel->api_name = $apiName; - } - - $tokenModel->token = $token; - $tokenModel->expires_at = $expiresAt; - - try { - $tokenModel->save(false); - error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); - //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); - } catch (\Throwable $e) { - //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); - error_log("Error saving new token: " . $e->getMessage(), 'error'); - if ($tokenModel && !empty($tokenModel->token)) { - return $tokenModel->token; // fallback - } - $tokenError = $e->getMessage(); - return null; - } - } else { - $token = $tokenModel->token; - //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - } - return $token; - } +params['external_api'] ?? null; + if (!$cfg || empty($cfg['receiveUrl'])) { + error_log("Missing external_api config or receiveUrl"); + return 1; + } + + // get token + $tokenError = null; + $token = $this->getValidApiToken($cfg, $tokenError); + if (empty($token)) { + error_log("Cannot obtain valid API token: " . + (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown'))); + return 1; + } + + $url = $cfg['receiveUrl']; + error_log("Using API token (ready to send)"); + + // ---------- build grouped payloads ---------- + $groupedByLabNo = []; + $groupedRows = []; // เก็บแถวต้นฉบับต่อ labNo เพื่อใช้บันทึก LisResult ทีหลัง + + $query_lab = HanumanResultSikarinManual::find() + ->where(['not', ['RiaHDocID' => null]]) + ->andWhere(['not', ['PatHN' => null]]) + ->andWhere(['<>', 'RiaHDocID', '']) + ->andWhere(['<>', 'PatHN', '']) + ->asArray() + ->orderBy(['RiaHDocDate' => SORT_DESC]) + ->all(); + + /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ + $fallback = [ + 'report_name' => '', + 'report_dt' => '', + 'approve_name' => '', + 'approve_dt' => '', + ]; + + foreach ($query_lab as $r) { + + if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { + $fallback['report_name'] = $r['RiaLDUserNameEntry']; + } + + if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { + $fallback['report_dt'] = $r['RiaLDUserDateEntry']; + } + + if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { + $fallback['approve_name'] = $r['RiaLDUserNameAppr']; + } + + if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { + $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; + } + } + + foreach ($query_lab as $rows) { + $filesData = []; + $timestamp = date('dmYHis'); + $labNo = $rows['LabNo'] ?? ''; + $docIdParam = $rows['RiaHDocID'] ?? ''; + $docDate = !empty($rows['RiaHDocDate']) ? date('Y-m-d', strtotime($rows['RiaHDocDate'])) : ''; + + if (!empty($docIdParam)) { + $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}"; + $pdfContent = $this->fetchUrl($LisResultUrl, 20); + if ($pdfContent !== false && strlen($pdfContent) > 0) { + $filesData[] = [ + 'FileName' => "{$labNo}_{$timestamp}.pdf", + 'Filebase64' => base64_encode($pdfContent), + ]; + } + + $localDir = "/var/www/html/pdflink/{$docIdParam}"; + if (is_dir($localDir)) { + foreach (scandir($localDir) as $fn) { + if ($fn === '.' || $fn === '..') continue; + if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue; + $path = "{$localDir}/{$fn}"; + if (is_file($path) && is_readable($path)) { + $content = @file_get_contents($path); + if ($content !== false) { + $filesData[] = [ + 'FileName' => "{$labNo}_{$timestamp}_{$fn}", + 'Filebase64' => base64_encode($content), + ]; + } + } + } + } + } + + // build test item + $testItem = [ + 'TestCode' => (string)($rows['TestCode'] ?? ''), + 'TestName' => (string)($rows['TestName'] ?? ''), + 'TestRemark' => $rows['TestRemark'] ?? '', + 'InformCriticalByCode' => '', + 'InformCriticalBy' => '', + 'InformCriticalTo' => '', + 'InformCriticalDateTime' => '', + 'CriticalFlag' => '', + 'ReportResultByCode' => '', + 'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), + 'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), + 'ApproveResultByCode' => '', + 'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), + 'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), + 'Filedata' => $filesData, + 'ResultList' => [[ + 'ResultCode' => (string)($rows['ResultCode'] ?? ''), + 'ResultName' => (string)($rows['ResultName'] ?? ''), + 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), + 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), + 'ResultFlag' => '', + 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), + 'ResultRemark' => '', + ]], + ]; + + if (!isset($groupedByLabNo[$labNo])) { + $groupedByLabNo[$labNo] = [ + 'LabNo' => $labNo, + 'RequestRemark' => '', + 'ResultStatus' => '', + 'TestList' => [], + ]; + } + + $groupedByLabNo[$labNo]['TestList'][] = $testItem; + $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab + $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; + } // end foreach build + + // ---------- ส่งแต่ละ payload ที่ grouped ---------- + foreach ($groupedByLabNo as $labNo => $payload) { + if (empty($payload) || !isset($payload['LabNo'])) { + $labKey = $payload['LabNo'] ?? ($labNo ?? '(unknown)'); + error_log("Skipping send: payload missing LabNo for key={$labKey}"); + continue; + } + + $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $payloadJson = ($payloadJson === false) ? var_export($payload, true) : $payloadJson; + error_log("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); + + $tests = array_map(function ($t) { + $code = $t['TestCode'] ?? '(noCode)'; + $name = $t['TestName'] ?? '(noName)'; + return $code . ':' . $name; + }, $payload['TestList'] ?? []); + + error_log("Sending payload for LabNo={$labNo} (tests=" . json_encode($tests, JSON_UNESCAPED_UNICODE) . ")"); + + $response = $this->postJsonCurl($url, $payload, [ + 'Authorization' => "Bearer {$token}", + 'Content-Type' => 'application/json', + ]); + + $lastResponse = $response; + $rawResp = $response['response'] ?? ''; + $httpCode = (int)($response['http_code'] ?? 0); + $curlErr = $response['curl_error'] ?? null; + + if (!empty($curlErr)) { + error_log("WARNING: cURL error: {$curlErr}"); + } + + if ($httpCode === 401) { + error_log("401 Unauthorized received — refreshing token and retrying"); + $token = $this->getValidApiToken($cfg, $tokenError); + if (!empty($token)) { + error_log("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); + $response = $this->postJsonCurl($url, $payload, [ + 'Authorization' => "Bearer {$token}", + 'Content-Type' => 'application/json', + ]); + $lastResponse = $response; + $rawResp = $response['response'] ?? ''; + $httpCode = (int)($response['http_code'] ?? 0); + $curlErr = $response['curl_error'] ?? null; + if (!empty($curlErr)) { + error_log("WARNING: cURL error on retry: {$curlErr}"); + } + } else { + error_log("ERROR: Failed to refresh token after 401"); + } + } + + error_log("Response (http={$httpCode}): " . $this->truncateForLog((string)$rawResp, 8192)); + + // decode and check success + $decoded = json_decode($rawResp, true); + $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null; + $messageVal = $decoded['Message'] ?? $decoded['message'] ?? ''; + + $success = false; + if (is_numeric($statusVal)) { + $success = (intval($statusVal) === 0); + } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') { + $success = true; + } elseif (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) { + $success = true; + } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) { + $success = true; + } + + if ($success) { + error_log("Success LabNo={$labNo}"); + + // บันทึก LisResult สำหรับทุก TestList ใน lab นี้ + $testList = $payload['TestList'] ?? []; + foreach ($testList as $testItem) { + $testCode = (string)($testItem['TestCode'] ?? ''); + $testName = (string)($testItem['TestName'] ?? ''); + $resultValue = $testItem['ResultList'][0]['ResultValue'] ?? ''; + $resultUnit = $testItem['ResultList'][0]['ResultUnit'] ?? ''; + $referenceRange = $testItem['ResultList'][0]['ReferenceRange'] ?? ''; + + // หาแถวต้นฉบับที่ตรงกับ testCode เพื่อดึง CustID/HN/RiaHDocID ถ้ามี + $matchRow = null; + if (!empty($groupedRows[$labNo])) { + foreach ($groupedRows[$labNo] as $r) { + if ((string)($r['TestCode'] ?? '') === $testCode) { + $matchRow = $r; + break; + } + } + } + // ถ้าไม่พบ ให้ใช้แถวแรกเป็น fallback (ถ้ามี) + if ($matchRow === null && !empty($groupedRows[$labNo])) { + $matchRow = $groupedRows[$labNo][0]; + } + + $custId = $matchRow['CustID'] ?? null; + $hn = $matchRow['PatHN'] ?? null; + $riahDocId = $matchRow['RiaHDocID'] ?? null; + $riahDocDate = $matchRow['RiaHDocDate'] ?? null; + + // ตรวจสอบว่ามีอยู่แล้วหรือยัง (ใช้ lab_no + test_code + cust_id + hn) + $existsQuery = [ + 'lab_no' => $labNo, + 'test_code' => $testCode, + ]; + if ($custId !== null) $existsQuery['cust_id'] = $custId; + if ($hn !== null) $existsQuery['hn'] = $hn; + + $exists = LisResult::find()->where($existsQuery)->exists(); + + if (!$exists) { + $LisResult = new LisResult([ + 'lab_no' => $labNo, + 'riah_doc_id' => $riahDocId, + 'riah_doc_date' => $riahDocDate, + 'test_code' => $testCode, + 'test_name' => $testName, + 'cust_id' => $custId, + 'hn' => $hn, + 'result_value' => $resultValue ?? '', + 'result_unit' => $resultUnit ?? '', + 'reference_range' => trim((string)$referenceRange), + 'status' => 1, + ]); + $LisResult->save(false); + } else { + error_log( + "Record already exists in LisResult, skipping insert: " + . "LabNo={$labNo}, TestCode={$testCode}, CustID={$custId}, HN={$hn}" + ); + } + } // end foreach testList + } else { + $msg = $lastResponse['response'] ?? ($lastResponse['curl_error'] ?? 'ไม่พบข้อมูลตอบกลับ'); + error_log("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024)); + } + + // optional small delay: usleep(200000); + } // end foreach groupedByLabNo + + error_log("=== Hanuman Result Sikarin END ==="); + return 0; + } + + + /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ + protected function postJsonCurl($url, $payload, $headers = []) + { + $ch = curl_init($url); + $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); + + // security: allow config to control SSL verify + $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); + + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; + foreach ($headers as $k => $v) { + if (is_int($k)) { + $curlHeaders[] = $v; + } else { + $curlHeaders[] = "{$k}: {$v}"; + } + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErrno = curl_errno($ch); + $curlError = $curlErrno ? curl_error($ch) : null; + curl_close($ch); + + return [ + 'http_code' => $httpCode, + 'response' => $response, + 'curl_error' => $curlError, + 'curl_errno' => $curlErrno, + 'url' => $url, + 'payload_size' => strlen($jsonPayload), + ]; + } + + /** + * helper: fetch URL via cURL (robust) + */ + protected function fetchUrl($url, $timeout = 10) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $data = curl_exec($ch); + $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $err = curl_errno($ch) ? curl_error($ch) : null; + curl_close($ch); + + if ($data === false || $http >= 400) { + //$this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); + error_log("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); + return false; + } + return $data; + } + + /** + * Log helper — writes to STDOUT with timestamp and also to Yii log. + * $level: 'info'|'warning'|'error' + */ + protected function logConsole($message, $level = 'info') + { + $time = date('Y-m-d H:i:s'); + $line = "[{$time}] {$message}" . PHP_EOL; + // write to console + $this->stdout($line); + // write to Yii logger with appropriate level + switch (strtolower($level)) { + case 'error': + Yii::error($message, __METHOD__); + break; + case 'warning': + Yii::warning($message, __METHOD__); + break; + default: + Yii::info($message, __METHOD__); + } + } + + /** + * Truncate long strings for logging. $maxBytes = null => no truncation. + */ + protected function truncateForLog($text, $maxBytes = 4096) + { + if ($text === null) return ''; + if ($maxBytes === null) return $text; + $len = strlen($text); + if ($len <= $maxBytes) return $text; + $prefix = substr($text, 0, $maxBytes); + return $prefix . "\n...TRUNCATED... (original_length={$len})"; + } + + /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ + public function dateTimeFormat($value) + { + if (empty($value)) return ''; + try { + $dt = new \DateTime($value); + return $dt->format('d/m/Y H:i:s'); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) + */ + protected function getApiToken($cfg) + { + $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; + $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; + $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; + + $payload = ['username' => $username, 'password' => $password]; + + $ch = curl_init($tokenUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + $raw = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErr = curl_errno($ch) ? curl_error($ch) : null; + curl_close($ch); + + $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; + + if ($raw === false || $curlErr) { + $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); + return $result; + } + + $decoded = json_decode($raw, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $result['error'] = 'Invalid JSON response from token endpoint'; + return $result; + } + + if (!empty($decoded['access_token'])) { + $result['ok'] = true; + $result['token'] = $decoded['access_token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } elseif (!empty($decoded['token'])) { + $result['ok'] = true; + $result['token'] = $decoded['token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } else { + $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); + } + + return $result; + } + + /** + * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) + * พร้อม debug log วันที่หมดอายุของ Token + */ + protected function getValidApiToken($cfg, &$tokenError = null) + { + $apiName = $cfg['apiName'] ?? 'gems_api'; + $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); + $token = null; + $nowTs = time(); + $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที + + $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); + + if ($tokenModel) { + //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + } else { + error_log("No token found in DB for {$apiName}, will request new token"); + //$this->logConsole("No token found in DB for {$apiName}, will request new token"); + } + + if ($needRequest) { + $tokenResp = $this->getApiToken($cfg); + + if (empty($tokenResp) || empty($tokenResp['ok'])) { + $tokenError = [ + 'message' => 'Failed to get token from API', + 'tokenUrl' => $cfg['tokenUrl'] ?? null, + 'error' => $tokenResp['error'] ?? 'Unknown error', + 'http_code' => $tokenResp['http_code'] ?? 0, + 'raw' => $tokenResp['raw'] ?? null, + ]; + + if ($tokenModel && !empty($tokenModel->token)) { + error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + return $tokenModel->token; // fallback + } + + return null; + } + + $token = $tokenResp['token']; + $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); + + if (!$tokenModel) { + $tokenModel = new ApiToken(); + $tokenModel->api_name = $apiName; + } + + $tokenModel->token = $token; + $tokenModel->expires_at = $expiresAt; + + try { + $tokenModel->save(false); + error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); + //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); + } catch (\Throwable $e) { + //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); + error_log("Error saving new token: " . $e->getMessage(), 'error'); + if ($tokenModel && !empty($tokenModel->token)) { + return $tokenModel->token; // fallback + } + $tokenError = $e->getMessage(); + return null; + } + } else { + $token = $tokenModel->token; + //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + } + return $token; + } } \ No newline at end of file diff --git a/console/controllers/LisSendController.php b/console/controllers/LisSendController.php index 88c7aafd..c4dbb7b0 100755 --- a/console/controllers/LisSendController.php +++ b/console/controllers/LisSendController.php @@ -1,573 +1,573 @@ -where(['not', ['RiaHDocID' => null]]) - ->andWhere(['not', ['PatHN' => null]]) - ->andWhere(['<>', 'RiaHDocID', '']) - ->andWhere(['<>', 'PatHN', '']) - ->asArray() - ->orderBy(['RiaHDocDate' => SORT_DESC]) - ->one(); - - if (empty($query)) { - error_log('No lab data'); - return; - } - - $sendMapping = []; - - // config - $cfg = Yii::$app->params['external_api'] ?? null; - if (!$cfg || empty($cfg['receiveUrl'])) { - error_log("Missing external_api config or receiveUrl"); - return 1; - } - - // get token - $tokenError = null; - $token = $this->getValidApiToken($cfg, $tokenError); - if (empty($token)) { - error_log("Cannot obtain valid API token: " . - (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown'))); - return 1; - } - - $url = $cfg['receiveUrl']; - error_log("Using API token (ready to send)"); - - // ---------- build grouped payloads ---------- - $groupedByLabNo = []; - $groupedRows = []; // เก็บแถวต้นฉบับต่อ labNo เพื่อใช้บันทึก LisResult ทีหลัง - - $query_lab = HanumanResultSikarinCheck::find() - ->where(['not', ['RiaHDocID' => null]]) - ->andWhere(['not', ['PatHN' => null]]) - ->andWhere(['<>', 'RiaHDocID', '']) - ->andWhere(['<>', 'PatHN', '']) - ->andWhere(['LabNo' => $query['LabNo']]) - ->asArray() - ->orderBy(['RiaHDocDate' => SORT_DESC]) - ->all(); - - /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ - $fallback = [ - 'report_name' => '', - 'report_dt' => '', - 'approve_name' => '', - 'approve_dt' => '', - ]; - - foreach ($query_lab as $r) { - - if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { - $fallback['report_name'] = $r['RiaLDUserNameEntry']; - } - - if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { - $fallback['report_dt'] = $r['RiaLDUserDateEntry']; - } - - if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { - $fallback['approve_name'] = $r['RiaLDUserNameAppr']; - } - - if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { - $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; - } - } - - foreach ($query_lab as $rows) { - $filesData = []; - $timestamp = date('dmYHis'); - $labNo = $rows['LabNo'] ?? ''; - $docIdParam = $rows['RiaHDocID'] ?? ''; - $docDate = !empty($rows['RiaHDocDate']) ? date('Y-m-d', strtotime($rows['RiaHDocDate'])) : ''; - - if (!empty($docIdParam)) { - $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}"; - $pdfContent = $this->fetchUrl($LisResultUrl, 20); - if ($pdfContent !== false && strlen($pdfContent) > 0) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}.pdf", - 'Filebase64' => base64_encode($pdfContent), - ]; - } - - $localDir = "/var/www/html/pdflink/{$docIdParam}"; - if (is_dir($localDir)) { - foreach (scandir($localDir) as $fn) { - if ($fn === '.' || $fn === '..') continue; - if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue; - $path = "{$localDir}/{$fn}"; - if (is_file($path) && is_readable($path)) { - $content = @file_get_contents($path); - if ($content !== false) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}_{$fn}", - 'Filebase64' => base64_encode($content), - ]; - } - } - } - } - } - - // build test item - $testItem = [ - 'TestCode' => (string)($rows['TestCode'] ?? ''), - 'TestName' => (string)($rows['TestName'] ?? ''), - 'TestRemark' => $rows['TestRemark'] ?? '', - 'InformCriticalByCode' => '', - 'InformCriticalBy' => '', - 'InformCriticalTo' => '', - 'InformCriticalDateTime' => '', - 'CriticalFlag' => '', - 'ReportResultByCode' => '', - 'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), - 'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), - 'ApproveResultByCode' => '', - 'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), - 'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), - 'Filedata' => $filesData, - 'ResultList' => [[ - 'ResultCode' => (string)($rows['ResultCode'] ?? ''), - 'ResultName' => (string)($rows['ResultName'] ?? ''), - 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), - 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), - 'ResultFlag' => '', - 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), - 'ResultRemark' => '', - ]], - ]; - - if (!isset($groupedByLabNo[$labNo])) { - $groupedByLabNo[$labNo] = [ - 'LabNo' => $labNo, - 'RequestRemark' => '', - 'ResultStatus' => '', - 'TestList' => [], - ]; - } - - $groupedByLabNo[$labNo]['TestList'][] = $testItem; - $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab - $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; - } // end foreach build - - // ---------- ส่งแต่ละ payload ที่ grouped ---------- - foreach ($groupedByLabNo as $labNo => $payload) { - if (empty($payload) || !isset($payload['LabNo'])) { - $labKey = $payload['LabNo'] ?? ($labNo ?? '(unknown)'); - error_log("Skipping send: payload missing LabNo for key={$labKey}"); - continue; - } - - $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - $payloadJson = ($payloadJson === false) ? var_export($payload, true) : $payloadJson; - error_log("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - - $tests = array_map(function ($t) { - $code = $t['TestCode'] ?? '(noCode)'; - $name = $t['TestName'] ?? '(noName)'; - return $code . ':' . $name; - }, $payload['TestList'] ?? []); - - error_log("Sending payload for LabNo={$labNo} (tests=" . json_encode($tests, JSON_UNESCAPED_UNICODE) . ")"); - - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = (int)($response['http_code'] ?? 0); - $curlErr = $response['curl_error'] ?? null; - - if (!empty($curlErr)) { - error_log("WARNING: cURL error: {$curlErr}"); - } - - if ($httpCode === 401) { - error_log("401 Unauthorized received — refreshing token and retrying"); - $token = $this->getValidApiToken($cfg, $tokenError); - if (!empty($token)) { - error_log("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = (int)($response['http_code'] ?? 0); - $curlErr = $response['curl_error'] ?? null; - if (!empty($curlErr)) { - error_log("WARNING: cURL error on retry: {$curlErr}"); - } - } else { - error_log("ERROR: Failed to refresh token after 401"); - } - } - - error_log("Response (http={$httpCode}): " . $this->truncateForLog((string)$rawResp, 8192)); - - // decode and check success - $decoded = json_decode($rawResp, true); - $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null; - $messageVal = $decoded['Message'] ?? $decoded['message'] ?? ''; - - $success = false; - if (is_numeric($statusVal)) { - $success = (intval($statusVal) === 0); - } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') { - $success = true; - } elseif (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) { - $success = true; - } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) { - $success = true; - } - - if ($success) { - error_log("Success LabNo={$labNo}"); - - // บันทึก LisResult สำหรับทุก TestList ใน lab นี้ - $testList = $payload['TestList'] ?? []; - foreach ($testList as $testItem) { - $testCode = (string)($testItem['TestCode'] ?? ''); - $testName = (string)($testItem['TestName'] ?? ''); - $resultValue = $testItem['ResultList'][0]['ResultValue'] ?? ''; - $resultUnit = $testItem['ResultList'][0]['ResultUnit'] ?? ''; - $referenceRange = $testItem['ResultList'][0]['ReferenceRange'] ?? ''; - - // หาแถวต้นฉบับที่ตรงกับ testCode เพื่อดึง CustID/HN/RiaHDocID ถ้ามี - $matchRow = null; - if (!empty($groupedRows[$labNo])) { - foreach ($groupedRows[$labNo] as $r) { - if ((string)($r['TestCode'] ?? '') === $testCode) { - $matchRow = $r; - break; - } - } - } - // ถ้าไม่พบ ให้ใช้แถวแรกเป็น fallback (ถ้ามี) - if ($matchRow === null && !empty($groupedRows[$labNo])) { - $matchRow = $groupedRows[$labNo][0]; - } - - $custId = $matchRow['CustID'] ?? null; - $hn = $matchRow['PatHN'] ?? null; - $riahDocId = $matchRow['RiaHDocID'] ?? null; - $riahDocDate = $matchRow['RiaHDocDate'] ?? null; - - // ตรวจสอบว่ามีอยู่แล้วหรือยัง (ใช้ lab_no + test_code + cust_id + hn) - $existsQuery = [ - 'lab_no' => $labNo, - 'test_code' => $testCode, - ]; - if ($custId !== null) $existsQuery['cust_id'] = $custId; - if ($hn !== null) $existsQuery['hn'] = $hn; - - $exists = LisResult::find()->where($existsQuery)->exists(); - - if (!$exists) { - $LisResult = new LisResult([ - 'lab_no' => $labNo, - 'riah_doc_id' => $riahDocId, - 'riah_doc_date' => $riahDocDate, - 'test_code' => $testCode, - 'test_name' => $testName, - 'cust_id' => $custId, - 'hn' => $hn, - 'result_value' => $resultValue ?? '', - 'result_unit' => $resultUnit ?? '', - 'reference_range' => trim((string)$referenceRange), - 'status' => 1, - ]); - $LisResult->save(false); - } else { - error_log( - "Record already exists in LisResult, skipping insert: " - . "LabNo={$labNo}, TestCode={$testCode}, CustID={$custId}, HN={$hn}" - ); - } - } // end foreach testList - } else { - $msg = $lastResponse['response'] ?? ($lastResponse['curl_error'] ?? 'ไม่พบข้อมูลตอบกลับ'); - error_log("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024)); - } - - // optional small delay: usleep(200000); - } // end foreach groupedByLabNo - - error_log("=== Hanuman Result Sikarin END ==="); - return 0; - } - - - /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ - protected function postJsonCurl($url, $payload, $headers = []) - { - $ch = curl_init($url); - $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); - - // security: allow config to control SSL verify - $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); - - curl_setopt($ch, CURLOPT_TIMEOUT, 60); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - - $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; - foreach ($headers as $k => $v) { - if (is_int($k)) { - $curlHeaders[] = $v; - } else { - $curlHeaders[] = "{$k}: {$v}"; - } - } - curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErrno = curl_errno($ch); - $curlError = $curlErrno ? curl_error($ch) : null; - curl_close($ch); - - return [ - 'http_code' => $httpCode, - 'response' => $response, - 'curl_error' => $curlError, - 'curl_errno' => $curlErrno, - 'url' => $url, - 'payload_size' => strlen($jsonPayload), - ]; - } - - /** - * helper: fetch URL via cURL (robust) - */ - protected function fetchUrl($url, $timeout = 10) - { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - $data = curl_exec($ch); - $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $err = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - if ($data === false || $http >= 400) { - //$this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - error_log("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - return false; - } - return $data; - } - - /** - * Log helper — writes to STDOUT with timestamp and also to Yii log. - * $level: 'info'|'warning'|'error' - */ - protected function logConsole($message, $level = 'info') - { - $time = date('Y-m-d H:i:s'); - $line = "[{$time}] {$message}" . PHP_EOL; - // write to console - $this->stdout($line); - // write to Yii logger with appropriate level - switch (strtolower($level)) { - case 'error': - Yii::error($message, __METHOD__); - break; - case 'warning': - Yii::warning($message, __METHOD__); - break; - default: - Yii::info($message, __METHOD__); - } - } - - /** - * Truncate long strings for logging. $maxBytes = null => no truncation. - */ - protected function truncateForLog($text, $maxBytes = 4096) - { - if ($text === null) return ''; - if ($maxBytes === null) return $text; - $len = strlen($text); - if ($len <= $maxBytes) return $text; - $prefix = substr($text, 0, $maxBytes); - return $prefix . "\n...TRUNCATED... (original_length={$len})"; - } - - /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ - public function dateTimeFormat($value) - { - if (empty($value)) return ''; - try { - $dt = new \DateTime($value); - return $dt->format('d/m/Y H:i:s'); - } catch (\Throwable $e) { - return ''; - } - } - - /** - * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) - */ - protected function getApiToken($cfg) - { - $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; - $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; - $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; - - $payload = ['username' => $username, 'password' => $password]; - - $ch = curl_init($tokenUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - - $raw = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErr = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; - - if ($raw === false || $curlErr) { - $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); - return $result; - } - - $decoded = json_decode($raw, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $result['error'] = 'Invalid JSON response from token endpoint'; - return $result; - } - - if (!empty($decoded['access_token'])) { - $result['ok'] = true; - $result['token'] = $decoded['access_token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } elseif (!empty($decoded['token'])) { - $result['ok'] = true; - $result['token'] = $decoded['token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } else { - $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); - } - - return $result; - } - - /** - * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) - * พร้อม debug log วันที่หมดอายุของ Token - */ - protected function getValidApiToken($cfg, &$tokenError = null) - { - $apiName = $cfg['apiName'] ?? 'gems_api'; - $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); - $token = null; - $nowTs = time(); - $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที - - $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); - - if ($tokenModel) { - //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - } else { - error_log("No token found in DB for {$apiName}, will request new token"); - //$this->logConsole("No token found in DB for {$apiName}, will request new token"); - } - - if ($needRequest) { - $tokenResp = $this->getApiToken($cfg); - - if (empty($tokenResp) || empty($tokenResp['ok'])) { - $tokenError = [ - 'message' => 'Failed to get token from API', - 'tokenUrl' => $cfg['tokenUrl'] ?? null, - 'error' => $tokenResp['error'] ?? 'Unknown error', - 'http_code' => $tokenResp['http_code'] ?? 0, - 'raw' => $tokenResp['raw'] ?? null, - ]; - - if ($tokenModel && !empty($tokenModel->token)) { - error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - return $tokenModel->token; // fallback - } - - return null; - } - - $token = $tokenResp['token']; - $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); - - if (!$tokenModel) { - $tokenModel = new ApiToken(); - $tokenModel->api_name = $apiName; - } - - $tokenModel->token = $token; - $tokenModel->expires_at = $expiresAt; - - try { - $tokenModel->save(false); - error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); - //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); - } catch (\Throwable $e) { - //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); - error_log("Error saving new token: " . $e->getMessage(), 'error'); - if ($tokenModel && !empty($tokenModel->token)) { - return $tokenModel->token; // fallback - } - $tokenError = $e->getMessage(); - return null; - } - } else { - $token = $tokenModel->token; - //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - } - return $token; - } -} +where(['not', ['RiaHDocID' => null]]) + ->andWhere(['not', ['PatHN' => null]]) + ->andWhere(['<>', 'RiaHDocID', '']) + ->andWhere(['<>', 'PatHN', '']) + ->asArray() + ->orderBy(['RiaHDocDate' => SORT_DESC]) + ->one(); + + if (empty($query)) { + error_log('No lab data'); + return; + } + + $sendMapping = []; + + // config + $cfg = Yii::$app->params['external_api'] ?? null; + if (!$cfg || empty($cfg['receiveUrl'])) { + error_log("Missing external_api config or receiveUrl"); + return 1; + } + + // get token + $tokenError = null; + $token = $this->getValidApiToken($cfg, $tokenError); + if (empty($token)) { + error_log("Cannot obtain valid API token: " . + (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown'))); + return 1; + } + + $url = $cfg['receiveUrl']; + error_log("Using API token (ready to send)"); + + // ---------- build grouped payloads ---------- + $groupedByLabNo = []; + $groupedRows = []; // เก็บแถวต้นฉบับต่อ labNo เพื่อใช้บันทึก LisResult ทีหลัง + + $query_lab = HanumanResultSikarinCheck::find() + ->where(['not', ['RiaHDocID' => null]]) + ->andWhere(['not', ['PatHN' => null]]) + ->andWhere(['<>', 'RiaHDocID', '']) + ->andWhere(['<>', 'PatHN', '']) + ->andWhere(['LabNo' => $query['LabNo']]) + ->asArray() + ->orderBy(['RiaHDocDate' => SORT_DESC]) + ->all(); + + /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ + $fallback = [ + 'report_name' => '', + 'report_dt' => '', + 'approve_name' => '', + 'approve_dt' => '', + ]; + + foreach ($query_lab as $r) { + + if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { + $fallback['report_name'] = $r['RiaLDUserNameEntry']; + } + + if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { + $fallback['report_dt'] = $r['RiaLDUserDateEntry']; + } + + if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { + $fallback['approve_name'] = $r['RiaLDUserNameAppr']; + } + + if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { + $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; + } + } + + foreach ($query_lab as $rows) { + $filesData = []; + $timestamp = date('dmYHis'); + $labNo = $rows['LabNo'] ?? ''; + $docIdParam = $rows['RiaHDocID'] ?? ''; + $docDate = !empty($rows['RiaHDocDate']) ? date('Y-m-d', strtotime($rows['RiaHDocDate'])) : ''; + + if (!empty($docIdParam)) { + $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}"; + $pdfContent = $this->fetchUrl($LisResultUrl, 20); + if ($pdfContent !== false && strlen($pdfContent) > 0) { + $filesData[] = [ + 'FileName' => "{$labNo}_{$timestamp}.pdf", + 'Filebase64' => base64_encode($pdfContent), + ]; + } + + $localDir = "/var/www/html/pdflink/{$docIdParam}"; + if (is_dir($localDir)) { + foreach (scandir($localDir) as $fn) { + if ($fn === '.' || $fn === '..') continue; + if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue; + $path = "{$localDir}/{$fn}"; + if (is_file($path) && is_readable($path)) { + $content = @file_get_contents($path); + if ($content !== false) { + $filesData[] = [ + 'FileName' => "{$labNo}_{$timestamp}_{$fn}", + 'Filebase64' => base64_encode($content), + ]; + } + } + } + } + } + + // build test item + $testItem = [ + 'TestCode' => (string)($rows['TestCode'] ?? ''), + 'TestName' => (string)($rows['TestName'] ?? ''), + 'TestRemark' => $rows['TestRemark'] ?? '', + 'InformCriticalByCode' => '', + 'InformCriticalBy' => '', + 'InformCriticalTo' => '', + 'InformCriticalDateTime' => '', + 'CriticalFlag' => '', + 'ReportResultByCode' => '', + 'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), + 'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), + 'ApproveResultByCode' => '', + 'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), + 'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), + 'Filedata' => $filesData, + 'ResultList' => [[ + 'ResultCode' => (string)($rows['ResultCode'] ?? ''), + 'ResultName' => (string)($rows['ResultName'] ?? ''), + 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), + 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), + 'ResultFlag' => '', + 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), + 'ResultRemark' => '', + ]], + ]; + + if (!isset($groupedByLabNo[$labNo])) { + $groupedByLabNo[$labNo] = [ + 'LabNo' => $labNo, + 'RequestRemark' => '', + 'ResultStatus' => '', + 'TestList' => [], + ]; + } + + $groupedByLabNo[$labNo]['TestList'][] = $testItem; + $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab + $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; + } // end foreach build + + // ---------- ส่งแต่ละ payload ที่ grouped ---------- + foreach ($groupedByLabNo as $labNo => $payload) { + if (empty($payload) || !isset($payload['LabNo'])) { + $labKey = $payload['LabNo'] ?? ($labNo ?? '(unknown)'); + error_log("Skipping send: payload missing LabNo for key={$labKey}"); + continue; + } + + $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $payloadJson = ($payloadJson === false) ? var_export($payload, true) : $payloadJson; + error_log("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); + + $tests = array_map(function ($t) { + $code = $t['TestCode'] ?? '(noCode)'; + $name = $t['TestName'] ?? '(noName)'; + return $code . ':' . $name; + }, $payload['TestList'] ?? []); + + error_log("Sending payload for LabNo={$labNo} (tests=" . json_encode($tests, JSON_UNESCAPED_UNICODE) . ")"); + + $response = $this->postJsonCurl($url, $payload, [ + 'Authorization' => "Bearer {$token}", + 'Content-Type' => 'application/json', + ]); + + $lastResponse = $response; + $rawResp = $response['response'] ?? ''; + $httpCode = (int)($response['http_code'] ?? 0); + $curlErr = $response['curl_error'] ?? null; + + if (!empty($curlErr)) { + error_log("WARNING: cURL error: {$curlErr}"); + } + + if ($httpCode === 401) { + error_log("401 Unauthorized received — refreshing token and retrying"); + $token = $this->getValidApiToken($cfg, $tokenError); + if (!empty($token)) { + error_log("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); + $response = $this->postJsonCurl($url, $payload, [ + 'Authorization' => "Bearer {$token}", + 'Content-Type' => 'application/json', + ]); + $lastResponse = $response; + $rawResp = $response['response'] ?? ''; + $httpCode = (int)($response['http_code'] ?? 0); + $curlErr = $response['curl_error'] ?? null; + if (!empty($curlErr)) { + error_log("WARNING: cURL error on retry: {$curlErr}"); + } + } else { + error_log("ERROR: Failed to refresh token after 401"); + } + } + + error_log("Response (http={$httpCode}): " . $this->truncateForLog((string)$rawResp, 8192)); + + // decode and check success + $decoded = json_decode($rawResp, true); + $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null; + $messageVal = $decoded['Message'] ?? $decoded['message'] ?? ''; + + $success = false; + if (is_numeric($statusVal)) { + $success = (intval($statusVal) === 0); + } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') { + $success = true; + } elseif (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) { + $success = true; + } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) { + $success = true; + } + + if ($success) { + error_log("Success LabNo={$labNo}"); + + // บันทึก LisResult สำหรับทุก TestList ใน lab นี้ + $testList = $payload['TestList'] ?? []; + foreach ($testList as $testItem) { + $testCode = (string)($testItem['TestCode'] ?? ''); + $testName = (string)($testItem['TestName'] ?? ''); + $resultValue = $testItem['ResultList'][0]['ResultValue'] ?? ''; + $resultUnit = $testItem['ResultList'][0]['ResultUnit'] ?? ''; + $referenceRange = $testItem['ResultList'][0]['ReferenceRange'] ?? ''; + + // หาแถวต้นฉบับที่ตรงกับ testCode เพื่อดึง CustID/HN/RiaHDocID ถ้ามี + $matchRow = null; + if (!empty($groupedRows[$labNo])) { + foreach ($groupedRows[$labNo] as $r) { + if ((string)($r['TestCode'] ?? '') === $testCode) { + $matchRow = $r; + break; + } + } + } + // ถ้าไม่พบ ให้ใช้แถวแรกเป็น fallback (ถ้ามี) + if ($matchRow === null && !empty($groupedRows[$labNo])) { + $matchRow = $groupedRows[$labNo][0]; + } + + $custId = $matchRow['CustID'] ?? null; + $hn = $matchRow['PatHN'] ?? null; + $riahDocId = $matchRow['RiaHDocID'] ?? null; + $riahDocDate = $matchRow['RiaHDocDate'] ?? null; + + // ตรวจสอบว่ามีอยู่แล้วหรือยัง (ใช้ lab_no + test_code + cust_id + hn) + $existsQuery = [ + 'lab_no' => $labNo, + 'test_code' => $testCode, + ]; + if ($custId !== null) $existsQuery['cust_id'] = $custId; + if ($hn !== null) $existsQuery['hn'] = $hn; + + $exists = LisResult::find()->where($existsQuery)->exists(); + + if (!$exists) { + $LisResult = new LisResult([ + 'lab_no' => $labNo, + 'riah_doc_id' => $riahDocId, + 'riah_doc_date' => $riahDocDate, + 'test_code' => $testCode, + 'test_name' => $testName, + 'cust_id' => $custId, + 'hn' => $hn, + 'result_value' => $resultValue ?? '', + 'result_unit' => $resultUnit ?? '', + 'reference_range' => trim((string)$referenceRange), + 'status' => 1, + ]); + $LisResult->save(false); + } else { + error_log( + "Record already exists in LisResult, skipping insert: " + . "LabNo={$labNo}, TestCode={$testCode}, CustID={$custId}, HN={$hn}" + ); + } + } // end foreach testList + } else { + $msg = $lastResponse['response'] ?? ($lastResponse['curl_error'] ?? 'ไม่พบข้อมูลตอบกลับ'); + error_log("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024)); + } + + // optional small delay: usleep(200000); + } // end foreach groupedByLabNo + + error_log("=== Hanuman Result Sikarin END ==="); + return 0; + } + + + /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ + protected function postJsonCurl($url, $payload, $headers = []) + { + $ch = curl_init($url); + $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); + + // security: allow config to control SSL verify + $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); + + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; + foreach ($headers as $k => $v) { + if (is_int($k)) { + $curlHeaders[] = $v; + } else { + $curlHeaders[] = "{$k}: {$v}"; + } + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErrno = curl_errno($ch); + $curlError = $curlErrno ? curl_error($ch) : null; + curl_close($ch); + + return [ + 'http_code' => $httpCode, + 'response' => $response, + 'curl_error' => $curlError, + 'curl_errno' => $curlErrno, + 'url' => $url, + 'payload_size' => strlen($jsonPayload), + ]; + } + + /** + * helper: fetch URL via cURL (robust) + */ + protected function fetchUrl($url, $timeout = 10) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $data = curl_exec($ch); + $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $err = curl_errno($ch) ? curl_error($ch) : null; + curl_close($ch); + + if ($data === false || $http >= 400) { + //$this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); + error_log("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); + return false; + } + return $data; + } + + /** + * Log helper — writes to STDOUT with timestamp and also to Yii log. + * $level: 'info'|'warning'|'error' + */ + protected function logConsole($message, $level = 'info') + { + $time = date('Y-m-d H:i:s'); + $line = "[{$time}] {$message}" . PHP_EOL; + // write to console + $this->stdout($line); + // write to Yii logger with appropriate level + switch (strtolower($level)) { + case 'error': + Yii::error($message, __METHOD__); + break; + case 'warning': + Yii::warning($message, __METHOD__); + break; + default: + Yii::info($message, __METHOD__); + } + } + + /** + * Truncate long strings for logging. $maxBytes = null => no truncation. + */ + protected function truncateForLog($text, $maxBytes = 4096) + { + if ($text === null) return ''; + if ($maxBytes === null) return $text; + $len = strlen($text); + if ($len <= $maxBytes) return $text; + $prefix = substr($text, 0, $maxBytes); + return $prefix . "\n...TRUNCATED... (original_length={$len})"; + } + + /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ + public function dateTimeFormat($value) + { + if (empty($value)) return ''; + try { + $dt = new \DateTime($value); + return $dt->format('d/m/Y H:i:s'); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) + */ + protected function getApiToken($cfg) + { + $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; + $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; + $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; + + $payload = ['username' => $username, 'password' => $password]; + + $ch = curl_init($tokenUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + $raw = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErr = curl_errno($ch) ? curl_error($ch) : null; + curl_close($ch); + + $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; + + if ($raw === false || $curlErr) { + $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); + return $result; + } + + $decoded = json_decode($raw, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $result['error'] = 'Invalid JSON response from token endpoint'; + return $result; + } + + if (!empty($decoded['access_token'])) { + $result['ok'] = true; + $result['token'] = $decoded['access_token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } elseif (!empty($decoded['token'])) { + $result['ok'] = true; + $result['token'] = $decoded['token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } else { + $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); + } + + return $result; + } + + /** + * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) + * พร้อม debug log วันที่หมดอายุของ Token + */ + protected function getValidApiToken($cfg, &$tokenError = null) + { + $apiName = $cfg['apiName'] ?? 'gems_api'; + $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); + $token = null; + $nowTs = time(); + $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที + + $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); + + if ($tokenModel) { + //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + } else { + error_log("No token found in DB for {$apiName}, will request new token"); + //$this->logConsole("No token found in DB for {$apiName}, will request new token"); + } + + if ($needRequest) { + $tokenResp = $this->getApiToken($cfg); + + if (empty($tokenResp) || empty($tokenResp['ok'])) { + $tokenError = [ + 'message' => 'Failed to get token from API', + 'tokenUrl' => $cfg['tokenUrl'] ?? null, + 'error' => $tokenResp['error'] ?? 'Unknown error', + 'http_code' => $tokenResp['http_code'] ?? 0, + 'raw' => $tokenResp['raw'] ?? null, + ]; + + if ($tokenModel && !empty($tokenModel->token)) { + error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + return $tokenModel->token; // fallback + } + + return null; + } + + $token = $tokenResp['token']; + $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); + + if (!$tokenModel) { + $tokenModel = new ApiToken(); + $tokenModel->api_name = $apiName; + } + + $tokenModel->token = $token; + $tokenModel->expires_at = $expiresAt; + + try { + $tokenModel->save(false); + error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); + //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); + } catch (\Throwable $e) { + //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); + error_log("Error saving new token: " . $e->getMessage(), 'error'); + if ($tokenModel && !empty($tokenModel->token)) { + return $tokenModel->token; // fallback + } + $tokenError = $e->getMessage(); + return null; + } + } else { + $token = $tokenModel->token; + //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + } + return $token; + } +} diff --git a/console/controllers/LisSendPathoController.php b/console/controllers/LisSendPathoController.php index 84f2fcd4..31d2e838 100755 --- a/console/controllers/LisSendPathoController.php +++ b/console/controllers/LisSendPathoController.php @@ -1,430 +1,432 @@ -joinWith(['approveBy',]) - ->andWhere(['<>', 'ln', '']) - ->andWhere(['not like', 'ln', '-%', false]) - ->andWhere(['not', ['report_at' => null]]) - ->andWhere(['not', ['approve_at' => null]]) - ->andWhere(['approve_status' => 3]) - ->andWhere(['>', 'register_at', '2025-11-01 00:00:00']) - ->andWhere(['in', 'patient_case_approve.hospital_id', [703, 366, 367, 169]]) - ->asArray() - ->all(); - - foreach ($resultPatho as $case) { - - $ln = (string)$case['ln']; - $requestLn = substr($ln, 0, -2); - - $hisRequestCheck = HisRequest::find()->where(['HN' => (string)$case['h_n']])->andFilterWhere(['like', 'LabNo', (string)$requestLn])->one(); - if ($hisRequestCheck && isset($hisRequestCheck->testCode)) { - $lisResultCheck = LisResult::find()->where(['lab_no' => $hisRequestCheck->LabNo])->one(); - if (!$lisResultCheck) { - - $hospital_code = Yii::$app->patho->createCommand("select code from const_hospital where id = " . $case['hospital_id'] . "")->queryOne(); - $filesData = []; - $timestamp = date('dmYHis'); - - $LisResultUrl = "https://pathology.prolab.co.th/reports/H" . $hospital_code['code'] . "/" . $case['ln'] . "_" . $case['h_n'] . "_Prolab-" . $case['id_case'] . ".pdf"; - //$url = 'https://example.com/report.pdf'; - /*var_dump(stream_get_wrappers()); - die();*/ - var_dump(fopen('https://www.google.com', 'rb')); - die(); - $path = Yii::getAlias('@runtime') . '/report.pdf'; - - if ($this->downloadFileStream($LisResultUrl, $path)) { - echo 'Download completed'; - } else { - echo 'Download failed'; - } - - die(); - - $pdfContent = $this->fetchUrl($LisResultUrl, 6000); - if ($pdfContent !== false && strlen($pdfContent) > 0) { - $filesData[] = [ - 'FileName' => "{$hisRequestCheck->LabNo}_{$timestamp}.pdf", - 'Filebase64' => base64_encode($pdfContent), - ]; - - $testList = [ - 'LabNo' => $hisRequestCheck->LabNo ?? '', - 'RequestRemark' => '', - 'ResultStatus' => '', - 'TestList' => [ - [ - 'TestCode' => (string)($hisRequestCheck->testCode->TestCode ?? ''), - 'TestName' => (string)($hisRequestCheck->testCode->TestName ?? ''), - 'TestRemark' => '', - 'InformCriticalByCode' => '', - 'InformCriticalBy' => '', - 'InformCriticalTo' => '', - 'InformCriticalDateTime' => '', - 'CriticalFlag' => '', - 'ReportResultByCode' => '', - 'ReportResultBy' => (string)($case->pathologist ?? ''), - 'ReportResultDateTime' => $this->dateTimeFormat($case->report_at ?? ''), - 'ApproveResultByCode' => '', - 'ApproveResultBy' => (string)($case->approveBy->realname ?? ''), - 'ApproveResultDateTime' => $this->dateTimeFormat($case->approve_at ?? ''), - 'Filedata' => $filesData, - 'ResultList' => [ - [ - 'ResultCode' => (string)($hisRequestCheck->testCode->resultCode->ResultCode ?? ''), - 'ResultName' => (string)($hisRequestCheck->testCode->resultCode->ResultName ?? ''), - 'ResultValue' => '', - 'ResultUnit' => '', - 'ResultFlag' => '', - 'ReferenceRange' => '', - 'ResultRemark' => '', - ] - ], - ] - ] - ]; - var_dump($testList); - } - } - } - } - - error_log("=== Hanuman Result Sikarin Patho END ==="); - } - - /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ - protected function postJsonCurl($url, $payload, $headers = []) - { - $ch = curl_init($url); - $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); - - // security: allow config to control SSL verify - $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); - - curl_setopt($ch, CURLOPT_TIMEOUT, 60); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - - $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; - foreach ($headers as $k => $v) { - if (is_int($k)) { - $curlHeaders[] = $v; - } else { - $curlHeaders[] = "{$k}: {$v}"; - } - } - curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErrno = curl_errno($ch); - $curlError = $curlErrno ? curl_error($ch) : null; - curl_close($ch); - - return [ - 'http_code' => $httpCode, - 'response' => $response, - 'curl_error' => $curlError, - 'curl_errno' => $curlErrno, - 'url' => $url, - 'payload_size' => strlen($jsonPayload), - ]; - } - - protected function downloadFileStream($url, $savePath) - { - $context = stream_context_create([ - 'http' => [ - 'method' => 'GET', - 'timeout' => 30, // วินาที - 'header' => "User-Agent: FileFetcher/1.0\r\n", - ], - 'ssl' => [ - 'verify_peer' => false, - 'verify_peer_name' => false, - ], - ]); - - $read = @fopen($url, 'rb', false, $context); - if ($read === false) { - error_log("Stream open failed: {$url}"); - return false; - } - - $write = fopen($savePath, 'w+b'); - if ($write === false) { - fclose($read); - error_log("Cannot open file for writing: {$savePath}"); - return false; - } - - stream_set_timeout($read, 30); - - stream_copy_to_stream($read, $write); - - $meta = stream_get_meta_data($read); - - fclose($read); - fclose($write); - - if ($meta['timed_out']) { - @unlink($savePath); - error_log("Stream timeout while downloading: {$url}"); - return false; - } - - return $savePath; - } - - /** - * helper: fetch URL via cURL (robust) - */ - protected function fetchUrl($url) - { - $ch = curl_init($url); - - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FOLLOWLOCATION => true, - - CURLOPT_CONNECTTIMEOUT => 600, // 10 วินาที - CURLOPT_TIMEOUT => 600, // รวมทั้งหมด 20 วินาที - - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_SSL_VERIFYHOST => false, - CURLOPT_HEADER => false, - CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PDFFetcher/1.0)', - CURLOPT_BUFFERSIZE => 128 * 1024, - - // ลดปัญหา IPv6 hang - CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, - ]); - - $data = curl_exec($ch); - $errno = curl_errno($ch); - $error = curl_error($ch); - $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); - - curl_close($ch); - - if ($data === false) { - error_log("fetchUrl timeout or error: {$url} errno={$errno} err={$error}"); - return false; - } - - if ($http >= 400) { - error_log("fetchUrl http error: {$url} http={$http}"); - return false; - } - - return $data; - } - - - /** - * Log helper — writes to STDOUT with timestamp and also to Yii log. - * $level: 'info'|'warning'|'error' - */ - protected function logConsole($message, $level = 'info') - { - $time = date('Y-m-d H:i:s'); - $line = "[{$time}] {$message}" . PHP_EOL; - // write to console - $this->stdout($line); - // write to Yii logger with appropriate level - switch (strtolower($level)) { - case 'error': - Yii::error($message, __METHOD__); - break; - case 'warning': - Yii::warning($message, __METHOD__); - break; - default: - Yii::info($message, __METHOD__); - } - } - - /** - * Truncate long strings for logging. $maxBytes = null => no truncation. - */ - protected function truncateForLog($text, $maxBytes = 4096) - { - if ($text === null) return ''; - if ($maxBytes === null) return $text; - $len = strlen($text); - if ($len <= $maxBytes) return $text; - $prefix = substr($text, 0, $maxBytes); - return $prefix . "\n...TRUNCATED... (original_length={$len})"; - } - - /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ - public function dateTimeFormat($value) - { - if (empty($value)) return ''; - try { - $dt = new \DateTime($value); - return $dt->format('d/m/Y H:i:s'); - } catch (\Throwable $e) { - return ''; - } - } - - /** - * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) - */ - protected function getApiToken($cfg) - { - $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; - $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; - $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; - - $payload = ['username' => $username, 'password' => $password]; - - $ch = curl_init($tokenUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - - $raw = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErr = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; - - if ($raw === false || $curlErr) { - $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); - return $result; - } - - $decoded = json_decode($raw, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $result['error'] = 'Invalid JSON response from token endpoint'; - return $result; - } - - if (!empty($decoded['access_token'])) { - $result['ok'] = true; - $result['token'] = $decoded['access_token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } elseif (!empty($decoded['token'])) { - $result['ok'] = true; - $result['token'] = $decoded['token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } else { - $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); - } - - return $result; - } - - /** - * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) - * พร้อม debug log วันที่หมดอายุของ Token - */ - protected function getValidApiToken($cfg, &$tokenError = null) - { - $apiName = $cfg['apiName'] ?? 'gems_api'; - $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); - $token = null; - $nowTs = time(); - $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที - - $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); - - if ($tokenModel) { - //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - } else { - error_log("No token found in DB for {$apiName}, will request new token"); - //$this->logConsole("No token found in DB for {$apiName}, will request new token"); - } - - if ($needRequest) { - $tokenResp = $this->getApiToken($cfg); - - if (empty($tokenResp) || empty($tokenResp['ok'])) { - $tokenError = [ - 'message' => 'Failed to get token from API', - 'tokenUrl' => $cfg['tokenUrl'] ?? null, - 'error' => $tokenResp['error'] ?? 'Unknown error', - 'http_code' => $tokenResp['http_code'] ?? 0, - 'raw' => $tokenResp['raw'] ?? null, - ]; - - if ($tokenModel && !empty($tokenModel->token)) { - error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - return $tokenModel->token; // fallback - } - - return null; - } - - $token = $tokenResp['token']; - $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); - - if (!$tokenModel) { - $tokenModel = new ApiToken(); - $tokenModel->api_name = $apiName; - } - - $tokenModel->token = $token; - $tokenModel->expires_at = $expiresAt; - - try { - $tokenModel->save(false); - error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); - //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); - } catch (\Throwable $e) { - //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); - error_log("Error saving new token: " . $e->getMessage(), 'error'); - if ($tokenModel && !empty($tokenModel->token)) { - return $tokenModel->token; // fallback - } - $tokenError = $e->getMessage(); - return null; - } - } else { - $token = $tokenModel->token; - //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - } - return $token; - } -} +joinWith(['approveBy',]) + ->andWhere(['<>', 'ln', '']) + ->andWhere(['not like', 'ln', '-%', false]) + ->andWhere(['not', ['report_at' => null]]) + ->andWhere(['not', ['approve_at' => null]]) + ->andWhere(['approve_status' => 3]) + ->andWhere(['>', 'register_at', '2025-11-01 00:00:00']) + ->andWhere(['in', 'patient_case_approve.hospital_id', [703, 366, 367, 169]]) + ->asArray() + ->all(); + + foreach ($resultPatho as $case) { + + + $ln = (string)$case['ln']; + $requestLn = substr($ln, 0, -2); + + $hisRequestCheck = HisRequest::find()->where(['HN' => (string)$case['h_n']])->andFilterWhere(['like', 'LabNo', (string)$requestLn])->one(); + if ($hisRequestCheck && isset($hisRequestCheck->testCode)) { + $lisResultCheck = LisResult::find()->where(['lab_no' => $hisRequestCheck->LabNo])->one(); + if (!$lisResultCheck) { + + $hospital_code = Yii::$app->patho->createCommand("select code from const_hospital where id = " . $case['hospital_id'] . "")->queryOne(); + $filesData = []; + $timestamp = date('dmYHis'); + + $LisResultUrl = "https://pathology.prolab.co.th/reports/H" . $hospital_code['code'] . "/" . $case['ln'] . "_" . $case['h_n'] . "_Prolab-" . $case['id_case'] . ".pdf"; + //$url = 'https://example.com/report.pdf'; + /*var_dump(stream_get_wrappers()); + die();*/ + var_dump(fopen('https://www.google.com', 'rb')); + die(); + $path = Yii::getAlias('@runtime') . '/report.pdf'; + + if ($this->downloadFileStream($LisResultUrl, $path)) { + echo 'Download completed'; + } else { + echo 'Download failed'; + } + + die(); + + $pdfContent = $this->fetchUrl($LisResultUrl, 6000); + if ($pdfContent !== false && strlen($pdfContent) > 0) { + $filesData[] = [ + 'FileName' => "{$hisRequestCheck->LabNo}_{$timestamp}.pdf", + 'Filebase64' => base64_encode($pdfContent), + ]; + + $testList = [ + 'LabNo' => $hisRequestCheck->LabNo ?? '', + 'RequestRemark' => '', + 'ResultStatus' => '', + 'TestList' => [ + [ + 'TestCode' => (string)($hisRequestCheck->testCode->TestCode ?? ''), + 'TestName' => (string)($hisRequestCheck->testCode->TestName ?? ''), + 'TestRemark' => '', + 'InformCriticalByCode' => '', + 'InformCriticalBy' => '', + 'InformCriticalTo' => '', + 'InformCriticalDateTime' => '', + 'CriticalFlag' => '', + 'ReportResultByCode' => '', + 'ReportResultBy' => (string)($case->pathologist ?? ''), + 'ReportResultDateTime' => $this->dateTimeFormat($case->report_at ?? ''), + 'ApproveResultByCode' => '', + 'ApproveResultBy' => (string)($case->approveBy->realname ?? ''), + 'ApproveResultDateTime' => $this->dateTimeFormat($case->approve_at ?? ''), + 'Filedata' => $filesData, + 'ResultList' => [ + [ + 'ResultCode' => (string)($hisRequestCheck->testCode->resultCode->ResultCode ?? ''), + 'ResultName' => (string)($hisRequestCheck->testCode->resultCode->ResultName ?? ''), + 'ResultValue' => '', + 'ResultUnit' => '', + 'ResultFlag' => '', + 'ReferenceRange' => '', + 'ResultRemark' => '', + ] + ], + ] + ] + ]; + var_dump($testList); + } + } + } + } + + error_log("=== Hanuman Result Sikarin Patho END ==="); + } + + /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ + protected function postJsonCurl($url, $payload, $headers = []) + { + $ch = curl_init($url); + $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); + + // security: allow config to control SSL verify + $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); + + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; + foreach ($headers as $k => $v) { + if (is_int($k)) { + $curlHeaders[] = $v; + } else { + $curlHeaders[] = "{$k}: {$v}"; + } + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErrno = curl_errno($ch); + $curlError = $curlErrno ? curl_error($ch) : null; + curl_close($ch); + + return [ + 'http_code' => $httpCode, + 'response' => $response, + 'curl_error' => $curlError, + 'curl_errno' => $curlErrno, + 'url' => $url, + 'payload_size' => strlen($jsonPayload), + ]; + } + + protected function downloadFileStream($url, $savePath) + { + $context = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'timeout' => 30, // วินาที + 'header' => "User-Agent: FileFetcher/1.0\r\n", + ], + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]); + + $read = @fopen($url, 'rb', false, $context); + if ($read === false) { + error_log("Stream open failed: {$url}"); + return false; + } + + $write = fopen($savePath, 'w+b'); + if ($write === false) { + fclose($read); + error_log("Cannot open file for writing: {$savePath}"); + return false; + } + + stream_set_timeout($read, 30); + + stream_copy_to_stream($read, $write); + + $meta = stream_get_meta_data($read); + + fclose($read); + fclose($write); + + if ($meta['timed_out']) { + @unlink($savePath); + error_log("Stream timeout while downloading: {$url}"); + return false; + } + + return $savePath; + } + + /** + * helper: fetch URL via cURL (robust) + */ + protected function fetchUrl($url) + { + $ch = curl_init($url); + + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + + CURLOPT_CONNECTTIMEOUT => 600, // 10 วินาที + CURLOPT_TIMEOUT => 600, // รวมทั้งหมด 20 วินาที + + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_HEADER => false, + CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PDFFetcher/1.0)', + CURLOPT_BUFFERSIZE => 128 * 1024, + + // ลดปัญหา IPv6 hang + CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, + ]); + + $data = curl_exec($ch); + $errno = curl_errno($ch); + $error = curl_error($ch); + $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + curl_close($ch); + + if ($data === false) { + error_log("fetchUrl timeout or error: {$url} errno={$errno} err={$error}"); + return false; + } + + if ($http >= 400) { + error_log("fetchUrl http error: {$url} http={$http}"); + return false; + } + + return $data; + } + + + /** + * Log helper — writes to STDOUT with timestamp and also to Yii log. + * $level: 'info'|'warning'|'error' + */ + protected function logConsole($message, $level = 'info') + { + $time = date('Y-m-d H:i:s'); + $line = "[{$time}] {$message}" . PHP_EOL; + // write to console + $this->stdout($line); + // write to Yii logger with appropriate level + switch (strtolower($level)) { + case 'error': + Yii::error($message, __METHOD__); + break; + case 'warning': + Yii::warning($message, __METHOD__); + break; + default: + Yii::info($message, __METHOD__); + } + } + + /** + * Truncate long strings for logging. $maxBytes = null => no truncation. + */ + protected function truncateForLog($text, $maxBytes = 4096) + { + if ($text === null) return ''; + if ($maxBytes === null) return $text; + $len = strlen($text); + if ($len <= $maxBytes) return $text; + $prefix = substr($text, 0, $maxBytes); + return $prefix . "\n...TRUNCATED... (original_length={$len})"; + } + + /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ + public function dateTimeFormat($value) + { + if (empty($value)) return ''; + try { + $dt = new \DateTime($value); + return $dt->format('d/m/Y H:i:s'); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) + */ + protected function getApiToken($cfg) + { + $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; + $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; + $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; + + $payload = ['username' => $username, 'password' => $password]; + + $ch = curl_init($tokenUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + $raw = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErr = curl_errno($ch) ? curl_error($ch) : null; + curl_close($ch); + + $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; + + if ($raw === false || $curlErr) { + $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); + return $result; + } + + $decoded = json_decode($raw, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $result['error'] = 'Invalid JSON response from token endpoint'; + return $result; + } + + if (!empty($decoded['access_token'])) { + $result['ok'] = true; + $result['token'] = $decoded['access_token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } elseif (!empty($decoded['token'])) { + $result['ok'] = true; + $result['token'] = $decoded['token']; + $expiresIn = intval($decoded['expires_in'] ?? 3600); + $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); + } else { + $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); + } + + return $result; + } + + /** + * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) + * พร้อม debug log วันที่หมดอายุของ Token + */ + protected function getValidApiToken($cfg, &$tokenError = null) + { + $apiName = $cfg['apiName'] ?? 'gems_api'; + $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); + $token = null; + $nowTs = time(); + $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที + + $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); + + if ($tokenModel) { + //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); + } else { + error_log("No token found in DB for {$apiName}, will request new token"); + //$this->logConsole("No token found in DB for {$apiName}, will request new token"); + } + + if ($needRequest) { + $tokenResp = $this->getApiToken($cfg); + + if (empty($tokenResp) || empty($tokenResp['ok'])) { + $tokenError = [ + 'message' => 'Failed to get token from API', + 'tokenUrl' => $cfg['tokenUrl'] ?? null, + 'error' => $tokenResp['error'] ?? 'Unknown error', + 'http_code' => $tokenResp['http_code'] ?? 0, + 'raw' => $tokenResp['raw'] ?? null, + ]; + + if ($tokenModel && !empty($tokenModel->token)) { + error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); + return $tokenModel->token; // fallback + } + + return null; + } + + $token = $tokenResp['token']; + $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); + + if (!$tokenModel) { + $tokenModel = new ApiToken(); + $tokenModel->api_name = $apiName; + } + + $tokenModel->token = $token; + $tokenModel->expires_at = $expiresAt; + + try { + $tokenModel->save(false); + error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); + //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); + } catch (\Throwable $e) { + //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); + error_log("Error saving new token: " . $e->getMessage(), 'error'); + if ($tokenModel && !empty($tokenModel->token)) { + return $tokenModel->token; // fallback + } + $tokenError = $e->getMessage(); + return null; + } + } else { + $token = $tokenModel->token; + //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); + } + return $token; + } +} diff --git a/console/controllers/LisSendTestController.php b/console/controllers/LisSendTestController.php index 9ce0b10f..7208eafc 100755 --- a/console/controllers/LisSendTestController.php +++ b/console/controllers/LisSendTestController.php @@ -1,147 +1,141 @@ -patho->createCommand("select top (10) h.* - FROM [prolab_api].[dbo].[his_request] h - WHERE h.CustomerCode COLLATE Thai_CI_AS = '1086' - AND NOT EXISTS ( - SELECT 1 - FROM [DB_PL].[dbo].[RIALABH] lh - INNER JOIN [prolab_api].[dbo].[lis_result] lr - ON lh.RiaHDocID = lr.riah_doc_id - WHERE lh.CustID COLLATE Thai_CI_AS IN ('1086', '1087') - AND lh.RiaLHApprvAllFlag = 1 - AND lr.id IS NOT NULL - AND lh.RiaHDocDate > '2025-11-04' - AND lr.lab_no COLLATE Thai_CI_AS = h.LabNo COLLATE Thai_CI_AS - ) - ORDER BY h.RequestDate ASC")->queryAll(); - - $RIALABH = Yii::$app->patho->createCommand("select 1 * - FROM [DB_PL].dbo.RIALABH lh - WHERE lh.CustID COLLATE Thai_CI_AS IN ('1086', '1087') - AND lh.PatHN COLLATE Thai_CI_AS = " . $his_request->PatHN . " - AND lh.RiaHDocDate > '2025-11-04' - AND lh.RiaLHApprvAllFlag = 1 - AND NOT EXISTS ( - SELECT 1 - FROM [prolab_api].[dbo].[lis_result] lr - WHERE lr.riah_doc_id = lh.RiaHDocID - AND lr.id IS NOT NULL - ) - ORDER BY lh.RiaHDocDate ASC")->queryAll(); - - /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ - $fallback = [ - 'report_name' => '', - 'report_dt' => '', - 'approve_name' => '', - 'approve_dt' => '', - ]; - - - foreach ($query_lab as $r) { - - if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { - $fallback['report_name'] = $r['RiaLDUserNameEntry']; - } - - if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { - $fallback['report_dt'] = $r['RiaLDUserDateEntry']; - } - - if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { - $fallback['approve_name'] = $r['RiaLDUserNameAppr']; - } - - if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { - $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; - } - } - - foreach ($query_lab as $rows) { - $labNo = $rows['LabNo'] ?? ''; - - // build test item - $testItem = [ - 'TestCode' => (string)($rows['TestCode'] ?? ''), - 'TestName' => (string)($rows['TestName'] ?? ''), - 'TestRemark' => $rows['TestRemark'] ?? '', - 'InformCriticalByCode' => '', - 'InformCriticalBy' => '', - 'InformCriticalTo' => '', - 'InformCriticalDateTime' => '', - 'CriticalFlag' => '', - 'ReportResultByCode' => '', - 'ReportResultBy' => - (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), - 'ReportResultDateTime' => - $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), - 'ApproveResultByCode' => '', - 'ApproveResultBy' => - (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), - 'ApproveResultDateTime' => - $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), - //'Filedata' => $filesData, - 'ResultList' => [[ - 'ResultCode' => (string)($rows['ResultCode'] ?? ''), - 'ResultName' => (string)($rows['ResultName'] ?? ''), - 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), - 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), - 'ResultFlag' => '', - 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), - 'ResultRemark' => '', - ]], - ]; - - if (!isset($groupedByLabNo[$labNo])) { - $groupedByLabNo[$labNo] = [ - 'LabNo' => $labNo, - 'RequestRemark' => '', - 'ResultStatus' => '', - 'TestList' => [], - ]; - } - - $groupedByLabNo[$labNo]['TestList'][] = $testItem; - $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab - $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; - } // end foreach build - - error_log("=== Hanuman Result Sikarin END ==="); - var_dump($groupedByLabNo); - die(); - } - - /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ - public function dateTimeFormat($value) - { - if (empty($value)) return ''; - try { - $dt = new \DateTime($value); - return $dt->format('d/m/Y H:i:s'); - } catch (\Throwable $e) { - return ''; - } - } -} \ No newline at end of file +where(['not', ['RiaHDocID' => null]]) + ->andWhere(['not', ['PatHN' => null]]) + ->andWhere(['<>', 'RiaHDocID', '']) + ->andWhere(['<>', 'PatHN', '']) + ->asArray() + ->orderBy(['RiaHDocDate' => SORT_DESC]) + ->one(); + + if (!$query) { + error_log('No data found'); + return; + } + + $sendMapping = []; + + // ---------- build grouped payloads ---------- + $groupedByLabNo = []; + $groupedRows = []; // เก็บแถวต้นฉบับต่อ labNo เพื่อใช้บันทึก LisResult ทีหลัง + + $query_lab = HanumanResultSikarinCheck::find() + ->where(['not', ['RiaHDocID' => null]]) + ->andWhere(['not', ['PatHN' => null]]) + ->andWhere(['<>', 'RiaHDocID', '']) + ->andWhere(['<>', 'PatHN', '']) + ->andWhere(['LabNo' => $query['LabNo']]) + ->asArray() + ->orderBy(['RiaHDocDate' => SORT_DESC]) + ->all(); + + /* ===== เพิ่มส่วนนี้: scan fallback แบบสั้น ===== */ + $fallback = [ + 'report_name' => '', + 'report_dt' => '', + 'approve_name' => '', + 'approve_dt' => '', + ]; + + foreach ($query_lab as $r) { + + if (empty($fallback['report_name']) && !empty($r['RiaLDUserNameEntry'])) { + $fallback['report_name'] = $r['RiaLDUserNameEntry']; + } + + if (empty($fallback['report_dt']) && !empty($r['RiaLDUserDateEntry'])) { + $fallback['report_dt'] = $r['RiaLDUserDateEntry']; + } + + if (empty($fallback['approve_name']) && !empty($r['RiaLDUserNameAppr'])) { + $fallback['approve_name'] = $r['RiaLDUserNameAppr']; + } + + if (empty($fallback['approve_dt']) && !empty($r['RiaLDUserDateAppr'])) { + $fallback['approve_dt'] = $r['RiaLDUserDateAppr']; + } + } + + foreach ($query_lab as $rows) { + $labNo = $rows['LabNo'] ?? ''; + + // build test item + $testItem = [ + 'TestCode' => (string)($rows['TestCode'] ?? ''), + 'TestName' => (string)($rows['TestName'] ?? ''), + 'TestRemark' => $rows['TestRemark'] ?? '', + 'InformCriticalByCode' => '', + 'InformCriticalBy' => '', + 'InformCriticalTo' => '', + 'InformCriticalDateTime' => '', + 'CriticalFlag' => '', + 'ReportResultByCode' => '', + 'ReportResultBy' => + (string)($rows['RiaLDUserNameEntry'] ?: $fallback['report_name']), + 'ReportResultDateTime' => + $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?: $fallback['report_dt']), + 'ApproveResultByCode' => '', + 'ApproveResultBy' => + (string)($rows['RiaLDUserNameAppr'] ?: $fallback['approve_name']), + 'ApproveResultDateTime' => + $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?: $fallback['approve_dt']), + //'Filedata' => $filesData, + 'ResultList' => [[ + 'ResultCode' => (string)($rows['ResultCode'] ?? ''), + 'ResultName' => (string)($rows['ResultName'] ?? ''), + 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), + 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), + 'ResultFlag' => '', + 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), + 'ResultRemark' => '', + ]], + ]; + + if (!isset($groupedByLabNo[$labNo])) { + $groupedByLabNo[$labNo] = [ + 'LabNo' => $labNo, + 'RequestRemark' => '', + 'ResultStatus' => '', + 'TestList' => [], + ]; + } + + $groupedByLabNo[$labNo]['TestList'][] = $testItem; + $groupedRows[$labNo][] = $rows; // เก็บแถวต้นฉบับต่อ lab + $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; + } // end foreach build + + error_log("=== Hanuman Result Sikarin END ==="); + var_dump($groupedByLabNo); + die(); + } + + /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ + public function dateTimeFormat($value) + { + if (empty($value)) return ''; + try { + $dt = new \DateTime($value); + return $dt->format('d/m/Y H:i:s'); + } catch (\Throwable $e) { + return ''; + } + } +} diff --git a/console/controllers/MlController.php b/console/controllers/MlController.php index ea47b429..a2a06fde 100755 --- a/console/controllers/MlController.php +++ b/console/controllers/MlController.php @@ -1,12 +1,12 @@ -get('patho'); + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['gender', 'hospital_id', 'status_id', 'pathologist_id', 'approve_status', 'approve_by', 'approve_id', 'is_send_request', 'is_critical_diagnosis'], 'integer'], + [['birthdate', 'collect_at', 'register_at', 'cut_at', 'report_at', 'approve_at', 'send_request_at'], 'safe'], + [['age'], 'number'], + [['diagnosis'], 'string'], + [['id_case'], 'string', 'max' => 12], + [['title'], 'string', 'max' => 100], + [['name'], 'string', 'max' => 1000], + [['age_unit'], 'string', 'max' => 15], + [['hospital'], 'string', 'max' => 400], + [['status'], 'string', 'max' => 500], + [['pathologist'], 'string', 'max' => 600], + [['remark'], 'string', 'max' => 300], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id_case' => 'Id Case', + 'title' => 'Title', + 'name' => 'Name', + 'gender' => 'Gender', + 'birthdate' => 'Birthdate', + 'age' => 'Age', + 'age_unit' => 'Age Unit', + 'hospital_id' => 'Hospital ID', + 'hospital' => 'Hospital', + 'collect_at' => 'Collect At', + 'register_at' => 'Register At', + 'cut_at' => 'Cut At', + 'status_id' => 'Status ID', + 'status' => 'Status', + 'pathologist_id' => 'Pathologist ID', + 'pathologist' => 'Pathologist', + 'report_at' => 'Report At', + 'diagnosis' => 'Diagnosis', + 'approve_status' => 'Approve Status', + 'approve_by' => 'Approve By', + 'approve_at' => 'Approve At', + 'remark' => 'Remark', + 'approve_id' => 'Approve ID', + 'is_send_request' => 'Is Send Request', + 'is_critical_diagnosis' => 'Is Critical Diagnosis', + 'send_request_at' => 'Send Request At', + ]; + } + + /** + * ดึงข้อมูล PatientCaseApprove จาก LabNo ล่าสุดใน HisRequest + */ + public static function findByLatestHisLabNo() + { + // หา LabNo จาก HIS + $his = HisRequest::find() + ->where(['not', ['LabNo' => null]]) + ->andWhere(['<>', 'LabNo', '']) + ->orderBy(['ID' => SORT_DESC]) + ->one(); + + if ($his === null) { + return []; + } + + $labNoHis = $his->LabNo; + + // ตัด OR ออก เหลือเฉพาะตัวเลข + $labNoNumeric = substr($labNoHis, 2); + + return self::find() + ->joinWith(['approveBy']) + ->andWhere(['<>', 'ln', '']) + ->andWhere(['not like', 'ln', '-%', false]) + ->andWhere(['like', 'ln', $labNoNumeric]) + ->andWhere(['not', ['report_at' => null]]) + ->andWhere(['not', ['approve_at' => null]]) + ->andWhere(['approve_status' => 3]) + ->asArray() + ->all(); + } + + public function getApproveBy() + { + return $this->hasOne(User::class, ['id' => 'approve_by']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getApproveAt() + { + return $this->hasOne(User::class, ['id' => 'approve_at']); + } +} diff --git a/console/controllers/ReportController.php b/console/controllers/ReportController.php index 7d87af25..5e8cd143 100755 --- a/console/controllers/ReportController.php +++ b/console/controllers/ReportController.php @@ -1,146 +1,146 @@ -where(['is', 'is_send', new Expression('NULL')])->orderBy(['id' => SORT_DESC])->all() as $send_his) { - if (Yii::$app->pathology->requestHisReport($send_his->approve_id, $send_his->id_case, $send_his->diagnosis_id)) { - SendHis::updateAll(['is_send' => 1, 'send_at' => date('Y-m-d H:i:s')], ['approve_id' => $send_his->approve_id]); - error_log('Send His Report: ' . $send_his->id_case . ' ' . $send_his->report_type); - } - } - } - - - - public function actionGenerate() - { - //Yii::$app->controllerNamespace = 'frontend\modules\report\controllers'; - $year = date('Y'); - $month = date('m') - 3; - $day = date('d'); - echo 'start' . PHP_EOL; - foreach (PatientCase::find()->where([ - 'status_id' => 4 - ]) //->andFilterWhere(['>', 'report_at', ($year.'-'.$month.'-'.$day)]) - ->orderBy(['register_at' => SORT_DESC]) - ->limit(100) - ->orderBy(['report_at' => SORT_DESC])->all() as $case) { - echo $case->report_at . PHP_EOL; - $prefix = substr($case->id_case, 0, 2); - echo $case->id_case . PHP_EOL; - - switch ($prefix) { - case 'CS': - $model = CaseConsult::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/CS/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/consult-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - /*case 'EM': - return $this->redirect(['/report/case/em', 'id_case' => $id_case, 'mode' => $mode]); - break;*/ - case 'FI': - $model = CaseFish::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FI/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/fish-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'FC': - $model = CaseFlow::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FC/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/flow-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'MP': - $model = CaseMolecular::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/MP/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/molecular-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'FN': - $model = CaseNonGyn::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FN/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/non-gyn-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'PN': - $model = CasePap::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/PN/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/pap-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'SN': - $model = CaseSurgical::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/SN/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/surgical-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => 1]); - } - break; - /*case 'FS': - return $this->redirect(['/report/case/frozen', 'id_case' => $id_case, 'mode' => $mode]); - break;*/ - case 'AU': - $model = CaseAutopsy::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->body->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/AU/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/autopsy-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - case 'HN': - $model = CaseHpv::findOne(['id_case' => $case->id_case]); - $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; - $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/HN/'; - FileHelper::createDirectory($path); - if (!file_exists($path . $reportName)) { - Yii::$app->runAction('/report/case/hpv-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); - } - break; - } - echo 'End' . PHP_EOL; - } // foreach - } -} +where(['is', 'is_send', new Expression('NULL')])->orderBy(['id' => SORT_DESC])->all() as $send_his) { + if (Yii::$app->pathology->requestHisReport($send_his->approve_id, $send_his->id_case, $send_his->diagnosis_id)) { + SendHis::updateAll(['is_send' => 1, 'send_at' => date('Y-m-d H:i:s')], ['approve_id' => $send_his->approve_id]); + error_log('Send His Report: ' . $send_his->id_case . ' ' . $send_his->report_type); + } + } + } + + + + public function actionGenerate() + { + //Yii::$app->controllerNamespace = 'frontend\modules\report\controllers'; + $year = date('Y'); + $month = date('m') - 3; + $day = date('d'); + echo 'start' . PHP_EOL; + foreach (PatientCase::find()->where([ + 'status_id' => 4 + ]) //->andFilterWhere(['>', 'report_at', ($year.'-'.$month.'-'.$day)]) + ->orderBy(['register_at' => SORT_DESC]) + ->limit(100) + ->orderBy(['report_at' => SORT_DESC])->all() as $case) { + echo $case->report_at . PHP_EOL; + $prefix = substr($case->id_case, 0, 2); + echo $case->id_case . PHP_EOL; + + switch ($prefix) { + case 'CS': + $model = CaseConsult::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/CS/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/consult-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + /*case 'EM': + return $this->redirect(['/report/case/em', 'id_case' => $id_case, 'mode' => $mode]); + break;*/ + case 'FI': + $model = CaseFish::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FI/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/fish-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'FC': + $model = CaseFlow::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FC/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/flow-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'MP': + $model = CaseMolecular::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/MP/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/molecular-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'FN': + $model = CaseNonGyn::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/FN/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/non-gyn-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'PN': + $model = CasePap::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/PN/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/pap-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'SN': + $model = CaseSurgical::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/SN/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/surgical-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => 1]); + } + break; + /*case 'FS': + return $this->redirect(['/report/case/frozen', 'id_case' => $id_case, 'mode' => $mode]); + break;*/ + case 'AU': + $model = CaseAutopsy::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->body->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/AU/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/autopsy-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + case 'HN': + $model = CaseHpv::findOne(['id_case' => $case->id_case]); + $reportName = $case->id_case . '_' . $model->patient->h_n . '.pdf'; + $path = Yii::getAlias('@webroot') . '/reports/H' . $case->hospital_id . '/HN/'; + FileHelper::createDirectory($path); + if (!file_exists($path . $reportName)) { + Yii::$app->runAction('/report/case/hpv-pdf', ['id_case' => $case->id_case, 'mode' => 'final', 'file' => true]); + } + break; + } + echo 'End' . PHP_EOL; + } // foreach + } +} diff --git a/console/controllers/SetPriceController.php b/console/controllers/SetPriceController.php index ce02c667..15f541f3 100755 --- a/console/controllers/SetPriceController.php +++ b/console/controllers/SetPriceController.php @@ -1,30 +1,30 @@ -orderBy(['id' => SORT_DESC])->limit(10)->all() as $f){ - $charge = ConstServiceCharge::findOne(['id' => $f->charge_id]); - $hospital = ConstHospital::findOne(['id' => $f->hospital_id]); - - $finance = FinanceServiceCharge::findOne(['id' => $f->id]); - $finance->price = Yii::$app->pathology->getHosPrice($f->hospital_id, $f->charge_id); - $finance->save(); - } - //return $this->render('index'); - } - - public function actionTest() - { - echo Yii::$app->pathology->getHosPrice(33,76); - } - -} +orderBy(['id' => SORT_DESC])->limit(10)->all() as $f){ + $charge = ConstServiceCharge::findOne(['id' => $f->charge_id]); + $hospital = ConstHospital::findOne(['id' => $f->hospital_id]); + + $finance = FinanceServiceCharge::findOne(['id' => $f->id]); + $finance->price = Yii::$app->pathology->getHosPrice($f->hospital_id, $f->charge_id); + $finance->save(); + } + //return $this->render('index'); + } + + public function actionTest() + { + echo Yii::$app->pathology->getHosPrice(33,76); + } + +} diff --git a/console/controllers/WebSocketController.php b/console/controllers/WebSocketController.php index d10032c8..eb4da95a 100755 --- a/console/controllers/WebSocketController.php +++ b/console/controllers/WebSocketController.php @@ -1,520 +1,27 @@ -logConsole("=== Hanuman Result Sikarin START ==="); - error_log("=== Hanuman Result Sikarin START ==="); - - $query = HanumanResultSikarinCheck::find() - ->where(['not', ['RiaHDocID' => null]]) - ->andWhere(['not', ['PatHN' => null]]) - ->andWhere(['<>', 'RiaHDocID', '']) - ->andWhere(['<>', 'PatHN', '']) - ->limit(1) - ->asArray() - ->orderBy(['RiaHDocDate' => SORT_DESC]) - ->all(); - - //$this->logConsole("Total records: " . count($query)); - error_log("Total records: " . count($query)); - - $sendMapping = []; - - // config - $cfg = Yii::$app->params['external_api'] ?? null; - if (!$cfg || empty($cfg['receiveUrl'])) { - //$this->logConsole("Missing external_api config or receiveUrl", 'error'); - error_log("Missing external_api config or receiveUrl", 'error'); - return 1; - } - - // get token - $tokenError = null; - $token = $this->getValidApiToken($cfg, $tokenError); - if (empty($token)) { - //$this->logConsole("Cannot obtain valid API token: " . (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown')), 'error'); - error_log("Cannot obtain valid API token: " . (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown')), 'error'); - return 1; - } - - $url = $cfg['receiveUrl']; - //$this->logConsole("Using API token (ready to send)"); - error_log("Using API token (ready to send)"); - - $groupedByLabNo = []; - - foreach ($query as $rows) { - // collect files - $filesData = []; - $timestamp = date('dmYHis'); - $labNo = $rows['LabNo'] ?? ''; - $docIdParam = $rows['RiaHDocID'] ?? ''; - $docDate = !empty($rows['RiaHDocDate']) ? date('Y-m-d', strtotime($rows['RiaHDocDate'])) : ''; - - if (!empty($docIdParam)) { - $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}"; - $pdfContent = $this->fetchUrl($LisResultUrl, 20); - if ($pdfContent !== false && strlen($pdfContent) > 0) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}.pdf", - 'Filebase64' => base64_encode($pdfContent), - ]; - } - - $localDir = "/var/www/html/pdflink/{$docIdParam}"; - if (is_dir($localDir)) { - foreach (scandir($localDir) as $fn) { - if ($fn === '.' || $fn === '..') continue; - if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue; - $path = "{$localDir}/{$fn}"; - if (is_file($path) && is_readable($path)) { - $content = @file_get_contents($path); - if ($content !== false) { - $filesData[] = [ - 'FileName' => "{$labNo}_{$timestamp}_{$fn}", - 'Filebase64' => base64_encode($content), - ]; - } - } - } - } - } - - // build test item (do not filter out empty strings) - $testItem = [ - 'TestCode' => (string)($rows['TestCode'] ?? ''), - 'TestName' => (string)($rows['TestName'] ?? ''), - 'TestRemark' => $rows['TestRemark'] ?? '', - 'InformCriticalByCode' => '', - 'InformCriticalBy' => '', - 'InformCriticalTo' => '', - 'InformCriticalDateTime' => '', - 'CriticalFlag' => '', - 'ReportResultByCode' => '', - 'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?? ''), - 'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?? ''), - 'ApproveResultByCode' => '', - 'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?? ''), - 'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?? ''), - 'Filedata' => $filesData, - 'ResultList' => [[ - 'ResultCode' => (string)($rows['ResultCode'] ?? ''), - 'ResultName' => (string)($rows['ResultName'] ?? ''), - 'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''), - 'ResultUnit' => (string)($rows['LabUnit'] ?? ''), - 'ResultFlag' => '', - 'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), - 'ResultRemark' => '', - ]], - ]; - - if (!isset($groupedByLabNo[$labNo])) { - $groupedByLabNo[$labNo] = [ - 'LabNo' => $labNo, - 'RequestRemark' => '', - 'ResultStatus' => '', - 'TestList' => [], - ]; - } - - $groupedByLabNo[$labNo]['TestList'][] = $testItem; - $sendMapping[$rows['RiaHDocID']] = $rows['RiaHDocID']; - - // prepare payload for this lab (do not strip empty strings) - $payload = $groupedByLabNo[$labNo]; - - if (empty($payload) || !isset($payload['LabNo'])) { - //$this->logConsole("Skipping send: payload missing LabNo for key={$labNo}", 'warning'); - error_log("Skipping send: payload missing LabNo for key={$labNo}", 'warning'); - continue; - } - - // --- NEW: log the JSON payload (truncated) --- - $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - //$this->logConsole("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - error_log("Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - // ------------------------------------------------ - - // แปลง TestList เป็น array ของ "TestCode:TestName" - $tests = array_map(function ($t) { - // ป้องกันกรณี key หายไป - $code = isset($t['TestCode']) ? $t['TestCode'] : '(noCode)'; - $name = isset($t['TestName']) ? $t['TestName'] : '(noName)'; - return $code . ':' . $name; - }, $payload['TestList'] ?? []); - - // ใส่ลงใน error_log แบบเดียวกับของเดิม - error_log( - "Sending payload for LabNo={$payload['LabNo']} " . - "(tests=" . json_encode($tests, JSON_UNESCAPED_UNICODE) . "), " . - "RiaHDocID={$rows['RiaHDocID']}" - ); - //$this->logConsole("Sending payload for LabNo={$payload['LabNo']} (tests=" . count($payload['TestList']) . ")"); - - // FIRST attempt - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = $response['http_code'] ?? 0; - $curlErr = $response['curl_error'] ?? null; - - if (!empty($curlErr)) { - //$this->logConsole("cURL error: {$curlErr}", 'warning'); - error_log("cURL error: {$curlErr}", 'warning'); - } - - // IF 401, refresh token and RETRY ONCE (no loop) - if ($httpCode == 401) { - //$this->logConsole("401 Unauthorized received — refreshing token and retrying", 'warning'); - error_log("401 Unauthorized received — refreshing token and retrying", 'warning'); - $token = $this->getValidApiToken($cfg, $tokenError); - if (!empty($token)) { - // --- NEW: log payload again before retry (may help to confirm unchanged payload) --- - //$this->logConsole("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - error_log("Retrying with refreshed token. Payload (truncated): " . $this->truncateForLog($payloadJson, 8192)); - // ------------------------------------------------------------------------------- - $response = $this->postJsonCurl($url, $payload, [ - 'Authorization' => "Bearer {$token}", - 'Content-Type' => 'application/json', - ]); - $lastResponse = $response; - $rawResp = $response['response'] ?? ''; - $httpCode = $response['http_code'] ?? 0; - $curlErr = $response['curl_error'] ?? null; - if (!empty($curlErr)) { - //$this->logConsole("cURL error on retry: {$curlErr}", 'warning'); - error_log("cURL error on retry: {$curlErr}", 'warning'); - } - } else { - //$this->logConsole("Failed to refresh token after 401", 'error'); - error_log("Failed to refresh token after 401", 'error'); - } - } - error_log("Response (http={$httpCode}): " . $this->truncateForLog($rawResp, 8192)); - //$this->logConsole("Response (http={$httpCode}): " . $this->truncateForLog($rawResp, 8192)); - - // decode and check success - $decoded = json_decode($rawResp, true); - $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null; - $messageVal = $decoded['Message'] ?? $decoded['message'] ?? ''; - - $success = false; - if (is_numeric($statusVal)) { - $success = (intval($statusVal) === 0); - } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') { - $success = true; - } elseif (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) { - $success = true; - } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) { - $success = true; - } - - if ($success) { - //$this->logConsole("SUCCESS LabNo={$labNo}"); - error_log("Success LabNo={$labNo}, RiaHDocID=" . ($rows['RiaHDocID'] ?? '')); - - $exists = LisResult::find()->where([ - 'lab_no' => $rows['LabNo'], - 'test_code' => $rows['TestCode'], - 'cust_id' => $rows['CustID'], - 'hn' => $rows['PatHN'], - ])->exists(); - - if (!$exists) { - $LisResult = new LisResult([ - 'lab_no' => $rows['LabNo'], - 'riah_doc_id' => $rows['RiaHDocID'], - 'riah_doc_date' => $rows['RiaHDocDate'], - 'test_code' => $rows['TestCode'], - 'test_name' => $rows['TestName'], - 'cust_id' => $rows['CustID'], - 'hn' => $rows['PatHN'], - 'result_value' => $rows['RiaLDRsltAnal'] ?? '', - 'result_unit' => $rows['LabUnit'] ?? '', - 'reference_range' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')), - 'status' => 1, - ]); - $LisResult->save(false); - } else { - error_log("Record already exists in LisResult, skipping insert: LabNo={$rows['LabNo']}, TestCode={$rows['TestCode']}, CustID={$rows['CustID']}, HN={$rows['PatHN']}", 'warning'); - //$this->logConsole("Record already exists in LisResult, skipping insert: LabNo={$rows['LabNo']}, TestCode={$rows['TestCode']}, CustID={$rows['CustID']}, HN={$rows['PatHN']}", 'warning'); - } - } else { - $msg = $lastResponse['response'] ?? ($lastResponse['curl_error'] ?? 'ไม่พบข้อมูลตอบกลับ'); - error_log("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024)); - //$this->logConsole("FAIL LabNo={$labNo}; last_http=" . ($lastResponse['http_code'] ?? 'ไม่พบข้อมูลตอบกลับ') . "; message=" . $this->truncateForLog($msg, 1024), 'error'); - } - } // end foreach - error_log("=== Hanuman Result Sikarin END ==="); - //$this->logConsole("=== Hanuman Result Sikarin END ==="); - return 0; - //var_dump($payload); - } - - /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */ - protected function postJsonCurl($url, $payload, $headers = []) - { - $ch = curl_init($url); - $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload); - - // security: allow config to control SSL verify - $verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true; - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); - - curl_setopt($ch, CURLOPT_TIMEOUT, 60); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - - $curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0']; - foreach ($headers as $k => $v) { - if (is_int($k)) { - $curlHeaders[] = $v; - } else { - $curlHeaders[] = "{$k}: {$v}"; - } - } - curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErrno = curl_errno($ch); - $curlError = $curlErrno ? curl_error($ch) : null; - curl_close($ch); - - return [ - 'http_code' => $httpCode, - 'response' => $response, - 'curl_error' => $curlError, - 'curl_errno' => $curlErrno, - 'url' => $url, - 'payload_size' => strlen($jsonPayload), - ]; - } - - /** - * helper: fetch URL via cURL (robust) - */ - protected function fetchUrl($url, $timeout = 10) - { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - $data = curl_exec($ch); - $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $err = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - if ($data === false || $http >= 400) { - //$this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - error_log("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning'); - return false; - } - return $data; - } - - /** - * Log helper — writes to STDOUT with timestamp and also to Yii log. - * $level: 'info'|'warning'|'error' - */ - protected function logConsole($message, $level = 'info') - { - $time = date('Y-m-d H:i:s'); - $line = "[{$time}] {$message}" . PHP_EOL; - // write to console - $this->stdout($line); - // write to Yii logger with appropriate level - switch (strtolower($level)) { - case 'error': - Yii::error($message, __METHOD__); - break; - case 'warning': - Yii::warning($message, __METHOD__); - break; - default: - Yii::info($message, __METHOD__); - } - } - - /** - * Truncate long strings for logging. $maxBytes = null => no truncation. - */ - protected function truncateForLog($text, $maxBytes = 4096) - { - if ($text === null) return ''; - if ($maxBytes === null) return $text; - $len = strlen($text); - if ($len <= $maxBytes) return $text; - $prefix = substr($text, 0, $maxBytes); - return $prefix . "\n...TRUNCATED... (original_length={$len})"; - } - - /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */ - public function dateTimeFormat($value) - { - if (empty($value)) return ''; - try { - $dt = new \DateTime($value); - return $dt->format('d/m/Y H:i:s'); - } catch (\Throwable $e) { - return ''; - } - } - - /** - * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error}) - */ - protected function getApiToken($cfg) - { - $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null; - $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null; - $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null; - - $payload = ['username' => $username, 'password' => $password]; - - $ch = curl_init($tokenUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - - $raw = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlErr = curl_errno($ch) ? curl_error($ch) : null; - curl_close($ch); - - $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null]; - - if ($raw === false || $curlErr) { - $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown'); - return $result; - } - - $decoded = json_decode($raw, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $result['error'] = 'Invalid JSON response from token endpoint'; - return $result; - } - - if (!empty($decoded['access_token'])) { - $result['ok'] = true; - $result['token'] = $decoded['access_token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } elseif (!empty($decoded['token'])) { - $result['ok'] = true; - $result['token'] = $decoded['token']; - $expiresIn = intval($decoded['expires_in'] ?? 3600); - $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn); - } else { - $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response'); - } - - return $result; - } - - /** - * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา) - * พร้อม debug log วันที่หมดอายุของ Token - */ - protected function getValidApiToken($cfg, &$tokenError = null) - { - $apiName = $cfg['apiName'] ?? 'gems_api'; - $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one(); - $token = null; - $nowTs = time(); - $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที - - $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin)); - - if ($tokenModel) { - //$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "..."); - } else { - error_log("No token found in DB for {$apiName}, will request new token"); - //$this->logConsole("No token found in DB for {$apiName}, will request new token"); - } - - if ($needRequest) { - $tokenResp = $this->getApiToken($cfg); - - if (empty($tokenResp) || empty($tokenResp['ok'])) { - $tokenError = [ - 'message' => 'Failed to get token from API', - 'tokenUrl' => $cfg['tokenUrl'] ?? null, - 'error' => $tokenResp['error'] ?? 'Unknown error', - 'http_code' => $tokenResp['http_code'] ?? 0, - 'raw' => $tokenResp['raw'] ?? null, - ]; - - if ($tokenModel && !empty($tokenModel->token)) { - error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - //$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})"); - return $tokenModel->token; // fallback - } - - return null; - } - - $token = $tokenResp['token']; - $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600); - - if (!$tokenModel) { - $tokenModel = new ApiToken(); - $tokenModel->api_name = $apiName; - } - - $tokenModel->token = $token; - $tokenModel->expires_at = $expiresAt; - - try { - $tokenModel->save(false); - error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "..."); - //$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "..."); - } catch (\Throwable $e) { - //$this->logConsole("Error saving new token: " . $e->getMessage(), 'error'); - error_log("Error saving new token: " . $e->getMessage(), 'error'); - if ($tokenModel && !empty($tokenModel->token)) { - return $tokenModel->token; // fallback - } - $tokenError = $e->getMessage(); - return null; - } - } else { - $token = $tokenModel->token; - //$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "..."); - } - return $token; - } +run(); + } } \ No newline at end of file diff --git a/console/models/.gitkeep b/console/models/.gitkeep index 72e8ffc0..5e4debcc 100755 --- a/console/models/.gitkeep +++ b/console/models/.gitkeep @@ -1 +1 @@ -* +* diff --git a/console/models/ApiToken.php b/console/models/ApiToken.php index 79d09228..afca9443 100755 --- a/console/models/ApiToken.php +++ b/console/models/ApiToken.php @@ -1,39 +1,39 @@ - 255], - ]; - } -} + 255], + ]; + } +} diff --git a/console/models/HanumanResultSikarinCheck.php b/console/models/HanumanResultSikarinCheck.php index 60ce237b..25f519ab 100755 --- a/console/models/HanumanResultSikarinCheck.php +++ b/console/models/HanumanResultSikarinCheck.php @@ -1,108 +1,108 @@ - 255], - [[ - 'ResultCode', - 'ResultName', - ], 'string'], - // allow other attributes to be assigned - [['LabNo'], 'string', 'max' => 100], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'LabNo' => 'Lab No', - 'RiaHDocDate' => 'Ria H Doc Date', - 'RiaHDocID' => 'Ria H Doc ID', - 'status' => 'Status', - 'TestCode' => 'Test Code', - 'TestName' => 'Test Name', - 'PatName' => 'Patient Name', - 'PatHN' => 'Patient HN', - 'CustID' => 'Customer ID', - 'LabID' => 'Lab ID', - 'LabName' => 'Lab Name', - 'RiaLDRsltAnal' => 'Result (Analytical)', - 'LabNormal1' => 'Normal Range 1', - 'LabNormal2' => 'Normal Range 2', - 'LabUnit' => 'Unit', - 'RiaLDUserNameEntry' => 'Entry By', - 'RiaLDUserDateEntry' => 'Entry Date', - 'RiaLDUserNameAppr' => 'Approved By', - 'RiaLDUserDateAppr' => 'Approved Date', - 'RiaLDApprvFlag' => 'Approved Flag', - ]; - } -} + 255], + [[ + 'ResultCode', + 'ResultName', + ], 'string'], + // allow other attributes to be assigned + [['LabNo'], 'string', 'max' => 100], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'LabNo' => 'Lab No', + 'RiaHDocDate' => 'Ria H Doc Date', + 'RiaHDocID' => 'Ria H Doc ID', + 'status' => 'Status', + 'TestCode' => 'Test Code', + 'TestName' => 'Test Name', + 'PatName' => 'Patient Name', + 'PatHN' => 'Patient HN', + 'CustID' => 'Customer ID', + 'LabID' => 'Lab ID', + 'LabName' => 'Lab Name', + 'RiaLDRsltAnal' => 'Result (Analytical)', + 'LabNormal1' => 'Normal Range 1', + 'LabNormal2' => 'Normal Range 2', + 'LabUnit' => 'Unit', + 'RiaLDUserNameEntry' => 'Entry By', + 'RiaLDUserDateEntry' => 'Entry Date', + 'RiaLDUserNameAppr' => 'Approved By', + 'RiaLDUserDateAppr' => 'Approved Date', + 'RiaLDApprvFlag' => 'Approved Flag', + ]; + } +} diff --git a/console/models/HanumanResultSikarinManual.php b/console/models/HanumanResultSikarinManual.php index a42f1fa4..c00ccc96 100644 --- a/console/models/HanumanResultSikarinManual.php +++ b/console/models/HanumanResultSikarinManual.php @@ -1,114 +1,114 @@ -get('prolab'); - } - - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['LabNo'], 'required'], - [[ - 'RiaHDocDate', - 'RiaLDUserDateEntry', - 'RiaLDUserDateAppr' - ], 'safe'], - [[ - 'RiaHDocID', - 'status', - 'TestCode', - 'TestName', - 'PatName', - 'PatHN', - 'CustID', - 'LabID', - 'LabName', - 'RiaLDRsltAnal', - 'LabNormal1', - 'LabNormal2', - 'LabUnit', - 'RiaLDUserNameEntry', - 'RiaLDUserNameAppr', - 'RiaLDApprvFlag' - ], 'string', 'max' => 255], - [[ - 'ResultCode', - 'ResultName', - ], 'string'], - // allow other attributes to be assigned - [['LabNo'], 'string', 'max' => 100], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'LabNo' => 'Lab No', - 'RiaHDocDate' => 'Ria H Doc Date', - 'RiaHDocID' => 'Ria H Doc ID', - 'status' => 'Status', - 'TestCode' => 'Test Code', - 'TestName' => 'Test Name', - 'PatName' => 'Patient Name', - 'PatHN' => 'Patient HN', - 'CustID' => 'Customer ID', - 'LabID' => 'Lab ID', - 'LabName' => 'Lab Name', - 'RiaLDRsltAnal' => 'Result (Analytical)', - 'LabNormal1' => 'Normal Range 1', - 'LabNormal2' => 'Normal Range 2', - 'LabUnit' => 'Unit', - 'RiaLDUserNameEntry' => 'Entry By', - 'RiaLDUserDateEntry' => 'Entry Date', - 'RiaLDUserNameAppr' => 'Approved By', - 'RiaLDUserDateAppr' => 'Approved Date', - 'RiaLDApprvFlag' => 'Approved Flag', - ]; - } +get('prolab'); + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['LabNo'], 'required'], + [[ + 'RiaHDocDate', + 'RiaLDUserDateEntry', + 'RiaLDUserDateAppr' + ], 'safe'], + [[ + 'RiaHDocID', + 'status', + 'TestCode', + 'TestName', + 'PatName', + 'PatHN', + 'CustID', + 'LabID', + 'LabName', + 'RiaLDRsltAnal', + 'LabNormal1', + 'LabNormal2', + 'LabUnit', + 'RiaLDUserNameEntry', + 'RiaLDUserNameAppr', + 'RiaLDApprvFlag' + ], 'string', 'max' => 255], + [[ + 'ResultCode', + 'ResultName', + ], 'string'], + // allow other attributes to be assigned + [['LabNo'], 'string', 'max' => 100], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'LabNo' => 'Lab No', + 'RiaHDocDate' => 'Ria H Doc Date', + 'RiaHDocID' => 'Ria H Doc ID', + 'status' => 'Status', + 'TestCode' => 'Test Code', + 'TestName' => 'Test Name', + 'PatName' => 'Patient Name', + 'PatHN' => 'Patient HN', + 'CustID' => 'Customer ID', + 'LabID' => 'Lab ID', + 'LabName' => 'Lab Name', + 'RiaLDRsltAnal' => 'Result (Analytical)', + 'LabNormal1' => 'Normal Range 1', + 'LabNormal2' => 'Normal Range 2', + 'LabUnit' => 'Unit', + 'RiaLDUserNameEntry' => 'Entry By', + 'RiaLDUserDateEntry' => 'Entry Date', + 'RiaLDUserNameAppr' => 'Approved By', + 'RiaLDUserDateAppr' => 'Approved Date', + 'RiaLDApprvFlag' => 'Approved Flag', + ]; + } } \ No newline at end of file diff --git a/console/models/HisRequest.php b/console/models/HisRequest.php index 739ba392..2cc4cbf8 100755 --- a/console/models/HisRequest.php +++ b/console/models/HisRequest.php @@ -1,144 +1,144 @@ - 20], - [['AN', 'VN', 'Phone', 'ProvinceName', 'Room', 'RightsCode', 'DepartmentCode', 'WardCode', 'RequestByCode', 'DateOfBirth'], 'string', 'max' => 50], - [['PrefixName', 'FirstName', 'LastName', 'Address', 'SubDistrict', 'District', 'VisitType', 'RequestUnitName', 'Priority', 'RightsName', 'DepartmentName', 'WardName', 'DoctorCode', 'DoctorName', 'RequestBy'], 'string', 'max' => 120], - [['ProvinceCode'], 'string', 'max' => 2], - [['PostalCode'], 'string', 'max' => 5], - [['Sex', 'RequestStatus'], 'string', 'max' => 1], - [['RequestUnit', 'ReceiveLocation'], 'string', 'max' => 10], - [['RequestRemark'], 'string', 'max' => 4000], - [['Message'], 'string', 'max' => 250], - [['ProlabMessage'], 'string', 'max' => 500], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'ID' => 'ID', - 'LabNo' => 'Lab No', - 'HN' => 'Hn', - 'AN' => 'An', - 'VN' => 'Vn', - 'PrefixName' => 'Prefix Name', - 'FirstName' => 'First Name', - 'LastName' => 'Last Name', - 'DateOfBirth' => 'Date Of Birth', - 'PersonalID' => 'Personal ID', - 'PassportNo' => 'Passport No', - 'Phone' => 'Phone', - 'Address' => 'Address', - 'SubDistrict' => 'Sub District', - 'District' => 'District', - 'ProvinceCode' => 'Province Code', - 'ProvinceName' => 'Province Name', - 'PostalCode' => 'Postal Code', - 'Sex' => 'Sex', - 'Weight' => 'Weight', - 'Height' => 'Height', - 'VisitType' => 'Visit Type', - 'RequestUnit' => 'Request Unit', - 'RequestUnitName' => 'Request Unit Name', - 'ReceiveLocation' => 'Receive Location', - 'Room' => 'Room', - 'Priority' => 'Priority', - 'RightsCode' => 'Rights Code', - 'RightsName' => 'Rights Name', - 'DepartmentCode' => 'Department Code', - 'DepartmentName' => 'Department Name', - 'WardCode' => 'Ward Code', - 'WardName' => 'Ward Name', - 'AdmissionDate' => 'Admission Date', - 'DoctorCode' => 'Doctor Code', - 'DoctorName' => 'Doctor Name', - 'RequestRemark' => 'Request Remark', - 'RequestDate' => 'Request Date', - 'RequestByCode' => 'Request By Code', - 'RequestBy' => 'Request By', - 'RequestStatus' => 'Request Status', - 'Status' => 'Status', - 'Message' => 'Message', - 'ProlabMessage' => 'Prolab Message', - ]; - } - - public function getTestCode() - { - return $this->hasOne(HisRequestTestList::class, ['HisRequestID' => 'ID']); - } -} + 20], + [['AN', 'VN', 'Phone', 'ProvinceName', 'Room', 'RightsCode', 'DepartmentCode', 'WardCode', 'RequestByCode', 'DateOfBirth'], 'string', 'max' => 50], + [['PrefixName', 'FirstName', 'LastName', 'Address', 'SubDistrict', 'District', 'VisitType', 'RequestUnitName', 'Priority', 'RightsName', 'DepartmentName', 'WardName', 'DoctorCode', 'DoctorName', 'RequestBy'], 'string', 'max' => 120], + [['ProvinceCode'], 'string', 'max' => 2], + [['PostalCode'], 'string', 'max' => 5], + [['Sex', 'RequestStatus'], 'string', 'max' => 1], + [['RequestUnit', 'ReceiveLocation'], 'string', 'max' => 10], + [['RequestRemark'], 'string', 'max' => 4000], + [['Message'], 'string', 'max' => 250], + [['ProlabMessage'], 'string', 'max' => 500], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'ID' => 'ID', + 'LabNo' => 'Lab No', + 'HN' => 'Hn', + 'AN' => 'An', + 'VN' => 'Vn', + 'PrefixName' => 'Prefix Name', + 'FirstName' => 'First Name', + 'LastName' => 'Last Name', + 'DateOfBirth' => 'Date Of Birth', + 'PersonalID' => 'Personal ID', + 'PassportNo' => 'Passport No', + 'Phone' => 'Phone', + 'Address' => 'Address', + 'SubDistrict' => 'Sub District', + 'District' => 'District', + 'ProvinceCode' => 'Province Code', + 'ProvinceName' => 'Province Name', + 'PostalCode' => 'Postal Code', + 'Sex' => 'Sex', + 'Weight' => 'Weight', + 'Height' => 'Height', + 'VisitType' => 'Visit Type', + 'RequestUnit' => 'Request Unit', + 'RequestUnitName' => 'Request Unit Name', + 'ReceiveLocation' => 'Receive Location', + 'Room' => 'Room', + 'Priority' => 'Priority', + 'RightsCode' => 'Rights Code', + 'RightsName' => 'Rights Name', + 'DepartmentCode' => 'Department Code', + 'DepartmentName' => 'Department Name', + 'WardCode' => 'Ward Code', + 'WardName' => 'Ward Name', + 'AdmissionDate' => 'Admission Date', + 'DoctorCode' => 'Doctor Code', + 'DoctorName' => 'Doctor Name', + 'RequestRemark' => 'Request Remark', + 'RequestDate' => 'Request Date', + 'RequestByCode' => 'Request By Code', + 'RequestBy' => 'Request By', + 'RequestStatus' => 'Request Status', + 'Status' => 'Status', + 'Message' => 'Message', + 'ProlabMessage' => 'Prolab Message', + ]; + } + + public function getTestCode() + { + return $this->hasOne(HisRequestTestList::class, ['HisRequestID' => 'ID']); + } +} diff --git a/console/models/HisRequestTestList.php b/console/models/HisRequestTestList.php index 77543cfb..755dfcb4 100755 --- a/console/models/HisRequestTestList.php +++ b/console/models/HisRequestTestList.php @@ -1,70 +1,70 @@ - 20], - [['TestName', 'SpacimenName'], 'string', 'max' => 120], - [['TestType', 'TestStatus'], 'string', 'max' => 1], - [['TestRemark'], 'string', 'max' => 4000], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'ID' => 'ID', - 'HisRequestID' => 'His Request ID', - 'TestCode' => 'Test Code', - 'TestName' => 'Test Name', - 'TestType' => 'Test Type', - 'SpacimenCode' => 'Spacimen Code', - 'SpacimenName' => 'Spacimen Name', - 'TestStatus' => 'Test Status', - 'CollectionDateTime' => 'Collection Date Time', - 'TestRemark' => 'Test Remark', - ]; - } - - public function getResultCode() - { - return $this->hasOne(ResultCodeMapping::class, ['TestCode' => 'TestCode']); - } -} + 20], + [['TestName', 'SpacimenName'], 'string', 'max' => 120], + [['TestType', 'TestStatus'], 'string', 'max' => 1], + [['TestRemark'], 'string', 'max' => 4000], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'ID' => 'ID', + 'HisRequestID' => 'His Request ID', + 'TestCode' => 'Test Code', + 'TestName' => 'Test Name', + 'TestType' => 'Test Type', + 'SpacimenCode' => 'Spacimen Code', + 'SpacimenName' => 'Spacimen Name', + 'TestStatus' => 'Test Status', + 'CollectionDateTime' => 'Collection Date Time', + 'TestRemark' => 'Test Remark', + ]; + } + + public function getResultCode() + { + return $this->hasOne(ResultCodeMapping::class, ['TestCode' => 'TestCode']); + } +} diff --git a/console/models/LisApiD.php b/console/models/LisApiD.php index f0d1ea0c..14cb1ebe 100755 --- a/console/models/LisApiD.php +++ b/console/models/LisApiD.php @@ -1,88 +1,88 @@ -get('prolab'); - } - - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['RiaHDocID', 'LabID', 'LabName', 'RiaLDRsltAnal'], 'required'], - [['RiaHDocID'], 'integer'], - [['RiaHDocDate', 'RiaLDUserDateReEntry', 'RiaLDUserDateAppr', 'RiaLDUserDateEntry'], 'safe'], - [['LabID'], 'string', 'max' => 50], - [['LabName', 'RiaLDRsltAnal', 'LabNormal1', 'LabUnit', 'PatName', 'PatAge', 'PatHN', 'CustID', 'CustTitle', 'CustName', 'RiaLDUserNameReEntry', 'RiaLDUserNameAppr', 'RiaLDApprvFlag', 'RiaLDUserNameEntry'], 'string', 'max' => 255], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'RiaHDocID' => 'Doc ID', - 'RiaHDocDate' => 'Doc Date', - 'LabID' => 'Lab ID', - 'LabName' => 'Lab Name', - 'RiaLDRsltAnal' => 'Result', - 'LabNormal1' => 'Normal Range', - 'LabUnit' => 'Unit', - 'PatName' => 'Patient Name', - 'PatAge' => 'Patient Age', - 'PatHN' => 'HN', - 'CustID' => 'Customer ID', - 'CustTitle' => 'Customer Title', - 'CustName' => 'Customer Name', - 'RiaLDUserNameReEntry' => 'Re-Entry User', - 'RiaLDUserDateReEntry' => 'Re-Entry Date', - 'RiaLDUserNameAppr' => 'Approved User', - 'RiaLDUserDateAppr' => 'Approved Date', - 'RiaLDApprvFlag' => 'Approval Flag', - 'RiaLDUserNameEntry' => 'Entry User', - 'RiaLDUserDateEntry' => 'Entry Date', - ]; - } -} +get('prolab'); + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['RiaHDocID', 'LabID', 'LabName', 'RiaLDRsltAnal'], 'required'], + [['RiaHDocID'], 'integer'], + [['RiaHDocDate', 'RiaLDUserDateReEntry', 'RiaLDUserDateAppr', 'RiaLDUserDateEntry'], 'safe'], + [['LabID'], 'string', 'max' => 50], + [['LabName', 'RiaLDRsltAnal', 'LabNormal1', 'LabUnit', 'PatName', 'PatAge', 'PatHN', 'CustID', 'CustTitle', 'CustName', 'RiaLDUserNameReEntry', 'RiaLDUserNameAppr', 'RiaLDApprvFlag', 'RiaLDUserNameEntry'], 'string', 'max' => 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'RiaHDocID' => 'Doc ID', + 'RiaHDocDate' => 'Doc Date', + 'LabID' => 'Lab ID', + 'LabName' => 'Lab Name', + 'RiaLDRsltAnal' => 'Result', + 'LabNormal1' => 'Normal Range', + 'LabUnit' => 'Unit', + 'PatName' => 'Patient Name', + 'PatAge' => 'Patient Age', + 'PatHN' => 'HN', + 'CustID' => 'Customer ID', + 'CustTitle' => 'Customer Title', + 'CustName' => 'Customer Name', + 'RiaLDUserNameReEntry' => 'Re-Entry User', + 'RiaLDUserDateReEntry' => 'Re-Entry Date', + 'RiaLDUserNameAppr' => 'Approved User', + 'RiaLDUserDateAppr' => 'Approved Date', + 'RiaLDApprvFlag' => 'Approval Flag', + 'RiaLDUserNameEntry' => 'Entry User', + 'RiaLDUserDateEntry' => 'Entry Date', + ]; + } +} diff --git a/console/models/LisApiSend.php b/console/models/LisApiSend.php index 570ddb51..e6dfd136 100755 --- a/console/models/LisApiSend.php +++ b/console/models/LisApiSend.php @@ -1,87 +1,87 @@ -get('prolab'); - } - - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['RiaHDocID', 'LabID', 'LabName', 'CustID', 'PatHN', 'PatName'], 'required'], - [['RiaHDocID', 'status', 'RiaLDApprvFlag'], 'integer'], - [['RiaHDocDate', 'RiaLDUserDateEntry', 'RiaLDUserDateAppr'], 'safe'], - [['LabID'], 'string', 'max' => 50], - [['LabName', 'PatName', 'PatHN', 'CustID', 'CustTitle'], 'string', 'max' => 255], - [['RiaLDRsltAnal', 'LabNormal1', 'LabUnit', 'RiaLDUserNameEntry', 'RiaLDUserNameAppr'], 'string', 'max' => 255], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'RiaHDocID' => 'Doc ID', - 'RiaHDocDate' => 'Doc Date', - 'LabID' => 'Lab ID', - 'LabName' => 'Lab Name', - 'CustID' => 'Customer ID', - 'status' => 'Status', - 'PatHN' => 'HN', - 'PatName' => 'Patient Name', - 'CustTitle' => 'Customer Title', - - // additional labels - 'RiaLDRsltAnal' => 'Result Anal (RiaLDRsltAnal)', - 'LabNormal1' => 'Lab Normal 1', - 'LabUnit' => 'Lab Unit', - 'RiaLDUserNameEntry' => 'User Entry', - 'RiaLDUserDateEntry' => 'User Entry Date', - 'RiaLDUserNameAppr' => 'User Approver', - 'RiaLDUserDateAppr' => 'Approve Date', - 'RiaLDApprvFlag' => 'Approved Flag', - ]; - } -} +get('prolab'); + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['RiaHDocID', 'LabID', 'LabName', 'CustID', 'PatHN', 'PatName'], 'required'], + [['RiaHDocID', 'status', 'RiaLDApprvFlag'], 'integer'], + [['RiaHDocDate', 'RiaLDUserDateEntry', 'RiaLDUserDateAppr'], 'safe'], + [['LabID'], 'string', 'max' => 50], + [['LabName', 'PatName', 'PatHN', 'CustID', 'CustTitle'], 'string', 'max' => 255], + [['RiaLDRsltAnal', 'LabNormal1', 'LabUnit', 'RiaLDUserNameEntry', 'RiaLDUserNameAppr'], 'string', 'max' => 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'RiaHDocID' => 'Doc ID', + 'RiaHDocDate' => 'Doc Date', + 'LabID' => 'Lab ID', + 'LabName' => 'Lab Name', + 'CustID' => 'Customer ID', + 'status' => 'Status', + 'PatHN' => 'HN', + 'PatName' => 'Patient Name', + 'CustTitle' => 'Customer Title', + + // additional labels + 'RiaLDRsltAnal' => 'Result Anal (RiaLDRsltAnal)', + 'LabNormal1' => 'Lab Normal 1', + 'LabUnit' => 'Lab Unit', + 'RiaLDUserNameEntry' => 'User Entry', + 'RiaLDUserDateEntry' => 'User Entry Date', + 'RiaLDUserNameAppr' => 'User Approver', + 'RiaLDUserDateAppr' => 'Approve Date', + 'RiaLDApprvFlag' => 'Approved Flag', + ]; + } +} diff --git a/console/models/LisResult.php b/console/models/LisResult.php index 4c93e970..0ce353b5 100755 --- a/console/models/LisResult.php +++ b/console/models/LisResult.php @@ -1,74 +1,74 @@ - 20], - [['riah_doc_id'], 'string', 'max' => 50], - [['test_code'], 'string', 'max' => 20], - [['test_name'], 'string', 'max' => 120], - [['cust_id'], 'string', 'max' => 10], - [['hn'], 'string', 'max' => 20], - [['result_value'], 'string', 'max' => 200], - [['result_unit'], 'string', 'max' => 20], - [['reference_range'], 'string', 'max' => 250], - [['status'], 'integer'], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'id' => 'ID', - 'lab_no' => 'Lab No', - 'riah_doc_id' => 'RiaH Doc ID', - 'riah_doc_date' => 'RiaH Doc Date', - 'test_code' => 'Test Code', - 'test_name' => 'Test Name', - 'cust_id' => 'Customer ID', - 'hn' => 'HN', - 'result_value' => 'Result Value', - 'result_unit' => 'Result Unit', - 'reference_range' => 'Reference Range', - 'status' => 'Status', - ]; - } -} + 20], + [['riah_doc_id'], 'string', 'max' => 50], + [['test_code'], 'string', 'max' => 20], + [['test_name'], 'string', 'max' => 120], + [['cust_id'], 'string', 'max' => 10], + [['hn'], 'string', 'max' => 20], + [['result_value'], 'string', 'max' => 200], + [['result_unit'], 'string', 'max' => 20], + [['reference_range'], 'string', 'max' => 250], + [['status'], 'integer'], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'lab_no' => 'Lab No', + 'riah_doc_id' => 'RiaH Doc ID', + 'riah_doc_date' => 'RiaH Doc Date', + 'test_code' => 'Test Code', + 'test_name' => 'Test Name', + 'cust_id' => 'Customer ID', + 'hn' => 'HN', + 'result_value' => 'Result Value', + 'result_unit' => 'Result Unit', + 'reference_range' => 'Reference Range', + 'status' => 'Status', + ]; + } +} diff --git a/console/models/PatientCase.php b/console/models/PatientCase.php index 9acc0a17..8d8bb194 100755 --- a/console/models/PatientCase.php +++ b/console/models/PatientCase.php @@ -1,310 +1,310 @@ -get('patho'); - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [['id_case', 'title', 'given_name', 'surname', 'gender', 'age', 'hospital_id', 'hospital', 'status_id', 'status'], 'required'], - [['id_case', 'title', 'given_name', 'surname', 'age_unit', 'hospital_name', 'status', 'pathologist', 'diagnosis', 'additional_diagnosis', 'gross_img'], 'string'], - [['gender', 'hospital_id', 'status_id', 'pathologist_id', 'is_outlab', 'cnt_critical'], 'integer'], - [['birthdate', 'collect_at', 'register_at', 'cut_at', 'report_at', 'center_out_at', 'id_case', 'bex', 'bin', 'additional_at', 'h_n', 'his_ln'], 'safe'], - [['age'], 'number'] - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id_case' => 'Case', - 'title' => 'คำนำหน้า', - 'patient_name' => 'ชื่อ นามสกุล', - //'patient' => 'ชื่อ - นามสกุล', - //'name' => 'ชื่อ นามสกุล', - 'gender' => 'เพศ', - 'birthdate' => 'วดป เกิด', - 'age' => 'อายุ', - 'age_unit' => 'หน่วยอายุ', - 'hospital_id' => 'Hospital ID', - 'hospital_name' => 'หน่วยงาน', - 'collect_at' => 'Collect At', - 'register_at' => 'วันที่ลงทะเบียน', - 'cut_at' => 'Cut At', - 'status_id' => 'สถานะ', - 'status' => 'สถานะ', - 'pathologist_id' => 'Pathologist ID', - 'pathologist' => 'พยาธิแพทย์', - 'report_at' => 'วันที่ออกผล', - 'diagnosis' => 'Diagnosis', - 'center_out_at' => 'Center Out At', - 'bex' => 'Block Ex', - 'bin' => 'Block In', - 'is_outlab' => 'Case Outlab', - 'cnt_critical' => 'Is Critical Diagnosis', - 'h_n' => 'H N', - 'his_ln' => 'Lab Number' - ]; - } - - - public function getCaseBlock() - { - return $this->hasOne(CaseBlock::class, ['id_case' => 'id_case']); - } - - - public function getCenterOut() - { - return $this->hasOne(CenterOut::class, ['id_case' => 'id_case']); - } - public function getHospitals() - { - return $this->hasOne(ConstHospital::class, ['id' => 'hospital_id']); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getHospitalCaseOpen() - { - return $this->hasOne(HospitalCaseOpen::class, ['id_case' => 'id_case']); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getCoder() - { - return $this->hasOne(Coder::class, ['id_case' => 'id_case']); - } - - - /** - * @return array|false|string[] - */ - public function getReport() - { - $case = false; - $case_type = Yii::$app->pathology->getCaseType($this->id_case); - if ($case_type == 'surgical') { - $case = CaseSurgical::findOne(['id_case' => $this->id_case]); - } - if ($case_type == 'non-gyn') { - $case = CaseNonGyn::findOne(['id_case' => $this->id_case]); - } - if ($case_type == 'pap') { - $case = CasePap::findOne(['id_case' => $this->id_case]); - } - - return $case; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getApprove() - { - return $this->hasOne(PatientCaseApprove::class, ['id_case' => 'id_case']); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getAdd() - { - return $this->hasOne(SurgicalAdd::class, ['id' => 'add_id']); - } - - - - public function uploadGross($model, $attribute) - { - $grosses = UploadedFile::getInstances($model, $attribute); - FileHelper::createDirectory($this->getPath()); - if ($grosses) { - $file_names = []; - foreach ($grosses as $gross) { - $file_name = $this->id_case . '_' . $gross->baseName . '.' . $gross->extension; - $gross->saveAs(Yii::getAlias('@webroot') . '/' . $this->uploadGross . '/' . $file_name); - $file_names[] = $file_name; - } - if ($this->isNewRecord) { //ถ้าเป็นการเพิ่ม Record ใหม่ให้บันทึกไฟล์ aaa.aaa,bbb.bbb .... - return implode(',', $file_names); - } else { //ถ้าเป็นการปรับปรุงให้เพิ่มจากของเดิม - return implode(',', ArrayHelper::merge($file_names, $this->getOldAttribute($attribute) ? explode(',', $this->getOldAttribute($attribute)) : [])); - } - } //end files upload - - return $this->isNewRecord ? false : $this->getOldAttribute($attribute); //ถ้าไม่มีการ upload ให้ใช้ข้อมูลเดิม - } - - - /** - * @return string - */ - public function getPath() - { - return Yii::getAlias('@webroot') . '/' . $this->uploadGross; - } - - /** - * @return string - */ - public function getFileUrl() - { - return Yii::getAlias('@web') . '/' . $this->uploadGross; - } - - - /** - * @return false|string[] - */ - public function getGrosses() - { - return !is_array($this->gross_img) ? explode(',', $this->gross_img) : []; - } - - - /** - * @return string - */ - public function getGrossImgs() - { - $img = ''; - if (!empty($this->gross_img)) { - foreach ($this->getGrosses() as $gross) { - $img .= Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['class' => 'img-fluid']), Url::to(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, true), ['target' => '_blank']); - } - } - return $img; - } - - /** - * @return string - */ - public function getGrossImgsForGross() - { - $img = ''; - if (!empty($this->gross_img)) { - foreach ($this->getGrosses() as $gross) { - $img .= Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['width' => 200, 'class' => '']), ['/surgical/gross/view-gross', 'id_case' => $this->id_case, 'gross_img' => $gross], ['target' => '_blank']); - } - } - return $img; - } - - - /** - * @return string - */ - public function getGrossDelImgs() - { - $img = ''; - if (!empty($this->gross_img)) { - foreach ($this->getGrosses() as $gross) { - $img .= '
' . Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['width' => 200, 'class' => 'img-fluid']), Url::to(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, true), ['target' => '_blank']) . '
' . - Html::a('Delete', ['delete-gross-img', 'img' => $gross, 'id_case' => $this->id_case], ['class' => 'btn btn-xs btn-danger', 'data' => ['confirm' => 'แน่ใจนะว่าต้องการลบรูปนี้?']]) - . '
'; - } - } - return $img; - } - - public function getCaseSurgical() - { - return $this->hasOne(CaseSurgical::class, ['id_case' => 'id_case']); - } - - public function getNonGynConsult() - { - return $this->hasOne(CytoNonGynConsult::class, ['id_case' => 'id_case']); - } - - public function getNonGynApprove() - { - return $this->hasOne(CenterApprove::class, ['id_case' => 'id_case']); - } - - public function getGynConsult() - { - return $this->hasOne(CytoPapConsult::class, ['id_case' => 'id_case']); - } - - public function getGynApprove() - { - return $this->hasOne(CenterApprove::class, ['id_case' => 'id_case']); - } - /** - * @return \yii\db\ActiveQuery - */ - public function getCytotech1() - { - return $this->hasOne(User::className(), ['id' => 'cytotech1_id']); - } -} +get('patho'); + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['id_case', 'title', 'given_name', 'surname', 'gender', 'age', 'hospital_id', 'hospital', 'status_id', 'status'], 'required'], + [['id_case', 'title', 'given_name', 'surname', 'age_unit', 'hospital_name', 'status', 'pathologist', 'diagnosis', 'additional_diagnosis', 'gross_img'], 'string'], + [['gender', 'hospital_id', 'status_id', 'pathologist_id', 'is_outlab', 'cnt_critical'], 'integer'], + [['birthdate', 'collect_at', 'register_at', 'cut_at', 'report_at', 'center_out_at', 'id_case', 'bex', 'bin', 'additional_at', 'h_n', 'his_ln'], 'safe'], + [['age'], 'number'] + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id_case' => 'Case', + 'title' => 'คำนำหน้า', + 'patient_name' => 'ชื่อ นามสกุล', + //'patient' => 'ชื่อ - นามสกุล', + //'name' => 'ชื่อ นามสกุล', + 'gender' => 'เพศ', + 'birthdate' => 'วดป เกิด', + 'age' => 'อายุ', + 'age_unit' => 'หน่วยอายุ', + 'hospital_id' => 'Hospital ID', + 'hospital_name' => 'หน่วยงาน', + 'collect_at' => 'Collect At', + 'register_at' => 'วันที่ลงทะเบียน', + 'cut_at' => 'Cut At', + 'status_id' => 'สถานะ', + 'status' => 'สถานะ', + 'pathologist_id' => 'Pathologist ID', + 'pathologist' => 'พยาธิแพทย์', + 'report_at' => 'วันที่ออกผล', + 'diagnosis' => 'Diagnosis', + 'center_out_at' => 'Center Out At', + 'bex' => 'Block Ex', + 'bin' => 'Block In', + 'is_outlab' => 'Case Outlab', + 'cnt_critical' => 'Is Critical Diagnosis', + 'h_n' => 'H N', + 'his_ln' => 'Lab Number' + ]; + } + + + public function getCaseBlock() + { + return $this->hasOne(CaseBlock::class, ['id_case' => 'id_case']); + } + + + public function getCenterOut() + { + return $this->hasOne(CenterOut::class, ['id_case' => 'id_case']); + } + public function getHospitals() + { + return $this->hasOne(ConstHospital::class, ['id' => 'hospital_id']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getHospitalCaseOpen() + { + return $this->hasOne(HospitalCaseOpen::class, ['id_case' => 'id_case']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getCoder() + { + return $this->hasOne(Coder::class, ['id_case' => 'id_case']); + } + + + /** + * @return array|false|string[] + */ + public function getReport() + { + $case = false; + $case_type = Yii::$app->pathology->getCaseType($this->id_case); + if ($case_type == 'surgical') { + $case = CaseSurgical::findOne(['id_case' => $this->id_case]); + } + if ($case_type == 'non-gyn') { + $case = CaseNonGyn::findOne(['id_case' => $this->id_case]); + } + if ($case_type == 'pap') { + $case = CasePap::findOne(['id_case' => $this->id_case]); + } + + return $case; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getApprove() + { + return $this->hasOne(PatientCaseApprove::class, ['id_case' => 'id_case']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getAdd() + { + return $this->hasOne(SurgicalAdd::class, ['id' => 'add_id']); + } + + + + public function uploadGross($model, $attribute) + { + $grosses = UploadedFile::getInstances($model, $attribute); + FileHelper::createDirectory($this->getPath()); + if ($grosses) { + $file_names = []; + foreach ($grosses as $gross) { + $file_name = $this->id_case . '_' . $gross->baseName . '.' . $gross->extension; + $gross->saveAs(Yii::getAlias('@webroot') . '/' . $this->uploadGross . '/' . $file_name); + $file_names[] = $file_name; + } + if ($this->isNewRecord) { //ถ้าเป็นการเพิ่ม Record ใหม่ให้บันทึกไฟล์ aaa.aaa,bbb.bbb .... + return implode(',', $file_names); + } else { //ถ้าเป็นการปรับปรุงให้เพิ่มจากของเดิม + return implode(',', ArrayHelper::merge($file_names, $this->getOldAttribute($attribute) ? explode(',', $this->getOldAttribute($attribute)) : [])); + } + } //end files upload + + return $this->isNewRecord ? false : $this->getOldAttribute($attribute); //ถ้าไม่มีการ upload ให้ใช้ข้อมูลเดิม + } + + + /** + * @return string + */ + public function getPath() + { + return Yii::getAlias('@webroot') . '/' . $this->uploadGross; + } + + /** + * @return string + */ + public function getFileUrl() + { + return Yii::getAlias('@web') . '/' . $this->uploadGross; + } + + + /** + * @return false|string[] + */ + public function getGrosses() + { + return !is_array($this->gross_img) ? explode(',', $this->gross_img) : []; + } + + + /** + * @return string + */ + public function getGrossImgs() + { + $img = ''; + if (!empty($this->gross_img)) { + foreach ($this->getGrosses() as $gross) { + $img .= Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['class' => 'img-fluid']), Url::to(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, true), ['target' => '_blank']); + } + } + return $img; + } + + /** + * @return string + */ + public function getGrossImgsForGross() + { + $img = ''; + if (!empty($this->gross_img)) { + foreach ($this->getGrosses() as $gross) { + $img .= Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['width' => 200, 'class' => '']), ['/surgical/gross/view-gross', 'id_case' => $this->id_case, 'gross_img' => $gross], ['target' => '_blank']); + } + } + return $img; + } + + + /** + * @return string + */ + public function getGrossDelImgs() + { + $img = ''; + if (!empty($this->gross_img)) { + foreach ($this->getGrosses() as $gross) { + $img .= '
' . Html::a(Html::img(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, ['width' => 200, 'class' => 'img-fluid']), Url::to(Yii::getAlias('@web') . '/' . $this->uploadGross . '/' . $gross, true), ['target' => '_blank']) . '
' . + Html::a('Delete', ['delete-gross-img', 'img' => $gross, 'id_case' => $this->id_case], ['class' => 'btn btn-xs btn-danger', 'data' => ['confirm' => 'แน่ใจนะว่าต้องการลบรูปนี้?']]) + . '
'; + } + } + return $img; + } + + public function getCaseSurgical() + { + return $this->hasOne(CaseSurgical::class, ['id_case' => 'id_case']); + } + + public function getNonGynConsult() + { + return $this->hasOne(CytoNonGynConsult::class, ['id_case' => 'id_case']); + } + + public function getNonGynApprove() + { + return $this->hasOne(CenterApprove::class, ['id_case' => 'id_case']); + } + + public function getGynConsult() + { + return $this->hasOne(CytoPapConsult::class, ['id_case' => 'id_case']); + } + + public function getGynApprove() + { + return $this->hasOne(CenterApprove::class, ['id_case' => 'id_case']); + } + /** + * @return \yii\db\ActiveQuery + */ + public function getCytotech1() + { + return $this->hasOne(User::className(), ['id' => 'cytotech1_id']); + } +} diff --git a/console/models/PatientCaseApprove.php b/console/models/PatientCaseApprove.php index 6376c6c6..341fe314 100644 --- a/console/models/PatientCaseApprove.php +++ b/console/models/PatientCaseApprove.php @@ -1,127 +1,127 @@ -get('patho'); - } - - public static function primaryKey() - { - return ['id_case']; - } - - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['gender', 'hospital_id', 'status_id', 'pathologist_id', 'approve_status', 'approve_by', 'approve_id', 'is_send_request', 'is_critical_diagnosis'], 'integer'], - [['birthdate', 'collect_at', 'register_at', 'cut_at', 'report_at', 'approve_at', 'send_request_at'], 'safe'], - [['age'], 'number'], - [['diagnosis'], 'string'], - [['id_case'], 'string', 'max' => 12], - [['title'], 'string', 'max' => 100], - [['name'], 'string', 'max' => 1000], - [['age_unit'], 'string', 'max' => 15], - [['hospital'], 'string', 'max' => 400], - [['status'], 'string', 'max' => 500], - [['pathologist'], 'string', 'max' => 600], - [['remark'], 'string', 'max' => 300], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'id_case' => 'Id Case', - 'title' => 'Title', - 'name' => 'Name', - 'gender' => 'Gender', - 'birthdate' => 'Birthdate', - 'age' => 'Age', - 'age_unit' => 'Age Unit', - 'hospital_id' => 'Hospital ID', - 'hospital' => 'Hospital', - 'collect_at' => 'Collect At', - 'register_at' => 'Register At', - 'cut_at' => 'Cut At', - 'status_id' => 'Status ID', - 'status' => 'Status', - 'pathologist_id' => 'Pathologist ID', - 'pathologist' => 'Pathologist', - 'report_at' => 'Report At', - 'diagnosis' => 'Diagnosis', - 'approve_status' => 'Approve Status', - 'approve_by' => 'Approve By', - 'approve_at' => 'Approve At', - 'remark' => 'Remark', - 'approve_id' => 'Approve ID', - 'is_send_request' => 'Is Send Request', - 'is_critical_diagnosis' => 'Is Critical Diagnosis', - 'send_request_at' => 'Send Request At', - ]; - } - - - public function getApproveBy() - { - return $this->hasOne(User::class, ['id' => 'approve_by']); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getApproveAt() - { - return $this->hasOne(User::class, ['id' => 'approve_at']); - } -} \ No newline at end of file +get('patho'); + } + + public static function primaryKey() + { + return ['id_case']; + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['gender', 'hospital_id', 'status_id', 'pathologist_id', 'approve_status', 'approve_by', 'approve_id', 'is_send_request', 'is_critical_diagnosis'], 'integer'], + [['birthdate', 'collect_at', 'register_at', 'cut_at', 'report_at', 'approve_at', 'send_request_at'], 'safe'], + [['age'], 'number'], + [['diagnosis'], 'string'], + [['id_case'], 'string', 'max' => 12], + [['title'], 'string', 'max' => 100], + [['name'], 'string', 'max' => 1000], + [['age_unit'], 'string', 'max' => 15], + [['hospital'], 'string', 'max' => 400], + [['status'], 'string', 'max' => 500], + [['pathologist'], 'string', 'max' => 600], + [['remark'], 'string', 'max' => 300], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id_case' => 'Id Case', + 'title' => 'Title', + 'name' => 'Name', + 'gender' => 'Gender', + 'birthdate' => 'Birthdate', + 'age' => 'Age', + 'age_unit' => 'Age Unit', + 'hospital_id' => 'Hospital ID', + 'hospital' => 'Hospital', + 'collect_at' => 'Collect At', + 'register_at' => 'Register At', + 'cut_at' => 'Cut At', + 'status_id' => 'Status ID', + 'status' => 'Status', + 'pathologist_id' => 'Pathologist ID', + 'pathologist' => 'Pathologist', + 'report_at' => 'Report At', + 'diagnosis' => 'Diagnosis', + 'approve_status' => 'Approve Status', + 'approve_by' => 'Approve By', + 'approve_at' => 'Approve At', + 'remark' => 'Remark', + 'approve_id' => 'Approve ID', + 'is_send_request' => 'Is Send Request', + 'is_critical_diagnosis' => 'Is Critical Diagnosis', + 'send_request_at' => 'Send Request At', + ]; + } + + + public function getApproveBy() + { + return $this->hasOne(User::class, ['id' => 'approve_by']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getApproveAt() + { + return $this->hasOne(User::class, ['id' => 'approve_at']); + } +} diff --git a/console/models/ResultCodeMapping.php b/console/models/ResultCodeMapping.php index 0b70cef4..eab0dd4b 100755 --- a/console/models/ResultCodeMapping.php +++ b/console/models/ResultCodeMapping.php @@ -1,65 +1,65 @@ - 20], - [['TestCode', 'PROLAB_LabID', 'ResultCode'], 'string', 'max' => 50], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'TestID' => 'Test ID', - 'TestCode' => 'Test Code', - 'TestName' => 'Test Name', - 'PROLAB_LabID' => 'PROLAB Lab ID', - 'LISName' => 'LIS Name', - 'ResultCode' => 'Result Code', - 'ResultName' => 'Result Name', - ]; - } - - /** - * กรณีตารางไม่มี primary key - */ - public static function primaryKey() - { - return ['TestCode', 'ResultCode']; - } -} + 20], + [['TestCode', 'PROLAB_LabID', 'ResultCode'], 'string', 'max' => 50], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'TestID' => 'Test ID', + 'TestCode' => 'Test Code', + 'TestName' => 'Test Name', + 'PROLAB_LabID' => 'PROLAB Lab ID', + 'LISName' => 'LIS Name', + 'ResultCode' => 'Result Code', + 'ResultName' => 'Result Name', + ]; + } + + /** + * กรณีตารางไม่มี primary key + */ + public static function primaryKey() + { + return ['TestCode', 'ResultCode']; + } +} diff --git a/console/models/ResultPathoSikarinCheck.php b/console/models/ResultPathoSikarinCheck.php index 87bbcb47..f8db4164 100755 --- a/console/models/ResultPathoSikarinCheck.php +++ b/console/models/ResultPathoSikarinCheck.php @@ -1,60 +1,60 @@ - 20], - [['PrefixName', 'FirstName', 'LastName', 'TestName'], 'string', 'max' => 120], - [['HisRequestID', 'status'], 'integer'], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'id' => 'ID', - 'LabNo' => 'Lab No', - 'HN' => 'HN', - 'HisRequestID' => 'HisRequestID', - 'TestCode' => 'Test Code', - 'TestName' => 'Test Name', - 'status' => 'status' - ]; - } - - public function getResultCode() - { - return $this->hasOne(ResultCodeMapping::class, ['TestCode' => 'TestCode']); - } -} + 20], + [['PrefixName', 'FirstName', 'LastName', 'TestName'], 'string', 'max' => 120], + [['HisRequestID', 'status'], 'integer'], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'LabNo' => 'Lab No', + 'HN' => 'HN', + 'HisRequestID' => 'HisRequestID', + 'TestCode' => 'Test Code', + 'TestName' => 'Test Name', + 'status' => 'status' + ]; + } + + public function getResultCode() + { + return $this->hasOne(ResultCodeMapping::class, ['TestCode' => 'TestCode']); + } +} diff --git a/console/models/SendHis.php b/console/models/SendHis.php index 9280aaad..a2e6fad2 100755 --- a/console/models/SendHis.php +++ b/console/models/SendHis.php @@ -1,73 +1,73 @@ - 50], - ]; - } - - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'id' => 'ID', - 'approve_id' => 'Approve ID', - 'id_case' => 'Id Case', - 'report_type' => 'Report Type', - 'diagnosis_id' => 'Diagnosis ID', - 'is_send' => 'Is Send', - 'send_at' => 'Send At', - 'created_at' => 'Created At', - 'updated_at' => 'Updated At', - 'created_by' => 'Created By', - 'updated_by' => 'Updated By', - ]; - } -} + 50], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'approve_id' => 'Approve ID', + 'id_case' => 'Id Case', + 'report_type' => 'Report Type', + 'diagnosis_id' => 'Diagnosis ID', + 'is_send' => 'Is Send', + 'send_at' => 'Send At', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + 'created_by' => 'Created By', + 'updated_by' => 'Updated By', + ]; + } +} diff --git a/console/models/User.php b/console/models/User.php index e737855d..5f834b71 100755 --- a/console/models/User.php +++ b/console/models/User.php @@ -1,326 +1,326 @@ -get('patho'); - } - - /** - * @inheritdoc - */ - public function behaviors() - { - return [ - TimestampBehavior::class, - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - ['status', 'default', 'value' => self::STATUS_ACTIVE], - ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED, self::STATUS_NOT_ACTIVE]], - [['username', 'password_hash', 'email', 'realname', 'status', 'role'], 'required'], - [['realname', 'report_name', 'email', 'position', 'role', 'tel_x', 'position_no', 'role'], 'string'], - [['hospital_id', 'department_id', 'supervisor_id', 'employment_type_id', 'is_partner'], 'integer'], - [['last_login_at', 'employment_type_id', 'last_update_password'], 'safe'], - [['sign_img'], 'file', 'extensions' => 'jpg, png'], - [['photo'], 'file', 'extensions' => 'jpg,png,jpeg,gif'], - [['username', 'email'], 'unique'], - [['is_change_password'], 'boolean'], - ]; - } - - public function attributeLabels() - { - return [ - 'username' => 'Username', - 'password_hash' => 'Password', - 'email' => 'Email', - 'realname' => 'ชื่อ-นามสกุล', - 'last_login_at' => 'เข้าระบบล่าสุด', - 'status' => 'สถานะ', - 'name_old' => 'ชื่อ-สกุลเดิม', - 'position_no' => 'เลขที่ตำแหน่ง', - 'position' => 'ตำแหน่ง', - 'department_id' => 'หน่วยงาน', - 'employment_type_id' => 'ประเภทบุคลากร', - 'supervisor_id' => 'หัวหน้างาน', - 'role' => 'บทบาทหลัก', - 'tel_x' => 'โทรศัพท์', - 'report_name' => 'ชื่อในใบรายงานผล', - 'sign_img' => 'ลายเซ็นต์', - 'hospital_id' => 'โรงพยาบาล', - 'last_update_password' => 'เปลี่ยนรหัสผ่านล่าสุด', - 'is_change_password' => 'ต้องการเปลี่ยนรหัสผ่าน?', - 'created_at' => 'เพิ่มเมื่อ', - 'updated_at' => 'แก้ไขเมื่อ', - 'photo' => 'รูปประจำตัว' - ]; - } - - public function attributeHints() - { - return [ - 'is_change_password' => 'หากต้องการเปลี่ยนรหัสผ่านให้คลิกที่ Checkbox แล้วใส่รหัสผ่านใหม่ที่ช่อง Password', - ]; - } - - /** - * @inheritdoc - */ - public static function findIdentity($id) - { - return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); - } - - /** - * @inheritdoc - */ - public static function findIdentityByAccessToken($token, $type = null) - { - //throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); - return static::findOne(['auth_key' => $token, 'status' => self::STATUS_ACTIVE]); - } - - /** - * Finds user by username - * - * @param string $username - * @return static|null - */ - public static function findByUsername($username) - { - return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); - } - - public static function findByHospital($username) - { - return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE, 'role' => 'hospital']); - } - - /** - * Finds user by password reset token - * - * @param string $token password reset token - * @return static|null - */ - public static function findByPasswordResetToken($token) - { - if (!static::isPasswordResetTokenValid($token)) { - return null; - } - - return static::findOne([ - 'password_reset_token' => $token, - 'status' => self::STATUS_ACTIVE, - ]); - } - - /** - * Finds out if password reset token is valid - * - * @param string $token password reset token - * @return boolean - */ - public static function isPasswordResetTokenValid($token) - { - if (empty($token)) { - return false; - } - - $timestamp = (int)substr($token, strrpos($token, '_') + 1); - $expire = Yii::$app->params['user.passwordResetTokenExpire']; - return $timestamp + $expire >= time(); - } - - /** - * @inheritdoc - */ - public function getId() - { - return $this->getPrimaryKey(); - } - - /** - * @inheritdoc - */ - public function getAuthKey() - { - return $this->auth_key; - } - - /** - * @inheritdoc - */ - public function validateAuthKey($authKey) - { - return $this->getAuthKey() === $authKey; - } - - /** - * Validates password - * - * @param string $password password to validate - * @return boolean if password provided is valid for current user - */ - public function validatePassword($password) - { - return Yii::$app->security->validatePassword($password, $this->password_hash); - } - - /** - * Generates password hash from password and sets it to the model - * - * @param string $password - */ - public function setPassword($password) - { - $this->password_hash = Yii::$app->security->generatePasswordHash($password); - } - - /** - * Generates "remember me" authentication key - */ - public function generateAuthKey() - { - $this->auth_key = Yii::$app->security->generateRandomString(); - } - - /** - * Generates new password reset token - */ - public function generatePasswordResetToken() - { - $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); - } - - /** - * Removes password reset token - */ - public function removePasswordResetToken() - { - $this->password_reset_token = null; - } - - /* Relational */ - public function getHrDepartment() - { - return $this->hasOne(HrDepartment::class, ['id' => 'department_id']); - } - - public function getHrEmploymentType() - { - return $this->hasOne(HrEmploymentType::class, ['id' => 'employment_type_id']); - } - - public function getHrWorking() - { - return $this->hasOne(HrWorking::class, ['user_id' => 'id']); - } - - public function getEmployee() - { - return $this->hasOne(HrEmployee::class, ['user_id' => 'id']); - } - - public function getHospital() - { - return $this->hasOne(ConstHospital::class, ['id' => 'hospital_id']); - } - - public function uploadFile($model, $attribute) - { - $file = UploadedFile::getInstance($model, $attribute); - if ($file) { - $file_name = substr(md5($file->baseName . time()), 0, 15) . '.' . $file->extension; - $file->saveAs(Yii::getAlias('@webroot') . '/' . $this->uploadFile . '/' . $file_name); - if ($model->isNewRecord) { - return $file_name; - } else { - if (empty($model->sign_img)) { - return $file_name; - } else { - $model->getOldAttribute($attribute); - } - } - } - return $model->isNewRecord ? false : $model->getOldAttribute($attribute); - } - - /** - * @return string - */ - public function getPicture() - { - if (empty($this->photo)) { - return Yii::getAlias('@web') . '/' . $this->userPhoto . '/blank.jpg'; - } else { - return Yii::getAlias('@web') . '/' . $this->userPhoto . '/' . $this->photo; - } - } - - /** - * @param $model - * @return false|string - */ - public function uploadPhoto($model) - { - $file = UploadedFile::getInstance($model, 'photo'); - if ($file) { - $filename = Yii::$app->user->getId() . '.' . $file->extension; - - $file->saveAs(Yii::getAlias('@webroot') . '/' . $this->userPhoto . '/' . $filename); - - return $filename; - } - - return $model->isNewRecord ? false : $model->getOldAttribute('photo'); - } -} +get('patho'); + } + + /** + * @inheritdoc + */ + public function behaviors() + { + return [ + TimestampBehavior::class, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + ['status', 'default', 'value' => self::STATUS_ACTIVE], + ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED, self::STATUS_NOT_ACTIVE]], + [['username', 'password_hash', 'email', 'realname', 'status', 'role'], 'required'], + [['realname', 'report_name', 'email', 'position', 'role', 'tel_x', 'position_no', 'role'], 'string'], + [['hospital_id', 'department_id', 'supervisor_id', 'employment_type_id', 'is_partner'], 'integer'], + [['last_login_at', 'employment_type_id', 'last_update_password'], 'safe'], + [['sign_img'], 'file', 'extensions' => 'jpg, png'], + [['photo'], 'file', 'extensions' => 'jpg,png,jpeg,gif'], + [['username', 'email'], 'unique'], + [['is_change_password'], 'boolean'], + ]; + } + + public function attributeLabels() + { + return [ + 'username' => 'Username', + 'password_hash' => 'Password', + 'email' => 'Email', + 'realname' => 'ชื่อ-นามสกุล', + 'last_login_at' => 'เข้าระบบล่าสุด', + 'status' => 'สถานะ', + 'name_old' => 'ชื่อ-สกุลเดิม', + 'position_no' => 'เลขที่ตำแหน่ง', + 'position' => 'ตำแหน่ง', + 'department_id' => 'หน่วยงาน', + 'employment_type_id' => 'ประเภทบุคลากร', + 'supervisor_id' => 'หัวหน้างาน', + 'role' => 'บทบาทหลัก', + 'tel_x' => 'โทรศัพท์', + 'report_name' => 'ชื่อในใบรายงานผล', + 'sign_img' => 'ลายเซ็นต์', + 'hospital_id' => 'โรงพยาบาล', + 'last_update_password' => 'เปลี่ยนรหัสผ่านล่าสุด', + 'is_change_password' => 'ต้องการเปลี่ยนรหัสผ่าน?', + 'created_at' => 'เพิ่มเมื่อ', + 'updated_at' => 'แก้ไขเมื่อ', + 'photo' => 'รูปประจำตัว' + ]; + } + + public function attributeHints() + { + return [ + 'is_change_password' => 'หากต้องการเปลี่ยนรหัสผ่านให้คลิกที่ Checkbox แล้วใส่รหัสผ่านใหม่ที่ช่อง Password', + ]; + } + + /** + * @inheritdoc + */ + public static function findIdentity($id) + { + return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); + } + + /** + * @inheritdoc + */ + public static function findIdentityByAccessToken($token, $type = null) + { + //throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); + return static::findOne(['auth_key' => $token, 'status' => self::STATUS_ACTIVE]); + } + + /** + * Finds user by username + * + * @param string $username + * @return static|null + */ + public static function findByUsername($username) + { + return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); + } + + public static function findByHospital($username) + { + return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE, 'role' => 'hospital']); + } + + /** + * Finds user by password reset token + * + * @param string $token password reset token + * @return static|null + */ + public static function findByPasswordResetToken($token) + { + if (!static::isPasswordResetTokenValid($token)) { + return null; + } + + return static::findOne([ + 'password_reset_token' => $token, + 'status' => self::STATUS_ACTIVE, + ]); + } + + /** + * Finds out if password reset token is valid + * + * @param string $token password reset token + * @return boolean + */ + public static function isPasswordResetTokenValid($token) + { + if (empty($token)) { + return false; + } + + $timestamp = (int)substr($token, strrpos($token, '_') + 1); + $expire = Yii::$app->params['user.passwordResetTokenExpire']; + return $timestamp + $expire >= time(); + } + + /** + * @inheritdoc + */ + public function getId() + { + return $this->getPrimaryKey(); + } + + /** + * @inheritdoc + */ + public function getAuthKey() + { + return $this->auth_key; + } + + /** + * @inheritdoc + */ + public function validateAuthKey($authKey) + { + return $this->getAuthKey() === $authKey; + } + + /** + * Validates password + * + * @param string $password password to validate + * @return boolean if password provided is valid for current user + */ + public function validatePassword($password) + { + return Yii::$app->security->validatePassword($password, $this->password_hash); + } + + /** + * Generates password hash from password and sets it to the model + * + * @param string $password + */ + public function setPassword($password) + { + $this->password_hash = Yii::$app->security->generatePasswordHash($password); + } + + /** + * Generates "remember me" authentication key + */ + public function generateAuthKey() + { + $this->auth_key = Yii::$app->security->generateRandomString(); + } + + /** + * Generates new password reset token + */ + public function generatePasswordResetToken() + { + $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); + } + + /** + * Removes password reset token + */ + public function removePasswordResetToken() + { + $this->password_reset_token = null; + } + + /* Relational */ + public function getHrDepartment() + { + return $this->hasOne(HrDepartment::class, ['id' => 'department_id']); + } + + public function getHrEmploymentType() + { + return $this->hasOne(HrEmploymentType::class, ['id' => 'employment_type_id']); + } + + public function getHrWorking() + { + return $this->hasOne(HrWorking::class, ['user_id' => 'id']); + } + + public function getEmployee() + { + return $this->hasOne(HrEmployee::class, ['user_id' => 'id']); + } + + public function getHospital() + { + return $this->hasOne(ConstHospital::class, ['id' => 'hospital_id']); + } + + public function uploadFile($model, $attribute) + { + $file = UploadedFile::getInstance($model, $attribute); + if ($file) { + $file_name = substr(md5($file->baseName . time()), 0, 15) . '.' . $file->extension; + $file->saveAs(Yii::getAlias('@webroot') . '/' . $this->uploadFile . '/' . $file_name); + if ($model->isNewRecord) { + return $file_name; + } else { + if (empty($model->sign_img)) { + return $file_name; + } else { + $model->getOldAttribute($attribute); + } + } + } + return $model->isNewRecord ? false : $model->getOldAttribute($attribute); + } + + /** + * @return string + */ + public function getPicture() + { + if (empty($this->photo)) { + return Yii::getAlias('@web') . '/' . $this->userPhoto . '/blank.jpg'; + } else { + return Yii::getAlias('@web') . '/' . $this->userPhoto . '/' . $this->photo; + } + } + + /** + * @param $model + * @return false|string + */ + public function uploadPhoto($model) + { + $file = UploadedFile::getInstance($model, 'photo'); + if ($file) { + $filename = Yii::$app->user->getId() . '.' . $file->extension; + + $file->saveAs(Yii::getAlias('@webroot') . '/' . $this->userPhoto . '/' . $filename); + + return $filename; + } + + return $model->isNewRecord ? false : $model->getOldAttribute('photo'); + } +} diff --git a/console/runtime/.gitignore b/console/runtime/.gitignore index c96a04f0..a3a0c8b5 100755 --- a/console/runtime/.gitignore +++ b/console/runtime/.gitignore @@ -1,2 +1,2 @@ -* +* !.gitignore \ No newline at end of file