diff --git a/app/Config/Acl.php b/app/Config/Acl.php index ce46852c..8b0476ea 100644 --- a/app/Config/Acl.php +++ b/app/Config/Acl.php @@ -44,7 +44,7 @@ class Acl '#^/sendDataToTPA#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]], '#^/swagger#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]], '#^/viewClaimFile#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID]], - + '#^/employee/tpaReportsDashboard#' => ['roles' => [ACCOUNT_MANAGER_ROLE_ID]], diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 32b98290..78a80888 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -35,6 +35,7 @@ $routes->get('/fedeploy', 'DeployController::fedeploy_view', ['filter' => 'authM $routes->post('/fedeploy', 'DeployController::fedeploy', ['filter' => 'authMVC']); $routes->get('/visitOffBoardCheck', 'EmployeeController::visitOffBoardCheck'); $routes->get('/metaDashboardDemo', 'TestingController::metaDashboardDemo'); +$routes->get('/apacheSuperSetDemo', 'TestingController::apacheSuperSetDemo'); $routes->get('/metaTpaDashboardDemo', 'TestingController::metaTpaDashboardDemo'); // Reminder Mail Notification @@ -49,6 +50,7 @@ $routes->get("updatajson", "EmpDataServiceController::updatajson"); $routes->get("view", "EmployeeController::viewECard/$1"); $routes->get("checkWellnessOnboardStatus/(:any)", "EmployeeController::checkWellnessOnboardStatus/$1"); $routes->get("initiateWellnessOnboard/(:any)", "EmployeeController::initiateWellnessOnboard/$1"); +$routes->get("resetWellnessOnboard/(:any)", "EmployeeController::resetWellnessOnboard/$1"); $routes->get("exportQCRandRFQ/(:any)", "LeadsController::exportQCRandRFQ/$1"); $routes->get("testMailAttachments", "ClientController::testMailAttachments"); $routes->get("updatePolicyTermsKey", "ClientController::updatePolicyTermsKey"); @@ -136,6 +138,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) { $routes->get('view_deposit/(:num)', 'ClientController::view_Deposit/$1'); $routes->get('createtransaction', 'ClientController::createtransaction'); $routes->post('save_deposit', 'ClientController::saveDeposit'); + $routes->post('update_deposit_amount', 'ClientController::updateDepositAmount'); $routes->post("saveApiData", "ClientController::saveApiData"); $routes->get("generateToken","ClientController::sendToken"); // $routes->get('view_Deposit/(:num)/(:num)','ClientController/view_Deposit/$1/$2'); @@ -239,7 +242,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3"); $routes->get('clearCdSession', 'EmployeeController::clearCdSession'); $routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus'); - + $routes->get("tpaReportsDashboard", "EmployeeController::tpaReportsDashboard"); }); @@ -459,6 +462,15 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->get('insertSampleTpaApiData/(:any)', 'TestingController::insertSampleTpaApiData/$1'); $routes->get('listEmployeeCountByClientPolicy', 'TestingController::listEmployeeCountByClientPolicy'); $routes->get('testMediAssistWellness','TestingController::testMediAssistWellness'); + + $routes->group('claims-collection-v2', static function ($routes) { + $routes->get('preview', 'ClaimsCollectionV2DashboardController::preview'); + $routes->get('preview/(:num)', 'ClaimsCollectionV2DashboardController::preview/$1'); + $routes->get('kpi/(:segment)', 'ClaimsCollectionV2DashboardController::kpi/$1'); + $routes->get('all', 'ClaimsCollectionV2DashboardController::all'); + $routes->get('debug', 'ClaimsCollectionV2DashboardController::debug'); + $routes->get('debug/(:num)', 'ClaimsCollectionV2DashboardController::debug/$1'); + }); }); $routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail"); @@ -766,6 +778,15 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel $routes->get("downloadPolicyFiles", "EmployeeRestController::downloadPolicyFiles"); $routes->post("bulkEcardDownloadAsZip", "EmployeeRestController::bulkEcardDownloadAsZip"); $routes->get("downloadSampleExcel/(:any)", "EmployeeController::downloadSampleExcelFile/$1"); + + $routes->group('claims-collection-v2', static function ($routes) { + $routes->get('preview', 'ClaimsCollectionV2DashboardController::preview'); + $routes->get('preview/(:num)', 'ClaimsCollectionV2DashboardController::preview/$1'); + $routes->get('kpi/(:segment)', 'ClaimsCollectionV2DashboardController::kpi/$1'); + $routes->get('all', 'ClaimsCollectionV2DashboardController::all'); + $routes->get('debug', 'ClaimsCollectionV2DashboardController::debug'); + $routes->get('debug/(:num)', 'ClaimsCollectionV2DashboardController::debug/$1'); + }); }); $routes->post("bulkEcardDownloadAsZip", "EmployeeRestController::bulkEcardDownloadAsZip"); 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 d14c8de7..4c9edd8d 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -902,6 +902,109 @@ class ClientController extends AdminController return $this->response->setJSON(['success' => $response['success']]); } + public function updateDepositAmount() + { + $rules = [ + 'deposit_id' => 'required|is_natural_no_zero', + 'amount' => 'required|numeric|greater_than_equal_to[0]', + 'transaction_type' => 'required|in_list[Credit,Debit]', + 'client_id' => 'required', + 'insurer_id' => 'required|is_natural_no_zero', + 'cd_ac_pk' => 'required|is_natural_no_zero', + ]; + + if (! $this->validate($rules)) { + return $this->response + ->setStatusCode(400) + ->setJSON([ + 'status' => 'error', + 'message' => 'Input validation failed', + 'errors' => $this->validator->getErrors(), + ]); + } + + $postData = sanitizeInputArrayAdvanced($this->request->getPost()); + $depositId = (int) ($postData['deposit_id'] ?? 0); + $clientId = $postData['client_id'] ?? null; + $insurerId = (int) ($postData['insurer_id'] ?? 0); + $cdAcPk = (int) ($postData['cd_ac_pk'] ?? 0); + $amount = (float) ($postData['amount'] ?? 0); + $transactionType = $postData['transaction_type'] ?? 'Credit'; + $loggedInUserID = get_session_userid(); + + $depositModel = new ClientDepositModel(); + + $targetRow = $depositModel + ->where('id', $depositId) + ->where('client_id', $clientId) + ->where('insurer_id', $insurerId) + ->where('cd_ac_pk', $cdAcPk) + ->where('is_active', 1) + ->first(); + + if (empty($targetRow)) { + return $this->response + ->setStatusCode(404) + ->setJSON([ + 'status' => 'error', + 'message' => 'Transaction not found for this account.', + ]); + } + + $existingType = $targetRow['transaction_type'] ?? ''; + if ($existingType !== $transactionType) { + return $this->response + ->setStatusCode(400) + ->setJSON([ + 'status' => 'error', + 'message' => 'Only ' . $existingType . ' value can be edited for this transaction.', + ]); + } + + $db = \Config\Database::connect(); + $db->transBegin(); + + $depositModel->update($depositId, [ + 'amount' => $amount, + 'transaction_type' => $transactionType, + 'updated_by' => $loggedInUserID, + ]); + + $rows = $depositModel + ->where('client_id', $clientId) + ->where('insurer_id', $insurerId) + ->where('cd_ac_pk', $cdAcPk) + ->where('is_active', 1) + ->orderBy('id', 'ASC') + ->findAll(); + + $runningBalance = 0.0; + foreach ($rows as $row) { + $rowAmount = (float) ($row['amount'] ?? 0); + $runningBalance += (($row['transaction_type'] ?? '') === 'Credit') ? $rowAmount : -$rowAmount; + $depositModel->update((int) $row['id'], [ + 'balance' => $runningBalance, + 'updated_by' => $loggedInUserID, + ]); + } + + if ($db->transStatus() === false) { + $db->transRollback(); + return $this->response + ->setStatusCode(500) + ->setJSON([ + 'status' => 'error', + 'message' => 'Failed to update transaction.', + ]); + } + + $db->transCommit(); + return $this->response->setJSON([ + 'status' => 'success', + 'message' => 'Transaction updated successfully.', + ]); + } + public function editClientOnboarding($id = null) { @@ -6652,7 +6755,7 @@ class ClientController extends AdminController // Check in Enrollment (client_policy) $client_policy_data = $this->clientPolicyModel ->where('is_active', 1) - ->where('policy_status', 1) + // ->where('policy_status', 1) ->where('TRIM(policy_no)', $policy_no) ->first(); @@ -7312,6 +7415,8 @@ class ClientController extends AdminController // dd($res); $employeeController = new EmployeeController(); + // $payload = json_decode('{"client_policy_id":"6272"}', true); + // $response = $employeeController->initiateWellnessOnboardJob($payload); dd($response); // $response = $employeeController->getEmployeeEcardFromTmpFolderAndZipToS3(json_decode('{"batch_no":2,"last_emp_policy_id":"13218","folder_name":"bulk_ecards_IOCL-77448855996699885555_2026-02-05_09-32-22","processed_in_this_batch_data_count":7,"pdf_count":0,"hr_id":"1"}', true)); // $response = $employeeController->bulkEcardDownloadAsZipFromS3(json_decode('{"client_policy_id":"6066","hr_id":"1"}', true)); // dd($response); diff --git a/app/Controllers/Docs/DocsController.php b/app/Controllers/Docs/DocsController.php index ee117868..d85d77b0 100644 --- a/app/Controllers/Docs/DocsController.php +++ b/app/Controllers/Docs/DocsController.php @@ -63,6 +63,14 @@ class DocsController extends BaseController ['id' => 'tpa-recon', 'label' => 'TPA Recon', 'url' => 'docs/tpa-recon'], ['id' => 'eb-rack-rate-config', 'label' => 'EB rack rate config', 'url' => 'docs/eb-rack-rate-config'], ['id' => 'eb-rack-rate-calculation', 'label' => 'EB rack rate calculation', 'url' => 'docs/eb-rack-rate-calculation'], + ['id' => 'bds-insurer-statement', 'label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'], + ['id' => 'bds-commission', 'label' => 'BDS commission', 'url' => 'docs/bds-commission'], + ['id' => 'inception', 'label' => 'Inception', 'url' => 'docs/inception'], + ['id' => 'deletion', 'label' => 'Deletion', 'url' => 'docs/deletion'], + ['id' => 'correction', 'label' => 'Correction', 'url' => 'docs/correction'], + ['id' => 'si-enhancement', 'label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'], + ['id' => 'non-eb-opportunities', 'label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'], + ['id' => 'non-eb-claims', 'label' => 'Non-EB Claims', 'url' => 'docs/non-eb-claims'], ], ], [ @@ -451,6 +459,226 @@ class DocsController extends BaseController ['label' => 'Related', 'href' => '#related'], ], 'prev' => ['label' => 'EB rack rate config', 'url' => 'docs/eb-rack-rate-config'], + 'next' => ['label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'], + ], + + 'bds-insurer-statement' => [ + 'view' => 'docs/bds-insurer-statement', + 'title' => 'BDS Insurer statement', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '12 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Row validation logic', 'href' => '#row-validation-flowchart'], + ['label' => 'Key files and routes', 'href' => '#key-files-routes'], + ['label' => 'Statement list UI', 'href' => '#statement-list-ui'], + ['label' => 'uploadInsurerStatement','href' => '#upload-flow'], + ['label' => 'validateInsurerStatement', 'href' => '#validate-flow'], + ['label' => 'Validation steps', 'href' => '#validate-steps', 'level' => 'h3'], + ['label' => 'Validation error codes', 'href' => '#validation-errors', 'level' => 'h3'], + ['label' => 'updateInsurerStatement', 'href' => '#update-flow'], + ['label' => 'Excel column mapping', 'href' => '#excel-columns'], + ['label' => 'NHance source query', 'href' => '#nhance-source-query'], + ['label' => 'Invoice and delete', 'href' => '#invoice-and-delete'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#common-pitfalls'], + ['label' => 'Related BDS features', 'href' => '#related-bds'], + ], + 'prev' => ['label' => 'EB rack rate calculation', 'url' => 'docs/eb-rack-rate-calculation'], + 'next' => ['label' => 'BDS commission', 'url' => 'docs/bds-commission'], + ], + + 'bds-commission' => [ + 'view' => 'docs/bds-commission', + 'title' => 'BDS commission', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '10 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Key files', 'href' => '#key-files'], + ['label' => 'Routes', 'href' => '#routes'], + ['label' => 'Commission file format', 'href' => '#commission-file-format'], + ['label' => 'Required columns', 'href' => '#required-columns', 'level' => 'h3'], + ['label' => 'Sample rows', 'href' => '#commission-file-samples', 'level' => 'h3'], + ['label' => 'Rule upload', 'href' => '#upload-flow'], + ['label' => 'Rules editor', 'href' => '#rules-editor'], + ['label' => 'Rule JSON shape', 'href' => '#rule-json'], + ['label' => 'Calculation API', 'href' => '#calculation-api'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'], + 'next' => ['label' => 'Inception', 'url' => 'docs/inception'], + ], + + 'inception' => [ + 'view' => 'docs/inception', + 'title' => 'Inception', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '8 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Key files and routes', 'href' => '#key-files-routes'], + ['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'], + ['label' => 'Format validation', 'href' => '#format-validation'], + ['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'], + ['label' => 'reason JSON', 'href' => '#reason-json', 'level' => 'h3'], + ['label' => 'Data validation', 'href' => '#data-validation'], + ['label' => 'Data error codes', 'href' => '#data-errors', 'level' => 'h3'], + ['label' => 'Onboard preprocess', 'href' => '#preprocess'], + ['label' => 'Excel columns', 'href' => '#excel-columns'], + ['label' => 'Family row example', 'href' => '#family-example'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'BDS commission', 'url' => 'docs/bds-commission'], + 'next' => ['label' => 'Deletion', 'url' => 'docs/deletion'], + ], + + 'deletion' => [ + 'view' => 'docs/deletion', + 'title' => 'Deletion', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '7 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Key files and routes', 'href' => '#key-files-routes'], + ['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'], + ['label' => 'Format validation', 'href' => '#format-validation'], + ['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'], + ['label' => 'Data validation', 'href' => '#data-validation'], + ['label' => 'employeeDisembark', 'href' => '#disembark'], + ['label' => 'TPA auto-deletion', 'href' => '#tpa-auto'], + ['label' => 'Excel columns', 'href' => '#excel-columns'], + ['label' => 'Row examples', 'href' => '#row-example'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'Inception', 'url' => 'docs/inception'], + 'next' => ['label' => 'Correction', 'url' => 'docs/correction'], + ], + + 'correction' => [ + 'view' => 'docs/correction', + 'title' => 'Correction', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '7 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Key files and routes', 'href' => '#key-files-routes'], + ['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'], + ['label' => 'Format validation', 'href' => '#format-validation'], + ['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'], + ['label' => 'Data validation', 'href' => '#data-validation'], + ['label' => 'employeesCorrectionProcess', 'href' => '#correction-process'], + ['label' => 'Excel columns', 'href' => '#excel-columns'], + ['label' => 'Row examples', 'href' => '#row-example'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'Deletion', 'url' => 'docs/deletion'], + 'next' => ['label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'], + ], + + 'si-enhancement' => [ + 'view' => 'docs/si-enhancement', + 'title' => 'SI Enhancement', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '7 min read', + 'toc' => [ + ['label' => 'Overview', 'href' => '#overview'], + ['label' => 'Key files and routes', 'href' => '#key-files-routes'], + ['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'], + ['label' => 'Format validation', 'href' => '#format-validation'], + ['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'], + ['label' => 'Data validation', 'href' => '#data-validation'], + ['label' => 'employeesSIEnhanceProcess', 'href' => '#si-process'], + ['label' => 'Excel columns', 'href' => '#excel-columns'], + ['label' => 'Row examples', 'href' => '#row-example'], + ['label' => 'Developer steps', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'Correction', 'url' => 'docs/correction'], + 'next' => ['label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'], + ], + + 'non-eb-opportunities' => [ + 'view' => 'docs/non-eb-opportunities', + 'title' => 'Non-EB Opportunities', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '18 min read', + 'toc' => [ + ['label' => 'End-to-end flow', 'href' => '#overview'], + ['label' => 'Key files', 'href' => '#key-files'], + ['label' => 'Google Sheet config', 'href' => '#google-sheet-config'], + ['label' => 'App ↔ Drive flow', 'href' => '#gsheet-app-drive-flow', 'level' => 'h3'], + ['label' => 'Before RFQ / QCR', 'href' => '#gsheet-prerequisites', 'level' => 'h3'], + ['label' => 'Sheet ID per product', 'href' => '#gsheet-product-config', 'level' => 'h3'], + ['label' => 'App-level config', 'href' => '#gsheet-app-config', 'level' => 'h3'], + ['label' => 'Create opportunity', 'href' => '#create-edit'], + ['label' => 'Edit flow', 'href' => '#edit-flow'], + ['label' => 'List table actions', 'href' => '#list-actions'], + ['label' => 'Action endpoint map', 'href' => '#action-map', 'level' => 'h3'], + ['label' => 'RFQ', 'href' => '#step-rfq'], + ['label' => 'QCR', 'href' => '#step-qcr'], + ['label' => 'Mail actions', 'href' => '#step-mails'], + ['label' => 'Placement', 'href' => '#step-placement'], + ['label' => 'Status lifecycle', 'href' => '#status-lifecycle'], + ['label' => 'Controller reference', 'href' => '#controller-reference'], + ['label' => 'Developer checklist', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'], + 'next' => ['label' => 'Non-EB Claims', 'url' => 'docs/non-eb-claims'], + ], + + 'non-eb-claims' => [ + 'view' => 'docs/non-eb-claims', + 'title' => 'Non-EB Claims', + 'breadcrumb' => 'Features', + 'last_updated' => 'May 2026', + 'author' => 'Core Team', + 'read_time' => '16 min read', + 'toc' => [ + ['label' => 'End-to-end flow', 'href' => '#overview'], + ['label' => 'Why policy type 50', 'href' => '#policy-type-50-default'], + ['label' => 'Design assumption', 'href' => '#policy-type-50-assumption', 'level' => 'h3'], + ['label' => '50 vs selected policy', 'href' => '#policy-type-50-vs-selected-policy', 'level' => 'h3'], + ['label' => 'Where 50 is hardcoded', 'href' => '#policy-type-50-where-hardcoded', 'level' => 'h3'], + ['label' => 'Different status set', 'href' => '#policy-type-50-future', 'level' => 'h3'], + ['label' => 'Key files and routes', 'href' => '#key-files'], + ['label' => 'Route map', 'href' => '#route-map', 'level' => 'h3'], + ['label' => 'Access control', 'href' => '#access'], + ['label' => 'List and filter', 'href' => '#list-flow'], + ['label' => 'Create and edit', 'href' => '#create-edit'], + ['label' => 'Status-driven sections', 'href' => '#status-sections'], + ['label' => 'Auto-mail', 'href' => '#auto-mail'], + ['label' => 'Mail template CRUD', 'href' => '#mail-template-crud'], + ['label' => 'Template fields', 'href' => '#template-fields', 'level' => 'h3'], + ['label' => 'Template actions', 'href' => '#template-actions', 'level' => 'h3'], + ['label' => 'Notes', 'href' => '#notes'], + ['label' => 'Documents', 'href' => '#documents'], + ['label' => 'Reports', 'href' => '#reports'], + ['label' => 'Data model', 'href' => '#data-model'], + ['label' => 'Controller reference', 'href' => '#controller-reference'], + ['label' => 'Developer checklist', 'href' => '#developer-steps'], + ['label' => 'Common pitfalls', 'href' => '#pitfalls'], + ], + 'prev' => ['label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'], 'next' => ['label' => 'Endpoints', 'url' => 'docs/endpoints'], ], diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 4722727d..d209ca84 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -55,6 +55,8 @@ use Dompdf\Dompdf; use Dompdf\Options; use Kint; +use Firebase\JWT\JWT; + class EmployeeController extends AdminController { @@ -896,7 +898,7 @@ class EmployeeController extends AdminController session()->setFlashdata('success', 'File Uploaded Successfully File is being validated.'); return redirect()->to(base_url('employee/upload')); } else { - session()->setFlashdata($return['status'], $return['message']); + session()->setFlashdata($return['status'] ?? 'error', $return['message'] ?? 'Something went wrong! Try Later'); return redirect()->to(base_url('employee/upload')); } } else if ($event_type == 'correction') { @@ -3622,7 +3624,22 @@ class EmployeeController extends AdminController public function checkWellnessOnboardStatus($client_policy_id) - { + { + + + $fetch_onboarded_employees = $this->request->getGet('fetch_onboarded_employees') ?? false; + + if($fetch_onboarded_employees){ + $data = $this->employeePolicyModel->select('employee_polices.*') + ->where('employee_polices.is_active', 1) + ->where('employee_polices.status', 'active') + ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('employee_polices.wellness_onboard !=', '0') + ->findAll(); + return $this->respond(['status' => true, 'code' => 200, 'data' => count($data)], 200); + } + + // echo $client_policy_id;die(); $data = $this->employeePolicyModel->select('employee_polices.*,emp.name,emp.relationship,emp.emp_code,emp.name,emp.email_corporate,emp.mobile,emp.dob,cp.policy_no,cp.wellness_plan_id,cp.wellness_vendor_id,cp.policy_start_date as cp_policy_start_date,cp.policy_end_date,cls.short_name') ->join('client_policy cp', 'cp.id = employee_polices.client_policy_id') @@ -3656,9 +3673,9 @@ class EmployeeController extends AdminController // return $this->respond(['status' => true, 'code' => 200, 'message' => 'Inception and Member data comparision skiped successfully!'], 200); } - public function initiateWellnessOnboard($client_policy_id) + public function initiateWellnessOnboard($client_policy_id, $emp_code = null) { - $r = Jobs::addJob(['job_name' => 'initiateWellnessOnboardJob','payload' => ['client_policy_id' => $client_policy_id]]); + $r = Jobs::addJob(['job_name' => 'initiateWellnessOnboardJob','payload' => ['client_policy_id' => $client_policy_id, 'emp_code' => $emp_code ?? null ]]); // $this->initiateWellnessOnboardJob(['client_policy_id' => $client_policy_id]); return $this->respond(['status' => true, 'code' => 200, 'message' => 'Process started'], 200); } @@ -3774,6 +3791,7 @@ class EmployeeController extends AdminController public function initiateWellnessOnboardJob($arr) { $client_policy_id = $arr['client_policy_id'] ?? null; + $emp_code = $arr['emp_code'] ?? null; if (!$client_policy_id) { log_message('error', '[WellnessOnboard] Missing client_policy_id in payload'); @@ -3789,7 +3807,7 @@ class EmployeeController extends AdminController // ---------------------------------------------------------------- // 1. FETCH DATA // ---------------------------------------------------------------- - $data = $this->employeePolicyModel + $query = $this->employeePolicyModel ->select('employee_polices.*, emp.name, emp.relationship, emp.emp_code, emp.email_corporate, emp.mobile, emp.dob, emp.gender, @@ -3817,8 +3835,13 @@ class EmployeeController extends AdminController ->orWhere('cp.wellness_vendor_id =', 0) ->groupEnd() ->where('cp.policy_status', 1) - ->where('cp.is_active', 1) - ->findAll($perPage, $offset); + ->where('cp.is_active', 1); + + if(!empty($emp_code)){ + $query->where('emp.emp_code', trim($emp_code)); + } + + $data = $query->findAll($perPage, $offset); // ---------------------------------------------------------------- // 2. NO DATA FOUND @@ -3852,7 +3875,7 @@ class EmployeeController extends AdminController foreach ($families as $empCode => $members) { $familiesPayload[$empCode] = $this->buildFamilyPayload($empCode, $members); } - log_message('error', "[WellnessOnboard] Page {$page} API calls done. Families processed: " . json_encode($familiesPayload)); + log_message('error', "[WellnessOnboard] Page {$page} API calls done. Families processed: " . json_encode($familiesPayload)); // ---------------------------------------------------------------- // 5. SEND TO WELLNESS API // ---------------------------------------------------------------- @@ -3885,6 +3908,33 @@ class EmployeeController extends AdminController return true; } + public function resetWellnessOnboard($client_policy_id, $emp_code = null) + { + if(!empty($emp_code)){ + + $employees = db_connect()->table('employees') + ->select('id') + ->where('emp_code', trim($emp_code)) + ->get() + ->getResult(); + + if (!empty($employees)) { + $employeeIds = array_column($employees, 'id'); + + $this->employeePolicyModel + ->where('client_policy_id', $client_policy_id) + ->whereIn('employee_id', $employeeIds) + ->set('wellness_onboard', '0') + ->update(); + } + + } else { + $this->employeePolicyModel->where('client_policy_id', $client_policy_id)->set('wellness_onboard', '0')->update(); + } + + return $this->respond(['status' => true, 'code' => 200, 'message' => 'Wellness onboard resetted successfully'], 200); + } + # ------------------ FUNCTION TO BUILD FAMILY PAYLOAD ------------------ @@ -4174,6 +4224,7 @@ class EmployeeController extends AdminController } // print_r($allUpdates);die(); // Do a single batch update for all families/members + log_message('error','[WellnessOnboard] DB update data - ' . json_encode($allUpdates)); if (!empty($allUpdates)) { // 2nd param is the key to match on; here it's 'id' $this->employeePolicyModel->updateBatch($allUpdates, 'id'); @@ -7290,4 +7341,75 @@ class EmployeeController extends AdminController } } + + public function tpaReportsDashboard() + { + $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); + $policy_id = $this->request->getGet('client_policy') ?? null; + + if ($this->request->getGet('api') != 1) { + $data = [ + 'tab_name' => 'TPA Reports', + 'page_name' => 'TPA Reports', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/employee/tpaReportsDashboard'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } + + $client_policy_data = $this->clientPolicyModel + ->select('tpa.dashboard_id') + ->join('tpa', 'client_policy.tpa_id = tpa.id') + ->where('client_policy.id', $policy_id) + ->first(); + + $database_id = isset($client_policy_data['dashboard_id']) ? (int) $client_policy_data['dashboard_id'] : null; + + if (empty($database_id)) { + + if ($this->request->getGet('api') == 1) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'There is no dashboard for this TPA.', + 'data' => [] + ]); + } + + return view('errors/404', [ + 'message' => 'There is no dashboard for this TPA.' + ]); + } + + $payload = [ + 'resource' => [ + 'dashboard' => $database_id + ], + 'exp' => time() + (10 * 60), // 10 minutes + 'params' => (object) ['client_policy' => $policy_id ], // MUST be object for Metabase + + ]; + + $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); + // dd($token); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], + ]); + } + + } diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 0b1fc090..49412b34 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -2288,6 +2288,8 @@ class LeadsController extends BaseController // 'Insurer' => $rfq_data['insurer_name'] ?? " - ", // 'TPA' => $rfq_data['tpa_name'] ?? " - ", 'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "", + 'Mortality Experience for last 3 years' => $claim_history == "1" ? "Mentioned in Claims sheet" : "Nil", + ]; }else { diff --git a/app/Controllers/TestingController.php b/app/Controllers/TestingController.php index eecbfb45..4de8ff71 100644 --- a/app/Controllers/TestingController.php +++ b/app/Controllers/TestingController.php @@ -365,7 +365,7 @@ class TestingController extends BaseController $insurerBranchModel = new InsurerBranchModel(); $insurer_branch = $insurerBranchModel->getInsurerBranchesWithInsurerNames(); - $dataArray = $data['pt_co_share_details']; // Example + $dataArray = $data['pt_co_share_details'] ?? []; // Example $cd_ac_pk = 'CD12345'; $role_id = 1; $team_id = ['6']; @@ -1099,6 +1099,64 @@ class TestingController extends BaseController public function metaDashboardDemo() { + // 🔐 Move this to .env in real projects + $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); + $database_id = (int) ($this->request->getGet('database_id') ?? 2); + $policy_id = $this->request->getGet('client_policy') ?? null; + $payload = [ + 'resource' => [ + // 'dashboard' => 1 + 'dashboard' => $database_id + ], + 'exp' => time() + (10 * 60), // 10 minutes + ]; + + if(!empty($policy_id)){ + $payload['params'] = (object)['client_policy' => $policy_id]; // MUST be object for Metabase + }else{ + $payload['params'] = (object)[]; // MUST be object for Metabase + } + + // dd($payload); + $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); + + // // You can either return token only + // return $this->response->setJSON([ + // 'token' => $token, + // 'iframe_url' => "https://your-metabase-domain/embed/dashboard/{$token}#bordered=true&titled=true" + // ]); + if ($this->request->getGet('api') == 1) { + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } + + return $this->respond([ + 'status' => 'success', + 'message' => 'Dashboard token generated.', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], + ]); + } + + $data = [ + 'tab_name' => 'Metabase Dashboard', + 'page_name' => 'Metabase Dashboard', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/metaDashboardDemo'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + public function apacheSuperSetDemo() + { + + // echo 'Dta';die; // 🔐 Move this to .env in real projects $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); $database_id = (int)$this->request->getGet('database_id') ?? 2; @@ -1108,7 +1166,7 @@ class TestingController extends BaseController $payload = [ 'resource' => [ // 'dashboard' => 1 - 'dashboard' => $database_id + 'dashboard' => 2 ], 'exp' => time() + (10 * 60), // 10 minutes ]; @@ -1138,16 +1196,11 @@ class TestingController extends BaseController ]); } - return view('meta_dashboard_demo_one', [ + return view('apache_dashboard_demo_one', [ 'metabaseToken' => $token, 'metabaseUrl' => 'https://nsights.nhanceindia.in', ]); } - - public function testingquerys1(){ - - } - public function testingquerys() { $calendar = new \App\Libraries\GoogleCalendarService(); @@ -1175,7 +1228,26 @@ class TestingController extends BaseController public function metaTpaDashboardDemo() { $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); - $policy_id = $this->request->getGet('client_policy') ?? 4687; + $policy_id = $this->request->getGet('client_policy') ?? null; + + if ($this->request->getGet('api') != 1) { + $data = [ + 'tab_name' => 'TPA Reports', + 'page_name' => 'TPA Reports', + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + 'dashboardApiUrl' => base_url('/metaTpaDashboardDemo'), + ]; + + return $this->loadLayout('meta_dashboard_demo_one', $data); + } + + if (empty($policy_id)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Client policy is required.', + 'data' => [], + ]); + } $client_policy_data = $this->ClientPolicyModel ->select('tpa.dashboard_id') @@ -1212,20 +1284,13 @@ class TestingController extends BaseController $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); // dd($token); - if ($this->request->getGet('api') == 1) { - return $this->respond([ - 'status' => 'success', - 'message' => 'Form data received successfully!', - 'data' => [ - 'metabaseToken' => $token, - 'metabaseUrl' => 'https://nsights.nhanceindia.in' - ] - ]); - } - - return view('meta_dashboard_demo_one', [ - 'metabaseToken' => $token, - 'metabaseUrl' => 'https://nsights.nhanceindia.in', + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ], ]); } diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index 55af0407..ab512abb 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -968,6 +968,58 @@ class TicketController extends BaseController return $extra_fields; } + /** + * Overlay conditional `required` rules onto a base validation rule-set based on the + * submitted `claim_status_id`. For example, when claim_status_id is a "Settled" status + * (11/24/34/44), fields like `utr_details` and `settled_date` are promoted from + * `permit_empty` to `required` so the request is rejected when they are blank. + * + * Letter URL fields (`approved_letter`, `settle_letter`) accept an alternative PDF + * upload, so they are intentionally not promoted here; that pairing is enforced by + * the upload validation flow. `approved_description` stays optional to match the + * existing UI contract. + */ + private function applyClaimStatusConditionalRules(array $rules): array + { + $claimStatusId = (int) $this->request->getPost('claim_status_id'); + if ($claimStatusId <= 0) { + return $rules; + } + + if (!isset($this->extraFields[$claimStatusId]) || !is_array($this->extraFields[$claimStatusId])) { + return $rules; + } + + $requiredFields = $this->extraFields[$claimStatusId]; + $skipFields = ['approved_letter', 'settle_letter', 'approved_description']; + + foreach ($requiredFields as $field) { + if (in_array($field, $skipFields, true) || !isset($rules[$field])) { + continue; + } + + $existingRules = $rules[$field]['rules'] ?? ''; + $parts = $existingRules === '' + ? [] + : array_values(array_filter(array_map('trim', explode('|', $existingRules)), function ($part) { + return $part !== '' && $part !== 'permit_empty'; + })); + + if (!in_array('required', $parts, true)) { + array_unshift($parts, 'required'); + } + $rules[$field]['rules'] = implode('|', array_unique($parts)); + + if (!isset($rules[$field]['errors']) || !is_array($rules[$field]['errors'])) { + $rules[$field]['errors'] = []; + } + $label = $rules[$field]['label'] ?? ucwords(str_replace('_', ' ', $field)); + $rules[$field]['errors']['required'] = $label . ' is required for the selected claim status.'; + } + + return $rules; + } + public function view_ticket($ticket_id) { // $ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first(); @@ -1456,6 +1508,8 @@ class TicketController extends BaseController ]; } + $rules = $this->applyClaimStatusConditionalRules($rules); + if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, @@ -1944,6 +1998,9 @@ class TicketController extends BaseController 'return_remark' => ['label' => 'Return Remark','rules' => 'permit_empty','errors' => []], ]; } + + $rules = $this->applyClaimStatusConditionalRules($rules); + if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, @@ -3090,7 +3147,7 @@ class TicketController extends BaseController } } - public function getLastMatchedStatus($incoming_form_values, $old_ticket_data) + public function getLastMatchedStatusOld($incoming_form_values, $old_ticket_data) { if (empty($incoming_form_values) || !isset($incoming_form_values['claim_status_id']) || !isset($incoming_form_values['extra_fields_array_for_validate'])) { return null; @@ -3159,6 +3216,71 @@ class TicketController extends BaseController return $lastMatchedStatus; } + public function getLastMatchedStatus($incoming_form_values, $old_ticket_data) + { + // Preserve the incoming claim_status_id (if any) as the safe fallback so callers + // never accidentally nullify the status when validation inputs are missing. + $incomingStatus = $incoming_form_values['claim_status_id'] ?? null; + + if (empty($incoming_form_values) || !isset($incoming_form_values['extra_fields_array_for_validate'])) { + return $incomingStatus; + } + + $lastMatchedStatus = $incomingStatus; + + $statusMapping = json_decode($incoming_form_values['extra_fields_array_for_validate'], true); + if (!is_array($statusMapping) || empty($statusMapping)) { + return $incomingStatus; + } + + // Drop transitional / aggregate statuses that should never be auto-selected. + $removeKeys = is_array($this->removeArrayKey) ? $this->removeArrayKey : []; + $statusMapping = array_diff_key($statusMapping, $removeKeys); + + $oldStatusId = is_array($old_ticket_data) ? ($old_ticket_data['claim_status_id'] ?? null) : null; + + $skipFields = ['approved_description']; + + foreach ($statusMapping as $status => $requiredFields) { + if (empty($requiredFields)) { + continue; + } + + // Skip the current/old status — we only advance forward to a newer status. + if ($oldStatusId !== null && (int) $status === (int) $oldStatusId) { + continue; + } + + if (!is_array($requiredFields)) { + $requiredFields = [$requiredFields]; + } + + $allFieldsMatched = true; + foreach ($requiredFields as $field) { + if (in_array($field, $skipFields, true)) { + continue; + } + + if (!isset($incoming_form_values[$field]) || $incoming_form_values[$field] === '' || $incoming_form_values[$field] === null) { + $allFieldsMatched = false; + break; + } + } + + if ($allFieldsMatched) { + $lastMatchedStatus = $status; + } + } + + // When a TPA number is present, promote the base "registered" statuses + // (1 -> 2 for standard, 67 -> 68 for the alternate flow). + if (!empty($incoming_form_values['tpa_no']) && in_array((int) $lastMatchedStatus, [1, 67], true)) { + $lastMatchedStatus = ((int) $lastMatchedStatus === 1) ? 2 : 68; + } + + return $lastMatchedStatus; + } + public function getPolicyStartDate() { @@ -3597,7 +3719,7 @@ class TicketController extends BaseController { $fileId = $this->claimFilesModel->insert([ 'ticket_id' => $ticketId, - 'ticket_type' => $ticketTypeId, + // 'ticket_type' => $ticketTypeId, 'file_type' => 3, 'doc_name' => $docName, 'file_name' => $uploadedFile['file_name'], 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/Models/ClientPolicyModel.php b/app/Models/ClientPolicyModel.php index c3acb681..b650d585 100755 --- a/app/Models/ClientPolicyModel.php +++ b/app/Models/ClientPolicyModel.php @@ -311,6 +311,24 @@ class ClientPolicyModel extends Model // Fetch the deposit data based on client and insurer IDs $query = $this->db->table('cash_deposit') ->select('cash_deposit.*') + ->select(" + (select old_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as old_amt, + (select new_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as new_amt, + (select created_by from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_by, + (select created_at from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_at, + ( + select up.first_name + from user_profiles up + where up.id = ( + select created_by + from auditing_history + where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' + order by id desc + limit 1 + ) + limit 1 + ) as amt_updated_by_name + ") ->select('insurers.name as insurer_name, insurers.short_name as insurer_short') ->select('clients.client_name as clientname, clients.short_name as clientshort, client_policy.policy_no') ->select('cd_master.cd_ac_no as cd_master_account_no') diff --git a/app/Models/UserModel.php b/app/Models/UserModel.php index 32272925..8f62c9ae 100755 --- a/app/Models/UserModel.php +++ b/app/Models/UserModel.php @@ -133,6 +133,7 @@ class UserModel extends Model ->select('user_profiles.*') ->select('roles.role as user_role') ->join('roles', 'roles.id = user_profiles.role') + ->where('user_profiles.is_active', 1) ->get() ->getResultArray(); } @@ -143,13 +144,26 @@ class UserModel extends Model ->select('roles.role as user_role') ->join('roles', 'roles.id = user_profiles.role') ->join('user_teams', 'user_teams.user_id = user_profiles.id') + ->where('user_profiles.is_active', 1) ->get() ->getResultArray(); + // The user_teams join produces one row per (user, team) pair, so users + // belonging to multiple teams appear multiple times. Collapse to one + // row per user while keeping a team_id from [6, 7] when available, so + // the existing in_array($user['team_id'], [6, 7]) checks in the views + // continue to work correctly. + $deduped = []; + foreach ($data as $row) { + $userId = $row['id']; + if (!isset($deduped[$userId])) { + $deduped[$userId] = $row; + } elseif (in_array($row['team_id'], [6, 7])) { + $deduped[$userId]['team_id'] = $row['team_id']; + } + } - // dd($data); - - return $data; + return array_values($deduped); } } 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 diff --git a/app/Views/employee_list.php b/app/Views/employee_list.php index a03e40e4..cb3e0ea8 100755 --- a/app/Views/employee_list.php +++ b/app/Views/employee_list.php @@ -81,7 +81,7 @@

-
@@ -90,7 +90,7 @@
// - // $year";} ?> + // $year";} ?> // // //
@@ -2187,7 +2187,7 @@ // // //
// //
@@ -2245,7 +2245,7 @@ } function getClaimHistoryLabel(leadType) { - return String(leadType) === '1' ? 'Mortality Claims' : 'Claims History'; + return String(leadType) === '1' ? 'Mortality Experience' : 'Claims Experience'; } function shouldShowClaimHistorySwitch(leadType, policyTypeId) { @@ -2304,7 +2304,7 @@ @@ -2361,7 +2361,7 @@ @@ -2381,7 +2381,7 @@ diff --git a/app/Views/leads_non_eb.php b/app/Views/leads_non_eb.php index d13ecb49..dd046a61 100644 --- a/app/Views/leads_non_eb.php +++ b/app/Views/leads_non_eb.php @@ -400,7 +400,7 @@ var pageBackButton = ' @@ -906,11 +906,11 @@ var pageBackButton = '" class="topbar-icon diff --git a/app/Views/meta_dashboard_demo_one.php b/app/Views/meta_dashboard_demo_one.php index 4652f9e9..fa558443 100644 --- a/app/Views/meta_dashboard_demo_one.php +++ b/app/Views/meta_dashboard_demo_one.php @@ -1,33 +1,345 @@ - - - - - Metabase Dashboard + - + + - - - - + $elBranch.on('change', function () { + const branchId = $(this).val(); + const clientId = $elClient.val(); + rebuildSelect($elPolicy, 'Select', !branchId); + clearDashboard(); + setStatus(branchId ? '' : 'Select branch and policy to load the dashboard.'); - - + if (!branchId || !clientId) { + return; + } + + const policiesForClient = policyByClient[clientId] || []; + const policies = policiesForClient.filter(function (p) { + return String(p.client_branch_id) === String(branchId); + }); + + rebuildSelect($elPolicy, 'Select', policies.length === 0, policies, function (p) { return p.id; }, function (p) { + const type = p.policy_type || ''; + const no = p.policy_no || ''; + return (type && no) ? (type + ' - ' + no) : (no || type || ('Policy ' + p.id)); + }); + if (policies.length === 0) { + setStatus('No policies found for this branch.', 'error'); + } + }); + + $elPolicy.on('change', function () { + fetchDashboard($(this).val()); + }); + + initSelect2($elClient, false); + initSelect2($elBranch, true); + initSelect2($elPolicy, true); + loadFilterData(); + }); + diff --git a/app/Views/non_eb_claim_list.php b/app/Views/non_eb_claim_list.php index f4622ffe..2136c661 100644 --- a/app/Views/non_eb_claim_list.php +++ b/app/Views/non_eb_claim_list.php @@ -64,7 +64,7 @@ - + diff --git a/app/Views/policy_transaction_endorsement_form.php b/app/Views/policy_transaction_endorsement_form.php index e203c8cb..d78428db 100644 --- a/app/Views/policy_transaction_endorsement_form.php +++ b/app/Views/policy_transaction_endorsement_form.php @@ -181,7 +181,7 @@ } - +
@@ -297,7 +297,7 @@
- +
@@ -326,7 +326,7 @@
- +
@@ -757,6 +757,9 @@ // console.log('tpa',tpa); $('#policy_no').val(policy_no); + if (typeof window.clearEndorsementExcludedFieldsValidation === 'function') { + window.clearEndorsementExcludedFieldsValidation('#endorsement_form_id'); + } $('#insurer_id').val(insurer); $('#tpa').val(tpa).change(); // $('#cd_ac_no').val(cd_ac_no); @@ -1206,6 +1209,9 @@ $('#policy_no').val(res.data.policy_no); $('#action_type').val(res.data.action_type); $('#endorsement_no').val(res.data.endorsement_no); + if (typeof window.clearEndorsementExcludedFieldsValidation === 'function') { + window.clearEndorsementExcludedFieldsValidation('#endorsement_form_id'); + } $('#data_received_date').val(res.data.data_received_date); $('#policy_issue_date').val(res.data.policy_issue_date); $('#emp_count').val(res.data.emp_count); diff --git a/app/Views/policy_transaction_endorsement_form_2.php b/app/Views/policy_transaction_endorsement_form_2.php index e61df749..d39f421b 100644 --- a/app/Views/policy_transaction_endorsement_form_2.php +++ b/app/Views/policy_transaction_endorsement_form_2.php @@ -195,7 +195,7 @@ } - +