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'] . '' ?> |
= $file['status'] ?>
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 = '= base_url('master/cash_deposite/create') ?>';
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_
| ( = $row['short_name'] ?> ) |
|
|
- |
+ |
|
|
- by |
+ by |
diff --git a/app/Views/claim_dump_file_list.php b/app/Views/claim_dump_file_list.php
index ee00683e..63d7cedf 100644
--- a/app/Views/claim_dump_file_list.php
+++ b/app/Views/claim_dump_file_list.php
@@ -161,7 +161,7 @@
|
- ' . $file['user_name'] . '' ?> |
+ ' . $file['user_name'] . '' ?> |
= $file['status'] ?>
@@ -262,7 +262,7 @@
diff --git a/app/Views/claims_collection_v2_dashboard.php b/app/Views/claims_collection_v2_dashboard.php
new file mode 100644
index 00000000..54093024
--- /dev/null
+++ b/app/Views/claims_collection_v2_dashboard.php
@@ -0,0 +1,103 @@
+
+
+
+
+
+ Claims Collection V2 Dashboard
+
+
+
+
+Claims Collection V2
+
+
+
+
+
+
+
+
+ $method): ?>
+
+ #= (int) $metabaseId ?> · = esc($kpi_labels[$method] ?? $method) ?>
+ = esc($method) ?>
+ Click “Load all KPIs” or open single KPI API.
+
+
+
+
+
+
+
diff --git a/app/Views/client_policy.php b/app/Views/client_policy.php
index 9e654e80..26bcab2c 100755
--- a/app/Views/client_policy.php
+++ b/app/Views/client_policy.php
@@ -209,7 +209,7 @@ input:checked + .slider_blue::before {
data-parsley-pattern="^[1-9][0-9]*$"
data-parsley-pattern-message="Please select a valid Policy Type.">
-
+
@@ -230,7 +230,7 @@ input:checked + .slider_blue::before {
| |