*/ protected array $vidalRelationshipMap = []; public function __construct() { $this->db = \Config\Database::connect(); $this->vidal_primary_key = getenv('VIDAL_PRIMARY_KEY_CONSTANT'); $this->ticketController = new TicketController(); $this->claim_type_array = $this->ticketController->claimType; $this->vidalRelationshipMap = self::vidalRelationshipReferenceMap(); } function uploadFileToVidal($filePath, $filename, $claimId = null) { helper('tpa_claim_push_log'); // $apiUrl = "https://devapigw.vidalhealthtpa.com/partner-integration/api/files/upload-url"; $apiUrl = getenv('VIDAL_API_BASE_URL').'/files/upload-url'; $subscriptionKey = getenv('VIDAL_API_SUBSCRIPTION_KEY'); tpa_claim_push_log($claimId, "VIDAL - Claim Push | Starting file upload process for filename: $filename | Path: $filePath"); // Step 1: Get signed URL from Vidal API // $filePath = '/opt/lampp/htdocs/nhance/writable/uploads/claim_files/1760013019_73d94af7b96ddc2e3d51.png'; $payload = json_encode(["scope" => 'document type' , 'fileName' => $filename]); $headers = [ "Content-Type: application/json", "Ocp-Apim-Subscription-Key: $subscriptionKey" ]; tpa_claim_push_log($claimId, "VIDAL - Claim Push | Requesting signed URL from Vidal API: $apiUrl | Payload: $payload"); $ch = curl_init($apiUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); if (curl_errno($ch)) { tpa_claim_push_log($claimId, "VIDAL - Claim Push Curl error while requesting signed URL: " . curl_error($ch)); return ["status" => false, "message" => curl_error($ch)]; } curl_close($ch); $responseData = json_decode($response, true); if (!isset($responseData['data']['signedUrl']) || !isset($responseData['data']['fileId'])) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | Invalid signed URL response received: " . json_encode($responseData)); return ["status" => false, "message" => "Invalid signed URL response", "response" => $responseData]; } $signedUrl = $responseData['data']['signedUrl']; $fileId = $responseData['data']['fileId']; tpa_claim_push_log($claimId, "VIDAL - Claim Push | Received signed URL & fileId. fileId: $fileId"); // Step 2: Upload file to signed URL using PUT (Azure Blob) $fileSize = filesize($filePath); $fileContent = fopen($filePath, 'r'); $ch2 = curl_init($signedUrl); curl_setopt($ch2, CURLOPT_PUT, true); curl_setopt($ch2, CURLOPT_INFILE, $fileContent); curl_setopt($ch2, CURLOPT_INFILESIZE, $fileSize); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch2, CURLOPT_HTTPHEADER, [ "x-ms-blob-type: BlockBlob", "Content-Length: $fileSize" , "Content-Type: multipart/form-data" ]); $uploadResponse = curl_exec($ch2); $httpCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE); $curlErr = curl_error($ch2); fclose($fileContent); curl_close($ch2); if ($curlErr) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | Curl error during file upload: $curlErr"); return ["status" => false, "message" => "File upload failed", "data" => $curlErr]; } if ($httpCode !== 200 && $httpCode !== 201) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse"); return [ "status" => false, "message" => "File upload failed", "httpCode" => $httpCode, "response" => $uploadResponse ]; } // Step 3: Return File ID for reference return [ "status" => true, "message" => "File uploaded successfully", "fileId" => $fileId, "signedUrl" => $signedUrl, "uploadResponse" => json_decode($uploadResponse, true) ]; } public function SubmitClaim ($claimId = null) //515 { helper(['api', 'tpa_claim_push_log', 'utility']); // Fetch the data from DB $data = $this->db->table('ticket_master tm') ->select(' tm.id, tm.emp_mobile as mobileNo, tm.emp_mail as emailId, tm.doa as admissionDate, tm.dod as dischargeDate, tm.hospital_name as hospitalName, tm.hospital_address as hospitalAddress, tm.hospital_state as hospitalState, tm.hospital_city as hospitalCity, tm.hospital_pin_code as hospitalPinCode, tm.hospital_phone_no as hospitalPhoneNo, tm.claim_amount as requestedAmount, tm.claim_type, tm.tpa_no as dependentUniqueId, cp.policy_no as policyNo, e.emp_code as memberId, tn.note as disease, tn.note as reasonForHospitalization, cf.id as cfFileId, cf.url as fileName, cf.url as filePath, pt.policy_type as typeOfClaim, ep.tpa_id as empanelmentNo, ') ->join('employees e', 'e.id = tm.emp_id', 'left') ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') ->join('policy_type pt', 'pt.id = cp.policy_type_id', 'left') ->join('employee_polices ep', 'ep.employee_id = e.id AND ep.client_policy_id = cp.id', 'left') ->join('ticket_notes tn', 'tn.ticket_id = tm.id', 'left') ->join('claim_files cf', "cf.ticket_id = tm.id AND cf.file_type = 4 AND cf.is_active = 1 AND cf.mime_type = 'application/pdf'", 'left') ->where('tm.id', $claimId) ->get() ->getRowArray(); // single record if (count($data) && $data['filePath'] == null) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | Submit claim failed - Claim or File Missing"); return ['status' => false, 'message' => 'Claim Push FAILED | Claim or File Missing']; } $filePath = $data['filePath'] ?? ''; $filename = basename($filePath); $resolved = storage_resolve_claim_file_path(WRITEPATH . 'uploads/claim_files', $filename); if (!($resolved['success'] ?? false) || empty($resolved['path'])) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | Submit claim failed - File not found in local/S3: {$filename}"); return $this->response->setJSON([ 'status' => false, 'message' => 'File upload failed', 'data' => ['message' => 'Claim file not found in storage'], ]); } // dd($data); // Upload file first $upload = $this->uploadFileToVidal($resolved['path'], $filename, $claimId); storage_cleanup_temp_claim_file($resolved); if ($upload['status'] !== true) { tpa_claim_push_log($claimId, "VIDAL - Claim Push | Submit claim failed - File upload failed"); return $this->response->setJSON([ 'status' => false, 'message' => 'File upload failed', 'data' => $upload ]); } $fileId = $upload['fileId']; $tpa_sent_file_id = $data['cfFileId'] ?? null; // $url = 'https://devapigw.vidalhealthtpa.com/partner-integration/api/claims/submit'; $url = getenv('VIDAL_API_BASE_URL').'/claims/submit'; $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key:' .getenv('VIDAL_API_SUBSCRIPTION_KEY').'', ]; $typeOfClaim = "Main hospitalization claim"; $allowedSubTypes = [ 'Hospitalization', 'OPD', 'Health check-up', 'Dental benefit', 'Day care', 'Domiciliary' ]; $mappedSubType = $this->claim_type_array[1][$data['claim_type'] ?? null] ?? null; $claimSubType = in_array($mappedSubType, $allowedSubTypes, true) ? $mappedSubType : 'Hospitalization'; // safe default $body = [ 'policyNo' => $data['policyNo'], 'dependentUniqueId' => $data['dependentUniqueId'], 'typeOfClaim' => $typeOfClaim, 'claimSubType' => $claimSubType, 'requestedAmount' => $data['requestedAmount'], 'ailmentType' => "Non covid", 'admissionDate' => change_date_format($data['admissionDate'], 'Y-m-d', 'd-m-Y'), 'dischargeDate' => change_date_format($data['dischargeDate'], 'Y-m-d', 'd-m-Y'), 'hospitalName' => $data['hospitalName'], 'empanelmentNo' => 0, "ailmentName" => "hospitalization", "hospitalAddress" => $data['hospitalAddress'] ?? null, "hospitalState" => $data['hospitalState'] ?? null, "hospitalCity" => $data['hospitalCity'] ?? null, "hospitalPinCode" => $data['hospitalPinCode'] ?? null, "hospitalPhoneNo" => $data['hospitalPhoneNo'] ?? null, "fileId" => $fileId, "bankDetails" => [ "accountHolderName" => null, "accountType" => null, "accountNo" => null, "ifscCode" => null, ], ]; // dd($body); tpa_claim_push_log($claimId, 'VIDAL - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); // $body = [ // 'policyNo' => "351500/D0534/PP/20-20/PC", // 'dependentUniqueId' => "EN000000182-C-41", // 'typeOfClaim' => "Main hospitalization claim", // 'claimSubType' => "Hospitalization", // 'requestedAmount' => "2500", // 'ailmentType' => "Non covid", // 'admissionDate' => "01-12-2025", // 'dischargeDate' => "01-12-2025", // 'hospitalName' => "AKSHAY SUPER SPECIALITY HOSPITAL", // 'empanelmentNo' => "HOS-MUM-031423", // "ailmentName" => "Cold", // "hospitalAddress" => "No. 6, Officers Colony, Puthur.,dfgdfgdfg,dfgdfg,560058", // "hospitalState" => "karnataka", // "hospitalCity" => "bangalore", // "hospitalPinCode" => "560036", // "hospitalPhoneNo" => "1234567890", // "bankDetails" => [ // "accountHolderName" =>"Testing claim", // "accountType" =>"savings", // "accountNo" =>"1234567890", // "ifscCode" =>"ICICI098768" // ], // "fileId" => "https://devapigw.vidalhealthtpa.com/doc-storage/api/private/691eda388973c60b83f80568" // ]; $response = call_third_party_api($url, $method, $headers, $body); if($response['status'] != true){ tpa_claim_push_log($claimId, 'VIDAL - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); $this->db->table('ticket_master') ->where('id',$claimId) ->update([ 'tpa_push_response' => json_encode($response) ]); return ['status' => false, 'message' => 'Claim Push FAILED | API call failed']; } // return $this->response->setJSON($response); if($response['data']['status'] == 'SUCCESS') { $claimNO = $response['data']['data']['claimNo'] ?? null; $claimInwardNO = $response['data']['data']['claimInwardNo'] ?? null; if(!empty($claimNO) && !empty($claimInwardNO)){ tpa_claim_push_log($claimId, 'VIDAL - Claim Push SUCCESS | claimId: '.$claimId.' | claimNO: '.$claimNO.' | claimInwardNO: '.$claimInwardNO); $this->db->table('ticket_master') ->where('id',$claimId) ->update([ 'tpa_claim_push_reference_no' => $claimInwardNO , 'tpa_claim_id' => $claimNO , 'claim_number' => $claimNO ]); // Update claim files table that file is sent to tpa for this claim if(!empty($tpa_sent_file_id)){ $this->db->table('claim_files') ->where('id', $tpa_sent_file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); tpa_claim_push_log($claimId, 'VIDAL - Claim Push | Updating claim_files table for file_id: '.$tpa_sent_file_id); }else{ tpa_claim_push_log($claimId, 'VIDAL - Claim Push | No file_id found to update claim_files table.'); } return ['status' => true, 'message' => 'Claim Push SUCCESS']; } else { tpa_claim_push_log($claimId, 'VIDAL - Claim Push API SUCCESS BUT claimNO,claimInwardNO EMPTY | claimId: '.$claimId.' | response: '.json_encode($response)); return ['status' => false, 'message' => 'Claim Push API SUCCESS BUT claimNO,claimInwardNO EMPTY']; } }else{ tpa_claim_push_log($claimId, 'VIDAL - Claim Push API FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); return ['status' => false, 'message' => 'Claim Push API FAILED']; } } function getWellnessSSORedirectUrl($email = 'test@getvisitapp.com') { log_message('info', "SSO: Starting authentication for email: $email"); // ---------- CONFIG ---------- $authUrl = env('VIDAL_WELLNESS_BASE_URL'); $subscriptionKey = env('VIDAL_WELLNESS_SUBSCRIPTION_KEY'); $apiVersion = "1"; // Provided Base64 AES key $base64Key = env('VIDAL_WELLNESS_BASE64_KEY'); $key = base64_decode($base64Key); log_message('info', "SSO: Config loaded, Auth URL: $authUrl"); // ---------- STEP 1: Build plaintext payload ---------- $plainPayload = json_encode([ "email" => $email, // "corporateId" => env('VIDAL_WELLNESS_CORPORATE_ID'), "urlIdentifier" => env('VIDAL_WELLNESS_URL_IDENTIFIER') ]); // ---------- STEP 2: Encrypt payload ---------- $iv = random_bytes(16); $encryptedRaw = openssl_encrypt($plainPayload, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv); $encryptedPayload = base64_encode($iv) . ":" . base64_encode($encryptedRaw); log_message('info', "SSO: Payload encrypted successfully"); // ---------- STEP 3: Call Authentication API ---------- $requestBody = json_encode([ "payload" => $encryptedPayload, "source" => env('VIDAL_WELLNESS_SUB_PARTNER_ID'), "subPartnerId" => env('VIDAL_WELLNESS_SUB_PARTNER_ID') ]); $headers = [ "Ocp-Apim-Subscription-Key: $subscriptionKey", "apiver: $apiVersion", "mode: encrypt", "Content-Type: application/json" ]; $ch = curl_init($authUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $apiResponse = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); log_message('info', "SSO: API response received, HTTP Code: $httpCode"); // Check for cURL errors if ($curlError) { log_message('error', "SSO: cURL error - $curlError"); return ['status' => 'failed','message' => $curlError]; } // Check HTTP status if ($httpCode !== 200) { log_message('error', "SSO: HTTP error - Code: $httpCode, Response: $apiResponse"); return ['status' => 'failed','message' => "HTTP error: $httpCode" , "response" => $apiResponse ]; } $jsonResponse = json_decode($apiResponse, true); // Check JSON decode error if (json_last_error() !== JSON_ERROR_NONE) { log_message('error', "SSO: JSON decode error - " . json_last_error_msg()); return ['status' => 'failed','message' => "JSON decode error: " . json_last_error_msg(), "response" => $apiResponse]; } // Check API response status if (!isset($jsonResponse["status"]) || $jsonResponse["status"] !== "success") { log_message('error', "SSO: API error - " . json_encode($jsonResponse)); return ['status' => 'failed','message' => "API error", "response" => $jsonResponse]; } if (!isset($jsonResponse["data"])) { log_message('error', "SSO: Missing data field in response"); return ['status' => 'failed','message' => "Invalid API response - missing data field", "response" => $jsonResponse]; } log_message('info', "SSO: API response validated successfully"); // ---------- STEP 4: Decrypt response ---------- log_message('info', "SSO: Starting response decryption"); $dataParts = explode(":", $jsonResponse["data"]); if (count($dataParts) !== 2) { log_message('error', "SSO: Invalid encrypted data format"); return ['status' => 'failed','message' => "Invalid encrypted data format", "data" => $jsonResponse["data"]]; } list($ivBase64, $cipherBase64) = $dataParts; $respIv = base64_decode($ivBase64); $respCipher = base64_decode($cipherBase64); if ($respIv === false || $respCipher === false) { log_message('error', "SSO: Base64 decode error"); return ['status' => 'failed','message' => "Base64 decode error"]; } $decryptedJson = openssl_decrypt($respCipher, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $respIv); if ($decryptedJson === false) { log_message('error', "SSO: Decryption failed"); return ['status' => 'failed','message' => "Decryption failed"]; } $decryptedData = json_decode($decryptedJson, true); if (json_last_error() !== JSON_ERROR_NONE) { log_message('error', "SSO: Decrypted JSON decode error - " . json_last_error_msg()); return ['status' => 'failed','message' => "Decrypted JSON decode error: " . json_last_error_msg()]; } if (!isset($decryptedData["redirectUrl"])) { log_message('error', "SSO: redirectUrl missing in decrypted data"); return ['status' => 'failed','message' => "redirectUrl missing", "decrypted" => $decryptedData]; } // ---------- FINAL ---------- log_message('info', "SSO: Authentication successful, redirectUrl obtained"); return ['status' => 'success','data' => $decryptedData["redirectUrl"]]; } public function ClaimDetail($claimId = null) //234 { helper('api'); $url = getenv('VIDAL_API_BASE_URL').'/claims/claim-dependent-info'; $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key:' .getenv('VIDAL_API_SUBSCRIPTION_KEY').'', ]; // Fetch ticket master details $ticket = $this->db->table('ticket_master tm') ->select(" tm.id, tm.doa, tm.tpa_no as memberId, tm.tpa_claim_push_reference_no as claimRefNo, tm.tpa_claim_id as claimID, tm.doa, cp.policy_no as policyNo, cp.policy_start_date as startDate, cp.policy_end_date as endDate, e.emp_code as employeeCode ") ->join('employees e', 'e.id = tm.emp_id', 'left') ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') ->where('tm.id', $claimId) ->get() ->getRowArray(); if (!$ticket) { return ['status' => false,'message' => 'Invalid Claim ID' ]; } $body = []; // REQUEST BODY if($ticket['claimID'] != null) { $body = [ 'empNO' => "", 'tpaCardID' => "", 'claimID' => $ticket['claimID'], //"GUR-0326-CL-0001471" 'emailID' => "", 'mobileNO' => "", ]; } $response = call_third_party_api($url, $method, $headers, $body); // dd($url, $method, $headers, $body, $response); if ($response['status'] != true || empty($response['data']['data']['claims'][0])) { log_message('error', 'VIDAL - Claim Status FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response)); return ['status' => false, 'message' => 'API call failed.','data' => $response]; } // Extract claim status // $claimData = $response['data']['data']['claims'][0]; $allClaimData = $response['data']['data']['claims']; $currentStatus = ""; foreach ($allClaimData as $claimData) { $tpa_claim_no = $claimData['claimNumber'] ?? ''; $currentStatus = $claimData['status'] ?? ''; $tpa_claim_type = $claimData['claimType'] ?? ''; $tpa_shortfall_no = $claimData['shortfallNo'] ?? ''; $doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null; // VALID STATUS LIST $validStatuses = [ "In-Progress" => 5, "Required Information" => 4, "Paid" => 11, "Rejected" => 8, "Approved" => 8, ]; $updateArray = [ 'tpa_claim_status' => $currentStatus, 'tpa_claim_id' => $tpa_claim_no, // 'claim_number' => $tpa_claim_no, // already updated 'updated_at' => date('Y-m-d H:i:s'), 'last_updated_by' => 'API', ]; if (isset($validStatuses[$currentStatus])) { $updateArray['claim_status_id'] = $validStatuses[$currentStatus]; } if (!empty($tpa_claim_type)) { $updateArray['tpa_claim_type'] = $tpa_claim_type; } if (!empty($tpa_shortfall_no)) { $updateArray['tpa_shortfall_no'] = $tpa_shortfall_no; } if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){ // UPDATE ticket_master $this->db->table('ticket_master')->where('id', $claimId)->update($updateArray); // LOG UPDATE log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus"); }else{ log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}"); } } return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response]; } public function ClaimStatusUpdate() { helper('api'); $url = getenv('VIDAL_API_BASE_URL').'/claims/claim-dependent-info'; $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key:' .getenv('VIDAL_API_SUBSCRIPTION_KEY').'', ]; // Fetch ticket master details $TicketData = $this->db->table('ticket_master tm') ->select(" tm.id, tm.doa, tm.tpa_no as memberId, tm.tpa_claim_push_reference_no as claimRefNo, tm.tpa_claim_id as claimNo, cp.policy_no as policyNo, cp.policy_start_date as startDate, cp.policy_end_date as endDate, e.emp_code as employeeCode ") ->join('employees e', 'e.id = tm.emp_id', 'left') ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') ->where('tm.tpa_claim_push_reference_no IS NOT NULL') ->where('tm.is_active', 1) ->whereNotIn('tm.claim_status_id', [8, 11, 12, 13, 66, 22, 24, 47, 49, 32, 34, 53, 55, 42, 44, 58, 60]) ->where('tm.tpa_id', $this->vidal_primary_key) ->get() ->getResultArray(); // dd($TicketData); log_message('error', "VIDAL - Claim Status | Total tickets fetched for status update: " . count($TicketData)); if (!$TicketData) { log_message('error', "VIDAL - Claim Status | Claims not found to update status"); return $this->response->setJSON(['status' => false,'message' => 'Claims not found' ]); } $error_data = []; $status_updated_count = 0; foreach ($TicketData as $key => $ticket) { $claimId = $ticket['id']; $body = []; // REQUEST BODY if($ticket['claimNo'] != null) { $body = [ 'empNO' => "", 'tpaCardID' => "", 'claimID' => $ticket['claimNo'], 'emailID' => "", 'mobileNO' => "", ]; } // CALL API $response = call_third_party_api($url, $method, $headers, $body); if ($response['status'] != true || empty($response['data']['data']['claims'][0])) { log_message('error', 'VIDAL - Claim Status FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response)); $error_data[$claimId] = [ 'status' => false, 'message' => 'API call failed.', 'data' => $response ]; continue; } // Extract claim status // $claimData = $response['data']['data']['claims'][0]; $allClaimData = $response['data']['data']['claims']; foreach ($allClaimData as $claimData) { $tpa_claim_no = $claimData['claimNumber'] ?? ''; $currentStatus = $claimData['status'] ?? ''; $tpa_claim_type = $claimData['claimType'] ?? ''; $doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null; // VALID STATUS LIST $validStatuses = [ "In-Progress" => 5, "Required Information" => 4, "Paid" => 11, "Rejected" => 8, "Approved" => 8, ]; $updateArray = [ 'tpa_claim_status' => $currentStatus, // 'claim_number' => $tpa_claim_no, // already updated 'updated_at' => date('Y-m-d H:i:s'), 'last_updated_by' => 'API', ]; if (isset($validStatuses[$currentStatus])) { $updateArray['claim_status_id'] = $validStatuses[$currentStatus]; } if (!empty($tpa_claim_type)) { $updateArray['tpa_claim_type'] = $tpa_claim_type; } if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) { $updateArray['tpa_claim_id'] = $tpa_claim_no; } if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) { $updateArray['claim_number'] = $tpa_claim_no; } // UPDATE ticket_master if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){ $this->db->table('ticket_master')->where('id', $claimId)->update($updateArray); // LOG UPDATE log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus"); $status_updated_count ++; }else{ log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}"); } } } log_message('error', "VIDAL - Claim Status ENDED | Total Tickets={".count($TicketData)."} | Status Updated={$status_updated_count} | Errors={".count($error_data)."}"); return $this->response->setJSON([ 'status' => true, 'message' => 'Claim status updated.', 'updated_status' => $currentStatus, 'api_response' => $response, 'count' => $status_updated_count, 'error_data' => $error_data, ]); } public function EcardRequest($employeeId = null, $policyNo = null , $tpaNo = null) { helper('api'); $url = getenv('VIDAL_API_BASE_URL').'/claims/claim-dependent-info'; $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key:' .getenv('VIDAL_API_SUBSCRIPTION_KEY').'', ]; $body = [ 'empNO' => "", 'tpaCardID' => $tpaNo, 'claimID' => "", 'emailID' => "", 'mobileNO' => "", ]; // $body = [ // 'empNO' => "", // 'tpaCardID' => "BLR-NI-H0351-03925-0030320-A", // 'claimID' => "", // 'emailID' => "", // 'mobileNO' => "", // ]; $response = call_third_party_api($url, $method, $headers, $body); // dd($response); if($response['status'] != true){ log_message('error', 'VIDAL - Ecard Request FAILED | employeeId: '.$employeeId.' | policyNo: '.$policyNo.' | response: '.json_encode($response)); return null; } $ecardUrl = $response['data']['data']['dependents'][0]['ecardLink'] ?? null; if(!empty($ecardUrl)){ log_message('error', 'VIDAL - Ecard Request PUSH SUCCESS | employeeId: '.$employeeId.' | policyNo: '.$policyNo.' | ecardUrl: '.$ecardUrl); return $ecardUrl; } else { log_message('error', 'VIDAL - Ecard Request SUCCESS BUT ecardUrl EMPTY | employeeId: '.$employeeId.' | policyNo: '.$policyNo.' | response: '.json_encode($response)); return null; } } public function VidalGetBenefDetails($requestData) { $function_calling_type = $requestData['return_type'] ?? 'job'; try { helper('api'); $url = getenv('VIDAL_API_BASE_URL').'/claims/claim-dependent-info'; $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key:' .getenv('VIDAL_API_SUBSCRIPTION_KEY').'', ]; $policyNo = $requestData['policy_no'] ?? null; $client_policy_id = $requestData['client_policy_id'] ?? null; if (empty($policyNo)) { log_message('error', 'VIDAL - TPA ID Pull | policy_no missing in request'); if($function_calling_type == "job"){ return ['status' => false, 'message' => 'policy_no required']; }else{ return $this->respond(['status' => false, 'message' => 'policy_no required']); } } if (empty($client_policy_id)) { log_message('error', 'VIDAL - TPA ID Pull | client_policy_id missing in request'); if($function_calling_type == "job"){ return ['status' => false, 'message' => 'client_policy_id required']; }else{ return $this->respond(['status' => false, 'message' => 'client_policy_id required']); } } log_message('error', "VIDAL - TPA ID Pull | called for policy_no: {$policyNo} , client_policy_id: {$client_policy_id}"); // Fetch file download dates $batchFiles = $this->db->table('batch_files f') ->select("f.created_at") ->where('f.client_policy_id', $client_policy_id) ->where('f.insurer_or_tpa', 'tpa') ->where('f.actions', 'export') ->get() ->getResultArray(); // dd($batchFiles); if (empty($batchFiles)) { log_message('error', 'VIDAL - TPA ID Pull FAILED | batchFiles is empty for this tpa id pull request'); $file_model = new BatchFileModel(); $bfData = [ 'error_data' => json_encode(['error_data' => 'No trace found for TPA member download (export).']), 'status' => 'failed-7', ]; $file_model->where('id', $requestData['file_id'])->set($bfData)->update(); if($function_calling_type == "job"){ return ['status' => false, 'message' => 'batchFiles not found']; }else{ return $this->respond(['status' => false, 'message' => 'batchFiles not found']); } } $allBenef = []; foreach ($batchFiles as $key => $value) { $createdAt = new \DateTime($value['created_at']); $startDate = $createdAt->format('d/m/Y'); $endDateObj = clone $createdAt; $endDateObj->modify('+2 days'); $endDate = $endDateObj->format('d/m/Y'); $body = [ 'policyNO' => $policyNo, 'startDate' => $startDate, 'endDate' => $endDate ]; // $body = [ 'empNO' => "Mem 1" ]; log_message('error', "VIDAL - TPA ID Pull | API parems " . json_encode([$url, $method, $headers, $body])); $response = call_third_party_api($url, $method, $headers, $body); if ($response['status'] != true) { // update file table status after the tpa id failed to update if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); log_message('error', "VIDAL - TPA ID Pull | Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', "VIDAL - TPA ID Pull | Failed to update file table status."); } log_message('error', 'VIDAL - TPA ID Pull API FAILED | API failed: ' . json_encode($response)); if($function_calling_type == "job"){ return ['status' => false, 'message' => 'API call failed', 'data' => $response]; }else{ return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $response]); } } $data = $response['data']['data'] ?? []; if (!isset($data['dependents'])) { log_message('error', "VIDAL - TPA ID Pull FAILED |: 'benefDetails' missing in API response: " . json_encode($data)); break; } $fetchedCount = count($data['dependents']); log_message('error', "VIDAL - TPA ID Pull | Fetched {$fetchedCount} records "); $allBenef = array_merge($allBenef, $data['dependents']); } // dd($allBenef ); //save API data as JSON for analysis $json = json_encode($allBenef, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); // $filename = time() . '.json'; $filePath = WRITEPATH . 'tmp/'.time().'_'.$requestData['file_id'].'.json'; file_put_contents($filePath, $json); //call a job for dump JSON data to DB $job_details = new Jobs(); $r = Jobs::addJob(['job_name' => 'saveVidalAPIData', 'payload' => [ 'file_id' => $requestData['file_id'],'json_file_path' => $filePath ]]); // now update DB $employeePolicyModel = new EmployeePolicyModel(); $employeePolicyData = $employeePolicyModel ->select(' employees.*, employee_polices.id as emp_policy_id, employee_polices.client_policy_id, ') ->join('employees', 'employees.id = employee_polices.employee_id') ->where('employee_polices.is_active', 1) ->where('employee_polices.status', 'active') ->where('employees.is_active', 1) ->where('employees.emp_status', 'active') ->where('employee_polices.tpa_id IS NULL') ->where('employee_polices.client_policy_id', $client_policy_id) ->findAll(); $batch_file_success = 'success'; $updated = 0; $totalCount = count($employeePolicyData); $employee_policy_ids = []; foreach ($employeePolicyData as $policy_data) { $hasMatchForThisPolicy = false; foreach ($allBenef as $row) { if ( strtolower(trim($policy_data['name'] ?? '')) == strtolower(trim($row['name'] ?? '')) && ($policy_data['emp_code'] ?? '') == ($row['empNo'] ?? '') && strtolower(trim($policy_data['relationship'] ?? '')) == strtolower(trim(str_replace('-', ' ', $row['relationship'] ?? ''))) && ($policy_data['gender'] ?? '') == ($row['gender'] ?? '') && ($policy_data['dob'] ?? '') == (change_date_format($row['dob'], 'Y-m-d H:i:s') ?? '') ) { $hasMatchForThisPolicy = true; // log_message('error', "✅ Match found: emp_code={$row['empNo']} policy={$row['policyNumber']}"); $sql = "UPDATE employee_polices SET tpa_id = :tpa_id:, WHERE id = :emp_policy_id:"; $this->db->query($sql, ["tpa_id"=>$row['enrollmentId'], "emp_policy_id"=>$policy_data['emp_policy_id']]); // for e-card send if(strtolower(trim($policy_data['relationship'])) == 'self'){ $employee_policy_ids[] = $policy_data['emp_policy_id']; } if ($this->db->affectedRows() > 0) { $updated++; log_message('error', "VIDAL - TPA ID Pull | Updated tpa_id={$row['enrollmentId']} for emp_code={$row['empNo']} policy={$row['policyNumber']}"); } else { log_message('error', "VIDAL - TPA ID Pull | No update (already set or not matched) for emp_code={$row['empNo']} policy={$row['policyNumber']}"); } } } // Handle NO MATCH for this policy if (!$hasMatchForThisPolicy) { $nhanceSideData = [ 'name' => $policy_data['name'] ?? null, 'emp_code' => $policy_data['emp_code'] ?? null, 'relationship' => $policy_data['relationship'] ?? null, 'gender' => $policy_data['gender'] ?? null, 'dob' => $policy_data['dob'] ?? null, ]; $batch_file_success = 'partially success'; log_message( 'error', "VIDAL - TPA ID Pull | No match for Nhance = " . json_encode($nhanceSideData) ); } } // send e-card if(!empty($employee_policy_ids)){ log_message('error', "sendMailForDownloadingECard JOB PUSHED."); Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $employee_policy_ids, 'client_policy_id' => $client_policy_id]]); } // update file table status after the tpa id successfully updated if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', $batch_file_success)->update(); log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', "Failed to update file table status."); } log_message('error', "VIDAL - TPA ID Pull SUCCESS | completed. Total fetched={$totalCount}, updated={$updated}"); if($function_calling_type == "job"){ return [ 'status' => true, 'message' => 'Updated successfully', 'total_fetched' => $totalCount, 'total_updated' => $updated ]; }else{ return $this->respond([ 'status' => true, 'message' => 'Updated successfully', 'total_fetched' => $totalCount, 'total_updated' => $updated ]); } } catch (\Throwable $th) { // update file table status after the tpa id failed to update if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', "Failed to update file table status."); } $errorData = [ 'message' => $th->getMessage(), 'file' => $th->getFile(), 'line' => $th->getLine(), 'code' => $th->getCode(), 'trace' => $th->getTraceAsString(), 'trace_array' => $th->getTrace(), // full array version (optional) 'function' => $th->getTrace()[0]['function'] ?? null, 'class' => $th->getTrace()[0]['class'] ?? null, ]; log_message('error', 'Exception thrown while calling GetBenefDetails API: ' . json_encode($errorData)); if($function_calling_type == "job"){ return ['status' => false, 'message' => 'API call failed', 'data' => $errorData]; }else{ return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $errorData]); } } } public function VidalGetBenefDetailsV2($requestData = null) { $requestData = is_array($requestData) ? $requestData : []; $function_calling_type = $requestData['return_type'] ?? 'job'; try { helper('api'); $url = $this->vidalEnrollmentInfoApiUrl(); $method = 'POST'; $subscriptionKey = getenv('VIDAL_API_SUBSCRIPTION_KEY'); if (empty($subscriptionKey)) { log_message('error', 'VIDAL V2 - TPA ID Pull | VIDAL_API_SUBSCRIPTION_KEY missing'); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'VIDAL_API_SUBSCRIPTION_KEY required']; } return $this->respond(['status' => false, 'message' => 'VIDAL_API_SUBSCRIPTION_KEY required']); } $headers = [ 'Content-Type: application/json', 'ocp-apim-subscription-key: ' . $subscriptionKey, ]; $policyNo = $requestData['policy_no'] ?? null; $client_policy_id = $requestData['client_policy_id'] ?? null; if (empty($policyNo)) { log_message('error', 'VIDAL V2 - TPA ID Pull | policy_no missing in request'); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'policy_no required']; } return $this->respond(['status' => false, 'message' => 'policy_no required']); } if (empty($client_policy_id)) { log_message('error', 'VIDAL V2 - TPA ID Pull | client_policy_id missing in request'); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'client_policy_id required']; } return $this->respond(['status' => false, 'message' => 'client_policy_id required']); } log_message('error', "VIDAL V2 - TPA ID Pull | called for policy_no: {$policyNo} , client_policy_id: {$client_policy_id}"); $batchFiles = $this->db->table('batch_files f') ->select('f.created_at') ->where('f.client_policy_id', $client_policy_id) ->where('f.insurer_or_tpa', 'tpa') ->where('f.actions', 'export') ->get() ->getResultArray(); if (empty($batchFiles)) { log_message('error', 'VIDAL V2 - TPA ID Pull FAILED | batchFiles is empty for this tpa id pull request'); $file_model = new BatchFileModel(); $bfData = [ 'error_data' => json_encode(['error_data' => 'No trace found for TPA member download (export).']), 'status' => 'failed-7', ]; $file_model->where('id', $requestData['file_id'])->set($bfData)->update(); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'batchFiles not found']; } return $this->respond(['status' => false, 'message' => 'batchFiles not found']); } $pageSize = 100; $startIndex = 1; $allBenef = []; while (true) { $endIndex = $startIndex + $pageSize - 1; $body = [ 'policyNo' => $policyNo, 'startIndex' => $startIndex, 'endIndex' => $endIndex, ]; log_message('error', 'VIDAL V2 - TPA ID Pull | API params ' . json_encode([$url, $method, $headers, $body])); $response = call_third_party_api($url, $method, $headers, $body); if ($response['status'] !== true) { if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); log_message('error', "VIDAL V2 - TPA ID Pull | Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', 'VIDAL V2 - TPA ID Pull | Failed to update file table status.'); } log_message('error', 'VIDAL V2 - TPA ID Pull API FAILED | API failed: ' . json_encode($response)); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'API call failed', 'data' => $response]; } return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $response]); } $apiRoot = $response['data'] ?? []; if (($apiRoot['status'] ?? '') !== 'SUCCESS' || (array_key_exists('successful', $apiRoot) && $apiRoot['successful'] === false)) { log_message('error', 'VIDAL V2 - TPA ID Pull API FAILED | envelope: ' . json_encode($apiRoot)); if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); } if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'API call failed', 'data' => $apiRoot]; } return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $apiRoot]); } $chunk = $apiRoot['data'] ?? null; if (!is_array($chunk)) { log_message('error', 'VIDAL V2 - TPA ID Pull FAILED | data is not an array: ' . json_encode($apiRoot)); break; } if (count($chunk) === 0) { if ($startIndex === 1) { log_message('error', "VIDAL V2 - TPA ID Pull FAILED | empty data on first page"); if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); } if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'No enrollment records returned', 'data' => $apiRoot]; } return $this->respond(['status' => false, 'message' => 'No enrollment records returned', 'data' => $apiRoot]); } break; } foreach ($chunk as $rec) { if (is_array($rec)) { $allBenef[] = $this->normalizeVidalEnrollmentRecordToDependentFormat($rec); } } log_message('error', 'VIDAL V2 - TPA ID Pull | Fetched ' . count($chunk) . ' records (page startIndex=' . $startIndex . ')'); if (count($chunk) < $pageSize) { break; } $startIndex += $pageSize; } $json = json_encode($allBenef, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); $filePath = WRITEPATH . 'tmp/' . time() . '_' . $requestData['file_id'] . '.json'; file_put_contents($filePath, $json); Jobs::addJob(['job_name' => 'saveVidalAPIData', 'payload' => ['file_id' => $requestData['file_id'], 'json_file_path' => $filePath]]); $employeePolicyModel = new EmployeePolicyModel(); $employeePolicyData = $employeePolicyModel ->select(' employees.*, employee_polices.id as emp_policy_id, employee_polices.client_policy_id, ') ->join('employees', 'employees.id = employee_polices.employee_id') ->where('employee_polices.is_active', 1) ->where('employee_polices.status', 'active') ->where('employees.is_active', 1) ->where('employees.emp_status', 'active') ->where('employee_polices.tpa_id IS NULL') ->where('employee_polices.client_policy_id', $client_policy_id) ->findAll(); $batch_file_success = 'success'; $updated = 0; $totalCount = count($employeePolicyData); $employee_policy_ids = []; foreach ($employeePolicyData as $policy_data) { $hasMatchForThisPolicy = false; foreach ($allBenef as $row) { if ( strtolower(trim($policy_data['name'] ?? '')) === strtolower(trim($row['name'] ?? '')) && ($policy_data['emp_code'] ?? '') === ($row['empNo'] ?? '') && strtolower(trim($policy_data['relationship'] ?? '')) === strtolower(trim(str_replace('-', ' ', $row['relationship'] ?? ''))) && ($policy_data['gender'] ?? '') === ($row['gender'] ?? '') && ($policy_data['dob'] ?? '') === (change_date_format($row['dob'], 'Y-m-d H:i:s') ?? '') ) { $hasMatchForThisPolicy = true; $sql = 'UPDATE employee_polices SET tpa_id = :tpa_id: WHERE id = :emp_policy_id:'; $this->db->query($sql, ['tpa_id' => $row['enrollmentId'], 'emp_policy_id' => $policy_data['emp_policy_id']]); if (strtolower(trim($policy_data['relationship'])) === 'self') { $employee_policy_ids[] = $policy_data['emp_policy_id']; } if ($this->db->affectedRows() > 0) { $updated++; log_message('error', "VIDAL V2 - TPA ID Pull | Updated tpa_id={$row['enrollmentId']} for emp_code={$row['empNo']} policy={$row['policyNumber']}"); } else { log_message('error', "VIDAL V2 - TPA ID Pull | No update (already set or not matched) for emp_code={$row['empNo']} policy={$row['policyNumber']}"); } } } if (!$hasMatchForThisPolicy) { $nhanceSideData = [ 'name' => $policy_data['name'] ?? null, 'emp_code' => $policy_data['emp_code'] ?? null, 'relationship' => $policy_data['relationship'] ?? null, 'gender' => $policy_data['gender'] ?? null, 'dob' => $policy_data['dob'] ?? null, ]; $batch_file_success = 'partially success'; log_message( 'error', 'VIDAL V2 - TPA ID Pull | No match for Nhance = ' . json_encode($nhanceSideData) ); } } if (!empty($employee_policy_ids)) { log_message('error', 'sendMailForDownloadingECard JOB PUSHED (V2).'); Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $employee_policy_ids, 'client_policy_id' => $client_policy_id]]); } if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', $batch_file_success)->update(); log_message('error', "VIDAL V2 - Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', 'VIDAL V2 - Failed to update file table status.'); } log_message('error', "VIDAL V2 - TPA ID Pull SUCCESS | completed. Total fetched={$totalCount}, updated={$updated}"); if ($function_calling_type === 'job') { return [ 'status' => true, 'message' => 'Updated successfully', 'total_fetched' => $totalCount, 'total_updated' => $updated, ]; } return $this->respond([ 'status' => true, 'message' => 'Updated successfully', 'total_fetched' => $totalCount, 'total_updated' => $updated, ]); } catch (\Throwable $th) { if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); log_message('error', "VIDAL V2 - Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', 'VIDAL V2 - Failed to update file table status.'); } $errorData = [ 'message' => $th->getMessage(), 'file' => $th->getFile(), 'line' => $th->getLine(), 'code' => $th->getCode(), 'trace' => $th->getTraceAsString(), 'trace_array' => $th->getTrace(), 'function' => $th->getTrace()[0]['function'] ?? null, 'class' => $th->getTrace()[0]['class'] ?? null, ]; log_message('error', 'VIDAL V2 - Exception thrown while calling GetBenefDetailsV2 API: ' . json_encode($errorData)); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'API call failed', 'data' => $errorData]; } return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $errorData]); } } /** * Nhance relationship => list of Vidal `relation` labels (any case). Edit the grouped list only; the flat map is built here. * Reference: public/tmp/relationship.csv (documentation only). * * @return array lowercase Vidal label (and hyphen/space variant) => Nhance relation key */ private static function vidalRelationshipReferenceMap(): array { $relationshipGrouped = [ 'self' => [ 'SELF', 'EMPLOYER', 'EMPLOYEE', 'EMPLOYEES', ], 'spouse' => [ 'SPOUSE', 'PARTNER', 'HUSBAND', 'HUSBAND (2)', 'HUSBAND (3)', 'HUSBAND (4)', 'HUSBAND (5)', 'WIFE', 'WIFE (2)', 'WIFE (3)', 'WIFE (4)', 'WIFE (5)', ], 'father' => [ 'FATHER', 'FATHER (2)', 'FATHER (3)', 'FATHER (4)', 'FATHER (5)', ], 'mother' => [ 'MOTHER', 'MOTHER (2)', 'MOTHER (3)', 'MOTHER (4)', 'MOTHER (5)', ], 'son' => [ 'SON', 'SON (2)', 'SON (3)', 'SON (4)', 'SON (5)', ], 'daughter' => [ 'DAUGHTER', 'DAUGHTER (2)', 'DAUGHTER (3)', 'DAUGHTER (4)', 'DAUGHTER (5)', ], 'father in law' => [ 'FATHER-IN-LAW', 'FATHER-IN-LAW (2)', ], 'mother in law' => [ 'MOTHER-IN-LAW', 'MOTHER-IN-LAW (2)', ], ]; return self::flattenVidalRelationshipGroupedToLookupMap($relationshipGrouped); } /** * @param array> $grouped * * @return array */ private static function flattenVidalRelationshipGroupedToLookupMap(array $grouped): array { $map = []; foreach ($grouped as $nhanceRelation => $vidalLabels) { foreach ($vidalLabels as $label) { $label = trim((string) $label); if ($label === '') { continue; } $variants = [ strtolower($label), strtolower(str_replace('-', ' ', $label)), ]; foreach (array_unique($variants) as $key) { $key = trim(preg_replace('/\s+/', ' ', $key)); if ($key === '') { continue; } $map[$key] = $nhanceRelation; } } } return $map; } /** * Map Vidal enrollment `relation` text to Nhance `employees.relationship` / `tpa_api_data.relation` style (lowercase). */ private function mapVidalRelationshipToNhance(?string $vidalRelationDescription): string { $raw = trim((string) $vidalRelationDescription); if ($raw === '') { return ''; } foreach (self::vidalRelationLookupKeyVariants($raw) as $key) { if (isset($this->vidalRelationshipMap[$key])) { return $this->vidalRelationshipMap[$key]; } } if (strcasecmp($raw, 'Employee') === 0 || strcasecmp($raw, 'Employees') === 0) { return 'self'; } return strtolower(str_replace('-', ' ', $raw)); } /** * Keys must stay in sync with {@see self::flattenVidalRelationshipGroupedToLookupMap()}. * * @return list */ private static function vidalRelationLookupKeyVariants(string $raw): array { $base = strtolower(trim($raw)); $withHyphensAsSpaces = strtolower(str_replace('-', ' ', $base)); $collapsed = trim(preg_replace('/\s+/', ' ', $withHyphensAsSpaces)); return array_values(array_unique(array_filter([$base, $collapsed]))); } private function normalizeVidalEnrollmentDateToYmd($value): ?string { if ($value === null || $value === '') { return null; } if (is_numeric($value)) { return null; } $s = trim((string) $value); $ts = strtotime(str_replace('/', '-', $s)); return $ts ? date('Y-m-d', $ts) : null; } /** * Map Enrollment Dump API row (see public/tmp/Enrollment Dump API.docx) to the dependent * shape used by VidalGetBenefDetailsV2 matching and saveVidalAPIData. */ private function normalizeVidalEnrollmentRecordToDependentFormat(array $row): array { $rel = trim((string) ($row['relation'] ?? '')); $relationship = $this->mapVidalRelationshipToNhance($rel); $si = $row['baseSumInsured'] ?? null; $si = $si !== null && $si !== '' ? trim((string) $si) : null; return [ 'name' => trim((string) ($row['beneficiaryName'] ?? '')), 'empNo' => trim((string) ($row['employeeNo'] ?? '')), 'relationship' => $relationship, 'vidal_relation_raw' => $rel, 'gender' => $row['gender'] ?? '', 'dob' => $row['dateOfBirth'] ?? null, 'enrollmentId' => trim((string) ($row['membershipNo'] ?? '')), 'policyNumber' => trim((string) ($row['policyNumber'] ?? '')), 'age' => $row['age'] ?? null, 'si' => $si, 'doj' => $this->normalizeVidalEnrollmentDateToYmd($row['dateOfJoining'] ?? null), 'desc' => $this->buildVidalEnrollmentDescForTpaRow($row), ]; } /** * Fills tpa_api_data.desc from enrollment fields (product / remarks / insured name). */ private function buildVidalEnrollmentDescForTpaRow(array $row): ?string { $chunks = array_filter([ trim((string) ($row['productName'] ?? '')), trim((string) ($row['remarks'] ?? '')), trim((string) ($row['insuredName'] ?? '')), ], static fn ($v) => $v !== ''); if ($chunks === []) { return null; } return substr(implode(' | ', $chunks), 0, 65000); } private function vidalEnrollmentInfoApiUrl(): string { $base = rtrim((string) getenv('VIDAL_API_BASE_URL'), '/'); if ($base !== '' && preg_match('#/api$#', $base)) { return preg_replace('#/api$#', '', $base) . '/enrollment/info'; } return 'https://devapigw.vidalhealthtpa.com/partner-integration/enrollment/info'; } public function saveVidalAPIData($array) { $file_id = $array['file_id']; $json = file_get_contents($array['json_file_path']); $records = json_decode($json, true); // log_message('error','FHPL - saveFhplAPIData' . json_encode($array));//die(); $file_model = new BatchFileModel(); // $file_model = model(BatchFileModel::class); $file_info = $file_model->where('id', $file_id)->find(); // $tpaApiDataModel = new TpaApiDataModel(); $tpaApiDataModel = model(TpaApiDataModel::class); //deactivate old data $tpaApiDataModel->set('is_active', 0)->where('file_id', $file_id)->update(); //covert tpa data to our model data $mappedRows = []; foreach ($records as $row) { $rawVidalRel = trim((string) ($row['vidal_relation_raw'] ?? '')); if ($rawVidalRel !== '') { $relation = $this->mapVidalRelationshipToNhance($rawVidalRel); } else { $relation = trim(strtolower((string) ($row['relationship'] ?? ''))); } $si = $row['si'] ?? null; $si = $si !== null && $si !== '' ? trim((string) $si) : null; $doj = null; if (!empty($row['doj'])) { $dojRaw = $row['doj']; if (is_string($dojRaw) && preg_match('/^\d{4}-\d{2}-\d{2}/', $dojRaw)) { $doj = substr($dojRaw, 0, 10); } else { $doj = $this->normalizeVidalEnrollmentDateToYmd($dojRaw); } } $descPieces = []; if ($rawVidalRel !== '') { $descPieces[] = 'Vidal relation: ' . $rawVidalRel; } $jsonDesc = trim((string) ($row['desc'] ?? '')); if ($jsonDesc !== '') { $descPieces[] = $jsonDesc; } $desc = $descPieces !== [] ? substr(implode(' | ', $descPieces), 0, 65000) : null; $mappedRows[] = [ 'file_id' => $file_id, 'emp_code' => trim($row['empNo'] ?? ''), 'name' => trim($row['name'] ?? ''), 'dob' => !empty($row['dob'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $row['dob']))) : null, 'relation' => $relation, 'gender' => format_gender_v2($row['gender'] ?? null), 'self' => $relation === 'self' ? 1 : 0, 'tpa_id' => trim($row['enrollmentId'] ?? null), 'age' => is_numeric($row['age'] ?? null) ? (int) $row['age'] : null, 'desc' => $desc, 'si' => $si, 'doj' => $doj, 'is_active' => 1, 'created_by' => $file_info[0]['created_by'] ?? null, 'action_flag_status' => $row['isDeleted'] == 0 ? 'D' : 'A' ]; } // log_message('error','FHPL - COUNT' . count($mappedRows)); $result = $tpaApiDataModel->insertBatchWithChunkLog($mappedRows,500,('FILE_ID_'.$file_id)); // unlink($file_array['json_file_path']); // delete temp json file return $result; } public function IRSubmission($claimId = null) { helper('utility'); log_message('error', "VIDAL - IR Submission | INIT for ticket_id={$claimId}"); // 1. FETCH TICKET DETAILS $ticket = $this->db->table('ticket_master tm') ->select(" tm.id, tm.doa, tm.tpa_no as memberId, tm.tpa_claim_push_reference_no as claimInwardNO, tm.tpa_claim_id as claimNO, tm.tpa_shortfall_no as shortfallNO, cp.policy_no as policyNo, cp.policy_start_date as startDate, cp.policy_end_date as endDate, e.emp_code as employeeCode ") ->join('employees e', 'e.id = tm.emp_id', 'left') ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') ->where('tm.id', $claimId) ->get() ->getRowArray(); if (!$ticket || empty($ticket['claimNO'])) { log_message('error', "VIDAL - IR Submission FAILED → ClaimID NOT FOUND for ticket_id={$claimId}"); return [ 'status' => false, 'message' => "ClaimID not found for ticket {$claimId}" ]; } // 2. FETCH IR ATTACHMENTS $fileData = $this->db->table('claim_files f') ->where('f.ticket_id', $claimId) ->where('f.docs_for_ir', 1) ->where('f.file_type', 2) ->where('f.is_file_sent_to_tpa', 0) ->get() ->getResultArray(); if (empty($fileData)) { log_message('error', "VIDAL - IR Submission FAILED → No IR attachments found for ticket_id={$claimId}"); return [ 'status' => false, 'message' => 'IR attachments not found for the claim' ]; } // 3. UPLOAD FILES TO VIDAL $fileIdList = []; $tpaSentFileIds = []; foreach ($fileData as $file) { $storedPath = (string) ($file['filePath'] ?? $file['url'] ?? ''); if ($storedPath === '') { continue; } $filename = basename($storedPath); $resolved = storage_resolve_claim_file_path(WRITEPATH . 'uploads/claim_files', $filename); if (!($resolved['success'] ?? false) || empty($resolved['path'])) { log_message('error', "VIDAL - IR Submission FAILED → File not found in local/S3 | {$filename}"); continue; } $upload = $this->uploadFileToVidal($resolved['path'], $filename); storage_cleanup_temp_claim_file($resolved); if (empty($upload['status']) || $upload['status'] !== true) { log_message('error', "VIDAL - IR Submission FAILED → File upload failed"); return [ 'status' => false, 'message' => 'File upload failed', 'data' => $upload ]; } $fileIdList[] = $upload['fileId']; // deeplink URL $tpaSentFileIds[] = $file['id']; // local file ID for updating } if (empty($fileIdList)) { return [ 'status' => false, 'message' => 'No valid files uploaded' ]; } // 4. PREPARE REQUEST BODY $body = [ "shortFallNo" => $ticket['shortfallNO'], ]; // If single file → fileId if (count($fileIdList) === 1) { $body["fileId"] = $fileIdList[0]; } else { $body["fileIdList"] = $fileIdList; } log_message('error', "VIDAL - IR Submission Request Body => " . json_encode($body)); // 5. API CALL helper('api'); $url = env('VIDAL_API_BASE_URL_IRSUBMISSION'); $method = 'POST'; $headers = [ 'Content-Type: application/json', 'Ocp-Apim-Subscription-Key: ' . getenv('VIDAL_SUBSCRIPTION_KEY'), ]; $response = call_third_party_api($url, $method, $headers, json_encode($body)); log_message('error', "VIDAL - IR Submission API Response => " . json_encode($response)); // 6. HANDLE RESPONSE if (empty($response['status']) || $response['status'] !== true) { log_message( 'error', "VIDAL - IR Submission FAILED for ClaimNO={$ticket['claimNO']} → Response=" . json_encode($response) ); return [ 'status' => false, 'message' => 'IR Submission failed', 'data' => $response ]; } log_message('error', "VIDAL - IR Submission SUCCESS → ClaimNO={$ticket['claimNO']}"); // 6. UPDATE FILES AS SENT TO TPA if(count($tpaSentFileIds) > 0){ $this->db->table('claim_files') ->whereIn('id', $tpaSentFileIds) ->update(['is_file_sent_to_tpa' => 1]); log_message('error',"VIDAL - IR Submission | Updating claim_files for file IDs: " . implode(', ', $tpaSentFileIds)); } else { log_message('error',"VIDAL - IR Submission | No files to update as sent to TPA"); } return [ 'status' => true, 'message' => 'IR Submitted successfully', 'data' => $response ]; } }