diff --git a/app/Config/Acl.php b/app/Config/Acl.php index ce46852c..8b0476ea 100644 --- a/app/Config/Acl.php +++ b/app/Config/Acl.php @@ -44,7 +44,7 @@ class Acl '#^/sendDataToTPA#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]], '#^/swagger#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]], '#^/viewClaimFile#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID]], - + '#^/employee/tpaReportsDashboard#' => ['roles' => [ACCOUNT_MANAGER_ROLE_ID]], diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 4b2155c4..78a80888 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -35,6 +35,7 @@ $routes->get('/fedeploy', 'DeployController::fedeploy_view', ['filter' => 'authM $routes->post('/fedeploy', 'DeployController::fedeploy', ['filter' => 'authMVC']); $routes->get('/visitOffBoardCheck', 'EmployeeController::visitOffBoardCheck'); $routes->get('/metaDashboardDemo', 'TestingController::metaDashboardDemo'); +$routes->get('/apacheSuperSetDemo', 'TestingController::apacheSuperSetDemo'); $routes->get('/metaTpaDashboardDemo', 'TestingController::metaTpaDashboardDemo'); // Reminder Mail Notification @@ -137,6 +138,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) { $routes->get('view_deposit/(:num)', 'ClientController::view_Deposit/$1'); $routes->get('createtransaction', 'ClientController::createtransaction'); $routes->post('save_deposit', 'ClientController::saveDeposit'); + $routes->post('update_deposit_amount', 'ClientController::updateDepositAmount'); $routes->post("saveApiData", "ClientController::saveApiData"); $routes->get("generateToken","ClientController::sendToken"); // $routes->get('view_Deposit/(:num)/(:num)','ClientController/view_Deposit/$1/$2'); @@ -240,7 +242,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3"); $routes->get('clearCdSession', 'EmployeeController::clearCdSession'); $routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus'); - + $routes->get("tpaReportsDashboard", "EmployeeController::tpaReportsDashboard"); }); @@ -460,6 +462,15 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->get('insertSampleTpaApiData/(:any)', 'TestingController::insertSampleTpaApiData/$1'); $routes->get('listEmployeeCountByClientPolicy', 'TestingController::listEmployeeCountByClientPolicy'); $routes->get('testMediAssistWellness','TestingController::testMediAssistWellness'); + + $routes->group('claims-collection-v2', static function ($routes) { + $routes->get('preview', 'ClaimsCollectionV2DashboardController::preview'); + $routes->get('preview/(:num)', 'ClaimsCollectionV2DashboardController::preview/$1'); + $routes->get('kpi/(:segment)', 'ClaimsCollectionV2DashboardController::kpi/$1'); + $routes->get('all', 'ClaimsCollectionV2DashboardController::all'); + $routes->get('debug', 'ClaimsCollectionV2DashboardController::debug'); + $routes->get('debug/(:num)', 'ClaimsCollectionV2DashboardController::debug/$1'); + }); }); $routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail"); @@ -767,6 +778,15 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel $routes->get("downloadPolicyFiles", "EmployeeRestController::downloadPolicyFiles"); $routes->post("bulkEcardDownloadAsZip", "EmployeeRestController::bulkEcardDownloadAsZip"); $routes->get("downloadSampleExcel/(:any)", "EmployeeController::downloadSampleExcelFile/$1"); + + $routes->group('claims-collection-v2', static function ($routes) { + $routes->get('preview', 'ClaimsCollectionV2DashboardController::preview'); + $routes->get('preview/(:num)', 'ClaimsCollectionV2DashboardController::preview/$1'); + $routes->get('kpi/(:segment)', 'ClaimsCollectionV2DashboardController::kpi/$1'); + $routes->get('all', 'ClaimsCollectionV2DashboardController::all'); + $routes->get('debug', 'ClaimsCollectionV2DashboardController::debug'); + $routes->get('debug/(:num)', 'ClaimsCollectionV2DashboardController::debug/$1'); + }); }); $routes->post("bulkEcardDownloadAsZip", "EmployeeRestController::bulkEcardDownloadAsZip"); @@ -804,6 +824,7 @@ $routes->group("/ticket", ["filter" => "authMVC"], function ($routes) { $routes->get('feedback-list','TicketController::feedbackList'); $routes->match(['get', 'post'], 'claim-upload','TicketController::claimDumpUpload'); $routes->get('remove','TicketController::removeTicket'); + $routes->get('tpa-claim-push-logs','TicketController::getTpaClaimPushLogs'); $routes->get('new/(:any)','TicketController::ticket_form/$1'); $routes->post('create','TicketController::createTicket'); $routes->post('update','TicketController::updateTicket'); diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php index 18eac66a..735cbfb0 100644 --- a/app/Controllers/ApiServiceController.php +++ b/app/Controllers/ApiServiceController.php @@ -50,7 +50,9 @@ class ApiServiceController extends BaseController // Push Claims public function pushClaims($claimId) { - + helper('tpa_claim_push_log'); + init_tpa_claim_push_logs($claimId); + $data = $this->db->table('ticket_master tm') ->select('tm.id,tm.tpa_id ')->where('tm.id', $claimId)->get()->getRowArray(); // single record @@ -79,7 +81,7 @@ class ApiServiceController extends BaseController $voloApiController = new VoloApiController(); return $voloApiController->SubmitClaim($claimId); }else{ - log_message('error', "This ticket id ( {$claimId} ) TPA has no API service enabled. TPA ID : {$tpaID}"); + tpa_claim_push_log($claimId, "This ticket id ( {$claimId} ) TPA has no API service enabled. TPA ID : {$tpaID}"); } } diff --git a/app/Controllers/ClaimsCollectionV2DashboardController.php b/app/Controllers/ClaimsCollectionV2DashboardController.php new file mode 100644 index 00000000..e49739d1 --- /dev/null +++ b/app/Controllers/ClaimsCollectionV2DashboardController.php @@ -0,0 +1,166 @@ +request->getGet('client_policy') + ?? $this->request->getGet('client_policy_id') + ?? $this->request->getPost('client_policy') + ?? $this->request->getPost('client_policy_id') + ?? 0 + ); + + if ($id > 0) { + return $id; + } + + if ($fallback !== null && $fallback > 0) { + return $fallback; + } + + return 0; + } + + /** + * Resolve KPI by Metabase numeric id or method slug. + */ + protected function resolveKpiMethod(string $kpiKey): ?string + { + $kpiKey = trim($kpiKey); + if ($kpiKey === '') { + return null; + } + + if (in_array($kpiKey, ClaimsCollectionV2DashboardModel::KPI_MAP, true)) { + return $kpiKey; + } + + if (ctype_digit($kpiKey)) { + $id = (int) $kpiKey; + return ClaimsCollectionV2DashboardModel::KPI_MAP[$id] ?? null; + } + + return null; + } + + /** + * JSON: single KPI by slug (e.g. incurred_ratio) or Metabase id (e.g. 207). + * Requires client_policy or client_policy_id. + */ + public function kpi(string $kpiMethod = '') + { + $policyId = $this->resolvePolicyId(); + + if ($policyId <= 0) { + return $this->respond([ + 'status' => false, + 'message' => 'client_policy or client_policy_id is required.', + ], 422); + } + + $kpiMethod = $this->resolveKpiMethod($kpiMethod); + if ($kpiMethod === null) { + return $this->respond([ + 'status' => false, + 'message' => 'Unknown KPI. Pass Metabase id or method slug.', + 'allowed' => ClaimsCollectionV2DashboardModel::KPI_MAP, + ], 404); + } + + $model = new ClaimsCollectionV2DashboardModel(); + $metabaseId = array_search($kpiMethod, ClaimsCollectionV2DashboardModel::KPI_MAP, true); + + return $this->respond([ + 'status' => true, + 'policy_id' => $policyId, + 'kpi_id' => $metabaseId !== false ? (int) $metabaseId : null, + 'kpi' => $kpiMethod, + 'label' => ClaimsCollectionV2DashboardModel::KPI_LABELS[$kpiMethod] ?? $kpiMethod, + 'rows' => $model->getKpi($kpiMethod, $policyId), + ]); + } + + /** + * JSON: all KPIs. Requires client_policy or client_policy_id. + */ + public function all() + { + $policyId = $this->resolvePolicyId(); + + if ($policyId <= 0) { + return $this->respond([ + 'status' => false, + 'message' => 'client_policy or client_policy_id is required.', + ], 422); + } + + $model = new ClaimsCollectionV2DashboardModel(); + + return $this->respond([ + 'status' => true, + 'policy_id' => $policyId, + 'data' => $model->getAllKpis($policyId), + ]); + } + + /** + * Admin preview UI (authMVC only) — KPI grid for manual testing. + * Default policy id only in method signature; override via query or /preview/{id}. + */ + public function preview(int $policyId = 4687) + { + $policyId = $this->resolvePolicyId($policyId); + $path = $this->request->getUri()->getPath(); + $isJwt = stripos($path, 'employeeRest') !== false; + $prefix = $isJwt ? 'employeeRest/claims-collection-v2' : 'util/claims-collection-v2'; + + return view('claims_collection_v2_dashboard', [ + 'policy_id' => $policyId, + 'kpi_map' => ClaimsCollectionV2DashboardModel::KPI_MAP, + 'kpi_labels' => ClaimsCollectionV2DashboardModel::KPI_LABELS, + 'api_all_url' => base_url($prefix . '/all'), + 'api_kpi_url' => base_url($prefix . '/kpi'), + ]); + } + + /** + * Admin check only: raw JSON on screen (no dashboard UI). + * Default policy id only here; override via ?client_policy= or /debug/{id}. + */ + public function debug(int $policyId = 4687) + { + $policyId = $this->resolvePolicyId($policyId); + + if ($policyId <= 0) { + return $this->response + ->setStatusCode(422) + ->setBody('client_policy or client_policy_id is required.'); + } + + $model = new ClaimsCollectionV2DashboardModel(); + $body = json_encode([ + 'status' => true, + 'policy_id' => $policyId, + 'data' => $model->getAllKpis($policyId), + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + + return $this->response + ->setHeader('Content-Type', 'application/json; charset=UTF-8') + ->setBody($body); + } +} diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index 56087644..4c9edd8d 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -902,6 +902,109 @@ class ClientController extends AdminController return $this->response->setJSON(['success' => $response['success']]); } + public function updateDepositAmount() + { + $rules = [ + 'deposit_id' => 'required|is_natural_no_zero', + 'amount' => 'required|numeric|greater_than_equal_to[0]', + 'transaction_type' => 'required|in_list[Credit,Debit]', + 'client_id' => 'required', + 'insurer_id' => 'required|is_natural_no_zero', + 'cd_ac_pk' => 'required|is_natural_no_zero', + ]; + + if (! $this->validate($rules)) { + return $this->response + ->setStatusCode(400) + ->setJSON([ + 'status' => 'error', + 'message' => 'Input validation failed', + 'errors' => $this->validator->getErrors(), + ]); + } + + $postData = sanitizeInputArrayAdvanced($this->request->getPost()); + $depositId = (int) ($postData['deposit_id'] ?? 0); + $clientId = $postData['client_id'] ?? null; + $insurerId = (int) ($postData['insurer_id'] ?? 0); + $cdAcPk = (int) ($postData['cd_ac_pk'] ?? 0); + $amount = (float) ($postData['amount'] ?? 0); + $transactionType = $postData['transaction_type'] ?? 'Credit'; + $loggedInUserID = get_session_userid(); + + $depositModel = new ClientDepositModel(); + + $targetRow = $depositModel + ->where('id', $depositId) + ->where('client_id', $clientId) + ->where('insurer_id', $insurerId) + ->where('cd_ac_pk', $cdAcPk) + ->where('is_active', 1) + ->first(); + + if (empty($targetRow)) { + return $this->response + ->setStatusCode(404) + ->setJSON([ + 'status' => 'error', + 'message' => 'Transaction not found for this account.', + ]); + } + + $existingType = $targetRow['transaction_type'] ?? ''; + if ($existingType !== $transactionType) { + return $this->response + ->setStatusCode(400) + ->setJSON([ + 'status' => 'error', + 'message' => 'Only ' . $existingType . ' value can be edited for this transaction.', + ]); + } + + $db = \Config\Database::connect(); + $db->transBegin(); + + $depositModel->update($depositId, [ + 'amount' => $amount, + 'transaction_type' => $transactionType, + 'updated_by' => $loggedInUserID, + ]); + + $rows = $depositModel + ->where('client_id', $clientId) + ->where('insurer_id', $insurerId) + ->where('cd_ac_pk', $cdAcPk) + ->where('is_active', 1) + ->orderBy('id', 'ASC') + ->findAll(); + + $runningBalance = 0.0; + foreach ($rows as $row) { + $rowAmount = (float) ($row['amount'] ?? 0); + $runningBalance += (($row['transaction_type'] ?? '') === 'Credit') ? $rowAmount : -$rowAmount; + $depositModel->update((int) $row['id'], [ + 'balance' => $runningBalance, + 'updated_by' => $loggedInUserID, + ]); + } + + if ($db->transStatus() === false) { + $db->transRollback(); + return $this->response + ->setStatusCode(500) + ->setJSON([ + 'status' => 'error', + 'message' => 'Failed to update transaction.', + ]); + } + + $db->transCommit(); + return $this->response->setJSON([ + 'status' => 'success', + 'message' => 'Transaction updated successfully.', + ]); + } + public function editClientOnboarding($id = null) { @@ -6652,7 +6755,7 @@ class ClientController extends AdminController // Check in Enrollment (client_policy) $client_policy_data = $this->clientPolicyModel ->where('is_active', 1) - ->where('policy_status', 1) + // ->where('policy_status', 1) ->where('TRIM(policy_no)', $policy_no) ->first(); diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index c345330c..d209ca84 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -55,6 +55,8 @@ use Dompdf\Dompdf; use Dompdf\Options; use Kint; +use Firebase\JWT\JWT; + class EmployeeController extends AdminController { @@ -7339,4 +7341,75 @@ class EmployeeController extends AdminController } } + + public function tpaReportsDashboard() + { + $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); + $policy_id = $this->request->getGet('client_policy') ?? null; + + if ($this->request->getGet('api') != 1) { + $data = [ + 'tab_name' => 'TPA Reports', + 'page_name' => 'TPA Reports', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/employee/tpaReportsDashboard'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } + + $client_policy_data = $this->clientPolicyModel + ->select('tpa.dashboard_id') + ->join('tpa', 'client_policy.tpa_id = tpa.id') + ->where('client_policy.id', $policy_id) + ->first(); + + $database_id = isset($client_policy_data['dashboard_id']) ? (int) $client_policy_data['dashboard_id'] : null; + + if (empty($database_id)) { + + if ($this->request->getGet('api') == 1) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'There is no dashboard for this TPA.', + 'data' => [] + ]); + } + + return view('errors/404', [ + 'message' => 'There is no dashboard for this TPA.' + ]); + } + + $payload = [ + 'resource' => [ + 'dashboard' => $database_id + ], + 'exp' => time() + (10 * 60), // 10 minutes + 'params' => (object) ['client_policy' => $policy_id ], // MUST be object for Metabase + + ]; + + $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); + // dd($token); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], + ]); + } + + } diff --git a/app/Controllers/FhplApiController.php b/app/Controllers/FhplApiController.php index 6d684e1c..16bc30ed 100644 --- a/app/Controllers/FhplApiController.php +++ b/app/Controllers/FhplApiController.php @@ -70,7 +70,7 @@ class FhplApiController extends BaseController public function SubmitClaim($claimId = null) // 515 { - helper('api'); + helper(['api', 'tpa_claim_push_log']); $data = $this->db->table('ticket_master tm') ->select(' @@ -99,7 +99,7 @@ class FhplApiController extends BaseController if (count($data) && $data['filePath'] == null) { - log_message('error', "FHPL - Claim Push FAILED | claimId: '.$claimId.' - Claim or File Missing"); + tpa_claim_push_log($claimId, "FHPL - Claim Push FAILED | claimId: {$claimId} - Claim or File Missing"); return ['status' => false, 'message' => 'Claim Push FAILED | Claim or File Missing']; } @@ -109,7 +109,7 @@ class FhplApiController extends BaseController $pdfPath = WRITEPATH . 'uploads/claim_files/' . $filename; if (!file_exists($pdfPath)) { - log_message('error', "FHPL - Claim Push FAILED | claimId: '.$claimId.' - PDF not found on server"); + tpa_claim_push_log($claimId, "FHPL - Claim Push FAILED | claimId: {$claimId} - PDF not found on server"); return ['status' => false, 'message' => 'Claim Push FAILED | PDF not found on server']; } @@ -119,7 +119,7 @@ class FhplApiController extends BaseController // Generate FHPL Token $tokenResponse = $this->generateAuthToken(); if (empty($tokenResponse['data']['access_token'])) { - log_message('error', "FHPL - Claim Push FAILED | claimId: '.$claimId.' - FHPL Token generation failed"); + tpa_claim_push_log($claimId, "FHPL - Claim Push FAILED | claimId: {$claimId} - FHPL Token generation failed"); return ['status' => false, 'message' => 'Claim Push FAILED | FHPL Token generation failed']; } $token = $tokenResponse['data']['access_token']; @@ -154,14 +154,14 @@ class FhplApiController extends BaseController "Content-Type: application/json" ]; - log_message('error', 'FHPL - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); + tpa_claim_push_log($claimId, 'FHPL - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); $response = call_third_party_api($url, 'POST', $headers, $body); - log_message('error', 'FHPL - Claim Push RESPONSE | ' . json_encode($response)); + tpa_claim_push_log($claimId, 'FHPL - Claim Push RESPONSE | ' . json_encode($response)); if($response['status'] != true){ - log_message('error', 'FHPL - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); + tpa_claim_push_log($claimId, 'FHPL - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); $this->db->table('ticket_master') ->where('id',$claimId) ->update([ 'tpa_push_response' => json_encode($response) ]); @@ -193,15 +193,15 @@ class FhplApiController extends BaseController ->where('id', $file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); - log_message('error','HEALTH_INDIA - Claim Push | Updating claim_files table for file_id: '.$file_id); + tpa_claim_push_log($claimId, 'FHPL - Claim Push | Updating claim_files table for file_id: '.$file_id); }else{ - log_message('error','HEALTH_INDIA - Claim Push | No file_id found to update claim_files table.'); + tpa_claim_push_log($claimId, 'FHPL - Claim Push | No file_id found to update claim_files table.'); } - log_message('error', 'FHPL - Claim Push SUCCESS | claimId: '.$claimId.' | claimNO: '.$fhplClaimNo); + tpa_claim_push_log($claimId, 'FHPL - Claim Push SUCCESS | claimId: '.$claimId.' | claimNO: '.$fhplClaimNo); return ['status' => true, 'message' => 'Claim Push SUCCESS', 'response' => $response]; }else { - log_message('error', 'FHPL - Claim Push API SUCCESS BUT claimsInfo EMPTY | response: '.json_encode($response)); + tpa_claim_push_log($claimId, 'FHPL - Claim Push API SUCCESS BUT claimsInfo EMPTY | response: '.json_encode($response)); return ['status' => false, 'message' => 'Claim Push API SUCCESS BUT claimsInfo EMPTY']; } } diff --git a/app/Controllers/HealthIndiaApiController.php b/app/Controllers/HealthIndiaApiController.php index 46b67043..485e81d9 100644 --- a/app/Controllers/HealthIndiaApiController.php +++ b/app/Controllers/HealthIndiaApiController.php @@ -84,9 +84,9 @@ class HealthIndiaApiController extends BaseController public function SubmitClaim($claimId = null) { - helper('api'); + helper(['api', 'tpa_claim_push_log']); - log_message('error', 'HEALTH_INDIA - Claim Push | Started for claimId: ' . $claimId); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push | Started for claimId: ' . $claimId); $data = $this->db->table('ticket_master tm') ->select(' @@ -121,12 +121,12 @@ class HealthIndiaApiController extends BaseController // dd($data); if (!$data) { - log_message('error', "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - Claim not found"); + tpa_claim_push_log($claimId, "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - Claim not found"); return ['status' => false, 'message' => 'Claim Push FAILED | Claim not found']; } if ($data['filePath'] == null) { - log_message('error', "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - File Missing"); + tpa_claim_push_log($claimId, "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - File Missing"); return ['status' => false, 'message' => 'Claim Push FAILED | File Missing']; } @@ -136,7 +136,7 @@ class HealthIndiaApiController extends BaseController $pdfPath = WRITEPATH . 'uploads/claim_files/' . $filename; if (!file_exists($pdfPath)) { - log_message('error', "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - PDF not found on server at path: {$pdfPath}"); + tpa_claim_push_log($claimId, "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - PDF not found on server at path: {$pdfPath}"); return ['status' => false, 'message' => 'Claim Push FAILED | PDF not found on server']; } @@ -146,7 +146,7 @@ class HealthIndiaApiController extends BaseController // Generate Health India Token $tokenResponse = $this->generateAuthToken(); if (empty($tokenResponse['data']['result'][0]['access_token'])) { - log_message('error', "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - Token generation failed"); + tpa_claim_push_log($claimId, "HEALTH_INDIA - Claim Push FAILED | claimId: {$claimId} - Token generation failed"); return; } $token = $tokenResponse['data']['result'][0]['access_token']; @@ -206,14 +206,14 @@ class HealthIndiaApiController extends BaseController "Content-Type: application/json" ]; - log_message('error', 'HEALTH_INDIA - Claim Push | claimId: ' . $claimId . ' | URL: ' . $url ); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push | claimId: ' . $claimId . ' | URL: ' . $url); $response = call_third_party_api($url, 'POST', $headers, $body); // dd($response); if ($response['status'] != true) { - log_message('error', 'HEALTH_INDIA - Claim Push FAILED | claimId: ' . $claimId . ' | response: ' . json_encode($response)); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push FAILED | claimId: ' . $claimId . ' | response: ' . json_encode($response)); $this->db->table('ticket_master') ->where('id', $claimId) ->update(['tpa_push_response' => json_encode($response)]); @@ -240,15 +240,15 @@ class HealthIndiaApiController extends BaseController ->where('id', $file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); - log_message('error','HEALTH_INDIA - Claim Push | Updating claim_files table for file_id: '.$file_id); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push | Updating claim_files table for file_id: '.$file_id); }else{ - log_message('error','HEALTH_INDIA - Claim Push | No file_id found to update claim_files table.'); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push | No file_id found to update claim_files table.'); } - log_message('error', 'HEALTH_INDIA - Claim Push SUCCESS | claimId: ' . $claimId . ' | CCN: ' . $ccn . ' | CCN_EXT: ' . $ccnExt); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push SUCCESS | claimId: ' . $claimId . ' | CCN: ' . $ccn . ' | CCN_EXT: ' . $ccnExt); return ['status' => true, 'message' => 'Claim Push SUCCESS', 'response' => $response]; } else { - log_message('error', 'HEALTH_INDIA - Claim Push API SUCCESS BUT CCN EMPTY | claimId: ' . $claimId . ' | response: ' . json_encode($response)); + tpa_claim_push_log($claimId, 'HEALTH_INDIA - Claim Push API SUCCESS BUT CCN EMPTY | claimId: ' . $claimId . ' | response: ' . json_encode($response)); return ['status' => false, 'message' => 'Claim Push API SUCCESS BUT CCN EMPTY']; } diff --git a/app/Controllers/MediAssistApiController.php b/app/Controllers/MediAssistApiController.php index cb39d93c..a05b8853 100644 --- a/app/Controllers/MediAssistApiController.php +++ b/app/Controllers/MediAssistApiController.php @@ -30,7 +30,7 @@ class MediAssistApiController extends BaseController public function SubmitClaim ($claimId = null) { - helper('api'); + helper(['api', 'tpa_claim_push_log']); // $url = ''. getenv('MEDI_ASSIST_API_BASE_URL').'/SubmitClaim'; $url = getenv('MEDI_ASSIST_API_BASE_URL_CLAIMSUBMIT'); @@ -133,14 +133,14 @@ class MediAssistApiController extends BaseController // ] // ]; - log_message('error','MEDI_ASSIST - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); $response = call_third_party_api($url, $method, $headers, $body); if($response['status'] != true){ - log_message('error','MEDI_ASSIST - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); $this->db->table('ticket_master') ->where('id',$claimId) ->update([ 'tpa_push_response' => json_encode($response) ]); @@ -153,7 +153,7 @@ class MediAssistApiController extends BaseController if(!empty($claimRef)){ - log_message('error','MEDI_ASSIST - Claim Push SUCCESS | claimId: '.$claimId.' | claimReferenceNo: '.$claimRef); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push SUCCESS | claimId: '.$claimId.' | claimReferenceNo: '.$claimRef); // Update claim reference number in ticket_master $this->db->table('ticket_master') @@ -167,15 +167,15 @@ class MediAssistApiController extends BaseController ->where('id', $file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); - log_message('error','MEDI_ASSIST - Claim Push | Updating claim_files table for file_id: '.$file_id); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push | Updating claim_files table for file_id: '.$file_id); }else{ - log_message('error','MEDI_ASSIST - Claim Push | No file_id found to update claim_files table.'); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push | No file_id found to update claim_files table.'); } return ['status' => true, 'message' => 'Claim Push SUCCESS', 'response' => $response]; } else { - log_message('error','MEDI_ASSIST - Claim Push API Failed | claimReferenceNo EMPTY | claimId: '.$claimId.' | response: '.json_encode($response)); + tpa_claim_push_log($claimId, 'MEDI_ASSIST - Claim Push API Failed | claimReferenceNo EMPTY | claimId: '.$claimId.' | response: '.json_encode($response)); return ['status' => false, 'message' => 'Claim Push API Failed', 'response' => $response]; } diff --git a/app/Controllers/TestingController.php b/app/Controllers/TestingController.php index eecbfb45..4de8ff71 100644 --- a/app/Controllers/TestingController.php +++ b/app/Controllers/TestingController.php @@ -365,7 +365,7 @@ class TestingController extends BaseController $insurerBranchModel = new InsurerBranchModel(); $insurer_branch = $insurerBranchModel->getInsurerBranchesWithInsurerNames(); - $dataArray = $data['pt_co_share_details']; // Example + $dataArray = $data['pt_co_share_details'] ?? []; // Example $cd_ac_pk = 'CD12345'; $role_id = 1; $team_id = ['6']; @@ -1099,6 +1099,64 @@ class TestingController extends BaseController public function metaDashboardDemo() { + // 🔐 Move this to .env in real projects + $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); + $database_id = (int) ($this->request->getGet('database_id') ?? 2); + $policy_id = $this->request->getGet('client_policy') ?? null; + $payload = [ + 'resource' => [ + // 'dashboard' => 1 + 'dashboard' => $database_id + ], + 'exp' => time() + (10 * 60), // 10 minutes + ]; + + if(!empty($policy_id)){ + $payload['params'] = (object)['client_policy' => $policy_id]; // MUST be object for Metabase + }else{ + $payload['params'] = (object)[]; // MUST be object for Metabase + } + + // dd($payload); + $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); + + // // You can either return token only + // return $this->response->setJSON([ + // 'token' => $token, + // 'iframe_url' => "https://your-metabase-domain/embed/dashboard/{$token}#bordered=true&titled=true" + // ]); + if ($this->request->getGet('api') == 1) { + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } + + return $this->respond([ + 'status' => 'success', + 'message' => 'Dashboard token generated.', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], + ]); + } + + $data = [ + 'tab_name' => 'Metabase Dashboard', + 'page_name' => 'Metabase Dashboard', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/metaDashboardDemo'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + public function apacheSuperSetDemo() + { + + // echo 'Dta';die; // 🔐 Move this to .env in real projects $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); $database_id = (int)$this->request->getGet('database_id') ?? 2; @@ -1108,7 +1166,7 @@ class TestingController extends BaseController $payload = [ 'resource' => [ // 'dashboard' => 1 - 'dashboard' => $database_id + 'dashboard' => 2 ], 'exp' => time() + (10 * 60), // 10 minutes ]; @@ -1138,16 +1196,11 @@ class TestingController extends BaseController ]); } - return view('meta_dashboard_demo_one', [ + return view('apache_dashboard_demo_one', [ 'metabaseToken' => $token, 'metabaseUrl' => 'https://nsights.nhanceindia.in', ]); } - - public function testingquerys1(){ - - } - public function testingquerys() { $calendar = new \App\Libraries\GoogleCalendarService(); @@ -1175,7 +1228,26 @@ class TestingController extends BaseController public function metaTpaDashboardDemo() { $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); - $policy_id = $this->request->getGet('client_policy') ?? 4687; + $policy_id = $this->request->getGet('client_policy') ?? null; + + if ($this->request->getGet('api') != 1) { + $data = [ + 'tab_name' => 'TPA Reports', + 'page_name' => 'TPA Reports', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/metaTpaDashboardDemo'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } $client_policy_data = $this->ClientPolicyModel ->select('tpa.dashboard_id') @@ -1212,20 +1284,13 @@ class TestingController extends BaseController $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); // dd($token); - if ($this->request->getGet('api') == 1) { - return $this->respond([ - 'status' => 'success', - 'message' => 'Form data received successfully!', - 'data' => [ - 'metabaseToken' => $token, - 'metabaseUrl' => 'https://nsights.nhanceindia.in' - ] - ]); - } - - return view('meta_dashboard_demo_one', [ - 'metabaseToken' => $token, - 'metabaseUrl' => 'https://nsights.nhanceindia.in', + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], ]); } diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index 4421fb71..a3e0e01c 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -509,6 +509,7 @@ class TicketController extends BaseController 'tm.is_head_approved', 'tcs.claim_status AS status', 'tm.claim_number AS claim_no', + 'tm.tpa_claim_push_reference_no', 'tm.tpa_id', 'tm.tpa_no', 'tm.emp_name', @@ -650,6 +651,7 @@ class TicketController extends BaseController 'tm.ticket_type_id', 'tcs.claim_status AS status', 'tm.claim_number AS claim_no', + 'tm.tpa_claim_push_reference_no', 'tm.claim_status_id', 'tm.claim_created_by', 'tm.is_head_approved', @@ -2535,6 +2537,43 @@ class TicketController extends BaseController } + public function getTpaClaimPushLogs() + { + $ticketId = $this->request->getGet('ticket_id'); + + if (empty($ticketId)) { + return $this->respond([ + 'status' => false, + 'message' => 'ticket_id is required', + ], 400); + } + + $ticket = $this->ticketMasterModel + ->select('id, tpa_claim_push_logs') + ->where('id', $ticketId) + ->where('is_active', 1) + ->first(); + + if (!$ticket) { + return $this->respond([ + 'status' => false, + 'message' => 'Claim not found', + ], 404); + } + + $logs = trim((string) ($ticket['tpa_claim_push_logs'] ?? '')); + + return $this->respond([ + 'status' => true, + 'message' => 'TPA claim push logs fetched successfully', + 'data' => [ + 'ticket_id' => (int) $ticketId, + 'logs' => $logs, + 'has_logs' => $logs !== '', + ], + ], 200); + } + //---- Email Trigger Part---------------------------------------------------------------------------------------------- public function constructMailContent($ticket_id, $status_id = null) diff --git a/app/Controllers/VidalApiController.php b/app/Controllers/VidalApiController.php index 889ae745..0fec2c73 100644 --- a/app/Controllers/VidalApiController.php +++ b/app/Controllers/VidalApiController.php @@ -41,13 +41,14 @@ class VidalApiController extends BaseController - function uploadFileToVidal($filePath,$filename) + 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'); - log_message('error', "VIDAL - Claim Push | Starting file upload process for filename: $filename | Path: $filePath"); + 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'; @@ -57,7 +58,7 @@ class VidalApiController extends BaseController "Ocp-Apim-Subscription-Key: $subscriptionKey" ]; - log_message('error', "VIDAL - Claim Push | Requesting signed URL from Vidal API: $apiUrl | Payload: $payload"); + 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); @@ -67,7 +68,7 @@ class VidalApiController extends BaseController $response = curl_exec($ch); if (curl_errno($ch)) { - log_message('error', "VIDAL - Claim Push Curl error while requesting signed URL: " . curl_error($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); @@ -75,14 +76,14 @@ class VidalApiController extends BaseController $responseData = json_decode($response, true); if (!isset($responseData['data']['signedUrl']) || !isset($responseData['data']['fileId'])) { - log_message('error', "VIDAL - Claim Push | Invalid signed URL response received: " . json_encode($responseData)); + 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']; - log_message('error', "VIDAL - Claim Push | Received signed URL & fileId. fileId: $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); @@ -107,12 +108,12 @@ class VidalApiController extends BaseController curl_close($ch2); if ($curlErr) { - log_message('error', "VIDAL - Claim Push | Curl error during file upload: $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) { - log_message('error', "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse"); + tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse"); return [ "status" => false, "message" => "File upload failed", @@ -133,7 +134,7 @@ class VidalApiController extends BaseController public function SubmitClaim ($claimId = null) //515 { - helper('api'); + helper(['api', 'tpa_claim_push_log']); // Fetch the data from DB $data = $this->db->table('ticket_master tm') @@ -174,7 +175,7 @@ class VidalApiController extends BaseController ->getRowArray(); // single record if (count($data) && $data['filePath'] == null) { - log_message('error', "VIDAL - Claim Push | Submit claim failed - Claim or File Missing"); + 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']; } @@ -185,9 +186,9 @@ class VidalApiController extends BaseController // dd($data); // Upload file first - $upload = $this->uploadFileToVidal($filePath,$filename); + $upload = $this->uploadFileToVidal($filePath, $filename, $claimId); if ($upload['status'] !== true) { - log_message('error', "VIDAL - Claim Push | Submit claim failed - File upload failed"); + tpa_claim_push_log($claimId, "VIDAL - Claim Push | Submit claim failed - File upload failed"); return $this->response->setJSON([ 'status' => false, 'message' => 'File upload failed', @@ -247,7 +248,7 @@ class VidalApiController extends BaseController ]; // dd($body); - log_message('error', 'VIDAL - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); + tpa_claim_push_log($claimId, 'VIDAL - Claim Push | claimId: '.$claimId.' | payload: '.json_encode($body)); // $body = [ // 'policyNo' => "351500/D0534/PP/20-20/PC", @@ -279,7 +280,7 @@ class VidalApiController extends BaseController $response = call_third_party_api($url, $method, $headers, $body); if($response['status'] != true){ - log_message('error', 'VIDAL - Claim Push FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); + 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) ]); @@ -295,7 +296,7 @@ class VidalApiController extends BaseController if(!empty($claimNO) && !empty($claimInwardNO)){ - log_message('error', 'VIDAL - Claim Push SUCCESS | claimId: '.$claimId.' | claimNO: '.$claimNO.' | claimInwardNO: '.$claimInwardNO); + tpa_claim_push_log($claimId, 'VIDAL - Claim Push SUCCESS | claimId: '.$claimId.' | claimNO: '.$claimNO.' | claimInwardNO: '.$claimInwardNO); $this->db->table('ticket_master') ->where('id',$claimId) @@ -308,19 +309,19 @@ class VidalApiController extends BaseController ->where('id', $tpa_sent_file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); - log_message('error','MEDI_ASSIST - Claim Push | Updating claim_files table for file_id: '.$tpa_sent_file_id); + tpa_claim_push_log($claimId, 'VIDAL - Claim Push | Updating claim_files table for file_id: '.$tpa_sent_file_id); }else{ - log_message('error','MEDI_ASSIST - Claim Push | No file_id found to update claim_files table.'); + 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 { - log_message('error', 'VIDAL - Claim Push API SUCCESS BUT claimNO,claimInwardNO EMPTY | claimId: '.$claimId.' | response: '.json_encode($response)); + 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{ - log_message('error', 'VIDAL - Claim Push API FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); + tpa_claim_push_log($claimId, 'VIDAL - Claim Push API FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); return ['status' => false, 'message' => 'Claim Push API FAILED']; } diff --git a/app/Controllers/VoloApiController.php b/app/Controllers/VoloApiController.php index b1bc0a4b..c256ecfa 100644 --- a/app/Controllers/VoloApiController.php +++ b/app/Controllers/VoloApiController.php @@ -682,7 +682,7 @@ class VoloApiController extends BaseController */ public function SubmitClaim($claimId = null) { - helper('api'); + helper(['api', 'tpa_claim_push_log']); $data = $this->db->table('ticket_master tm') ->select(' @@ -702,12 +702,12 @@ class VoloApiController extends BaseController ->getRowArray(); if (!$data) { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | claim not found'); + 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'])) { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | file missing'); + tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | file missing'); return ['status' => false, 'message' => 'Claim Push FAILED | File Missing']; } @@ -715,7 +715,7 @@ class VoloApiController extends BaseController $filename = basename($data['filePath']); $pdfPath = WRITEPATH . 'uploads/claim_files/' . $filename; if (!is_readable($pdfPath)) { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | PDF not readable'); + tpa_claim_push_log($claimId, 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | PDF not readable'); return ['status' => false, 'message' => 'Claim Push FAILED | PDF not found on server']; } @@ -723,13 +723,13 @@ class VoloApiController extends BaseController $entityId = $this->resolveEntityId($data['policyNo'] ?? ''); if ($entityId === null) { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | entity id not resolved'); + 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 === '') { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | VOLO_DEFAULT_HOSPITAL_ID not set'); + 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']; } @@ -754,12 +754,12 @@ class VoloApiController extends BaseController 'insurerPolicyNumber' => (string) ($data['policyNo'] ?? ''), ]; - log_message('error', 'VOLO - Claim Push | claimId: ' . $claimId . ' | payload: ' . json_encode($payload)); + tpa_claim_push_log($claimId, 'VOLO - Claim Push | claimId: ' . $claimId . ' | payload: ' . json_encode($payload)); $response = $this->intimateClaim($payload); if (empty($response['status'])) { - log_message('error', 'VOLO - Claim Push FAILED | claimId: ' . $claimId . ' | ' . json_encode($response)); + 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)]); @@ -781,7 +781,7 @@ class VoloApiController extends BaseController 'tpa_claim_id' => $ref, 'updated_at' => date('Y-m-d H:i:s'), ]); - log_message('error', 'VOLO - Claim Push SUCCESS | claimId: ' . $claimId . ' | ref: ' . $ref); + 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)){ @@ -790,15 +790,15 @@ class VoloApiController extends BaseController ->where('id', $file_id) ->update([ 'is_file_sent_to_tpa' => 1 ]); - log_message('error','VOLO - Claim Push | Updating claim_files table for file_id: '.$file_id); + tpa_claim_push_log($claimId, 'VOLO - Claim Push | Updating claim_files table for file_id: '.$file_id); }else{ - log_message('error','VOLO - Claim Push | No file_id found to update claim_files table.'); + 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]; } - log_message('error', 'VOLO - Claim Push empty reference | claimId: ' . $claimId . ' | ' . json_encode($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]; } diff --git a/app/Helpers/MailHelper.php b/app/Helpers/MailHelper.php index 6c16ca8a..0999dc4b 100755 --- a/app/Helpers/MailHelper.php +++ b/app/Helpers/MailHelper.php @@ -183,12 +183,24 @@ use App\Models\JobModel; class MailHelper { + private const SYSTEM_EMAIL_FOOTER = '
+ This is a system-generated email. Please do not reply. +
'; public static function send_email_smtp($params) { $myLogger = \Config\Services::mylogger(); $emaill = $params['mail']; $subject = $params['subject']; $message = $params['message']; + $message .= self::SYSTEM_EMAIL_FOOTER; if (isset($params['bcc'])) { $bcc = $params['bcc']; } else { @@ -206,7 +218,8 @@ class MailHelper $curl = curl_init(); $postData = [ 'from' => [ - 'address' => $from_address + 'address' => $from_address, + 'name' => 'Nhance India Insurance' ], 'to' => [ [ @@ -315,6 +328,9 @@ class MailHelper $subject = $params['subject']; $message = $params['message']; + $message .= self::SYSTEM_EMAIL_FOOTER; + + // echo $message;die; $attachments = isset($params['attachments']) ? $params['attachments'] : []; $common = isset($params['common']) ? $params['common'] : ''; $bcc = isset($params['bcc']) ? $params['bcc'] : ''; @@ -328,7 +344,8 @@ class MailHelper $postData = [ 'from' => [ - 'address' => $from_address + 'address' => $from_address, + 'name' => "Nhance India Insurance" ], 'to' => $emails, 'subject' => $subject, diff --git a/app/Helpers/tpa_claim_push_log_helper.php b/app/Helpers/tpa_claim_push_log_helper.php new file mode 100644 index 00000000..cf9b2cc9 --- /dev/null +++ b/app/Helpers/tpa_claim_push_log_helper.php @@ -0,0 +1,48 @@ +table('ticket_master') + ->where('id', $claimId) + ->update(['tpa_claim_push_logs' => "[{$started}] Claim push started\n"]); + } +} + +if (!function_exists('tpa_claim_push_log')) { + /** + * Write to application log and append the same line to ticket_master.tpa_claim_push_logs. + */ + function tpa_claim_push_log($claimId, string $message, string $level = 'error'): void + { + log_message($level, $message); + + if ($claimId === null || $claimId === '') { + return; + } + + $timestamp = date('Y-m-d H:i:s'); + $line = "[{$timestamp}] {$message}\n"; + + $db = \Config\Database::connect(); + $row = $db->table('ticket_master') + ->select('tpa_claim_push_logs') + ->where('id', $claimId) + ->get() + ->getRowArray(); + + $existing = $row['tpa_claim_push_logs'] ?? ''; + $db->table('ticket_master') + ->where('id', $claimId) + ->update(['tpa_claim_push_logs' => $existing . $line]); + } +} diff --git a/app/Models/ClaimsCollectionV2DashboardModel.php b/app/Models/ClaimsCollectionV2DashboardModel.php new file mode 100644 index 00000000..447dcee0 --- /dev/null +++ b/app/Models/ClaimsCollectionV2DashboardModel.php @@ -0,0 +1,2182 @@ + PHP method name. + */ + public const KPI_MAP = [ + 181 => 'policy_exposure_summary', + 185 => 'premium_as_on_date', + 186 => 'claims_experience_summary', + 190 => 'claim_amount_by_gender', + 191 => 'age_band', + 194 => 'top_5_hospitals_by_incurred_amount', + 197 => 'claims_incidence_rate', + 198 => 'policy_start_date', + 199 => 'policy_end_date', + 200 => 'insurer', + 201 => 'tpa', + 203 => 'earned_premium', + 204 => 'total_claims', + 206 => 'incurred_amount', + 207 => 'incurred_ratio', + 208 => 'projected_claims', + 209 => 'projected_ratio', + 213 => 'total_reimbursement_amount', + 214 => 'total_reimbursement_amt_pct', + 215 => 'cashless_claim_amt', + 216 => 'cashless_claim_amt_pct', + 217 => 'total_incurred_by_city', + 219 => 'claim_amount_by_claim_status', + 220 => 'hospitals_in_detail', + 221 => 'hospital_city_wise_si_limit_pregnancy', + 224 => 's_pregnancy_normal_delivery_exceeded_amt', + 225 => 's_pregnancy_c_sec_avg_exceeded_amt', + 228 => 'cataract_exceeded_claim_amount', + 229 => 'cataract_avg_exceeded_amount', + 230 => 'hospital_city_wise_si_limit_cataract', + 231 => 'total_incurred_by_cliam_status', + 232 => 'inception_emp_lives', + 233 => 'current_emp_lives', + 234 => 'claim_value_by_month', + ]; + + /** + * Human-readable KPI labels (Metabase name). + */ + public const KPI_LABELS = [ + 'policy_exposure_summary' => 'POLICY & EXPOSURE SUMMARY', + 'premium_as_on_date' => 'PREMIUM AS ON DATE', + 'claims_experience_summary' => 'CLAIMS EXPERIENCE SUMMARY', + 'claim_amount_by_gender' => 'Claim Amount by Gender', + 'age_band' => 'Age Band', + 'top_5_hospitals_by_incurred_amount' => 'Top 5 Hospitals by Incurred amount', + 'claims_incidence_rate' => 'Claims Incidence Rate', + 'policy_start_date' => 'Policy Start Date', + 'policy_end_date' => 'Policy End Date', + 'insurer' => 'Insurer', + 'tpa' => 'TPA', + 'earned_premium' => 'Earned Premium', + 'total_claims' => 'Total Claims', + 'incurred_amount' => 'Incurred Amount', + 'incurred_ratio' => 'Incurred Ratio', + 'projected_claims' => 'Projected Claims', + 'projected_ratio' => 'Projected Ratio', + 'total_reimbursement_amount' => 'Total Reimbursement Amount', + 'total_reimbursement_amt_pct' => 'Total Reimbursement Amt %', + 'cashless_claim_amt' => 'Cashless Claim Amt', + 'cashless_claim_amt_pct' => 'Cashless Claim Amt %', + 'total_incurred_by_city' => 'Total Incurred by city', + 'claim_amount_by_claim_status' => 'Claim Amount by Claim Status', + 'hospitals_in_detail' => 'Hospitals in detail', + 'hospital_city_wise_si_limit_pregnancy' => 'Hospital city wise SI Limit - Pregnancy', + 's_pregnancy_normal_delivery_exceeded_amt' => 'S-PREGNANCY - NORMAL DELIVERY Exceeded Amt', + 's_pregnancy_c_sec_avg_exceeded_amt' => 'S-Pregnancy C-Sec avg exceeded amt', + 'cataract_exceeded_claim_amount' => 'Cataract exceeded claim amount', + 'cataract_avg_exceeded_amount' => 'Cataract avg exceeded amount', + 'hospital_city_wise_si_limit_cataract' => 'Hospital city wise SI Limit - Cataract', + 'total_incurred_by_cliam_status' => 'Total Incurred by Cliam Status ', + 'inception_emp_lives' => 'Inception Employees & Lives', + 'current_emp_lives' => 'Current Employees & Lives', + 'claim_value_by_month' => 'Claim Value by Month', + ]; + + protected function runKpiQuery(string $sql, int $policyId): array + { + // Metabase export may contain literal \t / \n in CSV — normalize for MySQL. + $sql = str_replace(['\\t', '\\n', '\\r'], ["\t", "\n", "\r"], $sql); + + $db = \Config\Database::connect($this->DBGroup); + $query = $db->query($sql, ['policy_id' => $policyId]); + + return $query->getResultArray(); + } + + public function getKpi(string $method, int $policyId): array + { + if (!method_exists($this, $method)) { + return []; + } + return $this->{$method}($policyId); + } + + public function getAllKpis(int $policyId): array + { + $out = []; + foreach (self::KPI_MAP as $id => $method) { + $out[$method] = [ + 'id' => (int) $id, + 'label' => self::KPI_LABELS[$method] ?? $method, + 'rows' => $this->getKpi($method, $policyId), + ]; + } + return $out; + } + + /** Metabase #181: POLICY & EXPOSURE SUMMARY */ + public function policy_exposure_summary(int $policyId): array + { + $sql = <<<'SQL' +SELECT + cp.policy_start_date, + cp.policy_end_date, + i.name AS insurer_name, + t.name AS tpa_name +FROM client_policy cp +LEFT JOIN insurers i ON i.id = cp.insurer_id +LEFT JOIN tpa t ON t.id = cp.tpa_id +WHERE cp.id = :policy_id: +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #185: PREMIUM AS ON DATE */ + public function premium_as_on_date(int $policyId): array + { + $sql = <<<'SQL' +SELECT + CONCAT('💰 ', FORMAT( + COALESCE(SUM( + CASE + WHEN LOWER(e.change_event) = '%inception%' + THEN ep.rata_premimum + WHEN LOWER(e.change_event) NOT LIKE '%inception%' + AND (ep.date_of_exit IS NULL OR ep.date_of_exit = '') + THEN ep.rata_premimum + WHEN ep.date_of_exit IS NOT NULL + AND ep.date_of_exit != '' + AND ep.claim_status = 0 + THEN -ep.rata_premimum + ELSE 0 + END + ), 0) + , 0)) AS premium_as_on_date + +FROM employee_polices ep +JOIN employees e ON e.id = ep.employee_id +WHERE ep.client_policy_id = :policy_id: + AND ep.is_active = 1; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #186: CLAIMS EXPERIENCE SUMMARY */ + public function claims_experience_summary(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT( + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0) AS projected_claims, + + /* projected ratio */ + CONCAT( + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #190: Claim Amount by Gender */ + public function claim_amount_by_gender(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(e.gender, 'Unknown') AS gender, + COUNT(tm.id) AS claim_count, + CONCAT(ROUND(COUNT(tm.id) / totals.total_count * 100, 2), '%') + AS count_pct, + + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + AS claim_value, + CONCAT(ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2), '%') AS value_pct +FROM ticket_master tm +JOIN employees e ON e.id = tm.emp_id +JOIN ( + SELECT + COUNT(id) AS total_count, + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: AND is_active = 1 +) totals ON 1=1 +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 +GROUP BY e.gender, totals.total_count, totals.total_value +ORDER BY claim_count DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #191: Age Band */ + public function age_band(int $policyId): array + { + $sql = <<<'SQL' +WITH age_slabs AS ( + SELECT '0-18' AS age_band, 0 AS min_age, 18 AS max_age UNION ALL + SELECT '19-25', 19, 25 UNION ALL + SELECT '26-35', 26, 35 UNION ALL + SELECT '36-45', 36, 45 UNION ALL + SELECT '46-55', 46, 55 UNION ALL + SELECT '56-65', 56, 65 UNION ALL + SELECT '66-75', 66, 75 UNION ALL + SELECT '75+', 76, 200 +), + +base AS ( + SELECT + TIMESTAMPDIFF(YEAR, e.dob, cp.policy_end_date) AS age, + tm.claim_amount + FROM ticket_master tm + JOIN employees e ON e.id = tm.insured_emp_id +\tJOIN client_policy cp ON tm.client_policy_id = cp.id + WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 +), + +totals AS ( + SELECT + COUNT(*) AS total_count, + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) + +SELECT + s.age_band, + + COUNT(b.age) AS claim_count, + + ROUND(COUNT(b.age) / NULLIF(t.total_count, 0) * 100, 2) AS count_pct, + + COALESCE(SUM(CAST(NULLIF(b.claim_amount, '') AS DECIMAL(15,2))), 0) AS claim_value, + + ROUND( + COALESCE(SUM(CAST(NULLIF(b.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(t.total_value, 0) * 100 + , 2) AS value_pct + +FROM age_slabs s + +LEFT JOIN base b + ON b.age BETWEEN s.min_age AND s.max_age + +CROSS JOIN totals t + +GROUP BY s.age_band, s.min_age, t.total_count, t.total_value + +ORDER BY s.min_age; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #194: Top 5 Hospitals by Incurred amount */ + public function top_5_hospitals_by_incurred_amount(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(tm.hospital_name, 'Not Specified') AS hospital_name, + COALESCE(tm.hospital_city, '-') AS hospital_city, + COALESCE(tm.hospital_state, '-') AS hospital_state, + + COUNT(tm.id) AS claim_count, + + ROUND( + COUNT(tm.id) / totals.total_count * 100, + 2 + ) AS count_pct, + + COALESCE( + SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), + 0 + ) AS claim_value, + + ROUND( + COALESCE( + SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), + 0 + ) / NULLIF(totals.total_value, 0) * 100, + 2 + ) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COUNT(id) AS total_count, + + COALESCE( + SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), + 0 + ) AS total_value + + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND tm.hospital_name IS NOT NULL + +GROUP BY + tm.hospital_name, + tm.hospital_city, + tm.hospital_state, + totals.total_count, + totals.total_value + +ORDER BY claim_count DESC + +LIMIT 5; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #197: Claims Incidence Rate */ + public function claims_incidence_rate(int $policyId): array + { + $sql = <<<'SQL' +SELECT + CONCAT( + ROUND( + COUNT(tm.id) / NULLIF(lives.current_lives, 0) * 100 + , 2) + , '%') AS claims_incidence_rate +FROM ticket_master tm +JOIN ( + SELECT COUNT(DISTINCT ep.employee_id) AS current_lives + FROM employee_polices ep + JOIN employees e ON e.id = ep.employee_id + WHERE ep.client_policy_id = :policy_id: + AND ep.status = 'active' + AND ep.is_active = 1 + AND e.emp_status = 'active' + AND e.is_active = 1 +) lives ON 1=1 +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #198: Policy Start Date */ + public function policy_start_date(int $policyId): array + { + $sql = <<<'SQL' +SELECT + CONCAT(DATE_FORMAT(cp.policy_start_date, '%d %b, %Y'), ' 📅') AS policy_start_date, + -- cp.policy_start_date, + cp.policy_end_date, + i.name AS insurer_name, + t.name AS tpa_name +FROM client_policy cp +LEFT JOIN insurers i ON i.id = cp.insurer_id +LEFT JOIN tpa t ON t.id = cp.tpa_id +WHERE cp.id = :policy_id: +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #199: Policy End Date */ + public function policy_end_date(int $policyId): array + { + $sql = <<<'SQL' +SELECT + cp.policy_start_date,CONCAT(DATE_FORMAT(cp.policy_end_date, '%d %b, %Y'), ' 📅') AS policy_end_date, + -- cp.policy_end_date, + i.name AS insurer_name, + t.name AS tpa_name +FROM client_policy cp +LEFT JOIN insurers i ON i.id = cp.insurer_id +LEFT JOIN tpa t ON t.id = cp.tpa_id +WHERE cp.id = :policy_id: +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #200: Insurer */ + public function insurer(int $policyId): array + { + $sql = <<<'SQL' +SELECT + cp.policy_start_date, + cp.policy_end_date, + i.name AS insurer_name, + t.short_name AS tpa_name +FROM client_policy cp +LEFT JOIN insurers i ON i.id = cp.insurer_id +LEFT JOIN tpa t ON t.id = cp.tpa_id +WHERE cp.id = :policy_id: +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #201: TPA */ + public function tpa(int $policyId): array + { + $sql = <<<'SQL' +SELECT + cp.policy_start_date, + cp.policy_end_date, + i.name AS insurer_name, + t.name AS tpa_name +FROM client_policy cp +LEFT JOIN insurers i ON i.id = cp.insurer_id +LEFT JOIN tpa t ON t.id = cp.tpa_id +WHERE cp.id = :policy_id: +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #203: Earned Premium */ + public function earned_premium(int $policyId): array + { + $sql = <<<'SQL' +SELECT + + + /* earned premium */ + + CONCAT('💵 ', FORMAT( (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + ,0))AS earned_premium +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #204: Total Claims */ + public function total_claims(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT( + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0) AS projected_claims, + + /* projected ratio */ + CONCAT( + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #206: Incurred Amount */ + public function incurred_amount(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT( + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0) AS projected_claims, + + /* projected ratio */ + CONCAT( + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #207: Incurred Ratio */ + public function incurred_ratio(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT('💹 ', + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0) AS projected_claims, + + /* projected ratio */ + CONCAT( + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #208: Projected Claims */ + public function projected_claims(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT( + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + + CONCAT('🎯 ', FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0)) AS projected_claims, + + /* projected ratio */ + CONCAT( + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #209: Projected Ratio */ + public function projected_ratio(int $policyId): array + { + $sql = <<<'SQL' +SELECT + /* run days */ + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) AS run_days, + + /* earned premium */ + FORMAT( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) AS earned_premium, + + /* claims reported */ + FORMAT(COUNT(tm.id), 0) AS claims_reported, +\t\t/* incurred claims count */ +\tFORMAT( +\t SUM( +\t CASE +\t WHEN tcs.claim_status IN ('Settled', 'Paid') THEN 1 +\t WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') THEN 1 +\t ELSE 0 +\t END +\t ) +, 0) AS incurred_claims, + + /* incurred amount */ + FORMAT( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + , 0) AS incurred_amount, + + /* incurred ratio */ + CONCAT( + ROUND( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + (prem.premium_as_on_date / 365) + * DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ) + , 0) * 100 + , 2) + , '%') AS incurred_ratio, + + /* rejection rate */ + CONCAT( + ROUND( + SUM(CASE WHEN tcs.claim_status IN ('Rejected', 'Denied') THEN 1 ELSE 0 END) + / NULLIF(COUNT(tm.id), 0) * 100 + , 2) + , '%') AS rejection_rate, + + /* projected claims */ + FORMAT( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + , 0) AS projected_claims, + + /* projected ratio */ + CONCAT('💹 ', + ROUND( + ( + ( + COALESCE(SUM( + CASE + WHEN tcs.claim_status IN ('Settled', 'Paid') + THEN CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + WHEN tcs.claim_status NOT IN ('Settled', 'Paid', 'Rejected', 'Denied') + THEN CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2)) + ELSE 0 + END + ), 0) + / NULLIF( + DATEDIFF( + MAX(COALESCE(tm.claim_dump_date, cp.policy_end_date)), + cp.policy_start_date + ), 0 + ) + * 365 + ) * 1.10 + ) + / NULLIF(prem.premium_as_on_date, 0) * 100 + , 2) + , '%') AS projected_ratio + +FROM client_policy cp + +JOIN ( + SELECT + ep2.client_policy_id, + COALESCE(SUM( + CASE + WHEN e2.change_event = 'inception' + THEN ep2.rata_premimum + WHEN e2.change_event != 'inception' + AND (ep2.date_of_exit IS NULL OR ep2.date_of_exit = '') + THEN ep2.rata_premimum + WHEN ep2.date_of_exit IS NOT NULL + AND ep2.date_of_exit != '' + AND ep2.claim_status = 0 + THEN -ep2.rata_premimum + ELSE 0 + END + ), 0) AS premium_as_on_date + FROM employee_polices ep2 + JOIN employees e2 ON e2.id = ep2.employee_id + WHERE ep2.client_policy_id = :policy_id: + AND ep2.is_active = 1 + GROUP BY ep2.client_policy_id +) prem ON prem.client_policy_id = cp.id + +JOIN ticket_master tm + ON tm.client_policy_id = cp.id + AND tm.is_active = 1 + +JOIN ticket_claim_status tcs + ON tcs.id = tm.claim_status_id + +WHERE cp.id = :policy_id: + +GROUP BY cp.id, cp.policy_start_date, prem.premium_as_on_date; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #213: Total Reimbursement Amount */ + public function total_reimbursement_amount(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) AS claim_value, + + ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND ( + tm.tpa_claim_type IS NULL + OR TRIM(LOWER(tm.tpa_claim_type)) IN ('unknown', 'reimbursement', '') + ); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #214: Total Reimbursement Amt % */ + public function total_reimbursement_amt_pct(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) AS claim_value, + + ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND ( + tm.tpa_claim_type IS NULL + OR TRIM(LOWER(tm.tpa_claim_type)) IN ('unknown', 'reimbursement', '') + ); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #215: Cashless Claim Amt */ + public function cashless_claim_amt(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) AS claim_value, + + ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND TRIM(LOWER(tm.tpa_claim_type)) = 'cashless'; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #216: Cashless Claim Amt % */ + public function cashless_claim_amt_pct(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) AS claim_value, + + ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND TRIM(LOWER(tm.tpa_claim_type)) = 'cashless'; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #217: Total Incurred by city */ + public function total_incurred_by_city(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(tm.hospital_name, 'Not Specified') AS hospital_name, + COALESCE(tm.hospital_city, '-') AS hospital_city, + COALESCE(tm.hospital_state, '-') AS hospital_state, + + COUNT(tm.id) AS claim_count, + + ROUND( + COUNT(tm.id) / totals.total_count * 100, + 2 + ) AS count_pct, + + COALESCE( + SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), + 0 + ) AS claim_value, + + ROUND( + COALESCE( + SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), + 0 + ) / NULLIF(totals.total_value, 0) * 100, + 2 + ) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COUNT(id) AS total_count, + + COALESCE( + SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), + 0 + ) AS total_value + + FROM ticket_master + WHERE client_policy_id = :policy_id: + AND is_active = 1 +) totals ON 1=1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND tm.hospital_name IS NOT NULL + +GROUP BY + tm.hospital_name, + tm.hospital_city, + tm.hospital_state, + totals.total_count, + totals.total_value + +ORDER BY claim_count DESC + +LIMIT 5; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #219: Claim Amount by Claim Status */ + public function claim_amount_by_claim_status(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(tcs.display_name, 'Unknown') AS claim_status, + COUNT(tm.id) AS claim_count, + CONCAT(ROUND(COUNT(tm.id) / totals.total_count * 100, 2), '%') + AS count_pct, + + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + AS claim_value, + CONCAT(ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2), '%') AS value_pct +FROM ticket_master tm +JOIN ticket_claim_status tcs ON tcs.id = tm.claim_status_id +JOIN ( + SELECT + COUNT(id) AS total_count, + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: AND is_active = 1 +) totals ON 1=1 +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 +GROUP BY tcs.display_name, totals.total_count, totals.total_value +ORDER BY claim_count DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #220: Hospitals in detail */ + public function hospitals_in_detail(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(NULLIF(TRIM(tm.hospital_name), ''), 'Not Specified') AS hospital_name, + + COALESCE(NULLIF(TRIM(tm.hospital_city), ''), '-') AS hospital_city, + + COALESCE(NULLIF(TRIM(tm.hospital_state), ''), '-') AS hospital_state, + + COUNT(tm.id) AS claim_count, + + CAST( + GREATEST( + ROUND( + ( + COUNT(tm.id) / NULLIF(totals.total_count, 0) + ) * 100, + 2 + ), + 0.01 + ) AS DECIMAL(10,2) + ) AS count_pct, + + CAST( + COALESCE( + SUM( + CASE + WHEN TRIM(tm.claim_amount) REGEXP '^[0-9]+(\\\\.[0-9]+)?$' + THEN CAST(TRIM(tm.claim_amount) AS DECIMAL(15,2)) + ELSE 0 + END + ), + 0 + ) AS DECIMAL(15,2) + ) AS claim_value, + + CAST( + GREATEST( + ROUND( + ( + COALESCE( + SUM( + CASE + WHEN TRIM(tm.claim_amount) REGEXP '^[0-9]+(\\\\.[0-9]+)?$' + THEN CAST(TRIM(tm.claim_amount) AS DECIMAL(15,2)) + ELSE 0 + END + ), + 0 + ) / NULLIF(totals.total_value, 0) + ) * 100, + 2 + ), + 0.01 + ) AS DECIMAL(10,2) + ) AS value_pct + +FROM ticket_master tm + +JOIN ( + SELECT + COUNT(id) AS total_count, + + CAST( + COALESCE( + SUM( + CASE + WHEN TRIM(claim_amount) REGEXP '^[0-9]+(\\\\.[0-9]+)?$' + THEN CAST(TRIM(claim_amount) AS DECIMAL(15,2)) + ELSE 0 + END + ), + 0 + ) AS DECIMAL(15,2) + ) AS total_value + + FROM ticket_master + + WHERE client_policy_id = :policy_id: + AND is_active = 1 + +) totals ON 1 = 1 + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND NULLIF(TRIM(tm.hospital_name), '') IS NOT NULL + +GROUP BY + tm.hospital_name, + tm.hospital_city, + tm.hospital_state + +ORDER BY claim_count DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #221: Hospital city wise SI Limit - Pregnancy */ + public function hospital_city_wise_si_limit_pregnancy(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(tm.hospital_city, 'Not Specified') AS hospital_city, +\tCOALESCE(tm.hospital_name, 'Not Specified') AS hospital_name, + COUNT(tm.id) AS `Claim Count`, + + FORMAT( + COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0), + 0) AS `SUM OF CLAIMED AMOUNT`, + + CASE + WHEN COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0) + > + COALESCE( + SUM( + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)) + ), + 0) + THEN 'Exceeded' + ELSE 'Within Limit' + END AS `CLAIM STATUS LIMIT` + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND tm.hospital_name IS NOT NULL + + /* Pregnancy Related Conditions */ + AND ( + LOWER(tm.tpa_ailments) LIKE '%pregnancy%' + OR LOWER(tm.tpa_ailments) LIKE '%maternal%' + OR LOWER(tm.tpa_ailments) LIKE '%gestational%' + OR LOWER(tm.tpa_ailments) LIKE '%delivery%' + OR LOWER(tm.tpa_ailments) LIKE '%cesarean%' + OR LOWER(tm.tpa_ailments) LIKE '%caesarean%' + OR LOWER(tm.tpa_ailments) LIKE '%c-section%' + OR LOWER(tm.tpa_ailments) LIKE '%spontaneous delivery%' + OR LOWER(tm.tpa_ailments) LIKE '%normal delivery%' + OR LOWER(tm.tpa_ailments) LIKE '%forceps%' + OR LOWER(tm.tpa_ailments) LIKE '%vaccum extractor%' + ) + +GROUP BY tm.hospital_city,tm.hospital_name + +ORDER BY + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ) DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #224: S-PREGNANCY - NORMAL DELIVERY Exceeded Amt */ + public function s_pregnancy_normal_delivery_exceeded_amt(int $policyId): array + { + $sql = <<<'SQL' +SELECT + 'SINGLE PREGNANCY - NORMAL DELIVERY' AS diagnosis, + + COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ),0) AS exceeded_claim_amount + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + + AND ( + LOWER(tm.tpa_ailments) LIKE '%spontaneous delivery%' + OR LOWER(tm.tpa_ailments) LIKE '%normal delivery%' + OR LOWER(tm.tpa_ailments) LIKE '%forceps%' + OR LOWER(tm.tpa_ailments) LIKE '%vaccum extractor%' + ) + + AND CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + > + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #225: S-Pregnancy C-Sec avg exceeded amt */ + public function s_pregnancy_c_sec_avg_exceeded_amt(int $policyId): array + { + $sql = <<<'SQL' +SELECT + 'SINGLE PREGNANCY - CAESAREAN DELIVERY' AS diagnosis, + + COALESCE( + AVG( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 2) AS avg_exceeded_amount + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + + AND ( + LOWER(tm.tpa_ailments) LIKE '%cesarean%' + OR LOWER(tm.tpa_ailments) LIKE '%caesarean%' + OR LOWER(tm.tpa_ailments) LIKE '%c-section%' + ) + + AND CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + > + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #228: Cataract exceeded claim amount */ + public function cataract_exceeded_claim_amount(int $policyId): array + { + $sql = <<<'SQL' +SELECT + 'CATARACT' AS diagnosis, + + + COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0) AS exceeded_claim_amount + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + + AND LOWER(tm.tpa_ailments) LIKE '%cataract%' + + AND CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + > + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #229: Cataract avg exceeded amount */ + public function cataract_avg_exceeded_amount(int $policyId): array + { + $sql = <<<'SQL' +SELECT + 'CATARACT' AS diagnosis, + + FORMAT( + COALESCE( + AVG( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0), + 2) AS avg_exceeded_claim_amount + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + + AND LOWER(tm.tpa_ailments) LIKE '%cataract%' + + AND CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + > + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)); +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #230: Hospital city wise SI Limit - Cataract */ + public function hospital_city_wise_si_limit_cataract(int $policyId): array + { + $sql = <<<'SQL' +SELECT +\tCOALESCE(tm.hospital_city, 'Not Specified') AS hospital_city, + COALESCE(tm.hospital_name, 'Not Specified') AS hospital_name, + + COUNT(tm.id) AS claim_count, + + FORMAT( + COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0), + 0) AS `SUM OF CLAIMED AMOUNT`, + + CASE + WHEN COALESCE( + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ), + 0) + > + COALESCE( + SUM( + CAST(NULLIF(ep.basic_cover_si, '') AS DECIMAL(15,2)) + ), + 0) + THEN 'Exceeded' + ELSE 'Within Limit' + END AS `CLAIM STATUS LIMIT` + +FROM ticket_master tm + +INNER JOIN employee_polices ep + ON ep.employee_id = tm.emp_id + AND ep.client_policy_id = tm.client_policy_id + AND ep.is_active = 1 + AND ep.status = 'active' + +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 + AND tm.hospital_name IS NOT NULL + + /* Cataract Related */ + AND LOWER(tm.tpa_ailments) LIKE '%cataract%' + +GROUP BY tm.hospital_city,tm.hospital_name + +ORDER BY + SUM( + CAST(NULLIF(tm.approved_amount, '') AS DECIMAL(15,2)) + ) DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #231: Total Incurred by Cliam Status */ + public function total_incurred_by_cliam_status(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COALESCE(tcs.display_name, 'Unknown') AS `Claim Status`, + -- COUNT(tm.id) AS claim_count, + -- CONCAT(ROUND(COUNT(tm.id) / totals.total_count * 100, 2), '%') + -- AS count_pct, + + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + AS `Total Incurred Amount`, + CONCAT(ROUND( + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + / NULLIF(totals.total_value, 0) * 100 + , 2), '%') AS `Value PCT` +FROM ticket_master tm +JOIN ticket_claim_status tcs ON tcs.id = tm.claim_status_id +JOIN ( + SELECT + COUNT(id) AS total_count, + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: AND is_active = 1 +) totals ON 1=1 +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 +GROUP BY tcs.display_name, totals.total_count, totals.total_value +-- ORDER BY claim_count DESC; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #232: Inception Employees & Lives */ + public function inception_emp_lives(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COUNT(DISTINCT CASE + WHEN e.relationship = 'Self' THEN e.id + END) AS inception_emp, + + COUNT(DISTINCT e.id) AS inception_lives + +FROM employee_polices ep +JOIN employees e ON e.id = ep.employee_id +WHERE ep.client_policy_id = :policy_id: + AND LOWER(e.change_event) LIKE '%inception%' + AND ep.is_active = 1 + AND e.is_active = 1; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #233: Current Employees & Lives */ + public function current_emp_lives(int $policyId): array + { + $sql = <<<'SQL' +SELECT + COUNT(DISTINCT CASE + WHEN e.relationship = 'Self' THEN e.id + END) AS current_emp, + + COUNT(DISTINCT e.id) AS current_lives, + + ROUND( + COUNT(DISTINCT e.id) + / NULLIF(COUNT(DISTINCT CASE + WHEN e.relationship = 'Self' THEN e.id + END), 0) + , 2) AS avg_family_size + +FROM employee_polices ep +JOIN employees e ON e.id = ep.employee_id +WHERE ep.client_policy_id = :policy_id: + AND ep.status = 'active' + AND ep.is_active = 1 + AND e.emp_status = 'active' + AND e.is_active = 1; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + + /** Metabase #234: Claim Value by Month */ + public function claim_value_by_month(int $policyId): array + { + $sql = <<<'SQL' +SELECT + DATE_FORMAT(tm.created_at, '%b %Y') AS claim_month, + DATE_FORMAT(tm.created_at, '%Y-%m') AS month_sort, + -- COUNT(tm.id) AS claim_count, + -- CONCAT(ROUND(COUNT(tm.id) / totals.total_count * 100, 2), '%') AS count_pct, + + COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + AS claim_value + -- CONCAT(ROUND( + -- COALESCE(SUM(CAST(NULLIF(tm.claim_amount, '') AS DECIMAL(15,2))), 0) + -- / NULLIF(totals.total_value, 0) * 100 + -- , 2), '%') AS value_pct +FROM ticket_master tm +JOIN ( + SELECT + COUNT(id) AS total_count, + COALESCE(SUM(CAST(NULLIF(claim_amount, '') AS DECIMAL(15,2))), 0) AS total_value + FROM ticket_master + WHERE client_policy_id = :policy_id: AND is_active = 1 +) totals ON 1=1 +WHERE tm.client_policy_id = :policy_id: + AND tm.is_active = 1 +GROUP BY + DATE_FORMAT(tm.created_at, '%b %Y'), + DATE_FORMAT(tm.created_at, '%Y-%m'), + totals.total_count, + totals.total_value +ORDER BY month_sort; +SQL; + return $this->runKpiQuery($sql, $policyId); + } + +} diff --git a/app/Models/ClientPolicyModel.php b/app/Models/ClientPolicyModel.php index c3acb681..b650d585 100755 --- a/app/Models/ClientPolicyModel.php +++ b/app/Models/ClientPolicyModel.php @@ -311,6 +311,24 @@ class ClientPolicyModel extends Model // Fetch the deposit data based on client and insurer IDs $query = $this->db->table('cash_deposit') ->select('cash_deposit.*') + ->select(" + (select old_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as old_amt, + (select new_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as new_amt, + (select created_by from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_by, + (select created_at from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_at, + ( + select up.first_name + from user_profiles up + where up.id = ( + select created_by + from auditing_history + where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' + order by id desc + limit 1 + ) + limit 1 + ) as amt_updated_by_name + ") ->select('insurers.name as insurer_name, insurers.short_name as insurer_short') ->select('clients.client_name as clientname, clients.short_name as clientshort, client_policy.policy_no') ->select('cd_master.cd_ac_no as cd_master_account_no') diff --git a/app/Views/apache_dashboard_demo_one.php b/app/Views/apache_dashboard_demo_one.php new file mode 100644 index 00000000..fe1da55a --- /dev/null +++ b/app/Views/apache_dashboard_demo_one.php @@ -0,0 +1,113 @@ + + + + + + Claims Overview + + + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/app/Views/batch_list.php b/app/Views/batch_list.php index 5ebdebc5..61d395e7 100755 --- a/app/Views/batch_list.php +++ b/app/Views/batch_list.php @@ -34,7 +34,7 @@ if (!empty($batch_list) && is_array($batch_list)) { $countStr = ($file['count'] ?? null) === null ? '-' : (string) $file['count']; $batch_col_max_len[9] = max($batch_col_max_len[9], mb_strlen($countStr)); $batch_col_max_len[10] = max($batch_col_max_len[10], mb_strlen((string) format_indian_number($file['amount']))); - $userTime = change_date_format($file['created_at'] ?? '', 'Y-m-d H:i:s', 'd M Y h:i a') + $userTime = change_date_format($file['created_at'] ?? '', 'Y-m-d H:i:s', 'd/m/Y h:i a') . ' by ' . get_username($file['created_by'] ?? ''); $batch_col_max_len[11] = max($batch_col_max_len[11], mb_strlen($userTime)); @@ -350,7 +350,7 @@ for ($i = 0; $i < $batch_col_count; $i++) { - by + by diff --git a/app/Views/bds_dump_file_list.php b/app/Views/bds_dump_file_list.php index b56448fa..2ea6207a 100644 --- a/app/Views/bds_dump_file_list.php +++ b/app/Views/bds_dump_file_list.php @@ -49,7 +49,7 @@ - ' . $file['user_name'] . '' ?> + ' . $file['user_name'] . '' ?> diff --git a/app/Views/cd_master_add_modal.php b/app/Views/cd_master_add_modal.php index e73b807f..29597a75 100644 --- a/app/Views/cd_master_add_modal.php +++ b/app/Views/cd_master_add_modal.php @@ -107,13 +107,50 @@ var isCdMasterPage = ; console.log("isCdMasterPage", isCdMasterPage); + function formatCdOpeningDateForInput(inputDate) { + if (!inputDate || String(inputDate).trim() === '') { + return ''; + } + var str = String(inputDate).trim(); + var iso = str.match(/^(\d{4})-(\d{2})-(\d{2})/); + if (iso) { + return iso[3] + '/' + iso[2] + '/' + iso[1]; + } + var dmy = str.match(/^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/); + if (dmy) { + var day = ('0' + dmy[1]).slice(-2); + var month = ('0' + dmy[2]).slice(-2); + return day + '/' + month + '/' + dmy[3]; + } + return str; + } + + function cdOpeningDateForSubmit(inputDate) { + var formatted = formatCdOpeningDateForInput(inputDate); + if (!formatted) { + return inputDate || ''; + } + var parts = formatted.split('/'); + return parts[0] + '-' + parts[1] + '-' + parts[2]; + } + + var openingDatePicker; + $(document).ready(function(){ - var openingDatePicker = flatpickr("#opening_date", { - dateFormat: "d-m-Y", + openingDatePicker = flatpickr("#opening_date", { + dateFormat: "d/m/Y", allowInput: false }); + $('#con-close-modal').on('shown.bs.modal', function() { + var val = $('#opening_date').val(); + if (val) { + var formatted = formatCdOpeningDateForInput(val); + openingDatePicker.setDate(formatted, false); + } + }); + if(isCdMasterPage == true){ $('#cd_client_id').select2(); $('#insurer_id_for_cd').select2(); @@ -172,6 +209,10 @@ } let formData = new FormData($('#CDMasterForm')[0]); + var openingDate = formData.get('opening_date'); + if (openingDate) { + formData.set('opening_date', cdOpeningDateForSubmit(openingDate)); + } console.log("formData", formData); let url = ''; diff --git a/app/Views/cd_master_list.php b/app/Views/cd_master_list.php index 1314066a..1bd8dc4e 100755 --- a/app/Views/cd_master_list.php +++ b/app/Views/cd_master_list.php @@ -15,12 +15,12 @@ foreach ($CD_Master_Data as $index => $row) { $cdm_col_max_len[1] = max($cdm_col_max_len[1], mb_strlen($clientCell)); $cdm_col_max_len[2] = max($cdm_col_max_len[2], mb_strlen((string) ($row['insurer_name'] ?? ''))); $cdm_col_max_len[3] = max($cdm_col_max_len[3], mb_strlen((string) ($row['insurer_branch_name'] ?? ''))); - $od = ! empty($row['opening_date']) ? date('d-m-Y', strtotime((string) $row['opening_date'])) : ''; + $od = ! empty($row['opening_date']) ? date('d/m/Y', strtotime((string) $row['opening_date'])) : ''; $cdm_col_max_len[4] = max($cdm_col_max_len[4], mb_strlen($od)); $cdm_col_max_len[5] = max($cdm_col_max_len[5], mb_strlen((string) ($row['cd_ac_no'] ?? ''))); $cdm_col_max_len[6] = max($cdm_col_max_len[6], mb_strlen((string) ($row['opening_bal'] ?? ''))); $du = ! empty($row['created_at']) - ? (date('d-M-Y h:i A', strtotime((string) $row['created_at'])) . ' by ' . ($row['user_name'] ?? '')) + ? (date('d/m/Y h:i A', strtotime((string) $row['created_at'])) . ' by ' . ($row['user_name'] ?? '')) : ''; $cdm_col_max_len[7] = max($cdm_col_max_len[7], mb_strlen($du)); $cdm_col_max_len[8] = max($cdm_col_max_len[8], 4); @@ -118,10 +118,10 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_ ( ) - + -
by +
by