db = \Config\Database::connect(); $this->voloTpaId = getenv('VOLO_PRIMARY_KEY_CONSTANT'); } protected function adminBaseUrl(): string { return rtrim((string) getenv('VOLO_API_ADMIN_BASE_URL'), '/'); } protected function consumerBaseUrl(): string { return rtrim((string) getenv('VOLO_API_CONSUMER_BASE_URL'), '/'); } /** * Obtain API access token (login). * * @return string|null Full accessToken value as returned by API (includes "Token " prefix when applicable) */ public function getAccessToken(): ?string { if ($this->cachedAccessToken !== null) { return $this->cachedAccessToken; } helper('api'); $url = $this->adminBaseUrl() . '/external/login'; $body = [ 'emailId' => getenv('VOLO_API_EMAIL'), 'password' => getenv('VOLO_API_PASSWORD'), 'loggedInPortal' => getenv('VOLO_LOGGED_IN_PORTAL') ?: 'POLICY_CONFIGURATION_PORTAL', ]; $headers = ['Content-Type: application/json']; $response = call_third_party_api($url, 'POST', $headers, $body); if (empty($response['status'])) { log_message('error', 'VOLO - Login failed | ' . json_encode($response)); return null; } $data = $response['data'] ?? null; if (!is_array($data)) { log_message('error', 'VOLO - Login invalid response shape | ' . json_encode($response)); return null; } $token = $data['accessToken'] ?? null; if ($token === null || $token === '' || $token === 'Token null') { log_message('error', 'VOLO - Login rejected or empty token | ' . json_encode($data)); return null; } $this->cachedAccessToken = $token; return $this->cachedAccessToken; } /** * @return array{0: bool, 1: array} */ protected function authorizedJsonHeaders(): array { $token = $this->getAccessToken(); if ($token === null) { return [false, []]; } return [true, [ 'Content-Type: application/json', 'Authorization: ' . $token, ]]; } /** * GET /trueclaim/tpa/get-Enrollment-dump */ public function getEnrollmentDumpByEndorsement(string $insurerPolicyNumber, string $endorsmentNo) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query([ 'insurerPolicyNumber' => $insurerPolicyNumber, 'endorsmentNo' => $endorsmentNo, ]); $url = $this->adminBaseUrl() . '/trueclaim/tpa/get-Enrollment-dump?' . $query; return call_third_party_api($url, 'GET', $headers, []); } /** * GET /trueclaim/tpa/getHospitalByInsurerName */ public function getHospitalByInsurerName(string $insurerName) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query(['insurerName' => $insurerName]); $url = $this->adminBaseUrl() . '/trueclaim/tpa/getHospitalByInsurerName?' . $query; return call_third_party_api($url, 'GET', $headers, []); } /** * POST /trueclaim/tpa/doc * * @param array $body */ public function uploadDocument(array $body) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $url = $this->adminBaseUrl() . '/trueclaim/tpa/doc'; return call_third_party_api($url, 'POST', $headers, $body); } /** * GET /trueclaim/tpa/get-Enroll-dump */ public function getEnrollmentDump(string $insurerPolicyNumber) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query(['insurerPolicyNumber' => $insurerPolicyNumber]); $url = $this->adminBaseUrl() . '/trueclaim/tpa/get-Enroll-dump?' . $query; return call_third_party_api($url, 'GET', $headers, []); } /** * POST consumer — /truecover/external-service/claim-status */ public function fetchExternalClaimStatus(string $tpaClaimNo) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $url = $this->consumerBaseUrl() . '/truecover/external-service/claim-status'; $body = ['tpaClaimNo' => $tpaClaimNo]; return call_third_party_api($url, 'POST', $headers, $body); } /** * POST /trueclaim/tpa/getTpaData * * @return array */ public function getTpaData(string $startDate, string $endDate, string $entityId) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $url = $this->adminBaseUrl() . '/trueclaim/tpa/getTpaData'; $body = [ 'startDate' => $startDate, 'endDate' => $endDate, 'entityId' => $entityId, ]; return call_third_party_api($url, 'POST', $headers, $body); } /** * GET /trueclaim/create-new-all-member-id-card-pdf * Returns raw API response; body may contain base64 PDF payload. */ public function getEcardPdfRequest(string $employeeId, string $entityId) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query([ 'employeeId' => $employeeId, 'entityId' => $entityId, ]); $url = $this->adminBaseUrl() . '/trueclaim/create-new-all-member-id-card-pdf?' . $query; return call_third_party_api($url, 'GET', $headers, []); } /** * GET /trueclaim/get-entity-from-policy */ public function getEntityFromPolicy(string $policyNumber) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query(['policyNumber' => $policyNumber]); $url = $this->adminBaseUrl() . '/trueclaim/get-entity-from-policy?' . $query; return call_third_party_api($url, 'GET', $headers, []); } /** * POST /trueclaim/policy-bazaar/intimate-claim * * @param array $payload */ public function intimateClaim(array $payload) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $url = $this->adminBaseUrl() . '/trueclaim/policy-bazaar/intimate-claim'; return call_third_party_api($url, 'POST', $headers, $payload); } /** * POST /trueclaim/tpa/getClaimData?claimId= */ public function getClaimData(string $claimId) { helper('api'); [$ok, $headers] = $this->authorizedJsonHeaders(); if (!$ok) { return ['status' => false, 'message' => 'VOLO token failed']; } $query = http_build_query(['claimId' => $claimId]); $url = $this->adminBaseUrl() . '/trueclaim/tpa/getClaimData?' . $query; return call_third_party_api($url, 'POST', $headers, new \stdClass()); } /** * Pull enrollment from Volo (get-Enroll-dump), match members, update employee_polices.tpa_id (memberId). * Same flow as MediAssistApiController::MediAssistGetBenefDetails. * * @param array $requestData * @return array|\CodeIgniter\HTTP\Response */ public function VoloGetBenefDetails($requestData) { $function_calling_type = $requestData['return_type'] ?? 'job'; try { helper(['api', 'utility']); $policyNo = $requestData['policy_no'] ?? null; $client_policy_id = $requestData['client_policy_id'] ?? null; if (empty($policyNo)) { log_message('error', 'VOLO - 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', 'VOLO - 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', "VOLO - TPA ID Pull | called for policy_no: {$policyNo} , client_policy_id: {$client_policy_id}"); $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(); if (empty($employeePolicyData)) { log_message('error', 'VOLO - TPA ID Pull FAILED | employeePolicyData is empty (tpa_id IS NULL from nhance) for this TPA ID Pull request'); if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'employeePolicyData not found']; } return $this->respond(['status' => false, 'message' => 'employeePolicyData not found']); } $response = $this->getEnrollmentDump((string) $policyNo); if (empty($response['status'])) { 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', "VOLO - Files table status updated for the file id : {$requestData['file_id']}"); } log_message('error', 'VOLO - TPA ID Pull 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]); } $apiPayload = $response['data'] ?? null; if (!is_array($apiPayload)) { log_message('error', 'VOLO - TPA ID Pull FAILED | invalid response shape | ' . json_encode($response)); if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { (new BatchFileModel())->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); } if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'Invalid API response']; } return $this->respond(['status' => false, 'message' => 'Invalid API response']); } $allRows = $this->extractVoloEnrollmentRows($apiPayload); if ($allRows === []) { log_message('error', 'VOLO - TPA ID Pull FAILED | enrollment body empty | ' . json_encode($apiPayload)); if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { (new BatchFileModel())->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); } if ($function_calling_type === 'job') { return ['status' => false, 'message' => 'enrollment body empty']; } return $this->respond(['status' => false, 'message' => 'enrollment body empty']); } $totalCount = count($allRows); log_message('error', "VOLO - TPA ID Pull | fetched {$totalCount} enrollment row(s)"); $json = json_encode($allRows, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); $filePath = WRITEPATH . 'tmp/' . time() . '_' . ($requestData['file_id'] ?? '0') . '_volo.json'; file_put_contents($filePath, $json); if (!empty($requestData['file_id'])) { Jobs::addJob([ 'job_name' => 'saveVoloAPIData', 'payload' => [ 'file_id' => $requestData['file_id'], 'json_file_path' => $filePath, ], ]); } $batch_file_success = 'success'; $updated = 0; $employee_policy_ids = []; foreach ($employeePolicyData as $policy_data) { $hasMatchForThisPolicy = false; foreach ($allRows as $row) { if (!is_array($row)) { continue; } $voloEmpCode = trim((string) ($row['memberEmployeeNo'] ?? $row['memberAltEmployeeNo'] ?? $row['empId'] ?? '')); $voloMemberId = trim((string) ($row['memberId'] ?? '')); if ($voloMemberId === '') { continue; } if ( strtolower(trim((string) ($policy_data['name'] ?? ''))) === strtolower(trim((string) ($row['memberName'] ?? ''))) && (string) ($policy_data['emp_code'] ?? '') === $voloEmpCode && $this->voloRelationsMatch((string) ($policy_data['relationship'] ?? ''), (string) ($row['relation'] ?? '')) && $this->voloGendersMatch((string) ($policy_data['gender'] ?? ''), (string) ($row['gender'] ?? '')) && $this->voloDobsMatch((string) ($policy_data['dob'] ?? ''), $row['DOB'] ?? $row['dob'] ?? null) ) { $hasMatchForThisPolicy = true; $sql = 'UPDATE employee_polices SET tpa_id = ? WHERE id = ?'; $this->db->query($sql, [$voloMemberId, $policy_data['emp_policy_id']]); if (strtolower(trim((string) ($policy_data['relationship'] ?? ''))) === 'self') { $employee_policy_ids[] = $policy_data['emp_policy_id']; } if ($this->db->affectedRows() > 0) { $updated++; log_message('error', "VOLO - Updated tpa_id={$voloMemberId} for emp_code={$voloEmpCode} policy={$policyNo}"); } else { log_message('error', "VOLO - No update (already set or not matched) for emp_code={$voloEmpCode} policy={$policyNo}"); } } } 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', 'VOLO - No match for Nhance = ' . json_encode($nhanceSideData)); } } if ($employee_policy_ids !== []) { log_message('error', 'VOLO - sendMailForDownloadingECard JOB PUSHED.'); 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', "VOLO - Files table status updated for the file id : {$requestData['file_id']}"); } log_message('error', "VOLO - TPA ID Pull SUCCESS | 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', "VOLO - Files table status updated for the file id : {$requestData['file_id']}"); } $errorData = [ 'message' => $th->getMessage(), 'file' => $th->getFile(), 'line' => $th->getLine(), ]; log_message('error', 'VOLO - Exception in VoloGetBenefDetails: ' . 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]); } } /** * Persist raw Volo enrollment rows into tpa_api_data (same pattern as saveMediAssitAPIData). * * @param array $array */ public function saveVoloAPIData($array) { $file_id = $array['file_id']; $json = file_get_contents($array['json_file_path']); $records = json_decode($json, true); if (!is_array($records)) { return; } $file_model = new BatchFileModel(); $file_info = $file_model->where('id', $file_id)->find(); $tpaApiDataModel = new TpaApiDataModel(); $tpaApiDataModel->set('is_active', 0)->where('file_id', $file_id)->update(); $mappedRows = []; foreach ($records as $row) { if (!is_array($row)) { continue; } $dobRaw = $row['DOB'] ?? $row['dob'] ?? null; $dobYmd = null; if ($dobRaw !== null && $dobRaw !== '') { $dobYmd = $this->normalizeVoloDobToYmd($dobRaw); } $dojRaw = $row['DOJ'] ?? $row['doj'] ?? null; $dojYmd = null; if ($dojRaw !== null && $dojRaw !== '') { $dojYmd = $this->normalizeVoloDobToYmd($dojRaw); } $rel = trim((string) ($row['relation'] ?? '')); $mappedRows[] = [ 'file_id' => $file_id, 'emp_code' => trim((string) ($row['memberEmployeeNo'] ?? $row['empId'] ?? '')), 'name' => trim((string) ($row['memberName'] ?? '')), 'dob' => $dobYmd, 'relation' => $rel, 'gender' => $this->voloGenderToMediStyle((string) ($row['gender'] ?? '')), 'self' => in_array(strtoupper($rel), ['EMPLOYEE', 'SELF'], true) ? 1 : 0, 'tpa_id' => trim((string) ($row['memberId'] ?? '')), 'age' => isset($row['age']) && is_numeric($row['age']) ? (int) $row['age'] : null, 'si' => $row['sumInsured'] ?? null, 'doj' => $dojYmd, 'is_active' => 1, 'created_by' => $file_info[0]['created_by'] ?? null, ]; } if ($mappedRows !== []) { $tpaApiDataModel->insertBatchWithChunkLog($mappedRows, 500, 'FILE_ID_' . $file_id); } } /** * @param array $apiData Decoded JSON root from Volo get-Enroll-dump * @return list> */ protected function extractVoloEnrollmentRows(array $apiData): array { $body = $apiData['body'] ?? null; if (is_array($body)) { $out = []; foreach ($body as $item) { if (is_array($item)) { $out[] = $item; } } return $out; } return []; } protected function voloRelationsMatch(string $nhanceRel, string $voloRel): bool { $n = strtolower(trim(str_replace('-', ' ', $nhanceRel))); $v = strtoupper(trim(str_replace('-', ' ', $voloRel))); if ($n === 'self' && in_array($v, ['EMPLOYEE', 'PRIMARY', 'INSURED'], true)) { return true; } return strtolower($v) === $n || $n === strtolower($v); } protected function voloGendersMatch(string $nh, string $volo): bool { return $this->genderNorm($nh) === $this->genderNorm($volo); } protected function genderNorm(string $g): string { $g = strtolower(trim($g)); if ($g === '' || $g === 'm' || strpos($g, 'male') === 0) { return 'm'; } if ($g === 'f' || strpos($g, 'female') === 0) { return 'f'; } return $g; } protected function voloDobsMatch(string $nhanceDob, mixed $voloDob): bool { $v = $this->normalizeVoloDobToYmd($voloDob); if ($v === null || $v === '') { return false; } $tsN = strtotime($nhanceDob); if ($tsN === false) { return false; } $n = date('Y-m-d', $tsN); return $n === $v; } protected function normalizeVoloDobToYmd(mixed $dob): ?string { if ($dob === null || $dob === '') { return null; } if (is_numeric($dob) && (float) $dob > 1_000_000_000_000) { return date('Y-m-d', (int) (((float) $dob) / 1000)); } $ts = strtotime(str_replace('/', '-', (string) $dob)); return $ts ? date('Y-m-d', $ts) : null; } protected function voloGenderToMediStyle(string $g): string { return $this->genderNorm($g) === 'f' ? 'F' : 'M'; } /** * Resolve entity id for a policy number (uses get-entity-from-policy). */ protected function resolveEntityId(string $policyNo): ?string { $fallback = getenv('VOLO_DEFAULT_ENTITY_ID'); if (!empty($fallback)) { return (string) $fallback; } $res = $this->getEntityFromPolicy($policyNo); if (empty($res['status']) || !is_array($res['data'])) { return null; } $body = $res['data']['body'] ?? null; if ($body === null || $body === '') { return null; } return (string) $body; } //----------------------------------------------------------------------------------------------------------------------------- /** * Push claim via intimate-claim (policy-bazaar) API. */ public function SubmitClaim($claimId = null) { helper(['api', 'tpa_claim_push_log', 'utility']); $data = $this->db->table('ticket_master tm') ->select(' tm.id, tm.doa as admissionDate, tm.dod as dischargeDate, tm.claim_amount as requestedAmount, tm.tpa_no as memberId, cp.policy_no as policyNo, cf.id as fileId, cf.url as filePath ') ->join('client_policy cp', 'tm.client_policy_id = cp.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(); if (!$data) { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | claim not found'); return ['status' => false, 'message' => 'Claim Push FAILED | Claim not found']; } if (empty($data['filePath'])) { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | file missing'); return ['status' => false, 'message' => 'Claim Push FAILED | File Missing']; } $file_id = $data['fileId'] ?? null; $filename = basename($data['filePath']); $publicUrl = storage_claim_file_download_url($filename, $file_id); if ($publicUrl === '') { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | PDF not available on S3/local'); return ['status' => false, 'message' => 'Claim Push FAILED | PDF not found on server']; } $entityId = $this->resolveEntityId($data['policyNo'] ?? ''); if ($entityId === null) { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | entity id not resolved'); return ['status' => false, 'message' => 'Claim Push FAILED | entity id not resolved']; } $hospitalId = getenv('VOLO_DEFAULT_HOSPITAL_ID'); if ($hospitalId === false || $hospitalId === '') { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | VOLO_DEFAULT_HOSPITAL_ID not set'); return ['status' => false, 'message' => 'Claim Push FAILED | hospital id not configured']; } $doa = !empty($data['admissionDate']) ? date('Y-m-d', strtotime($data['admissionDate'])) : ''; $dod = !empty($data['dischargeDate']) ? date('Y-m-d', strtotime($data['dischargeDate'])) : $doa; $payload = [ 'hospitalId' => (string) $hospitalId, 'memberId' => (string) ($data['memberId'] ?? ''), 'dateOfAdmission' => $doa, 'dateOfDischarge' => $dod, 'potentialClaimAmount' => (float) ($data['requestedAmount'] ?? 0), 'claimDocuments' => [ [ 'documentName' => $filename, 'documentType' => 'medical_report', 'extension' => 'pdf', 'url' => $publicUrl, ], ], 'entityId' => (string) $entityId, 'insurerPolicyNumber' => (string) ($data['policyNo'] ?? ''), ]; tpa_claim_push_log($claimId, 'VOLO - Claim Push | claimId: ' . $claimId . ' | payload: ' . json_encode($payload)); $response = $this->intimateClaim($payload); if (empty($response['status'])) { tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | ' . 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', 'response' => $response]; } $apiData = $response['data'] ?? null; $ref = null; if (is_array($apiData)) { $ref = $apiData['message'] ?? null; } if (!empty($ref)) { $this->db->table('ticket_master') ->where('id', $claimId) ->update([ 'tpa_claim_push_reference_no' => $ref, 'tpa_claim_id' => $ref, 'updated_at' => date('Y-m-d H:i:s'), ]); tpa_claim_push_log($claimId, 'VOLO - Claim Push SUCCESS | claimId: ' . $claimId . ' | ref: ' . $ref); // Update claim files table that file is sent to tpa for this claim if(!empty($file_id)){ $this->db->table('claim_files') ->where('id', $file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); tpa_claim_push_log($claimId, 'VOLO - Claim Push | Updating claim_files table for file_id: '.$file_id); }else{ tpa_claim_push_log($claimId, 'VOLO - Claim Push | No file_id found to update claim_files table.'); } return ['status' => true, 'message' => 'Claim Push SUCCESS', 'response' => $response]; } tpa_claim_push_log($claimId, 'VOLO - Claim Push empty reference | claimId: ' . $claimId . ' | ' . json_encode($response)); return ['status' => false, 'message' => 'Claim Push API response missing reference', 'response' => $response]; } /** * Refresh claim status from consumer claim-status service. */ public function ClaimDetail($claimId = null) { helper('api'); $ticket = $this->db->table('ticket_master tm') ->select('tm.id, tm.tpa_claim_push_reference_no, tm.tpa_claim_id') ->where('tm.id', $claimId) ->get() ->getRowArray(); if (!$ticket) { return ['status' => false, 'message' => 'Invalid claim']; } $tpaClaimNo = $ticket['tpa_claim_push_reference_no'] ?? $ticket['tpa_claim_id'] ?? null; if (empty($tpaClaimNo)) { log_message('error', 'VOLO - Claim status | missing TPA claim ref | ticket: ' . $claimId); return ['status' => false, 'message' => 'TPA claim reference missing']; } $response = $this->fetchExternalClaimStatus((string) $tpaClaimNo); if (empty($response['status']) || !is_array($response['data'])) { log_message('error', 'VOLO - Claim status FAILED | ticket: ' . $claimId . ' | ' . json_encode($response)); return ['status' => false, 'message' => 'API call failed', 'data' => $response]; } $payload = $response['data']; $body = $payload['body'] ?? $payload; $currentStatus = ''; if (is_array($body)) { $currentStatus = (string) ($body['status'] ?? $body['claim_status'] ?? ''); } if ($currentStatus === '') { log_message('error', 'VOLO - Claim status empty | ticket: ' . $claimId . ' | ' . json_encode($response)); return ['status' => false, 'message' => 'Status not found in response', 'data' => $response]; } $validStatuses = [ 'READY_TO_PAY' => 11, 'PAID' => 11, 'SETTLED' => 11, 'REJECTED' => 8, 'DENIED' => 8, 'QUERY' => 4, 'PRE_AUTH_QUERY_RESPONDED' => 4, 'PRE_AUTH_QUERY' => 4, 'MEMBER_UNENDORSED' => 5, 'In-Progress' => 5, 'UNDER_PROCESS' => 5, 'UNDER PROCESS' => 5, ]; $normalized = strtoupper(str_replace(' ', '_', trim($currentStatus))); $claimStatusId = $validStatuses[$currentStatus] ?? ($validStatuses[$normalized] ?? 5); $updateArray = [ 'tpa_claim_status' => $currentStatus, 'updated_at' => date('Y-m-d H:i:s'), 'claim_status_id' => $claimStatusId, ]; $this->db->table('ticket_master')->where('id', $claimId)->update($updateArray); log_message('error', 'VOLO - Claim status SUCCESS | ticket: ' . $claimId . ' | status: ' . $currentStatus); return [ 'status' => true, 'message' => 'Claim status updated.', 'updated_status' => $currentStatus, 'api_response' => $response, ]; } public function ClaimStatusUpdate() { helper('api'); $tickets = $this->db->table('ticket_master tm') ->select('tm.id') ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') ->where('tm.tpa_claim_push_reference_no IS NOT NULL') ->whereNotIn('tm.claim_status_id', [8, 11, 12, 13, 66, 22, 24, 47, 49, 32, 34, 53, 55, 42, 44, 58, 60]) ->where('tm.is_active', 1) ->where('cp.tpa_id', $this->voloTpaId) ->get() ->getResultArray(); $count = 0; foreach ($tickets as $t) { $this->ClaimDetail($t['id']); $count++; } return $this->response->setJSON(['status' => true, 'updated' => $count]); } /** * E-card PDF — returns a data URL the app can open, or null on failure. * * @param string|null $voloEmployeeId Volo/TPA employee or member id stored in employee_polices.tpa_id */ public function EcardRequest($emp_code = null, $policy_no = null, $voloEmployeeId = null) { if (empty($voloEmployeeId)) { log_message('error', 'VOLO - Ecard | missing Volo employee/member id | emp_code: ' . ($emp_code ?? '')); return null; } $entityId = $this->resolveEntityId((string) $policy_no); if ($entityId === null) { log_message('error', 'VOLO - Ecard | entity id not resolved | policy: ' . ($policy_no ?? '')); return null; } $res = $this->getEcardPdfRequest((string) $voloEmployeeId, $entityId); if (empty($res['status']) || !is_array($res['data'])) { log_message('error', 'VOLO - Ecard FAILED | ' . json_encode($res)); return null; } $body = $res['data']['body'] ?? null; if (!is_string($body) || $body === '') { log_message('error', 'VOLO - Ecard FAILED | empty body | ' . json_encode($res)); return null; } return 'data:application/pdf;base64,' . $body; } }