diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 4b2155c4..bb09d35b 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 @@ -460,6 +461,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 +777,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"); 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..7a3a89a1 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -6652,7 +6652,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/TestingController.php b/app/Controllers/TestingController.php index eecbfb45..5f80d397 100644 --- a/app/Controllers/TestingController.php +++ b/app/Controllers/TestingController.php @@ -1143,11 +1143,54 @@ class TestingController extends BaseController 'metabaseUrl' => 'https://nsights.nhanceindia.in', ]); } + public function apacheSuperSetDemo() + { - public function testingquerys1(){ + // 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; + $policy_id = $this->request->getGet('client_policy') ?? null; + $tpa_url = 'https://nsights.nhanceindia.in/public/dashboard/4babf324-6c1e-4c5a-adbb-1c80a0f545b1'; + $policy_id = $policy_id ? $policy_id : 4687; + $payload = [ + 'resource' => [ + // 'dashboard' => 1 + 'dashboard' => 2 + ], + '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) + { + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in'] + ]); + } + + return view('apache_dashboard_demo_one', [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ]); } - public function testingquerys() { $calendar = new \App\Libraries\GoogleCalendarService(); 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/Models/ClaimsCollectionV2DashboardModel.php b/app/Models/ClaimsCollectionV2DashboardModel.php new file mode 100644 index 00000000..d459d79a --- /dev/null +++ b/app/Models/ClaimsCollectionV2DashboardModel.php @@ -0,0 +1,2090 @@ + 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', + ]; + + /** + * 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 ', + ]; + + 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); + } + +} 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