FEAT_TPA_REPORT_HTML
This commit is contained in:
parent
30ceb22676
commit
281516c5dc
@ -257,6 +257,8 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get('clearCdSession', 'EmployeeController::clearCdSession');
|
||||
$routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus');
|
||||
$routes->get("tpaReportsDashboard", "EmployeeController::tpaReportsDashboard");
|
||||
$routes->get("tpaReportsMetabase", "EmployeeController::tpaReportsMetabase");
|
||||
$routes->get("tpaMisReport", "EmployeeController::tpaMisReport");
|
||||
});
|
||||
|
||||
|
||||
@ -513,6 +515,13 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get('debug/(:num)', 'ClaimReportDashboardController::debug/$1');
|
||||
$routes->get('sync', 'ClaimReportDashboardController::sync');
|
||||
$routes->get('download-excel', 'ClaimReportDashboardController::downloadExcel');
|
||||
$routes->get('mis', 'ClaimReportDashboardController::mis');
|
||||
$routes->get('mis-pdf', 'ClaimReportDashboardController::misPdf');
|
||||
});
|
||||
|
||||
$routes->group('tpa-reports', static function ($routes) {
|
||||
$routes->get('metabase', 'EmployeeController::tpaReportsMetabase');
|
||||
$routes->get('mis', 'EmployeeController::tpaMisReport');
|
||||
});
|
||||
|
||||
$routes->group('enrollment-collection-v1', static function ($routes) {
|
||||
@ -883,6 +892,13 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel
|
||||
$routes->get('debug', 'ClaimReportDashboardController::debug');
|
||||
$routes->get('debug/(:num)', 'ClaimReportDashboardController::debug/$1');
|
||||
$routes->get('download-excel', 'ClaimReportDashboardController::downloadExcel');
|
||||
$routes->get('mis', 'ClaimReportDashboardController::mis');
|
||||
$routes->get('mis-pdf', 'ClaimReportDashboardController::misPdf');
|
||||
});
|
||||
|
||||
$routes->group('tpa-reports', static function ($routes) {
|
||||
$routes->get('metabase', 'EmployeeController::tpaReportsMetabase');
|
||||
$routes->get('mis', 'EmployeeController::tpaMisReport');
|
||||
});
|
||||
|
||||
$routes->group('enrollment-collection-v1', static function ($routes) {
|
||||
|
||||
@ -5,6 +5,14 @@ namespace App\Controllers;
|
||||
use App\Models\ClaimReportDashboardModel;
|
||||
use App\Models\ClaimsCollectionV2DashboardModel;
|
||||
use App\Models\ClaimDumpFileModel;
|
||||
use App\Libraries\AbhiMisReportService;
|
||||
use App\Libraries\AbhiMisPdfService;
|
||||
use App\Libraries\IciciMisReportService;
|
||||
use App\Libraries\IciciMisPdfService;
|
||||
use App\Libraries\MediAssistMisReportService;
|
||||
use App\Libraries\MediAssistMisPdfService;
|
||||
use App\Libraries\VidalMisReportService;
|
||||
use App\Libraries\VidalMisPdfService;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
@ -752,4 +760,257 @@ class ClaimReportDashboardController extends BaseController
|
||||
return array_values(array_slice($fields, $marker + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML preview of TPA-specific MIS report for a policy.
|
||||
* Resolves TPA from client_policy and routes to ABHI / ICICI templates.
|
||||
*
|
||||
* GET /util/claims-collection-report/mis?client_policy_id=
|
||||
*/
|
||||
public function mis()
|
||||
{
|
||||
$policyId = $this->resolvePolicyId();
|
||||
if ($policyId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'client_policy or client_policy_id is required.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$resolved = $this->resolveMisKind($policyId);
|
||||
if (($resolved['kind'] ?? null) === null) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $resolved['message'] ?? 'MIS report is not available for this policy.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$path = $this->request->getUri()->getPath();
|
||||
$isJwt = stripos($path, 'employeeRest') !== false;
|
||||
$prefix = $isJwt ? 'employeeRest/claims-collection-report' : 'util/claims-collection-report';
|
||||
$pdfUrl = base_url($prefix . '/mis-pdf?client_policy_id=' . $policyId);
|
||||
|
||||
if ($resolved['kind'] === 'icici') {
|
||||
$built = (new IciciMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build ICICI MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return view('claims_mis_icici', [
|
||||
'report' => $built['data'],
|
||||
'view_model' => $built['data']['view_model'] ?? [],
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => $pdfUrl,
|
||||
'embed' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($resolved['kind'] === 'medi_assist') {
|
||||
$built = (new MediAssistMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build Medi Assist MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return view('claims_mis_medi_assist', [
|
||||
'report' => $built['data'],
|
||||
'view_model' => $built['data']['view_model'] ?? [],
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => $pdfUrl,
|
||||
'embed' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($resolved['kind'] === 'vidal') {
|
||||
$built = (new VidalMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build Vidal MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return view('claims_mis_vidal', [
|
||||
'report' => $built['data'],
|
||||
'view_model' => $built['data']['view_model'] ?? [],
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => $pdfUrl,
|
||||
'embed' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
$built = (new AbhiMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return view('claims_mis_abhi', [
|
||||
'report' => $built['data'],
|
||||
'view_model' => $built['data']['view_model'] ?? [],
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => $pdfUrl,
|
||||
'embed' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download TPA-specific MIS report as PDF.
|
||||
* Resolves TPA from client_policy and routes to ABHI / ICICI PDF generators.
|
||||
*
|
||||
* GET /util/claims-collection-report/mis-pdf?client_policy_id=
|
||||
*/
|
||||
public function misPdf()
|
||||
{
|
||||
$policyId = $this->resolvePolicyId();
|
||||
if ($policyId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'client_policy or client_policy_id is required.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$resolved = $this->resolveMisKind($policyId);
|
||||
if (($resolved['kind'] ?? null) === null) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $resolved['message'] ?? 'MIS report is not available for this policy.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
if ($resolved['kind'] === 'icici') {
|
||||
$built = (new IciciMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build ICICI MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$pdf = (new IciciMisPdfService())->generateFromReport($built['data'], $policyId);
|
||||
if (!$pdf['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $pdf['message'] ?? 'PDF generation failed.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . ($pdf['filename'] ?? 'ICICI_MIS.pdf') . '"')
|
||||
->setBody($pdf['content'] ?? '');
|
||||
}
|
||||
|
||||
if ($resolved['kind'] === 'medi_assist') {
|
||||
$built = (new MediAssistMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build Medi Assist MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$pdf = (new MediAssistMisPdfService())->generateFromReport($built['data'], $policyId);
|
||||
if (!$pdf['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $pdf['message'] ?? 'PDF generation failed.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . ($pdf['filename'] ?? 'MA_MIS.pdf') . '"')
|
||||
->setBody($pdf['content'] ?? '');
|
||||
}
|
||||
|
||||
if ($resolved['kind'] === 'vidal') {
|
||||
$built = (new VidalMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build Vidal MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$pdf = (new VidalMisPdfService())->generateFromReport($built['data'], $policyId);
|
||||
if (!$pdf['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $pdf['message'] ?? 'PDF generation failed.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . ($pdf['filename'] ?? 'VIDAL_MIS.pdf') . '"')
|
||||
->setBody($pdf['content'] ?? '');
|
||||
}
|
||||
|
||||
$built = (new AbhiMisReportService())->build($policyId);
|
||||
if (!$built['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $built['message'] ?? 'Unable to build MIS report.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$pdf = (new AbhiMisPdfService())->generateFromReport($built['data'], $policyId);
|
||||
if (!$pdf['status']) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => $pdf['message'] ?? 'PDF generation failed.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . ($pdf['filename'] ?? 'ABH_MIS.pdf') . '"')
|
||||
->setBody($pdf['content'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve which MIS report to serve from the policy's TPA.
|
||||
*
|
||||
* @return array{kind:?string,message?:string,tpa_id?:int}
|
||||
*/
|
||||
private function resolveMisKind(int $policyId): array
|
||||
{
|
||||
$db = db_connect();
|
||||
$policy = $db->table('client_policy')
|
||||
->select('tpa_id')
|
||||
->where('id', $policyId)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if (!$policy) {
|
||||
return ['kind' => null, 'message' => 'Policy not found.'];
|
||||
}
|
||||
|
||||
$tpaId = (int) ($policy['tpa_id'] ?? 0);
|
||||
$map = [
|
||||
(int) env('ABHI_PRIMARY_KEY_CONSTANT') => 'abhi',
|
||||
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => 'icici',
|
||||
(int) env('MEDI_ASSIST_PRIMARY_KEY_CONSTANT') => 'medi_assist',
|
||||
(int) env('VIDAL_PRIMARY_KEY_CONSTANT') => 'vidal',
|
||||
];
|
||||
|
||||
$kind = $map[$tpaId] ?? null;
|
||||
if ($kind === null) {
|
||||
return [
|
||||
'kind' => null,
|
||||
'tpa_id' => $tpaId,
|
||||
'message' => 'MIS report is not available for this policy TPA. Supported: ABHI, ICICI, Medi Assist, Vidal.',
|
||||
];
|
||||
}
|
||||
|
||||
return ['kind' => $kind, 'tpa_id' => $tpaId];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7408,21 +7408,31 @@ class EmployeeController extends AdminController
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TPA Reports dashboard page — native MIS only (ABHI / ICICI / Medi Assist / Vidal).
|
||||
*/
|
||||
public function tpaReportsDashboard()
|
||||
{
|
||||
$data = [
|
||||
'tab_name' => 'TPA Reports',
|
||||
'page_name' => 'TPA Reports',
|
||||
'misApiUrl' => base_url('/util/tpa-reports/mis'),
|
||||
];
|
||||
|
||||
return $this->loadLayout('tpa_mis_reports_dashboard', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Metabase signed-embed token for a client policy.
|
||||
* GET /util/tpa-reports/metabase?client_policy=
|
||||
* GET /employeeRest/tpa-reports/metabase?client_policy=
|
||||
*/
|
||||
public function tpaReportsMetabase()
|
||||
{
|
||||
$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);
|
||||
}
|
||||
$policy_id = $this->request->getGet('client_policy')
|
||||
?? $this->request->getGet('client_policy_id')
|
||||
?? null;
|
||||
|
||||
if (empty($policy_id)) {
|
||||
return $this->respond([
|
||||
@ -7441,31 +7451,22 @@ class EmployeeController extends AdminController
|
||||
$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.'
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'There is no dashboard for this TPA.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'resource' => [
|
||||
'dashboard' => $database_id
|
||||
'dashboard' => $database_id,
|
||||
],
|
||||
'exp' => time() + (10 * 60), // 10 minutes
|
||||
'params' => (object) ['client_policy' => $policy_id ], // MUST be object for Metabase
|
||||
|
||||
'exp' => time() + (10 * 60),
|
||||
'params' => (object) ['client_policy' => $policy_id],
|
||||
];
|
||||
|
||||
$token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256');
|
||||
// dd($token);
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
@ -7477,5 +7478,84 @@ class EmployeeController extends AdminController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Native TPA MIS report embed info for the frontend (preview + PDF URLs).
|
||||
* GET /util/tpa-reports/mis?client_policy=
|
||||
* GET /employeeRest/tpa-reports/mis?client_policy=
|
||||
*/
|
||||
public function tpaMisReport()
|
||||
{
|
||||
$policyId = (int) (
|
||||
$this->request->getGet('client_policy')
|
||||
?? $this->request->getGet('client_policy_id')
|
||||
?? 0
|
||||
);
|
||||
|
||||
if ($policyId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Client policy is required.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$policy = $this->clientPolicyModel
|
||||
->select('id, tpa_id')
|
||||
->where('id', $policyId)
|
||||
->first();
|
||||
|
||||
if (!$policy) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Policy not found.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$tpaId = (int) ($policy['tpa_id'] ?? 0);
|
||||
$map = [
|
||||
(int) env('ABHI_PRIMARY_KEY_CONSTANT') => 'abhi',
|
||||
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => 'icici',
|
||||
(int) env('MEDI_ASSIST_PRIMARY_KEY_CONSTANT') => 'medi_assist',
|
||||
(int) env('VIDAL_PRIMARY_KEY_CONSTANT') => 'vidal',
|
||||
];
|
||||
$kind = $map[$tpaId] ?? null;
|
||||
|
||||
if ($kind === null) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'MIS report is not available for this policy TPA. Supported: ABHI, ICICI, Medi Assist, Vidal.',
|
||||
'data' => [
|
||||
'supported' => false,
|
||||
'tpa_id' => $tpaId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$path = $this->request->getUri()->getPath();
|
||||
$isJwt = stripos($path, 'employeeRest') !== false;
|
||||
$prefix = $isJwt ? 'employeeRest/claims-collection-report' : 'util/claims-collection-report';
|
||||
|
||||
$labels = [
|
||||
'abhi' => 'ABHI MIS Report',
|
||||
'icici' => 'ICICI Portfolio Analysis',
|
||||
'medi_assist' => 'Medi Assist Portfolio Analysis',
|
||||
'vidal' => 'Vidal Corporate Analysis',
|
||||
];
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'MIS report available.',
|
||||
'data' => [
|
||||
'supported' => true,
|
||||
'kind' => $kind,
|
||||
'tpa_id' => $tpaId,
|
||||
'title' => $labels[$kind] ?? 'TPA MIS Report',
|
||||
'preview_url' => base_url($prefix . '/mis?client_policy_id=' . $policyId),
|
||||
'pdf_url' => base_url($prefix . '/mis-pdf?client_policy_id=' . $policyId),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
396
app/Libraries/AbhiMisChartImageService.php
Normal file
396
app/Libraries/AbhiMisChartImageService.php
Normal file
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
/**
|
||||
* Renders simple chart PNGs with GD for ABHI MIS PDF (mPDF cannot run Chart.js).
|
||||
*/
|
||||
class AbhiMisChartImageService
|
||||
{
|
||||
private string $dir;
|
||||
|
||||
public function __construct(?string $dir = null)
|
||||
{
|
||||
$this->dir = $dir ?: (rtrim(WRITEPATH, '/\\') . '/cache/abhi_mis_charts');
|
||||
if (!is_dir($this->dir)) {
|
||||
@mkdir($this->dir, 0775, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $vm
|
||||
* @return array<string,string> chart key => absolute PNG path
|
||||
*/
|
||||
public function buildAll(array $vm): array
|
||||
{
|
||||
$out = [];
|
||||
|
||||
$status = $vm['claimStatus'] ?? [];
|
||||
$out['donut'] = $this->pie(
|
||||
'donut',
|
||||
array_map(static fn ($r) => (string) ($r['status'] ?? ''), $status),
|
||||
array_map(static fn ($r) => (float) ($r['reported'] ?? 0), $status),
|
||||
['#2E86DE', '#1B3F8C', '#E8862C', '#6A3D9A']
|
||||
);
|
||||
|
||||
$pre = $vm['preAuthTAT'] ?? ['within' => 100, 'above' => 0];
|
||||
$out['preAuth'] = $this->pie(
|
||||
'preauth',
|
||||
['Within 2 Hours %', 'Above 2 Hours %'],
|
||||
[(float) ($pre['within'] ?? 0), (float) ($pre['above'] ?? 0)],
|
||||
['#2E86DE', '#1B3F8C']
|
||||
);
|
||||
|
||||
$bars = $vm['premiumBars'] ?? [0, 0, 0];
|
||||
$out['premiumBars'] = $this->vBars(
|
||||
'premium_bars',
|
||||
['Premium', 'Earn Premium', 'Claim Incurred'],
|
||||
[(float) ($bars[0] ?? 0), (float) ($bars[1] ?? 0), (float) ($bars[2] ?? 0)],
|
||||
['#2E86DE', '#1B3F8C', '#E8862C']
|
||||
);
|
||||
|
||||
$gender = $vm['gender'] ?? [];
|
||||
$gLabels = [];
|
||||
$gCounts = [];
|
||||
$gAmts = [];
|
||||
$gColors = [];
|
||||
foreach (['Male' => '#6A3D9A', 'Female' => '#1A3399'] as $label => $color) {
|
||||
$row = null;
|
||||
foreach ($gender as $g) {
|
||||
if (($g['label'] ?? '') === $label) {
|
||||
$row = $g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$gLabels[] = $label;
|
||||
$gCounts[] = (float) ($row['count'] ?? 0);
|
||||
$gAmts[] = (float) ($row['amount'] ?? 0);
|
||||
$gColors[] = $color;
|
||||
}
|
||||
$out['genderCount'] = $this->hBars('gender_count', $gLabels, $gCounts, $gColors, false);
|
||||
$out['genderAmt'] = $this->hBars('gender_amt', $gLabels, $gAmts, $gColors, true);
|
||||
|
||||
$pc = $vm['paidCharts'] ?? [];
|
||||
$cbt = $pc['countByType'] ?? [];
|
||||
$out['paidCount'] = $this->vBars(
|
||||
'paid_count',
|
||||
['Cashless', 'Reimbursement'],
|
||||
[(float) ($pc['paidCount'][0] ?? 0), (float) ($pc['paidCount'][1] ?? 0)],
|
||||
['#2E86DE', '#1B3F8C']
|
||||
);
|
||||
$out['paidAmtAcs'] = $this->groupedVBars(
|
||||
'paid_amt_acs',
|
||||
['Cashless', 'Reimbursement'],
|
||||
[
|
||||
['label' => 'Paid Amt', 'values' => [(float) ($pc['paidAmt'][0] ?? 0), (float) ($pc['paidAmt'][1] ?? 0)], 'color' => '#2E86DE'],
|
||||
['label' => 'ACS', 'values' => [(float) ($pc['acs'][0] ?? 0), (float) ($pc['acs'][1] ?? 0)], 'color' => '#E8862C'],
|
||||
]
|
||||
);
|
||||
$out['countStatus'] = $this->groupedVBars(
|
||||
'count_status',
|
||||
['Settled', 'Outstanding', 'Rejected'],
|
||||
[
|
||||
[
|
||||
'label' => 'Cashless',
|
||||
'values' => [
|
||||
(float) ($cbt['Settled']['Cashless'] ?? 0),
|
||||
(float) ($cbt['Outstanding']['Cashless'] ?? 0),
|
||||
(float) ($cbt['Rejected']['Cashless'] ?? 0),
|
||||
],
|
||||
'color' => '#2E86DE',
|
||||
],
|
||||
[
|
||||
'label' => 'Reimbursement',
|
||||
'values' => [
|
||||
(float) ($cbt['Settled']['Reimbursement'] ?? 0),
|
||||
(float) ($cbt['Outstanding']['Reimbursement'] ?? 0),
|
||||
(float) ($cbt['Rejected']['Reimbursement'] ?? 0),
|
||||
],
|
||||
'color' => '#1B3F8C',
|
||||
],
|
||||
]
|
||||
);
|
||||
$out['amtStatus'] = $this->vBars(
|
||||
'amt_status',
|
||||
['Settled', 'Outstanding', 'Rejected'],
|
||||
[
|
||||
(float) ($pc['amtByStatus'][0] ?? 0),
|
||||
(float) ($pc['amtByStatus'][1] ?? 0),
|
||||
(float) ($pc['amtByStatus'][2] ?? 0),
|
||||
],
|
||||
['#2E86DE', '#E8862C', '#6A3D9A']
|
||||
);
|
||||
|
||||
$months = $vm['months'] ?? [];
|
||||
$out['monthCount'] = $this->groupedVBars(
|
||||
'month_count',
|
||||
$months,
|
||||
[
|
||||
['label' => 'Paid Count', 'values' => array_map('floatval', $vm['paidCountByMonth'] ?? []), 'color' => '#2E86DE'],
|
||||
['label' => 'Outstanding Count', 'values' => array_map('floatval', $vm['outCountByMonth'] ?? []), 'color' => '#1B3F8C'],
|
||||
],
|
||||
720,
|
||||
280
|
||||
);
|
||||
$out['monthAmt'] = $this->groupedVBars(
|
||||
'month_amt',
|
||||
$months,
|
||||
[
|
||||
['label' => 'Paid Amount', 'values' => array_map('floatval', $vm['paidAmtByMonth'] ?? []), 'color' => '#2E86DE'],
|
||||
['label' => 'Outstanding Amount', 'values' => array_map('floatval', $vm['outAmtByMonth'] ?? []), 'color' => '#1B3F8C'],
|
||||
],
|
||||
720,
|
||||
280
|
||||
);
|
||||
|
||||
return array_filter($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $labels
|
||||
* @param list<float> $values
|
||||
* @param list<string> $colors
|
||||
*/
|
||||
public function pie(string $key, array $labels, array $values, array $colors, int $w = 420, int $h = 260): string
|
||||
{
|
||||
$img = imagecreatetruecolor($w, $h);
|
||||
$white = imagecolorallocate($img, 255, 255, 255);
|
||||
imagefilledrectangle($img, 0, 0, $w, $h, $white);
|
||||
|
||||
$total = array_sum($values);
|
||||
$cx = (int) ($w * 0.32);
|
||||
$cy = (int) ($h / 2);
|
||||
$rx = 95;
|
||||
$ry = 70;
|
||||
$start = 0.0;
|
||||
|
||||
foreach ($values as $i => $val) {
|
||||
$slice = $total > 0 ? ($val / $total) * 360.0 : 0.0;
|
||||
if ($slice <= 0) {
|
||||
continue;
|
||||
}
|
||||
$rgb = $this->hex($colors[$i % count($colors)]);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
$this->filledArc($img, $cx, $cy, $rx * 2, $ry * 2, (int) $start, (int) ($start + $slice), $col);
|
||||
$start += $slice;
|
||||
}
|
||||
|
||||
// hole for donut
|
||||
$hole = imagecolorallocate($img, 255, 255, 255);
|
||||
imagefilledellipse($img, $cx, $cy, (int) ($rx * 0.9), (int) ($ry * 0.9), $hole);
|
||||
|
||||
$black = imagecolorallocate($img, 40, 40, 40);
|
||||
$lx = (int) ($w * 0.58);
|
||||
$ly = 40;
|
||||
foreach ($labels as $i => $label) {
|
||||
$rgb = $this->hex($colors[$i % count($colors)]);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
imagefilledrectangle($img, $lx, $ly, $lx + 12, $ly + 12, $col);
|
||||
$pct = $total > 0 ? round(($values[$i] / $total) * 100, 1) : 0;
|
||||
imagestring($img, 3, $lx + 18, $ly - 1, $this->fit($label . ' ' . $pct . '%', 28), $black);
|
||||
$ly += 22;
|
||||
}
|
||||
|
||||
return $this->save($key, $img);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $labels
|
||||
* @param list<float> $values
|
||||
* @param list<string> $colors
|
||||
*/
|
||||
public function hBars(string $key, array $labels, array $values, array $colors, bool $indianFmt, int $w = 480, int $h = 200): string
|
||||
{
|
||||
$img = imagecreatetruecolor($w, $h);
|
||||
$white = imagecolorallocate($img, 255, 255, 255);
|
||||
$black = imagecolorallocate($img, 40, 40, 40);
|
||||
imagefilledrectangle($img, 0, 0, $w, $h, $white);
|
||||
|
||||
$max = max(1.0, ...array_map('floatval', $values ?: [1]));
|
||||
$left = 70;
|
||||
$barH = 36;
|
||||
$gap = 28;
|
||||
$y = 40;
|
||||
$maxW = $w - $left - 30;
|
||||
|
||||
foreach ($labels as $i => $label) {
|
||||
$val = (float) ($values[$i] ?? 0);
|
||||
$bw = (int) (($val / $max) * $maxW);
|
||||
$rgb = $this->hex($colors[$i % count($colors)]);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
imagestring($img, 3, 8, $y + 10, $this->fit($label, 10), $black);
|
||||
imagefilledrectangle($img, $left, $y, $left + max(2, $bw), $y + $barH, $col);
|
||||
$txt = $indianFmt ? $this->fmtIn($val) : (string) (int) $val;
|
||||
$tx = $left + (int) ($bw / 2) - (int) (strlen($txt) * 3);
|
||||
$tx = max($left + 4, min($tx, $w - 80));
|
||||
$box = imagecolorallocate($img, 255, 255, 255);
|
||||
imagefilledrectangle($img, $tx - 2, $y + 10, $tx + strlen($txt) * 7 + 2, $y + 26, $box);
|
||||
imagestring($img, 3, $tx, $y + 12, $txt, $black);
|
||||
$y += $barH + $gap;
|
||||
}
|
||||
|
||||
return $this->save($key, $img);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $labels
|
||||
* @param list<float> $values
|
||||
* @param list<string> $colors
|
||||
*/
|
||||
public function vBars(string $key, array $labels, array $values, array $colors, int $w = 480, int $h = 240): string
|
||||
{
|
||||
$img = imagecreatetruecolor($w, $h);
|
||||
$white = imagecolorallocate($img, 255, 255, 255);
|
||||
$black = imagecolorallocate($img, 40, 40, 40);
|
||||
$grid = imagecolorallocate($img, 230, 230, 230);
|
||||
imagefilledrectangle($img, 0, 0, $w, $h, $white);
|
||||
|
||||
$max = max(1.0, ...array_map('floatval', $values ?: [1]));
|
||||
$top = 20;
|
||||
$bottom = $h - 40;
|
||||
$left = 40;
|
||||
$right = $w - 20;
|
||||
$plotH = $bottom - $top;
|
||||
$n = max(1, count($labels));
|
||||
$slot = ($right - $left) / $n;
|
||||
$barW = max(12, (int) ($slot * 0.45));
|
||||
|
||||
for ($g = 0; $g <= 4; $g++) {
|
||||
$gy = (int) ($bottom - ($plotH * $g / 4));
|
||||
imageline($img, $left, $gy, $right, $gy, $grid);
|
||||
}
|
||||
|
||||
foreach ($labels as $i => $label) {
|
||||
$val = (float) ($values[$i] ?? 0);
|
||||
$bh = (int) (($val / $max) * $plotH);
|
||||
$x = (int) ($left + $slot * $i + ($slot - $barW) / 2);
|
||||
$y = $bottom - $bh;
|
||||
$rgb = $this->hex($colors[$i % count($colors)]);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
imagefilledrectangle($img, $x, $y, $x + $barW, $bottom, $col);
|
||||
$txt = $this->fmtShort($val);
|
||||
imagestring($img, 2, $x, max(4, $y - 14), $txt, $black);
|
||||
imagestring($img, 2, $x - 4, $bottom + 8, $this->fit($label, 12), $black);
|
||||
}
|
||||
|
||||
return $this->save($key, $img);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $labels
|
||||
* @param list<array{label:string,values:list<float>,color:string}> $series
|
||||
*/
|
||||
public function groupedVBars(string $key, array $labels, array $series, int $w = 520, int $h = 240): string
|
||||
{
|
||||
$img = imagecreatetruecolor($w, $h);
|
||||
$white = imagecolorallocate($img, 255, 255, 255);
|
||||
$black = imagecolorallocate($img, 40, 40, 40);
|
||||
$grid = imagecolorallocate($img, 230, 230, 230);
|
||||
imagefilledrectangle($img, 0, 0, $w, $h, $white);
|
||||
|
||||
$all = [];
|
||||
foreach ($series as $s) {
|
||||
foreach ($s['values'] as $v) {
|
||||
$all[] = (float) $v;
|
||||
}
|
||||
}
|
||||
$max = max(1.0, ...($all ?: [1.0]));
|
||||
$top = 28;
|
||||
$bottom = $h - 50;
|
||||
$left = 30;
|
||||
$right = $w - 20;
|
||||
$plotH = $bottom - $top;
|
||||
$n = max(1, count($labels));
|
||||
$slot = ($right - $left) / $n;
|
||||
$seriesN = max(1, count($series));
|
||||
$barW = max(8, (int) (($slot * 0.7) / $seriesN));
|
||||
|
||||
for ($g = 0; $g <= 4; $g++) {
|
||||
$gy = (int) ($bottom - ($plotH * $g / 4));
|
||||
imageline($img, $left, $gy, $right, $gy, $grid);
|
||||
}
|
||||
|
||||
foreach ($labels as $i => $label) {
|
||||
foreach ($series as $si => $s) {
|
||||
$val = (float) ($s['values'][$i] ?? 0);
|
||||
$bh = (int) (($val / $max) * $plotH);
|
||||
$x = (int) ($left + $slot * $i + ($slot - $barW * $seriesN) / 2 + $si * $barW);
|
||||
$y = $bottom - $bh;
|
||||
$rgb = $this->hex($s['color']);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
imagefilledrectangle($img, $x, $y, $x + $barW - 2, $bottom, $col);
|
||||
}
|
||||
imagestring($img, 2, (int) ($left + $slot * $i + 4), $bottom + 6, $this->fit((string) $label, 10), $black);
|
||||
}
|
||||
|
||||
$lx = $left;
|
||||
foreach ($series as $s) {
|
||||
$rgb = $this->hex($s['color']);
|
||||
$col = imagecolorallocate($img, $rgb[0], $rgb[1], $rgb[2]);
|
||||
imagefilledrectangle($img, $lx, 8, $lx + 10, 18, $col);
|
||||
imagestring($img, 2, $lx + 14, 7, $this->fit($s['label'], 18), $black);
|
||||
$lx += 120;
|
||||
}
|
||||
|
||||
return $this->save($key, $img);
|
||||
}
|
||||
|
||||
/** @return array{0:int,1:int,2:int} */
|
||||
private function hex(string $hex): array
|
||||
{
|
||||
$hex = ltrim($hex, '#');
|
||||
if (strlen($hex) === 3) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
if (strlen($hex) < 6) {
|
||||
return [46, 134, 222];
|
||||
}
|
||||
|
||||
return [
|
||||
hexdec(substr($hex, 0, 2)),
|
||||
hexdec(substr($hex, 2, 2)),
|
||||
hexdec(substr($hex, 4, 2)),
|
||||
];
|
||||
}
|
||||
|
||||
private function filledArc($img, int $cx, int $cy, int $w, int $h, int $s, int $e, $col): void
|
||||
{
|
||||
if ($e <= $s) {
|
||||
return;
|
||||
}
|
||||
imagefilledarc($img, $cx, $cy, $w, $h, $s, $e, $col, IMG_ARC_PIE);
|
||||
}
|
||||
|
||||
private function fit(string $s, int $max): string
|
||||
{
|
||||
$s = preg_replace('/[^\x20-\x7E]/', '', $s) ?? $s;
|
||||
return strlen($s) > $max ? substr($s, 0, $max - 1) . '.' : $s;
|
||||
}
|
||||
|
||||
private function fmtIn(float $n): string
|
||||
{
|
||||
return number_format($n, 0, '.', ',');
|
||||
}
|
||||
|
||||
private function fmtShort(float $n): string
|
||||
{
|
||||
if (abs($n) >= 100000) {
|
||||
return number_format($n / 100000, 1) . 'L';
|
||||
}
|
||||
if (abs($n) >= 1000) {
|
||||
return number_format($n / 1000, 1) . 'k';
|
||||
}
|
||||
|
||||
return (string) (int) $n;
|
||||
}
|
||||
|
||||
/** @param resource|\GdImage $img */
|
||||
private function save(string $key, $img): string
|
||||
{
|
||||
$path = $this->dir . DIRECTORY_SEPARATOR . $key . '_' . uniqid('', true) . '.png';
|
||||
imagepng($img, $path);
|
||||
imagedestroy($img);
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
161
app/Libraries/AbhiMisPdfService.php
Normal file
161
app/Libraries/AbhiMisPdfService.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
|
||||
/**
|
||||
* Renders ABHI MIS HTML page to PDF via DomPDF (same layout as online report).
|
||||
*/
|
||||
class AbhiMisPdfService
|
||||
{
|
||||
/**
|
||||
* Build full MIS PDF from report payload using the same view as the online page.
|
||||
*
|
||||
* @param array<string,mixed> $report
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generateFromReport(array $report, int $policyId): array
|
||||
{
|
||||
$vm = $report['view_model'] ?? [];
|
||||
$policyNo = trim((string) ($report['header']['policy_number'] ?? (string) $policyId));
|
||||
$charts = [];
|
||||
|
||||
try {
|
||||
$chartSvc = new AbhiMisChartImageService();
|
||||
$charts = $chartSvc->buildAll(is_array($vm) ? $vm : []);
|
||||
|
||||
$html = view('claims_mis_abhi', [
|
||||
'report' => $report,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
'charts' => $charts,
|
||||
]);
|
||||
|
||||
return $this->generate($html, $policyNo);
|
||||
} finally {
|
||||
foreach ($charts as $path) {
|
||||
if (is_string($path) && is_file($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generate(string $html, string $policyNo): array
|
||||
{
|
||||
$safePolicy = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: 'policy';
|
||||
$filename = 'ABH_MIS_' . $safePolicy . '_' . date('Ymd') . '.pdf';
|
||||
|
||||
try {
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$options->set('isFontSubsettingEnabled', true);
|
||||
$options->set('defaultFont', 'DejaVu Sans');
|
||||
$options->setChroot([
|
||||
rtrim(ROOTPATH, '/\\'),
|
||||
rtrim(FCPATH, '/\\'),
|
||||
rtrim(WRITEPATH, '/\\'),
|
||||
sys_get_temp_dir(),
|
||||
]);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($this->prepareHtml($html));
|
||||
$dompdf->setPaper('A4', 'landscape');
|
||||
$dompdf->render();
|
||||
|
||||
$content = $dompdf->output();
|
||||
if ($content === '' || $content === null) {
|
||||
return ['status' => false, 'message' => 'PDF generation failed: empty output.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'content' => $content,
|
||||
'filename' => $filename,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'AbhiMisPdfService::generate failed | ' . $e->getMessage());
|
||||
|
||||
return ['status' => false, 'message' => 'PDF generation failed: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareHtml(string $html): string
|
||||
{
|
||||
$html = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
|
||||
$html = preg_replace('/[\x{1F300}-\x{1FAFF}]/u', '', $html) ?? $html;
|
||||
|
||||
// Replace CSS custom properties DomPDF cannot resolve.
|
||||
$vars = [
|
||||
'var(--red-dark)' => '#9c0c22',
|
||||
'var(--red)' => '#C8102E',
|
||||
'var(--navy)' => '#152A54',
|
||||
'var(--blue-dark)' => '#1B3F8C',
|
||||
'var(--blue)' => '#2E86DE',
|
||||
'var(--orange)' => '#E8862C',
|
||||
'var(--purple)' => '#6A3D9A',
|
||||
'var(--gold)' => '#D9A441',
|
||||
'var(--bg)' => '#EFEFF2',
|
||||
'var(--panel)' => '#FFFFFF',
|
||||
'var(--line)' => '#E2E2E6',
|
||||
'var(--text)' => '#26262B',
|
||||
'var(--muted)' => '#6B6B75',
|
||||
];
|
||||
$html = str_replace(array_keys($vars), array_values($vars), $html);
|
||||
$html = preg_replace('#:root\s*\{[^}]*\}#is', '', $html) ?? $html;
|
||||
|
||||
// Embed local images as data URIs.
|
||||
$html = preg_replace_callback(
|
||||
'#(<img\b[^>]*\bsrc=["\'])([^"\']+)(["\'][^>]*>)#i',
|
||||
function (array $m): string {
|
||||
$src = html_entity_decode($m[2], ENT_QUOTES);
|
||||
$dataUri = $this->toDataUri($src);
|
||||
|
||||
return $m[1] . ($dataUri ?: $src) . $m[3];
|
||||
},
|
||||
$html
|
||||
) ?? $html;
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function toDataUri(string $path): ?string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '' || str_starts_with($path, 'data:')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('#^file://#i', $path)) {
|
||||
$path = preg_replace('#^file://#i', '', $path) ?? $path;
|
||||
}
|
||||
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$mime = match ($ext) {
|
||||
'png' => 'image/png',
|
||||
'jpg', 'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
default => 'application/octet-stream',
|
||||
};
|
||||
|
||||
$bytes = @file_get_contents($path);
|
||||
if ($bytes === false || $bytes === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'data:' . $mime . ';base64,' . base64_encode($bytes);
|
||||
}
|
||||
}
|
||||
1238
app/Libraries/AbhiMisReportService.php
Normal file
1238
app/Libraries/AbhiMisReportService.php
Normal file
File diff suppressed because it is too large
Load Diff
137
app/Libraries/IciciMisPdfService.php
Normal file
137
app/Libraries/IciciMisPdfService.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
|
||||
/**
|
||||
* Renders ICICI Portfolio Analysis MIS HTML to PDF via DomPDF.
|
||||
*/
|
||||
class IciciMisPdfService
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $report
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generateFromReport(array $report, int $policyId): array
|
||||
{
|
||||
$vm = $report['view_model'] ?? [];
|
||||
$policyNo = trim((string) ($report['header']['policy_number'] ?? (string) $policyId));
|
||||
|
||||
$html = view('claims_mis_icici', [
|
||||
'report' => $report,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
return $this->generate($html, $policyNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generate(string $html, string $policyNo): array
|
||||
{
|
||||
$safePolicy = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: 'policy';
|
||||
$filename = 'ICICI_MIS_' . $safePolicy . '_' . date('Ymd') . '.pdf';
|
||||
|
||||
try {
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$options->set('isFontSubsettingEnabled', true);
|
||||
$options->set('defaultFont', 'DejaVu Sans');
|
||||
$options->setChroot([
|
||||
rtrim(ROOTPATH, '/\\'),
|
||||
rtrim(FCPATH, '/\\'),
|
||||
rtrim(WRITEPATH, '/\\'),
|
||||
sys_get_temp_dir(),
|
||||
]);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($this->prepareHtml($html));
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
|
||||
$content = $dompdf->output();
|
||||
if ($content === '' || $content === null) {
|
||||
return ['status' => false, 'message' => 'PDF generation failed: empty output.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'content' => $content,
|
||||
'filename' => $filename,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'IciciMisPdfService::generate failed | ' . $e->getMessage());
|
||||
|
||||
return ['status' => false, 'message' => 'PDF generation failed: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareHtml(string $html): string
|
||||
{
|
||||
$html = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
|
||||
$html = preg_replace('/[\x{1F300}-\x{1FAFF}]/u', '', $html) ?? $html;
|
||||
|
||||
$vars = [
|
||||
'var(--navy)' => '#1b3a6b',
|
||||
'var(--navy-dark)' => '#16305a',
|
||||
'var(--red)' => '#a63a3a',
|
||||
'var(--red-header)' => '#a13a3a',
|
||||
'var(--border)' => '#7f7f7f',
|
||||
'var(--page-bg)' => '#ffffff',
|
||||
];
|
||||
$html = str_replace(array_keys($vars), array_values($vars), $html);
|
||||
$html = preg_replace('#:root\s*\{[^}]*\}#is', '', $html) ?? $html;
|
||||
|
||||
$html = preg_replace_callback(
|
||||
'#(<img\b[^>]*\bsrc=["\'])([^"\']+)(["\'][^>]*>)#i',
|
||||
function (array $m): string {
|
||||
$src = html_entity_decode($m[2], ENT_QUOTES);
|
||||
$dataUri = $this->toDataUri($src);
|
||||
|
||||
return $m[1] . ($dataUri ?: $src) . $m[3];
|
||||
},
|
||||
$html
|
||||
) ?? $html;
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function toDataUri(string $path): ?string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '' || str_starts_with($path, 'data:')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('#^file://#i', $path)) {
|
||||
$path = preg_replace('#^file://#i', '', $path) ?? $path;
|
||||
}
|
||||
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$mime = match ($ext) {
|
||||
'png' => 'image/png',
|
||||
'jpg', 'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
default => 'application/octet-stream',
|
||||
};
|
||||
|
||||
$bytes = @file_get_contents($path);
|
||||
if ($bytes === false || $bytes === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'data:' . $mime . ';base64,' . base64_encode($bytes);
|
||||
}
|
||||
}
|
||||
1164
app/Libraries/IciciMisReportService.php
Normal file
1164
app/Libraries/IciciMisReportService.php
Normal file
File diff suppressed because it is too large
Load Diff
139
app/Libraries/MediAssistMisPdfService.php
Normal file
139
app/Libraries/MediAssistMisPdfService.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
|
||||
/**
|
||||
* Renders Medi Assist Portfolio Analysis MIS HTML to PDF via DomPDF.
|
||||
*/
|
||||
class MediAssistMisPdfService
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $report
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generateFromReport(array $report, int $policyId): array
|
||||
{
|
||||
$vm = $report['view_model'] ?? $report;
|
||||
$policyNo = trim((string) ($report['header']['policy_number'] ?? (string) $policyId));
|
||||
|
||||
$html = view('claims_mis_medi_assist', [
|
||||
'report' => $report,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
return $this->generate($html, $policyNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generate(string $html, string $policyNo): array
|
||||
{
|
||||
$safePolicy = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: 'policy';
|
||||
$filename = 'MA_MIS_' . $safePolicy . '_' . date('Ymd') . '.pdf';
|
||||
|
||||
try {
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$options->set('isFontSubsettingEnabled', true);
|
||||
$options->set('defaultFont', 'DejaVu Sans');
|
||||
$options->setChroot([
|
||||
rtrim(ROOTPATH, '/\\'),
|
||||
rtrim(FCPATH, '/\\'),
|
||||
rtrim(WRITEPATH, '/\\'),
|
||||
sys_get_temp_dir(),
|
||||
]);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($this->prepareHtml($html));
|
||||
$dompdf->setPaper('A4', 'landscape');
|
||||
$dompdf->render();
|
||||
|
||||
$content = $dompdf->output();
|
||||
if ($content === '' || $content === null) {
|
||||
return ['status' => false, 'message' => 'PDF generation failed: empty output.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'content' => $content,
|
||||
'filename' => $filename,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'MediAssistMisPdfService::generate failed | ' . $e->getMessage());
|
||||
|
||||
return ['status' => false, 'message' => 'PDF generation failed: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareHtml(string $html): string
|
||||
{
|
||||
$html = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
|
||||
$html = preg_replace('/[\x{1F300}-\x{1FAFF}]/u', '', $html) ?? $html;
|
||||
|
||||
$vars = [
|
||||
'var(--blue)' => '#1f4e9c',
|
||||
'var(--lightblue)' => '#eaf1fb',
|
||||
'var(--header-blue)'=> '#dbe7f8',
|
||||
'var(--border)' => '#9fb8dd',
|
||||
'var(--row-alt)' => '#f4f7fc',
|
||||
'var(--red)' => '#e94b5c',
|
||||
'var(--text)' => '#222',
|
||||
'var(--muted)' => '#555',
|
||||
];
|
||||
$html = str_replace(array_keys($vars), array_values($vars), $html);
|
||||
$html = preg_replace('#:root\s*\{[^}]*\}#is', '', $html) ?? $html;
|
||||
|
||||
$html = preg_replace_callback(
|
||||
'#(<img\b[^>]*\bsrc=["\'])([^"\']+)(["\'][^>]*>)#i',
|
||||
function (array $m): string {
|
||||
$src = html_entity_decode($m[2], ENT_QUOTES);
|
||||
$dataUri = $this->toDataUri($src);
|
||||
|
||||
return $m[1] . ($dataUri ?: $src) . $m[3];
|
||||
},
|
||||
$html
|
||||
) ?? $html;
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function toDataUri(string $path): ?string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '' || str_starts_with($path, 'data:')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('#^file://#i', $path)) {
|
||||
$path = preg_replace('#^file://#i', '', $path) ?? $path;
|
||||
}
|
||||
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$mime = match ($ext) {
|
||||
'png' => 'image/png',
|
||||
'jpg', 'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
default => 'application/octet-stream',
|
||||
};
|
||||
|
||||
$bytes = @file_get_contents($path);
|
||||
if ($bytes === false || $bytes === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'data:' . $mime . ';base64,' . base64_encode($bytes);
|
||||
}
|
||||
}
|
||||
1488
app/Libraries/MediAssistMisReportService.php
Normal file
1488
app/Libraries/MediAssistMisReportService.php
Normal file
File diff suppressed because it is too large
Load Diff
139
app/Libraries/VidalMisPdfService.php
Normal file
139
app/Libraries/VidalMisPdfService.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
|
||||
/**
|
||||
* Renders Vidal Health Corporate Analysis MIS HTML to PDF via DomPDF.
|
||||
*/
|
||||
class VidalMisPdfService
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $report
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generateFromReport(array $report, int $policyId): array
|
||||
{
|
||||
$vm = $report['view_model'] ?? $report;
|
||||
$policyNo = trim((string) ($report['header']['policy_number'] ?? (string) $policyId));
|
||||
|
||||
$html = view('claims_mis_vidal', [
|
||||
'report' => $report,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
return $this->generate($html, $policyNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status:bool,message?:string,content?:string,filename?:string}
|
||||
*/
|
||||
public function generate(string $html, string $policyNo): array
|
||||
{
|
||||
$safePolicy = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: 'policy';
|
||||
$filename = 'VIDAL_MIS_' . $safePolicy . '_' . date('Ymd') . '.pdf';
|
||||
|
||||
try {
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$options->set('isFontSubsettingEnabled', true);
|
||||
$options->set('defaultFont', 'DejaVu Sans');
|
||||
$options->setChroot([
|
||||
rtrim(ROOTPATH, '/\\'),
|
||||
rtrim(FCPATH, '/\\'),
|
||||
rtrim(WRITEPATH, '/\\'),
|
||||
sys_get_temp_dir(),
|
||||
]);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($this->prepareHtml($html));
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
|
||||
$content = $dompdf->output();
|
||||
if ($content === '' || $content === null) {
|
||||
return ['status' => false, 'message' => 'PDF generation failed: empty output.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'content' => $content,
|
||||
'filename' => $filename,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'VidalMisPdfService::generate failed | ' . $e->getMessage());
|
||||
|
||||
return ['status' => false, 'message' => 'PDF generation failed: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareHtml(string $html): string
|
||||
{
|
||||
$html = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
|
||||
$html = preg_replace('/[\x{1F300}-\x{1FAFF}]/u', '', $html) ?? $html;
|
||||
|
||||
$vars = [
|
||||
'var(--blue)' => '#0072bc',
|
||||
'var(--header-blue)' => '#dbe9f5',
|
||||
'var(--group-header)' => '#b9d6ec',
|
||||
'var(--border)' => '#888',
|
||||
'var(--text)' => '#111',
|
||||
'var(--muted)' => '#444',
|
||||
'var(--male)' => '#4472c4',
|
||||
'var(--female)' => '#ed7d31',
|
||||
];
|
||||
$html = str_replace(array_keys($vars), array_values($vars), $html);
|
||||
$html = preg_replace('#:root\s*\{[^}]*\}#is', '', $html) ?? $html;
|
||||
|
||||
$html = preg_replace_callback(
|
||||
'#(<img\b[^>]*\bsrc=["\'])([^"\']+)(["\'][^>]*>)#i',
|
||||
function (array $m): string {
|
||||
$src = html_entity_decode($m[2], ENT_QUOTES);
|
||||
$dataUri = $this->toDataUri($src);
|
||||
|
||||
return $m[1] . ($dataUri ?: $src) . $m[3];
|
||||
},
|
||||
$html
|
||||
) ?? $html;
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function toDataUri(string $path): ?string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '' || str_starts_with($path, 'data:')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('#^file://#i', $path)) {
|
||||
$path = preg_replace('#^file://#i', '', $path) ?? $path;
|
||||
}
|
||||
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$mime = match ($ext) {
|
||||
'png' => 'image/png',
|
||||
'jpg', 'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
default => 'application/octet-stream',
|
||||
};
|
||||
|
||||
$bytes = @file_get_contents($path);
|
||||
if ($bytes === false || $bytes === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'data:' . $mime . ';base64,' . base64_encode($bytes);
|
||||
}
|
||||
}
|
||||
1616
app/Libraries/VidalMisReportService.php
Normal file
1616
app/Libraries/VidalMisReportService.php
Normal file
File diff suppressed because it is too large
Load Diff
772
app/Views/claims_mis_abhi.php
Normal file
772
app/Views/claims_mis_abhi.php
Normal file
@ -0,0 +1,772 @@
|
||||
<?php
|
||||
/** @var array $report */
|
||||
/** @var array $view_model */
|
||||
/** @var int $policy_id */
|
||||
/** @var string $pdf_url */
|
||||
/** @var bool $embed */
|
||||
|
||||
$embed = $embed ?? false;
|
||||
$report = $report ?? [];
|
||||
$view_model = $view_model ?? ($report['view_model'] ?? []);
|
||||
$header = $report['header'] ?? [];
|
||||
$summary = $report['summary'] ?? [];
|
||||
$meta = $report['meta'] ?? [];
|
||||
$vm = $view_model;
|
||||
$charts = $charts ?? [];
|
||||
$fmt = static fn ($n, $d = 0) => number_format((float) $n, $d);
|
||||
$logoSrc = !empty($embed)
|
||||
? (rtrim(ROOTPATH, '/\\') . '/public/assets/images/abhi.png')
|
||||
: base_url('public/assets/images/abhi.png');
|
||||
include APPPATH . 'Views/partials/claims_mis_abhi_helpers.php';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ABH MIS Report — Policy <?= esc($header['policy_number'] ?? $policy_id) ?></title>
|
||||
<style>
|
||||
:root{
|
||||
--red:#C8102E;
|
||||
--red-dark:#9c0c22;
|
||||
--navy:#152A54;
|
||||
--blue:#2E86DE;
|
||||
--blue-dark:#1B3F8C;
|
||||
--orange:#E8862C;
|
||||
--purple:#6A3D9A;
|
||||
--gold:#D9A441;
|
||||
--bg:#f6f1ee;
|
||||
--panel:#FFFFFF;
|
||||
--line:#eee0e0;
|
||||
--text:#2b2020;
|
||||
--muted:#8a7676;
|
||||
--maroon-900:#4a0e10;
|
||||
--maroon-700:#7a1116;
|
||||
--maroon-600:#9c151b;
|
||||
--maroon-500:#b91c23;
|
||||
--maroon-050:#fbecec;
|
||||
}
|
||||
*{box-sizing:border-box;}
|
||||
body{
|
||||
margin:0;
|
||||
background:var(--bg);
|
||||
font-family:"Segoe UI", Arial, sans-serif;
|
||||
color:var(--text);
|
||||
padding:28px 48px;
|
||||
}
|
||||
.wrap{max-width:1580px;margin:0 auto;}
|
||||
|
||||
/* ---- Header (unified card) ---- */
|
||||
.header{
|
||||
background:var(--panel);
|
||||
border-radius:16px;
|
||||
border:1px solid var(--line);
|
||||
box-shadow:0 1px 3px rgba(74,14,16,0.06);
|
||||
display:flex;
|
||||
align-items:stretch;
|
||||
overflow:hidden;
|
||||
margin-bottom:18px;
|
||||
}
|
||||
.brand{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:14px;
|
||||
padding:18px 24px;
|
||||
border-right:1px solid var(--line);
|
||||
min-width:280px;
|
||||
flex-shrink:0;
|
||||
}
|
||||
.brand-logo{
|
||||
display:block;
|
||||
height:64px;
|
||||
width:auto;
|
||||
max-width:260px;
|
||||
object-fit:contain;
|
||||
object-position:left center;
|
||||
}
|
||||
.hdr-stats{
|
||||
flex:1;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
min-width:0;
|
||||
}
|
||||
.hdr-stat{
|
||||
flex:1;
|
||||
padding:16px 20px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
border-right:1px solid var(--line);
|
||||
min-width:0;
|
||||
}
|
||||
.hdr-stat:last-child{border-right:none;}
|
||||
.hdr-stat-icon{
|
||||
width:36px;
|
||||
height:36px;
|
||||
border-radius:10px;
|
||||
background:var(--maroon-050);
|
||||
color:var(--maroon-600);
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
flex-shrink:0;
|
||||
}
|
||||
.hdr-stat-icon svg{width:18px;height:18px;}
|
||||
.hdr-stat-body{min-width:0;}
|
||||
.hdr-stat-value{
|
||||
font-size:14px;
|
||||
font-weight:700;
|
||||
color:var(--text);
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.hdr-stat-label{
|
||||
font-size:11px;
|
||||
color:var(--muted);
|
||||
margin-top:2px;
|
||||
letter-spacing:0.3px;
|
||||
}
|
||||
|
||||
/* ---- Panel ---- */
|
||||
.panel{
|
||||
background:var(--panel);
|
||||
border-radius:10px;
|
||||
overflow:hidden;
|
||||
box-shadow:0 1px 3px rgba(0,0,0,.08);
|
||||
margin-bottom:16px;
|
||||
}
|
||||
.panel h2{
|
||||
background:var(--red);
|
||||
color:#fff;
|
||||
font-size:13px;
|
||||
font-weight:700;
|
||||
margin:0;
|
||||
padding:9px 16px;
|
||||
text-align:center;
|
||||
letter-spacing:.3px;
|
||||
}
|
||||
.panel .body{padding:14px 16px;}
|
||||
|
||||
table{width:100%;border-collapse:collapse;font-size:12.5px;}
|
||||
th,td{padding:7px 10px;text-align:left;}
|
||||
thead th{
|
||||
background:var(--red);
|
||||
color:#fff;
|
||||
font-size:12px;
|
||||
font-weight:700;
|
||||
}
|
||||
tbody tr:nth-child(even){background:#F7F7F9;}
|
||||
tbody tr:hover{background:#FDEDEF;}
|
||||
td.num, th.num{text-align:right;}
|
||||
tfoot td{font-weight:700;background:#F0F0F3;border-top:2px solid var(--red);}
|
||||
|
||||
.kv-table td:first-child{font-weight:600;color:var(--navy);width:55%;}
|
||||
.kv-table td:last-child{font-weight:600;}
|
||||
.kv-block{margin-bottom:16px;}
|
||||
.kv-block-title{
|
||||
font-size:14px;
|
||||
font-weight:700;
|
||||
font-style:italic;
|
||||
color:#111;
|
||||
margin:0 0 8px 2px;
|
||||
}
|
||||
.kv-block .panel{margin-bottom:0;}
|
||||
.kv-block .panel .body{padding:0;}
|
||||
.kv-block table{margin:0;}
|
||||
.kv-block thead th{text-align:left;}
|
||||
.kv-block thead th:last-child{text-align:left;}
|
||||
.kv-block tbody td{border-bottom:1px solid var(--line);}
|
||||
.kv-block tbody tr:last-child td{border-bottom:none;}
|
||||
|
||||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
|
||||
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;}
|
||||
.grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;}
|
||||
|
||||
.stat-panel{
|
||||
background:var(--panel);
|
||||
border-radius:10px;
|
||||
box-shadow:0 1px 3px rgba(0,0,0,.08);
|
||||
padding:20px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-around;
|
||||
text-align:center;
|
||||
}
|
||||
.stat-num{font-size:26px;font-weight:800;color:var(--navy);}
|
||||
.stat-label{font-size:12px;font-weight:700;color:var(--muted);margin-top:2px;}
|
||||
.stat-icon{font-size:34px;}
|
||||
|
||||
.chart-wrap{position:relative;height:260px;}
|
||||
.chart-wrap canvas{width:100% !important;height:100% !important;display:block;}
|
||||
.chart-wrap.short{height:220px;}
|
||||
.chart-wrap.medium{height:280px;}
|
||||
.chart-wrap.tall{height:400px;}
|
||||
|
||||
.center-metric{
|
||||
position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
|
||||
text-align:center;font-size:12px;font-weight:700;color:var(--navy);
|
||||
}
|
||||
|
||||
.pct-hero{
|
||||
font-size:28px;font-weight:800;color:var(--blue);text-align:center;margin-top:20px;
|
||||
}
|
||||
|
||||
.section-title{
|
||||
font-size:15px;font-weight:800;color:var(--navy);
|
||||
margin:26px 0 10px 2px;
|
||||
border-left:5px solid var(--red);
|
||||
padding-left:10px;
|
||||
}
|
||||
|
||||
.legend-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;font-size:11.5px;margin-bottom:6px;}
|
||||
.legend-row span{display:inline-flex;align-items:center;gap:5px;}
|
||||
.dot{width:10px;height:10px;border-radius:50%;display:inline-block;}
|
||||
|
||||
.subhead{
|
||||
background:#F0F0F3;
|
||||
font-weight:700;
|
||||
font-size:12.5px;
|
||||
color:var(--navy);
|
||||
}
|
||||
.scroll-y{max-height:420px;overflow-y:auto;}
|
||||
|
||||
@media(max-width:1100px){
|
||||
body{padding:24px 28px;}
|
||||
.header{flex-wrap:wrap;}
|
||||
.brand{width:100%;border-right:none;border-bottom:1px solid var(--line);min-width:0;}
|
||||
.hdr-stats{flex-wrap:wrap;}
|
||||
.hdr-stat{min-width:50%;border-bottom:1px solid var(--line);}
|
||||
.grid2,.grid3,.grid4{grid-template-columns:1fr;}
|
||||
}
|
||||
.chart-img{display:block;width:100%;max-width:100%;height:auto;margin:0 auto;}
|
||||
<?php if (!empty($embed)): ?>
|
||||
/* DomPDF-safe overrides (no CSS variables / flex / grid) */
|
||||
body{background:#f6f1ee;color:#2b2020;font-family:DejaVu Sans, Arial, sans-serif;padding:12px 18px;}
|
||||
.wrap{max-width:100%;}
|
||||
.header{display:table;width:100%;margin-bottom:12px;background:#fff;border:1px solid #eee0e0;border-radius:10px;}
|
||||
.brand,.hdr-stats{display:table-cell;vertical-align:middle;}
|
||||
.brand{width:220px;padding:10px 12px;border-right:1px solid #eee0e0;}
|
||||
.brand-logo{height:48px;width:auto;max-width:200px;}
|
||||
.hdr-stats{padding:0;}
|
||||
.hdr-stat{display:inline-block;vertical-align:middle;padding:8px 10px;width:24%;}
|
||||
.hdr-stat-icon{display:none;}
|
||||
.hdr-stat-value{font-size:11px;font-weight:700;color:#2b2020;}
|
||||
.hdr-stat-label{font-size:9px;color:#8a7676;}
|
||||
.panel{background:#fff;border-radius:8px;overflow:hidden;box-shadow:none;border:1px solid #E2E2E6;margin-bottom:10px;}
|
||||
.panel h2,.kv-block-title{background:#C8102E;color:#fff;font-size:12px;font-weight:700;margin:0;padding:7px 10px;text-align:center;}
|
||||
.kv-block-title{background:transparent;color:#111;font-style:italic;text-align:left;padding:0 0 4px;}
|
||||
.panel .body{padding:8px 10px;}
|
||||
table{width:100%;border-collapse:collapse;font-size:10px;}
|
||||
th,td{padding:5px 6px;text-align:left;border:1px solid #E2E2E6;}
|
||||
thead th{background:#C8102E;color:#fff;font-weight:700;}
|
||||
tbody tr:nth-child(even){background:#F7F7F9;}
|
||||
td.num,th.num{text-align:right;}
|
||||
tfoot td{font-weight:700;background:#F0F0F3;border-top:2px solid #C8102E;}
|
||||
.kv-table td:first-child{font-weight:600;color:#152A54;width:55%;}
|
||||
.grid2,.grid3,.grid4{display:table;width:100%;table-layout:fixed;border-collapse:separate;border-spacing:8px;margin:0 -8px 8px;}
|
||||
.grid2 > .panel,.grid3 > .panel,.grid4 > .panel{display:table-cell;vertical-align:top;margin:0;}
|
||||
.grid2 > .panel{width:50%;}
|
||||
.grid3 > .panel{width:33.33%;}
|
||||
.grid4 > .panel{width:25%;}
|
||||
.section-title{font-size:13px;font-weight:800;color:#152A54;margin:12px 0 6px;border-left:4px solid #C8102E;padding-left:8px;}
|
||||
.chart-wrap,.chart-wrap.short,.chart-wrap.medium,.chart-wrap.tall{height:auto !important;min-height:0;}
|
||||
.scroll-y{max-height:none;overflow:visible;}
|
||||
.stat-panel{display:block;text-align:center;padding:12px;}
|
||||
.stat-num{font-size:20px;font-weight:800;color:#152A54;}
|
||||
.stat-label{font-size:11px;font-weight:700;color:#6B6B75;}
|
||||
.stat-icon{display:none;}
|
||||
.legend-row{font-size:10px;margin-bottom:4px;}
|
||||
.dot{background:#2E86DE;}
|
||||
<?php endif; ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="brand">
|
||||
<img class="brand-logo" src="<?= esc($logoSrc) ?>" alt="Aditya Birla Capital Health Insurance">
|
||||
</div>
|
||||
<div class="hdr-stats">
|
||||
<div class="hdr-stat">
|
||||
<div class="hdr-stat-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18M8 4v5"/></svg>
|
||||
</div>
|
||||
<div class="hdr-stat-body">
|
||||
<div class="hdr-stat-value" title="<?= esc($header['policy_number'] ?? '') ?>"><?= esc($header['policy_number'] ?? '') ?></div>
|
||||
<div class="hdr-stat-label">Policy number</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hdr-stat">
|
||||
<div class="hdr-stat-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-6 8-6s8 2 8 6"/></svg>
|
||||
</div>
|
||||
<div class="hdr-stat-body">
|
||||
<div class="hdr-stat-value" title="<?= esc($meta['client_name'] ?? '') ?>"><?= esc($meta['client_name'] ?? '') ?></div>
|
||||
<div class="hdr-stat-label">Client name</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hdr-stat">
|
||||
<div class="hdr-stat-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 3 7v6c0 5 4 8 9 9 5-1 9-4 9-9V7l-9-5z"/></svg>
|
||||
</div>
|
||||
<div class="hdr-stat-body">
|
||||
<div class="hdr-stat-value" title="<?= esc($meta['tpa_name'] ?? ($header['claim_processor'] ?? '')) ?>"><?= esc($meta['tpa_name'] ?? ($header['claim_processor'] ?? '')) ?></div>
|
||||
<div class="hdr-stat-label">TPA name</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hdr-stat">
|
||||
<div class="hdr-stat-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/></svg>
|
||||
</div>
|
||||
<div class="hdr-stat-body">
|
||||
<div class="hdr-stat-value"><?= esc($meta['last_refresh'] ?? date('d-M-Y')) ?></div>
|
||||
<div class="hdr-stat-label">Last refresh</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PREMIUM / CLAIM SUMMARY -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Premium</h2>
|
||||
<div class="body">
|
||||
<table class="kv-table">
|
||||
<thead><tr><th>Parameter</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Insurer Name</td><td><?= esc($header['insurer_name'] ?? '') ?></td></tr>
|
||||
<tr><td>Claim processing team</td><td><?= esc($header['claim_processor'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy Number</td><td><?= esc($header['policy_number'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy Start Date</td><td><?= esc($header['policy_start_date'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy End Date</td><td><?= esc($header['policy_end_date'] ?? '') ?></td></tr>
|
||||
<tr><td>Lives as on date</td><td><?= esc($fmt($header['lives'] ?? 0, 0)) ?></td></tr>
|
||||
<tr><td>Premium paid as on date</td><td><?= esc($fmt($header['premium_paid'] ?? 0)) ?></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Claim</h2>
|
||||
<div class="body">
|
||||
<table class="kv-table">
|
||||
<thead><tr><th>Parameter</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Claim Paid + Outstanding</td><td><?= esc($fmt($summary['claim_paid_outstanding'] ?? 0)) ?></td></tr>
|
||||
<tr><td>Claims paid Amount</td><td><?= esc($fmt($summary['claims_paid_amount'] ?? 0)) ?></td></tr>
|
||||
<tr><td>Claim Outstanding Amount</td><td><?= esc($fmt($summary['claim_outstanding_amount'] ?? 0)) ?></td></tr>
|
||||
<tr><td>ACS IPD Claim</td><td><?= esc($fmt($summary['acs_ipd_claim'] ?? 0)) ?></td></tr>
|
||||
<tr><td>No. of claims paid</td><td><?= esc($fmt($summary['claims_paid_count'] ?? 0, 0)) ?></td></tr>
|
||||
<tr><td>No. of claims Outstanding</td><td><?= esc($fmt($summary['claims_outstanding_count'] ?? 0, 0)) ?></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DASHBOARD ROW 1 -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Total Claim Count/Amount</h2>
|
||||
<div class="body">
|
||||
<div class="legend-row">
|
||||
<span><i class="dot" style="background:#2E86DE"></i>Reported</span>
|
||||
<span><i class="dot" style="background:#1B3F8C"></i>Settled</span>
|
||||
<span><i class="dot" style="background:#E8862C"></i>Outstanding</span>
|
||||
<span><i class="dot" style="background:#6A3D9A"></i>Rejected</span>
|
||||
</div>
|
||||
<div class="chart-wrap"><?= $chartHtml('donut', 'chartDonut1') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Pre-Auth TAT By Claim Count</h2>
|
||||
<div class="body">
|
||||
<div class="legend-row">
|
||||
<span><i class="dot" style="background:#2E86DE"></i>With in 2 Hours %</span>
|
||||
<span><i class="dot" style="background:#1B3F8C"></i>Above 2 Hours %</span>
|
||||
</div>
|
||||
<div class="chart-wrap short"><?= $chartHtml('preAuth', 'chartPreAuth') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid3">
|
||||
<div class="panel">
|
||||
<h2>Claim Status</h2>
|
||||
<div class="body scroll-y">
|
||||
<table>
|
||||
<thead><tr><th>Claim Status</th><th class="num">Number</th><th class="num">Reported Amount</th><th class="num">Incurred Amount</th></tr></thead>
|
||||
<tbody id="tblClaimStatus">
|
||||
<?php foreach ($vm['claimStatus'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['status'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['number'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['reported'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurred'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Policy Premium</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>SrNo</th><th>Amount</th><th class="num">Value</th></tr></thead>
|
||||
<tbody id="tblPolicyPremium">
|
||||
<?php foreach ($vm['policyPremium'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['sr'] ?? '') ?></td>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<tr style="font-weight:700;background:#F0F0F3;"><td colspan="2">Total</td><td class="num"><?= esc($fmt($vm['policyPremiumTotal'] ?? 0)) ?></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2> </h2>
|
||||
<div class="body">
|
||||
<div class="stat-panel">
|
||||
<div>
|
||||
<div class="stat-icon">👥</div>
|
||||
<div class="stat-num"><?= esc($fmt($vm['lives'] ?? ($header['lives'] ?? 0), 0)) ?></div>
|
||||
<div class="stat-label">Lives</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="stat-icon">📊</div>
|
||||
<div class="stat-num"><?= esc(number_format((float) ($vm['icr'] ?? ($summary['icr_pct'] ?? 0)), 2)) ?>%</div>
|
||||
<div class="stat-label">ICR</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Reimbursement TAT By Claim Count</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Date Difference</th><th class="num">Claim Count</th><th class="num">TAT %</th></tr></thead>
|
||||
<tbody id="tblReimbTAT">
|
||||
<?php foreach ($vm['reimbTAT'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['range'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['pct'] ?? 0) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<tr style="font-weight:700;background:#F0F0F3;">
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['reimbTATTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($vm['reimbTATTotal']['pct'] ?? 0) ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Premium, Earn Premium and Claim Incurred Amount</h2>
|
||||
<div class="body">
|
||||
<div class="chart-wrap"><?= $chartHtml('premiumBars', 'chartPremiumBars') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GENDER -->
|
||||
<div class="section-title">Gender-wise Claims</div>
|
||||
<div class="grid3">
|
||||
<div class="panel">
|
||||
<h2>Total Claim Number</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('genderCount', 'chartGenderCount') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Total Claim Amount</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('genderAmt', 'chartGenderAmt') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Gender Incurred</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Gender</th><th class="num">Claim Count</th><th class="num">Claim Incurred Amount</th></tr></thead>
|
||||
<tbody id="tblGender">
|
||||
<?php foreach (['Male', 'Female'] as $gLabel):
|
||||
$grow = null;
|
||||
foreach ($vm['gender'] ?? [] as $g) {
|
||||
if (($g['label'] ?? '') === $gLabel) { $grow = $g; break; }
|
||||
}
|
||||
$grow = $grow ?: ['label' => $gLabel, 'count' => 0, 'amount' => 0];
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($grow['label']) ?></td>
|
||||
<td class="num"><?= esc($fmt($grow['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($grow['amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblGenderCountTotal"><?= esc($fmt($vm['genderTotal']['count'] ?? 0, 0)) ?></td><td class="num" id="tblGenderAmtTotal"><?= esc($fmt($vm['genderTotal']['incurred_amount'] ?? 0)) ?></td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CLAIM TYPE -->
|
||||
<div class="panel">
|
||||
<h2>Claim Type</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Claim Type</th><th class="num">Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Amount</th><th class="num">Claim Amount %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblClaimType">
|
||||
<?php foreach ($vm['claimType'] ?? [] as $r): ?>
|
||||
<tr<?= !empty($r['header']) ? ' class="subhead"' : '' ?>>
|
||||
<td><?= !empty($r['header']) ? '' : ' ' ?><?= esc($r['type'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amtPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurred'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['incPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblClaimTypeCount"><?= esc($fmt($vm['claimTypeTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblClaimTypeAmt"><?= esc($fmt($vm['claimTypeTotal']['amount'] ?? 0)) ?></td><td class="num">100.00</td><td class="num" id="tblClaimTypeInc"><?= esc($fmt($vm['claimTypeTotal']['incurred'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RELATION / MEMBER TYPE -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Relation</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Relation</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblRelation">
|
||||
<?php foreach ($vm['relation'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblRelationCount"><?= esc($fmt($vm['relationTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblRelationAmt"><?= esc($fmt($vm['relationTotal']['incurred_amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Member Type</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Member Type</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblMemberType">
|
||||
<?php foreach ($vm['memberType'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblMemberCount"><?= esc($fmt($vm['memberTypeTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblMemberAmt"><?= esc($fmt($vm['memberTypeTotal']['incurred_amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AGE / DIAGNOSIS -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Age</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>Age</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblAge">
|
||||
<?php foreach ($vm['age'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['band'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblAgeCount"><?= esc($fmt($vm['ageTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblAgeAmt"><?= esc($fmt($vm['ageTotal']['incurred_amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Diagnosis</h2>
|
||||
<div class="body scroll-y">
|
||||
<table>
|
||||
<thead><tr><th>Diagnosis</th><th class="num">Count</th><th class="num">Count %</th><th class="num">Amount</th><th class="num">Amt %</th><th class="num">ACS</th><th class="num">LR %</th></tr></thead>
|
||||
<tbody id="tblDiagnosis">
|
||||
<?php foreach ($vm['diagnosis'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['name'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc(!empty($r['acs']) ? $fmt($r['acs']) : '') ?></td>
|
||||
<td class="num"><?= esc($r['lr'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblDiagCount"><?= esc($fmt($vm['diagnosisTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblDiagAmt"><?= esc($fmt($vm['diagnosisTotal']['amt'] ?? 0)) ?></td><td class="num">100.00</td><td class="num" id="tblDiagAcs"><?= esc($fmt($vm['diagnosisTotal']['acs'] ?? 0)) ?></td><td class="num" id="tblDiagLr"><?= esc($vm['diagnosisTotal']['lr'] ?? '') ?></td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- UTILIZATION REPORTS -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Utilization Report for Employees (In-Patient Claims)</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>No. Of Claim</th><th class="num">Beneficiaries Count</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblUtilEmp">
|
||||
<?php
|
||||
$utilEmp = $vm['utilizationEmployees'] ?? [];
|
||||
$utilEmpRows = $utilEmp['rows'] ?? (is_array($utilEmp) && isset($utilEmp[0]) ? $utilEmp : []);
|
||||
foreach ($utilEmpRows as $r):
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($r['claims'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amountPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; $utEmp = $utilEmp['total'] ?? []; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblUtilEmpBen"><?= esc($fmt($utEmp['beneficiaries'] ?? 0, 0)) ?></td><td class="num" id="tblUtilEmpCnt"><?= esc($fmt($utEmp['incurredCount'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblUtilEmpAmt"><?= esc($fmt($utEmp['amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Utilization Report for Dependent (In-Patient Claims)</h2>
|
||||
<div class="body">
|
||||
<table>
|
||||
<thead><tr><th>No. of Claims</th><th class="num">Beneficiaries Count</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblUtilDep">
|
||||
<?php
|
||||
$utilDep = $vm['utilizationDependents'] ?? [];
|
||||
$utilDepRows = $utilDep['rows'] ?? (is_array($utilDep) && isset($utilDep[0]) ? $utilDep : []);
|
||||
foreach ($utilDepRows as $r):
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($r['claims'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amountPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; $utDep = $utilDep['total'] ?? []; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblUtilDepBen"><?= esc($fmt($utDep['beneficiaries'] ?? 0, 0)) ?></td><td class="num" id="tblUtilDepCnt"><?= esc($fmt($utDep['incurredCount'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblUtilDepAmt"><?= esc($fmt($utDep['amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HOSPITAL NAME -->
|
||||
<div class="panel">
|
||||
<h2>Hospital Name</h2>
|
||||
<div class="body scroll-y">
|
||||
<table>
|
||||
<thead><tr><th>Hospital Name</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody id="tblHospital">
|
||||
<?php foreach ($vm['hospitals'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['name'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblHospitalCount"><?= esc($fmt($vm['hospitalTotal']['count'] ?? 0, 0)) ?></td><td class="num">100.00</td><td class="num" id="tblHospitalAmt"><?= esc($fmt($vm['hospitalTotal']['incurred_amount'] ?? 0)) ?></td><td class="num">100.00</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CLAIM PAID / TYPE CHARTS -->
|
||||
<div class="grid4">
|
||||
<div class="panel">
|
||||
<h2>Claim Paid Count By Claim Type</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('paidCount', 'chartPaidCount') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Claim Paid Amount and ACS</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('paidAmtAcs', 'chartPaidAmtACS') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Claim Count By Claim Status</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('countStatus', 'chartCountStatus') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Claim Amount By Claim Status</h2>
|
||||
<div class="body"><div class="chart-wrap medium"><?= $chartHtml('amtStatus', 'chartAmtStatus') ?></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HOSPITAL CITY -->
|
||||
<div class="panel">
|
||||
<h2>Hospital City</h2>
|
||||
<div class="body scroll-y">
|
||||
<table>
|
||||
<thead><tr><th>Hospital City</th><th class="num">Claim Count</th><th class="num">Claim Amount</th><th class="num">Claim Paid Count</th><th class="num">Claim Paid Amount</th><th class="num">Claim Outstanding Count</th><th class="num">Claim Outstanding Amount</th><th class="num">Total ACS</th></tr></thead>
|
||||
<tbody id="tblCity">
|
||||
<?php foreach ($vm['cities'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['city'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc(($r['paidCount'] === '' || $r['paidCount'] === null) ? '—' : $fmt($r['paidCount'], 0)) ?></td>
|
||||
<td class="num"><?= esc(($r['paidAmt'] === '' || $r['paidAmt'] === null) ? '—' : $fmt($r['paidAmt'])) ?></td>
|
||||
<td class="num"><?= esc(($r['outCount'] === '' || $r['outCount'] === null) ? '—' : $fmt($r['outCount'], 0)) ?></td>
|
||||
<td class="num"><?= esc(($r['outAmt'] === '' || $r['outAmt'] === null) ? '—' : $fmt($r['outAmt'])) ?></td>
|
||||
<td class="num"><?= esc(($r['acs'] === '' || $r['acs'] === null) ? '—' : $fmt($r['acs'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; $cty = $vm['cityTotals'] ?? []; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td>Total</td><td class="num" id="tblCityCount"><?= esc($fmt($cty['count'] ?? 0, 0)) ?></td><td class="num" id="tblCityAmt"><?= esc($fmt($cty['amt'] ?? 0)) ?></td><td class="num" id="tblCityPaidCnt"><?= esc($fmt($cty['paidCount'] ?? 0, 0)) ?></td><td class="num" id="tblCityPaidAmt"><?= esc($fmt($cty['paidAmt'] ?? 0)) ?></td><td class="num" id="tblCityOutCnt"><?= esc($fmt($cty['outCount'] ?? 0, 0)) ?></td><td class="num" id="tblCityOutAmt"><?= esc($fmt($cty['outAmt'] ?? 0)) ?></td><td class="num" id="tblCityAcs"><?= esc($fmt($cty['acs'] ?? 0)) ?></td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- REGISTRATION MONTH -->
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h2>Claim Paid Count and Claim Outstanding Count by Registration Month</h2>
|
||||
<div class="body"><div class="chart-wrap tall"><?= $chartHtml('monthCount', 'chartMonthCount') ?></div></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Claim Paid Amount, Claim Outstanding Amount by Registration Date</h2>
|
||||
<div class="body"><div class="chart-wrap tall"><?= $chartHtml('monthAmt', 'chartMonthAmt') ?></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php if (!$embed): ?>
|
||||
<script src="<?= base_url('assets/libs/chart.js/chart.umd.min.js') ?>"></script>
|
||||
<?php include APPPATH . 'Views/partials/claims_mis_abhi_script.php'; ?>
|
||||
<?php endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
558
app/Views/claims_mis_abhi_pdf.php
Normal file
558
app/Views/claims_mis_abhi_pdf.php
Normal file
@ -0,0 +1,558 @@
|
||||
<?php
|
||||
/** @var array $report */
|
||||
/** @var array $view_model */
|
||||
/** @var array $charts */
|
||||
/** @var int $policy_id */
|
||||
|
||||
$report = $report ?? [];
|
||||
$view_model = $view_model ?? ($report['view_model'] ?? []);
|
||||
$header = $report['header'] ?? [];
|
||||
$summary = $report['summary'] ?? [];
|
||||
$meta = $report['meta'] ?? [];
|
||||
$vm = $view_model;
|
||||
$charts = $charts ?? [];
|
||||
$fmt = static fn ($n, $d = 0) => number_format((float) $n, $d);
|
||||
$dash = static fn ($v) => ($v === '' || $v === null) ? '—' : $v;
|
||||
$logoPath = rtrim(ROOTPATH, '/\\') . '/public/assets/images/abhi.png';
|
||||
$img = static function (string $key) use ($charts): string {
|
||||
$path = $charts[$key] ?? '';
|
||||
if ($path === '' || !is_file($path)) {
|
||||
return '<p class="muted">—</p>';
|
||||
}
|
||||
|
||||
return '<img class="chart-img" src="' . htmlspecialchars($path, ENT_QUOTES) . '" alt="">';
|
||||
};
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ABH MIS Report — <?= esc($header['policy_number'] ?? $policy_id) ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<table class="header-table" width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td width="160" valign="middle">
|
||||
<?php if (is_file($logoPath)): ?>
|
||||
<img src="<?= esc($logoPath) ?>" width="140" alt="ABHI">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<div class="info-pill"><?= esc($header['policy_number'] ?? '') ?><small>Policy Number</small></div>
|
||||
<div class="info-pill"><?= esc($meta['client_name'] ?? '') ?><small>Client Name</small></div>
|
||||
<div class="info-pill"><?= esc($meta['tpa_name'] ?? ($header['claim_processor'] ?? '')) ?><small>TPA Name</small></div>
|
||||
<div class="info-pill"><?= esc($meta['last_refresh'] ?? date('d-M-Y')) ?><small>Last Refresh</small></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="kv-title">Premium</div>
|
||||
<table class="data kv">
|
||||
<thead><tr><th>Parameter</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Insurer Name</td><td><?= esc($header['insurer_name'] ?? '') ?></td></tr>
|
||||
<tr><td>Claim processing team</td><td><?= esc($header['claim_processor'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy Number</td><td><?= esc($header['policy_number'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy Start Date</td><td><?= esc($header['policy_start_date'] ?? '') ?></td></tr>
|
||||
<tr><td>Policy End Date</td><td><?= esc($header['policy_end_date'] ?? '') ?></td></tr>
|
||||
<tr><td>Lives as on date</td><td><?= esc($fmt($header['lives'] ?? 0, 0)) ?></td></tr>
|
||||
<tr><td>Premium paid as on date</td><td><?= esc($fmt($header['premium_paid'] ?? 0)) ?></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="kv-title">Claim</div>
|
||||
<table class="data kv">
|
||||
<thead><tr><th>Parameter</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Claim Paid + Outstanding</td><td><?= esc($fmt($summary['claim_paid_outstanding'] ?? 0)) ?></td></tr>
|
||||
<tr><td>Claims paid Amount</td><td><?= esc($fmt($summary['claims_paid_amount'] ?? 0)) ?></td></tr>
|
||||
<tr><td>Claim Outstanding Amount</td><td><?= esc($fmt($summary['claim_outstanding_amount'] ?? 0)) ?></td></tr>
|
||||
<tr><td>ACS IPD Claim</td><td><?= esc($fmt($summary['acs_ipd_claim'] ?? 0)) ?></td></tr>
|
||||
<tr><td>No. of claims paid</td><td><?= esc($fmt($summary['claims_paid_count'] ?? 0, 0)) ?></td></tr>
|
||||
<tr><td>No. of claims Outstanding</td><td><?= esc($fmt($summary['claims_outstanding_count'] ?? 0, 0)) ?></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Total Claim Count/Amount</div>
|
||||
<div class="panel-b"><?= $img('donut') ?></div>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Pre-Auth TAT By Claim Count</div>
|
||||
<div class="panel-b"><?= $img('preAuth') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout3" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="40%" valign="top">
|
||||
<div class="panel-h">Claim Status</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Claim Status</th><th class="num">Number</th><th class="num">Reported Amount</th><th class="num">Incurred Amount</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['claimStatus'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['status'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['number'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['reported'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurred'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td width="35%" valign="top">
|
||||
<div class="panel-h">Policy Premium</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>SrNo</th><th>Amount</th><th class="num">Value</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['policyPremium'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['sr'] ?? '') ?></td>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot><tr><td colspan="2">Total</td><td class="num"><?= esc($fmt($vm['policyPremiumTotal'] ?? 0)) ?></td></tr></tfoot>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" valign="top">
|
||||
<div class="panel-h"> </div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-num"><?= esc($fmt($vm['lives'] ?? ($header['lives'] ?? 0), 0)) ?></div>
|
||||
<div class="stat-label">Lives</div>
|
||||
<div class="stat-num" style="margin-top:12px;"><?= esc(number_format((float) ($vm['icr'] ?? ($summary['icr_pct'] ?? 0)), 2)) ?>%</div>
|
||||
<div class="stat-label">ICR</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="45%" valign="top">
|
||||
<div class="panel-h">Reimbursement TAT By Claim Count</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Date Difference</th><th class="num">Claim Count</th><th class="num">TAT %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['reimbTAT'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['range'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['pct'] ?? 0) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['reimbTATTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($vm['reimbTATTotal']['pct'] ?? 0) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
<td width="55%" valign="top">
|
||||
<div class="panel-h">Premium, Earn Premium and Claim Incurred Amount</div>
|
||||
<div class="panel-b"><?= $img('premiumBars') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">Gender-wise Claims</div>
|
||||
<table class="layout3" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="34%" valign="top">
|
||||
<div class="panel-h">Total Claim Number</div>
|
||||
<div class="panel-b"><?= $img('genderCount') ?></div>
|
||||
</td>
|
||||
<td width="34%" valign="top">
|
||||
<div class="panel-h">Total Claim Amount</div>
|
||||
<div class="panel-b"><?= $img('genderAmt') ?></div>
|
||||
</td>
|
||||
<td width="32%" valign="top">
|
||||
<div class="panel-h">Gender Incurred</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Gender</th><th class="num">Claim Count</th><th class="num">Claim Incurred Amount</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach (['Male', 'Female'] as $gLabel):
|
||||
$row = null;
|
||||
foreach ($vm['gender'] ?? [] as $g) {
|
||||
if (($g['label'] ?? '') === $gLabel) { $row = $g; break; }
|
||||
}
|
||||
$row = $row ?: ['label' => $gLabel, 'count' => 0, 'amount' => 0];
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($row['label']) ?></td>
|
||||
<td class="num"><?= esc($fmt($row['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($row['amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['genderTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($vm['genderTotal']['incurred_amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="panel-h">Claim Type</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Claim Type</th><th class="num">Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Amount</th><th class="num">Claim Amount %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['claimType'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= !empty($r['header']) ? '<strong>' : ' ' ?><?= esc($r['type'] ?? '') ?><?= !empty($r['header']) ? '</strong>' : '' ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amtPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurred'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['incPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['claimTypeTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['claimTypeTotal']['amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['claimTypeTotal']['incurred'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Relation</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Relation</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['relation'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['relationTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['relationTotal']['incurred_amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Member Type</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Member Type</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['memberType'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['label'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['memberTypeTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['memberTypeTotal']['incurred_amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="45%" valign="top">
|
||||
<div class="panel-h">Age</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Age</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['age'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['band'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['ageTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['ageTotal']['incurred_amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
<td width="55%" valign="top">
|
||||
<div class="panel-h">Diagnosis</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Diagnosis</th><th class="num">Count</th><th class="num">Count %</th><th class="num">Amount</th><th class="num">Amt %</th><th class="num">ACS</th><th class="num">LR %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['diagnosis'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['name'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc(!empty($r['acs']) ? $fmt($r['acs']) : '') ?></td>
|
||||
<td class="num"><?= esc($r['lr'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['diagnosisTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['diagnosisTotal']['amt'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['diagnosisTotal']['acs'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($vm['diagnosisTotal']['lr'] ?? '') ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Utilization Report for Employees (In-Patient Claims)</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>No. Of Claim</th><th class="num">Beneficiaries</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$utilEmp = $vm['utilizationEmployees'] ?? [];
|
||||
$utilEmpRows = $utilEmp['rows'] ?? $utilEmp;
|
||||
foreach ($utilEmpRows as $r):
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($r['claims'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amountPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<?php $ut = $utilEmp['total'] ?? []; ?>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($ut['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ut['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($ut['amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Utilization Report for Dependent (In-Patient Claims)</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>No. of Claims</th><th class="num">Beneficiaries</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$utilDep = $vm['utilizationDependents'] ?? [];
|
||||
$utilDepRows = $utilDep['rows'] ?? $utilDep;
|
||||
foreach ($utilDepRows as $r):
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($r['claims'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['countPct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amount'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['amountPct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<?php $dt = $utilDep['total'] ?? []; ?>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($dt['beneficiaries'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($dt['incurredCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($dt['amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="panel-h">Hospital Name</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Hospital Name</th><th class="num">Incurred Claim Count</th><th class="num">Claim Count %</th><th class="num">Claim Incurred Amount</th><th class="num">Claim Incurred Amount %</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['hospitals'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['name'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($r['cpct'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($r['apct'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($vm['hospitalTotal']['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
<td class="num"><?= esc($fmt($vm['hospitalTotal']['incurred_amount'] ?? 0)) ?></td>
|
||||
<td class="num">100.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Paid Count By Claim Type</div>
|
||||
<div class="panel-b"><?= $img('paidCount') ?></div>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Paid Amount and ACS</div>
|
||||
<div class="panel-b"><?= $img('paidAmtAcs') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Count By Claim Status</div>
|
||||
<div class="panel-b"><?= $img('countStatus') ?></div>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Amount By Claim Status</div>
|
||||
<div class="panel-b"><?= $img('amtStatus') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="panel-h">Hospital City</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>Hospital City</th><th class="num">Claim Count</th><th class="num">Claim Amount</th><th class="num">Claim Paid Count</th><th class="num">Claim Paid Amount</th><th class="num">Claim Outstanding Count</th><th class="num">Claim Outstanding Amount</th><th class="num">Total ACS</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['cities'] ?? [] as $r): ?>
|
||||
<tr>
|
||||
<td><?= esc($r['city'] ?? '') ?></td>
|
||||
<td class="num"><?= esc($fmt($r['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($r['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($dash($r['paidCount'] ?? '')) ?></td>
|
||||
<td class="num"><?= esc(($r['paidAmt'] === '' || $r['paidAmt'] === null) ? '—' : $fmt($r['paidAmt'])) ?></td>
|
||||
<td class="num"><?= esc($dash($r['outCount'] ?? '')) ?></td>
|
||||
<td class="num"><?= esc(($r['outAmt'] === '' || $r['outAmt'] === null) ? '—' : $fmt($r['outAmt'])) ?></td>
|
||||
<td class="num"><?= esc($dash($r['acs'] ?? '')) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<?php $ct = $vm['cityTotals'] ?? []; ?>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="num"><?= esc($fmt($ct['count'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['amt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['paidCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['paidAmt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['outCount'] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['outAmt'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($ct['acs'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<table class="layout2" width="100%" cellpadding="0" cellspacing="8">
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Paid Count and Claim Outstanding Count by Registration Month</div>
|
||||
<div class="panel-b"><?= $img('monthCount') ?></div>
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
<div class="panel-h">Claim Paid Amount, Claim Outstanding Amount by Registration Date</div>
|
||||
<div class="panel-b"><?= $img('monthAmt') ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php if (!empty($vm['months'])): ?>
|
||||
<div class="panel-h">Registration Month Detail</div>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Month</th>
|
||||
<th class="num">Paid Count</th>
|
||||
<th class="num">Outstanding Count</th>
|
||||
<th class="num">Paid Amount</th>
|
||||
<th class="num">Outstanding Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($vm['months'] as $i => $month): ?>
|
||||
<tr>
|
||||
<td><?= esc($month) ?></td>
|
||||
<td class="num"><?= esc($fmt($vm['paidCountByMonth'][$i] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($vm['outCountByMonth'][$i] ?? 0, 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($vm['paidAmtByMonth'][$i] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($fmt($vm['outAmtByMonth'][$i] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1211
app/Views/claims_mis_icici.php
Normal file
1211
app/Views/claims_mis_icici.php
Normal file
File diff suppressed because it is too large
Load Diff
1015
app/Views/claims_mis_medi_assist.php
Normal file
1015
app/Views/claims_mis_medi_assist.php
Normal file
File diff suppressed because it is too large
Load Diff
869
app/Views/claims_mis_vidal.php
Normal file
869
app/Views/claims_mis_vidal.php
Normal file
@ -0,0 +1,869 @@
|
||||
<?php
|
||||
/** @var array $report */
|
||||
/** @var array $view_model */
|
||||
/** @var int $policy_id */
|
||||
/** @var string $pdf_url */
|
||||
/** @var bool $embed */
|
||||
|
||||
use App\Libraries\VidalMisReportService as VidalFmt;
|
||||
|
||||
$embed = $embed ?? false;
|
||||
$report = $report ?? [];
|
||||
$vm = $view_model ?? ($report['view_model'] ?? $report);
|
||||
$meta = $report['meta'] ?? ($vm['meta'] ?? []);
|
||||
$header = $report['header'] ?? ($vm['header'] ?? []);
|
||||
$toc = $vm['toc'] ?? ($report['toc'] ?? []);
|
||||
|
||||
$icr = $vm['icr'] ?? [];
|
||||
$hosp = $vm['hospitalization'] ?? [];
|
||||
$memberGender = $vm['member_gender'] ?? [];
|
||||
$memberAge = $vm['member_age'] ?? [];
|
||||
$claimsAge = $vm['claims_age'] ?? [];
|
||||
$claimsAmount = $vm['claims_amount'] ?? [];
|
||||
$ailments = $vm['ailments'] ?? [];
|
||||
$hospitals = $vm['hospitals'] ?? [];
|
||||
$cmSummary = $vm['cashless_member_summary'] ?? [];
|
||||
$tat = $vm['tat'] ?? [];
|
||||
$mom = $vm['month_on_month'] ?? [];
|
||||
$payout = $vm['payout'] ?? [];
|
||||
$chartGender = $vm['chart_gender'] ?? [];
|
||||
$chartAge = $vm['chart_age'] ?? [];
|
||||
|
||||
$ageRelations = ['Self', 'Spouse', 'Partner', 'Child', 'Parents', 'In Law', 'Other'];
|
||||
|
||||
$logoSrc = !empty($embed)
|
||||
? (rtrim(ROOTPATH, '/\\') . '/public/assets/images/vidal_mis.png')
|
||||
: base_url('public/assets/images/vidal_mis.png');
|
||||
|
||||
$num = static function ($n, int $d = 0): string {
|
||||
return VidalFmt::fmtNum($n, $d);
|
||||
};
|
||||
$pct = static function ($n, int $d = 0, bool $withSymbol = true): string {
|
||||
return VidalFmt::fmtPct($n, $d, $withSymbol);
|
||||
};
|
||||
$pct2 = static function ($n): string {
|
||||
return VidalFmt::fmtPct($n, 2, true);
|
||||
};
|
||||
|
||||
$cellCa = static function ($cell) use ($num): array {
|
||||
$c = is_array($cell) ? $cell : [];
|
||||
return [
|
||||
'count' => $num($c['count'] ?? 0),
|
||||
'amount' => $num($c['amount'] ?? 0),
|
||||
];
|
||||
};
|
||||
|
||||
$genderMax = 1;
|
||||
foreach ($chartGender as $g) {
|
||||
$genderMax = max($genderMax, (int) ($g['male'] ?? 0) + (int) ($g['female'] ?? 0));
|
||||
}
|
||||
$ageMax = 1;
|
||||
foreach ($chartAge as $a) {
|
||||
$ageMax = max($ageMax, (int) ($a['total'] ?? 0));
|
||||
}
|
||||
|
||||
$ageBarColor = static function (string $dominant): string {
|
||||
return match (true) {
|
||||
$dominant === 'Child' => '#4472c4',
|
||||
str_contains($dominant, 'Self') => '#264478',
|
||||
str_contains($dominant, 'Parents') => '#ffc000',
|
||||
default => '#70ad47',
|
||||
};
|
||||
};
|
||||
|
||||
/** DomPDF-safe horizontal bar (nested table widths). */
|
||||
$renderBar = static function (float $pct, string $color): void {
|
||||
$filled = (int) max(0, min(100, round($pct)));
|
||||
$empty = 100 - $filled;
|
||||
echo '<table width="100%" cellspacing="0" cellpadding="0"><tr>';
|
||||
if ($filled > 0) {
|
||||
echo '<td width="' . $filled . '%" style="background:' . esc($color) . ';height:10px;font-size:1px;line-height:1px;"> </td>';
|
||||
}
|
||||
if ($empty > 0) {
|
||||
echo '<td width="' . $empty . '%" style="background:#f2f2f2;height:10px;font-size:1px;line-height:1px;"> </td>';
|
||||
}
|
||||
if ($filled === 0 && $empty === 0) {
|
||||
echo '<td width="100%" style="background:#f2f2f2;height:10px;font-size:1px;line-height:1px;"> </td>';
|
||||
}
|
||||
echo '</tr></table>';
|
||||
};
|
||||
|
||||
$renderRelationClaimRow = static function (
|
||||
string $label,
|
||||
array $row,
|
||||
array $ageRelations,
|
||||
bool $isTotal = false
|
||||
) use ($num, $pct2, $cellCa): void {
|
||||
$cls = $isTotal ? ' class="total-row"' : '';
|
||||
echo '<tr' . $cls . '><td class="rowlabel">' . esc($label) . '</td>';
|
||||
foreach ($ageRelations as $rel) {
|
||||
$ca = $cellCa($row[$rel] ?? []);
|
||||
echo '<td>' . esc($ca['count']) . '</td><td class="num">' . esc($ca['amount']) . '</td>';
|
||||
}
|
||||
$tot = $cellCa($row['total'] ?? ($row['_total'] ?? []));
|
||||
echo '<td>' . esc($tot['count']) . '</td><td class="num">' . esc($tot['amount']) . '</td>';
|
||||
$pct = $row['pct'] ?? [];
|
||||
if (is_array($pct) && (isset($pct['count']) || isset($pct['amount']))) {
|
||||
echo '<td>' . esc($pct2($pct['count'] ?? 0) . ' / ' . $pct2($pct['amount'] ?? 0)) . '</td>';
|
||||
} else {
|
||||
echo '<td></td>';
|
||||
}
|
||||
echo '</tr>';
|
||||
};
|
||||
|
||||
$renderRelationPctRow = static function (array $colPct, array $ageRelations) use ($pct): void {
|
||||
echo '<tr><td class="rowlabel">%</td>';
|
||||
foreach ($ageRelations as $rel) {
|
||||
$p = $colPct[$rel] ?? [];
|
||||
if (is_array($p)) {
|
||||
echo '<td>' . esc($pct($p['count'] ?? 0)) . '</td><td>' . esc($pct($p['amount'] ?? 0)) . '</td>';
|
||||
} else {
|
||||
echo '<td>' . esc($pct($p)) . '</td><td></td>';
|
||||
}
|
||||
}
|
||||
echo '<td>' . esc($pct(100)) . '</td><td>' . esc($pct(100)) . '</td><td></td></tr>';
|
||||
};
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vidal Health - Corporate Analysis Report</title>
|
||||
<style>
|
||||
:root {
|
||||
--blue: #0072bc;
|
||||
--header-blue: #dbe9f5;
|
||||
--group-header: #b9d6ec;
|
||||
--border: #888;
|
||||
--text: #111;
|
||||
--muted: #444;
|
||||
--male: #4472c4;
|
||||
--female: #ed7d31;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
font-family: DejaVu Sans, Arial, Helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
color: var(--text);
|
||||
background: #e8e8e8;
|
||||
margin: 0;
|
||||
padding: 20px 0;
|
||||
}
|
||||
.page {
|
||||
background: #fff;
|
||||
width: 900px;
|
||||
margin: 0 auto 20px auto;
|
||||
padding: 18px 28px 28px 28px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.header-top {
|
||||
border-bottom: 1.5px solid #2f5f8f;
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
.logo-block {
|
||||
display: block;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
.brand-logo {
|
||||
height: 72px;
|
||||
width: auto;
|
||||
max-width: 420px;
|
||||
object-fit: contain;
|
||||
object-position: left top;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.addr-line {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
line-height: 1.35;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.report-title {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
margin: 8px 0 6px 0;
|
||||
}
|
||||
|
||||
.top-grid-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.top-grid-table > tbody > tr > td {
|
||||
vertical-align: top;
|
||||
width: 50%;
|
||||
padding: 0 16px 0 0;
|
||||
border: none;
|
||||
}
|
||||
.top-grid-table > tbody > tr > td:last-child { padding: 0 0 0 16px; }
|
||||
.policy-details table { border-collapse: collapse; }
|
||||
.policy-details td { padding: 1px 6px 1px 0; font-size: 11px; border: none; text-align: left; }
|
||||
.policy-details td.label { font-weight: bold; }
|
||||
.section-label { font-weight: bold; text-decoration: underline; margin-bottom: 4px; }
|
||||
.toc ol { margin: 0; padding-left: 18px; font-size: 11px; line-height: 1.6; }
|
||||
|
||||
h3.section-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin: 12px 0 6px 0;
|
||||
border-bottom: 1px solid #999;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
table.section-wrap {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 0;
|
||||
}
|
||||
table.section-wrap > tbody > tr > td {
|
||||
border: none;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.data {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
font-size: 10.5px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
table.data th, table.data td {
|
||||
border: 1px solid var(--border);
|
||||
padding: 3px 5px;
|
||||
text-align: center;
|
||||
}
|
||||
table.data th {
|
||||
background: var(--header-blue);
|
||||
font-weight: bold;
|
||||
}
|
||||
table.data td.rowlabel, table.data th.rowlabel { text-align: left; }
|
||||
table.data tr.total-row td {
|
||||
font-weight: bold;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.group-header { background: var(--group-header) !important; }
|
||||
table.data td.num, table.data th.num { text-align: right; }
|
||||
|
||||
.two-col-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.two-col-table > tbody > tr > td {
|
||||
vertical-align: top;
|
||||
width: 50%;
|
||||
padding: 0 10px 0 0;
|
||||
border: none;
|
||||
}
|
||||
.two-col-table > tbody > tr > td:last-child { padding: 0 0 0 10px; }
|
||||
|
||||
.notes-box {
|
||||
font-size: 9.5px;
|
||||
border: 1px solid #aaa;
|
||||
padding: 6px 10px;
|
||||
background: #fafafa;
|
||||
margin-top: 4px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.summary-mini { margin-top: 6px; }
|
||||
.summary-mini table { border-collapse: collapse; }
|
||||
.summary-mini td, .summary-mini th {
|
||||
border: 1px solid var(--border);
|
||||
padding: 3px 8px;
|
||||
font-size: 10.5px;
|
||||
}
|
||||
|
||||
.chart-container { width: 100%; }
|
||||
table.bar-chart {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 9.5px;
|
||||
}
|
||||
table.bar-chart td {
|
||||
border: none;
|
||||
padding: 2px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
table.bar-chart td.bar-label { width: 70px; text-align: left; white-space: nowrap; }
|
||||
table.bar-chart td.bar-track { padding: 1px 0; }
|
||||
table.bar-chart td.bar-track table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
table.bar-chart td.bar-track table td {
|
||||
height: 12px;
|
||||
font-size: 1px;
|
||||
line-height: 1px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
table.bar-chart td.bar-value { width: 80px; padding-left: 6px; text-align: left; white-space: nowrap; font-size: 9px; }
|
||||
.legend { margin-top: 6px; font-size: 9.5px; }
|
||||
.legend-swatch { width: 10px; height: 10px; display: inline-block; margin-right: 4px; vertical-align: middle; }
|
||||
.legend span { margin-right: 12px; }
|
||||
|
||||
.footer-disclaimer {
|
||||
font-size: 8.5px;
|
||||
color: var(--muted);
|
||||
border-top: 1px solid #ccc;
|
||||
margin-top: 20px;
|
||||
padding-top: 6px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.small-note { font-size: 9.5px; margin: 2px 0 6px 0; }
|
||||
.table-narrow { width: 60%; }
|
||||
.table-tat { width: 40%; }
|
||||
.page-break { page-break-before: always; }
|
||||
.keep-together { page-break-inside: avoid; }
|
||||
|
||||
@media print {
|
||||
body { background: #fff; padding: 0; }
|
||||
.page { box-shadow: none; border: none; margin: 0 auto; }
|
||||
}
|
||||
|
||||
<?php if (!empty($embed)): ?>
|
||||
body { background: #fff; padding: 0; }
|
||||
.page {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border: none;
|
||||
}
|
||||
.brand-logo { height: 64px; width: auto; max-width: 380px; object-fit: contain; object-position: left top; }
|
||||
.addr-line { font-size: 8px; font-weight: bold; margin-top: 8px; line-height: 1.3; text-align: left; }
|
||||
.report-title { margin: 4px 0 4px 0; font-size: 11px; }
|
||||
.top-grid-table { margin-bottom: 6px; }
|
||||
.top-grid-table > tbody > tr > td { padding: 0 10px 0 0; }
|
||||
.top-grid-table > tbody > tr > td:last-child { padding: 0 0 0 10px; }
|
||||
.policy-details td { font-size: 9px; padding: 0 4px 0 0; }
|
||||
.toc ol { font-size: 9px; line-height: 1.35; padding-left: 16px; }
|
||||
.two-col-table > tbody > tr > td { padding: 0 6px 0 0; }
|
||||
.two-col-table > tbody > tr > td:last-child { padding: 0 0 0 6px; }
|
||||
table.data { font-size: 8px; table-layout: fixed; }
|
||||
table.data th, table.data td { padding: 2px 2px; word-wrap: break-word; }
|
||||
h3.section-title { margin: 8px 0 3px 0; font-size: 10px; }
|
||||
table.bar-chart { font-size: 8px; }
|
||||
table.bar-chart td.bar-label { width: 55px; }
|
||||
table.bar-chart td.bar-track table td { height: 10px; }
|
||||
table.bar-chart td.bar-value { width: 70px; font-size: 7.5px; }
|
||||
.table-narrow { width: 70%; }
|
||||
.table-tat { width: 45%; }
|
||||
.summary-mini table { font-size: 8.5px; }
|
||||
.notes-box { font-size: 7.5px; page-break-inside: avoid; }
|
||||
.footer-disclaimer { font-size: 7px; }
|
||||
.page { padding: 8px 10px; }
|
||||
table.section-wrap { page-break-inside: avoid; }
|
||||
.intro-block, .section-block { page-break-inside: avoid; }
|
||||
<?php endif; ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
|
||||
<table class="section-wrap intro-block">
|
||||
<tr><td>
|
||||
<!-- HEADER: logo + address above blue line -->
|
||||
<div class="header-top">
|
||||
<div class="logo-block">
|
||||
<img src="<?= esc($logoSrc) ?>" alt="Vidal Health" class="brand-logo">
|
||||
</div>
|
||||
<div class="addr-line">
|
||||
<b>Corporate Office :</b> Tower-2, 1st floor, SJR I Park, Plot No: 13,14,15 , EPIP Zone, Whitefield, Bangalore-560066<br>
|
||||
Phone: 91-80-40125678 Fax : 91-80-41159215 Email: care@vidalhealthtpa.com Website : www.vidalhealthtpa.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="report-title">Corporate Analysis Report</div>
|
||||
|
||||
<!-- POLICY DETAILS + TOC -->
|
||||
<table class="top-grid-table">
|
||||
<tr>
|
||||
<td class="policy-details">
|
||||
<div class="section-label">Policy Details:</div>
|
||||
<table>
|
||||
<tr><td class="label">Corporate Name:</td><td><?= esc($header['corporate_name'] ?? '') ?></td></tr>
|
||||
<tr><td class="label">Insurer Policy Number:</td><td><?= esc($header['policy_number'] ?? '') ?></td></tr>
|
||||
<tr><td class="label">Policy Start Date:</td><td><?= esc($header['start'] ?? '') ?></td></tr>
|
||||
<tr><td class="label">Policy End Date:</td><td><?= esc($header['end'] ?? '') ?></td></tr>
|
||||
<tr><td class="label">Total Premium:(in Rs.)</td><td><?= esc($num($header['premium'] ?? 0)) ?></td></tr>
|
||||
<tr><td class="label">Earned Premium:(in Rs.)</td><td><?= esc($num($header['earned'] ?? 0)) ?></td></tr>
|
||||
<tr><td class="label">Lives Covered:(in Nos.)</td><td><?= esc($num($header['lives'] ?? 0)) ?></td></tr>
|
||||
<tr><td class="label">Report Generated By(Broker):</td><td><?= esc($header['generated_by'] ?? '') ?></td></tr>
|
||||
<tr><td class="label">Report Generated Date:</td><td><?= esc($header['generated_at'] ?? ($meta['generated_at'] ?? '')) ?></td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="toc">
|
||||
<div class="section-label">Table of Contents</div>
|
||||
<ol>
|
||||
<?php foreach (($toc ?: []) as $item): ?>
|
||||
<li><?= esc((string) $item) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- 1. ICR -->
|
||||
<table class="section-wrap section-block">
|
||||
<tr><td>
|
||||
<h3 class="section-title">1. Incurred Claims Ratio (ICR):</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Claim Status</th>
|
||||
<th colspan="2">Cashless</th>
|
||||
<th colspan="2">Member</th>
|
||||
<th colspan="2">Total</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th><th>Nos.</th><th>Amt. (in Rs.)</th><th>Nos.</th><th>Amt. (in Rs.)</th><th>Nos.</th><th>Amt. (in Rs.)</th>
|
||||
</tr>
|
||||
<?php foreach (($icr['rows'] ?? []) as $row):
|
||||
$isIncurred = str_contains((string) ($row['label'] ?? ''), 'Incurred');
|
||||
$c = $cellCa($row['cashless'] ?? []);
|
||||
$m = $cellCa($row['member'] ?? []);
|
||||
$t = $cellCa($row['total'] ?? []);
|
||||
?>
|
||||
<tr<?= $isIncurred ? ' class="total-row"' : '' ?>>
|
||||
<td class="rowlabel"><?= esc($row['label'] ?? '') ?></td>
|
||||
<td><?= esc($c['count']) ?></td><td class="num"><?= esc($c['amount']) ?></td>
|
||||
<td><?= esc($m['count']) ?></td><td class="num"><?= esc($m['amount']) ?></td>
|
||||
<td><?= esc($t['count']) ?></td><td class="num"><?= esc($t['amount']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
$sum = $icr['summary'] ?? [];
|
||||
$dr = $sum['disposal_rate'] ?? [];
|
||||
$cpc = $sum['cpc'] ?? [];
|
||||
?>
|
||||
<div class="summary-mini">
|
||||
<table>
|
||||
<tr><td>ICR On EP*</td><td colspan="2"></td><td><b><?= esc($pct($sum['icr_on_ep'] ?? 0, 1)) ?></b></td></tr>
|
||||
<tr><td>Incidence Rate</td><td colspan="2"></td><td><b><?= esc($pct($sum['incidence_rate'] ?? 0, 1)) ?></b></td></tr>
|
||||
<tr>
|
||||
<td>Disposal Rate</td>
|
||||
<td><?= esc($pct($dr['cashless'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct($dr['member'] ?? 0)) ?></td>
|
||||
<td><b><?= esc($pct($dr['total'] ?? 0)) ?></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cost per Claims(CPC)</td>
|
||||
<td><?= esc($num($cpc['cashless'] ?? 0)) ?></td>
|
||||
<td><?= esc($num($cpc['member'] ?? 0)) ?></td>
|
||||
<td><b><?= esc($num($cpc['total'] ?? 0)) ?></b></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- 2. Hospitalisation Type -->
|
||||
<table class="section-wrap section-block">
|
||||
<tr><td>
|
||||
<h3 class="section-title">2. Hospitalisation Type Details:</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Claim Subtype</th>
|
||||
<th colspan="2">Cashless</th>
|
||||
<th colspan="2">Member</th>
|
||||
</tr>
|
||||
<tr><th></th><th>Nos.</th><th>Amt. (in Rs.)</th><th>Nos.</th><th>Amt. (in Rs.)</th></tr>
|
||||
<?php foreach (($hosp['rows'] ?? []) as $row):
|
||||
$c = $cellCa($row['cashless'] ?? []);
|
||||
$m = $cellCa($row['member'] ?? []);
|
||||
?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['label'] ?? '') ?></td>
|
||||
<td><?= esc($c['count']) ?></td><td class="num"><?= esc($c['amount']) ?></td>
|
||||
<td><?= esc($m['count']) ?></td><td class="num"><?= esc($m['amount']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
$ht = $hosp['total'] ?? [];
|
||||
$hc = $cellCa($ht['cashless'] ?? []);
|
||||
$hm = $cellCa($ht['member'] ?? []);
|
||||
?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">Total</td>
|
||||
<td><?= esc($hc['count']) ?></td><td class="num"><?= esc($hc['amount']) ?></td>
|
||||
<td><?= esc($hm['count']) ?></td><td class="num"><?= esc($hm['amount']) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="small-note">*Considering Only Settled ,Approved and UTR Awaiting (Cheque Pending)</div>
|
||||
|
||||
<div class="notes-box">
|
||||
<b>Notes:</b><br>
|
||||
ICR = (Settled Amt + Outstanding Amt) / Annual Premium<br>
|
||||
ICR on EP* = (Settled Amt + Outstanding Amt) / Earned Premium<br>
|
||||
Earned Premium = Prorated premium as on report generated date<br>
|
||||
Cost Per Claim(CPC) = Approved Amt / Number of Events(Main Claims) for IPD + Daycare Cases<br>
|
||||
Incidents Rate = No of Claim Events/ Lives<br>
|
||||
Disposal Rate = (Settled+Rejected+Awaiting UTR+Cancelled / Claims Reported)<br>
|
||||
* EP- Earned Premium ; O/S - Outstanding<br>
|
||||
* Event = Main Claims Only (Excluding Prepost and Addendum)
|
||||
</div>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- 3. Member Details - Relationship & Gender -->
|
||||
<table class="section-wrap section-block">
|
||||
<tr><td>
|
||||
<h3 class="section-title">3. Member Details - Relationship & Gender wise :</h3>
|
||||
<table class="two-col-table">
|
||||
<tr>
|
||||
<td class="tbl-side">
|
||||
<table class="data">
|
||||
<tr><th class="rowlabel">Relation</th><th>Male</th><th>Female</th><th>Total</th><th>%</th></tr>
|
||||
<?php foreach (($memberGender['rows'] ?? []) as $row): ?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['relation'] ?? '') ?></td>
|
||||
<td><?= esc($num($row['male'] ?? 0)) ?></td>
|
||||
<td><?= esc($num($row['female'] ?? 0)) ?></td>
|
||||
<td><?= esc($num($row['total'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2($row['pct'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
$mgt = $memberGender['total'] ?? [];
|
||||
$mgp = $memberGender['pct_gender'] ?? [];
|
||||
?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">Total</td>
|
||||
<td><?= esc($num($mgt['male'] ?? 0)) ?></td>
|
||||
<td><?= esc($num($mgt['female'] ?? 0)) ?></td>
|
||||
<td><?= esc($num($mgt['total'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2(100)) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="rowlabel">%</td>
|
||||
<td><?= esc($pct($mgp['male'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct($mgp['female'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct(100)) ?></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="chart-side">
|
||||
<div class="chart-container">
|
||||
<table class="bar-chart">
|
||||
<?php foreach ($chartGender as $g):
|
||||
$rel = (string) ($g['relation'] ?? '');
|
||||
$male = (int) ($g['male'] ?? 0);
|
||||
$female = (int) ($g['female'] ?? 0);
|
||||
$total = $male + $female;
|
||||
$w = ($total / $genderMax) * 100;
|
||||
$color = ($female > $male) ? '#ed7d31' : '#4472c4';
|
||||
?>
|
||||
<tr>
|
||||
<td class="bar-label"><?= esc($rel) ?></td>
|
||||
<td class="bar-track"><?php $renderBar($w, $color); ?></td>
|
||||
<td class="bar-value"><?= esc($num($total)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<div class="legend">
|
||||
<span><span class="legend-swatch" style="background:#4472c4;"></span>Male</span>
|
||||
<span><span class="legend-swatch" style="background:#ed7d31;"></span>Female</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- 4. Member Details Age Band -->
|
||||
<table class="section-wrap section-block">
|
||||
<tr><td>
|
||||
<h3 class="section-title">4. Member Details - Age Band & Relationship wise :</h3>
|
||||
<table class="two-col-table">
|
||||
<tr>
|
||||
<td class="tbl-side">
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">AgeBand</th>
|
||||
<?php foreach ($ageRelations as $rel): ?><th><?= esc($rel) ?></th><?php endforeach; ?>
|
||||
<th>Total</th><th>%</th>
|
||||
</tr>
|
||||
<?php foreach (($memberAge['rows'] ?? []) as $row): ?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['band'] ?? '') ?></td>
|
||||
<?php foreach ($ageRelations as $rel): ?>
|
||||
<td><?= esc($num($row[$rel] ?? 0)) ?></td>
|
||||
<?php endforeach; ?>
|
||||
<td><?= esc($num($row['total'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2($row['pct'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
$mat = $memberAge['total'] ?? [];
|
||||
$mac = $memberAge['col_pct'] ?? [];
|
||||
?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">Total</td>
|
||||
<?php foreach ($ageRelations as $rel): ?>
|
||||
<td><?= esc($num($mat[$rel] ?? 0)) ?></td>
|
||||
<?php endforeach; ?>
|
||||
<td><?= esc($num($mat['_total'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2(100)) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="rowlabel">%</td>
|
||||
<?php foreach ($ageRelations as $rel): ?>
|
||||
<td><?= esc($pct($mac[$rel] ?? 0)) ?></td>
|
||||
<?php endforeach; ?>
|
||||
<td><?= esc($pct(100)) ?></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="chart-side">
|
||||
<div class="chart-container">
|
||||
<table class="bar-chart">
|
||||
<?php foreach ($chartAge as $a):
|
||||
$total = (int) ($a['total'] ?? 0);
|
||||
$w = ($total / $ageMax) * 100;
|
||||
$dom = (string) ($a['dominant'] ?? '');
|
||||
$color = $ageBarColor($dom);
|
||||
$label = $num($total) . ($dom !== '' ? ' (' . $dom . ')' : '');
|
||||
?>
|
||||
<tr>
|
||||
<td class="bar-label"><?= esc($a['band'] ?? '') ?></td>
|
||||
<td class="bar-track"><?php $renderBar($w, $color); ?></td>
|
||||
<td class="bar-value"><?= esc($label) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<div class="legend">
|
||||
<span><span class="legend-swatch" style="background:#264478;"></span>Self</span>
|
||||
<span><span class="legend-swatch" style="background:#a5a5a5;"></span>Spouse</span>
|
||||
<span><span class="legend-swatch" style="background:#ffc000;"></span>Parents</span>
|
||||
<span><span class="legend-swatch" style="background:#4472c4;"></span>Child</span>
|
||||
<span><span class="legend-swatch" style="background:#70ad47;"></span>In Law/Other</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<!-- 5. Claims Approved Age Band -->
|
||||
<h3 class="section-title">5. Claims Approved - Age Band & Relationship wise :</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Age Band</th>
|
||||
<?php foreach ($ageRelations as $rel): ?><th colspan="2"><?= esc($rel) ?></th><?php endforeach; ?>
|
||||
<th colspan="2">Total</th><th>Total %</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<?php foreach ($ageRelations as $_): ?><th>No.</th><th>Amt.(Rs.)</th><?php endforeach; ?>
|
||||
<th>No.</th><th>Amt.(Rs.)</th><th>No.%/Amt.%</th>
|
||||
</tr>
|
||||
<?php foreach (($claimsAge['rows'] ?? []) as $row):
|
||||
$renderRelationClaimRow((string) ($row['band'] ?? ''), $row, $ageRelations);
|
||||
endforeach;
|
||||
$cat = $claimsAge['total'] ?? [];
|
||||
$totalRow = $cat;
|
||||
$totalRow['total'] = $cat['_total'] ?? [];
|
||||
$totalRow['pct'] = ['count' => 100, 'amount' => 100];
|
||||
$renderRelationClaimRow('Total', $totalRow, $ageRelations, true);
|
||||
$renderRelationPctRow($claimsAge['col_pct'] ?? [], $ageRelations);
|
||||
?>
|
||||
</table>
|
||||
<div class="small-note">* Count is only for Approved Claims(Settled and Awaiting UTR(Cheque Pending)) .</div>
|
||||
|
||||
<!-- 6. Claims Approved Amount Band -->
|
||||
<h3 class="section-title">6. Claims Approved - Amount Band & Relationship wise :</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Amount Band</th>
|
||||
<?php foreach ($ageRelations as $rel): ?><th colspan="2"><?= esc($rel) ?></th><?php endforeach; ?>
|
||||
<th colspan="2">Total</th><th>Total %</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<?php foreach ($ageRelations as $_): ?><th>No.</th><th>Amt.(Rs.)</th><?php endforeach; ?>
|
||||
<th>No.</th><th>Amt.(Rs.)</th><th>No.%/Amt.%</th>
|
||||
</tr>
|
||||
<?php foreach (($claimsAmount['rows'] ?? []) as $row):
|
||||
$renderRelationClaimRow((string) ($row['band'] ?? ''), $row, $ageRelations);
|
||||
endforeach;
|
||||
$amtT = $claimsAmount['total'] ?? [];
|
||||
$amtTotalRow = $amtT;
|
||||
$amtTotalRow['total'] = $amtT['_total'] ?? [];
|
||||
$amtTotalRow['pct'] = ['count' => 100, 'amount' => 100];
|
||||
$renderRelationClaimRow('Total', $amtTotalRow, $ageRelations, true);
|
||||
$renderRelationPctRow($claimsAmount['col_pct'] ?? [], $ageRelations);
|
||||
?>
|
||||
</table>
|
||||
<div class="small-note">* Count is only for Approved Claims(Settled and Awaiting UTR (Cheque Pending)). * Banding for Incurred Amount</div>
|
||||
|
||||
<!-- 7. Ailment wise -->
|
||||
<h3 class="section-title">7. Claims Approved - Top 15 Ailment wise :</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Ailment Group</th>
|
||||
<?php foreach ($ageRelations as $rel): ?><th colspan="2"><?= esc($rel) ?></th><?php endforeach; ?>
|
||||
<th colspan="2">Total</th><th>Total %</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<?php foreach ($ageRelations as $_): ?><th>No.</th><th>Amt.(Rs.)</th><?php endforeach; ?>
|
||||
<th>No.</th><th>Amt.(Rs.)</th><th>No.%/Amt.%</th>
|
||||
</tr>
|
||||
<?php foreach (($ailments['rows'] ?? []) as $row):
|
||||
$renderRelationClaimRow((string) ($row['ailment'] ?? ''), $row, $ageRelations);
|
||||
endforeach;
|
||||
$ailT = $ailments['total'] ?? [];
|
||||
$ailTotalRow = $ailT;
|
||||
$ailTotalRow['total'] = $ailT['_total'] ?? [];
|
||||
$ailTotalRow['pct'] = ['count' => 100, 'amount' => 100];
|
||||
$renderRelationClaimRow('Total', $ailTotalRow, $ageRelations, true);
|
||||
$renderRelationPctRow($ailments['col_pct'] ?? [], $ageRelations);
|
||||
?>
|
||||
</table>
|
||||
<div class="small-note">* Count is only for Approved Claims(Settled and Awaiting UTR (Cheque Pending)) .</div>
|
||||
|
||||
<!-- 8. Top 15 Hospitals -->
|
||||
<h3 class="section-title">8. Top 15 Cashless Hospital wise utilization:</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Hospital_ID</th>
|
||||
<th class="rowlabel">Hospital_Name</th>
|
||||
<th>No of Claims</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
<?php if (($hospitals ?? []) === []): ?>
|
||||
<tr><td class="rowlabel" colspan="4">No cashless hospital utilization</td></tr>
|
||||
<?php else: foreach ($hospitals as $h): ?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($h['hospital_id'] ?? '') ?></td>
|
||||
<td class="rowlabel"><?= esc($h['hospital_name'] ?? '') ?></td>
|
||||
<td><?= esc($num($h['count'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($num($h['amount'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</table>
|
||||
|
||||
<!-- 9. Cashless & Member Summary -->
|
||||
<h3 class="section-title">9. Claims Approved - Cashless & Member Summary:</h3>
|
||||
<table class="data table-narrow">
|
||||
<tr>
|
||||
<th class="rowlabel">Type of claim</th>
|
||||
<th>Events</th><th>Events%</th><th>Amount</th><th>Amount%</th>
|
||||
</tr>
|
||||
<?php foreach (($cmSummary['rows'] ?? []) as $row): ?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['label'] ?? '') ?></td>
|
||||
<td><?= esc($num($row['count'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2($row['count_pct'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($num($row['amount'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2($row['amount_pct'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
$cmt = $cmSummary['total'] ?? [];
|
||||
?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">TOTAL</td>
|
||||
<td><?= esc($num($cmt['count'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2(100)) ?></td>
|
||||
<td class="num"><?= esc($num($cmt['amount'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2(100)) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- 10. TAT -->
|
||||
<h3 class="section-title">10. Turn Around Time (TAT) :</h3>
|
||||
<div class="small-note"><b>Claim Process TAT :</b></div>
|
||||
<table class="data table-tat">
|
||||
<tr><th class="rowlabel">TAT Band</th><th>Nos.</th><th>%</th></tr>
|
||||
<?php foreach (($tat['rows'] ?? []) as $row): ?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['band'] ?? '') ?></td>
|
||||
<td><?= esc($num($row['count'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2($row['pct'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">Total</td>
|
||||
<td><?= esc($num($tat['total'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct2(100)) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="small-note">Note: Only Settled,Awaiting UTR, Approved and Rejected claims are considered<br>* LDR to Decision date<br>* only for Member claims</div>
|
||||
|
||||
<!-- 11. Month on Month -->
|
||||
<h3 class="section-title">11. Month on Month</h3>
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th class="rowlabel">Admission Month</th>
|
||||
<th colspan="2">Hospitalization and Daycare</th>
|
||||
<th colspan="2">Other than Hospitalization</th>
|
||||
<th colspan="2">Total</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Inc Count</th><th>Inc Amount</th>
|
||||
<th>Inc Count</th><th>Inc Amount</th>
|
||||
<th>Inc Count</th><th>Inc Amount</th>
|
||||
</tr>
|
||||
<?php foreach (($mom['rows'] ?? []) as $row):
|
||||
$h = $cellCa($row['hosp'] ?? []);
|
||||
$o = $cellCa($row['other'] ?? []);
|
||||
$t = $cellCa($row['total'] ?? []);
|
||||
?>
|
||||
<tr>
|
||||
<td class="rowlabel"><?= esc($row['month'] ?? '') ?></td>
|
||||
<td><?= esc($h['count']) ?></td><td class="num"><?= esc($h['amount']) ?></td>
|
||||
<td><?= esc($o['count']) ?></td><td class="num"><?= esc($o['amount']) ?></td>
|
||||
<td><?= esc($t['count']) ?></td><td class="num"><?= esc($t['amount']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
$mt = $mom['total'] ?? [];
|
||||
$mh = $cellCa($mt['hosp'] ?? []);
|
||||
$mo = $cellCa($mt['other'] ?? []);
|
||||
$mtt = $cellCa($mt['total'] ?? []);
|
||||
?>
|
||||
<tr class="total-row">
|
||||
<td class="rowlabel">TOTAL</td>
|
||||
<td><?= esc($mh['count']) ?></td><td class="num"><?= esc($mh['amount']) ?></td>
|
||||
<td><?= esc($mo['count']) ?></td><td class="num"><?= esc($mo['amount']) ?></td>
|
||||
<td><?= esc($mtt['count']) ?></td><td class="num"><?= esc($mtt['amount']) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- 12. Payout Ratio -->
|
||||
<h3 class="section-title">12. Payout Ratio</h3>
|
||||
<table class="data table-narrow">
|
||||
<tr><th>Claimed Amount</th><th>Settled Amount</th><th>Payout %</th></tr>
|
||||
<tr>
|
||||
<td class="num"><?= esc($num($payout['claimed'] ?? 0)) ?></td>
|
||||
<td class="num"><?= esc($num($payout['settled'] ?? 0)) ?></td>
|
||||
<td><?= esc($pct($payout['payout_pct'] ?? 0)) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer-disclaimer">
|
||||
<b>DISCLAIMER:</b> Confidential information, not intended for public dissemination. Vidal Health Insurance TPA Pvt ltd makes no representations or warranties, express, implied or otherwise, regarding the accuracy and
|
||||
completeness of the information, and shall have no liability resulting from the use of the information. The Receiving Party will use information received in a safe and prudent manner and is responsible for all risk or loss arising
|
||||
out of its use of such information. Data will be refreshed on every day night. Report is based on previous Day Data.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
31
app/Views/partials/claims_mis_abhi_helpers.php
Normal file
31
app/Views/partials/claims_mis_abhi_helpers.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Shared helpers for ABHI MIS HTML + PDF embed mode.
|
||||
* Expects: $vm, $fmt, $embed, $charts
|
||||
*/
|
||||
|
||||
$charts = $charts ?? [];
|
||||
$embed = !empty($embed);
|
||||
|
||||
$chartHtml = static function (string $key, string $canvasId) use ($embed, $charts): string {
|
||||
if ($embed && !empty($charts[$key]) && is_file((string) $charts[$key])) {
|
||||
return '<img class="chart-img" src="' . htmlspecialchars((string) $charts[$key], ENT_QUOTES) . '" alt="">';
|
||||
}
|
||||
|
||||
return '<canvas id="' . htmlspecialchars($canvasId, ENT_QUOTES) . '"></canvas>';
|
||||
};
|
||||
|
||||
$num = static function ($n, int $d = 0) use ($fmt): string {
|
||||
return esc($fmt($n, $d));
|
||||
};
|
||||
|
||||
$dash = static function ($v) use ($fmt): string {
|
||||
if ($v === '' || $v === null) {
|
||||
return '—';
|
||||
}
|
||||
if (is_numeric($v)) {
|
||||
return esc($fmt($v));
|
||||
}
|
||||
|
||||
return esc((string) $v);
|
||||
};
|
||||
550
app/Views/partials/claims_mis_abhi_script.php
Normal file
550
app/Views/partials/claims_mis_abhi_script.php
Normal file
@ -0,0 +1,550 @@
|
||||
<script>
|
||||
(function () {
|
||||
const MIS_DATA = <?= json_encode($vm ?? [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP) ?>;
|
||||
const embedMode = <?= !empty($embed) ? 'true' : 'false' ?>;
|
||||
|
||||
const COLORS = {
|
||||
blue: '#2E86DE',
|
||||
navy: '#1B3F8C',
|
||||
orange: '#E8862C',
|
||||
purple: '#6A3D9A',
|
||||
red: '#C8102E',
|
||||
female: '#1A3399',
|
||||
male: '#6A3D9A'
|
||||
};
|
||||
|
||||
function fmt(n) {
|
||||
if (n === '' || n === null || n === undefined) return '';
|
||||
return Number(n).toLocaleString('en-IN');
|
||||
}
|
||||
function dash(v) { return (v === 0 || v === '0') ? '—' : v; }
|
||||
function el(id) { return document.getElementById(id); }
|
||||
function setHtml(id, html) { const node = el(id); if (node) node.innerHTML = html; }
|
||||
function setText(id, text) { const node = el(id); if (node) node.textContent = text; }
|
||||
|
||||
const claimStatus = MIS_DATA.claimStatus || [];
|
||||
setHtml('tblClaimStatus', claimStatus.map(r =>
|
||||
`<tr><td>${r.status}</td><td class="num">${r.number}</td><td class="num">${fmt(r.reported)}</td><td class="num">${fmt(r.incurred)}</td></tr>`
|
||||
).join(''));
|
||||
|
||||
const policyPremium = MIS_DATA.policyPremium || [];
|
||||
setHtml('tblPolicyPremium', policyPremium.map(r =>
|
||||
`<tr><td>${r.sr}</td><td>${r.label}</td><td class="num">${fmt(r.amount)}</td></tr>`
|
||||
).join('') +
|
||||
`<tr style="font-weight:700;background:#F0F0F3;border-top:2px solid var(--red)"><td colspan="2">Total</td><td class="num">${fmt(MIS_DATA.policyPremiumTotal || 0)}</td></tr>`);
|
||||
|
||||
const reimbTAT = MIS_DATA.reimbTAT || [];
|
||||
setHtml('tblReimbTAT', reimbTAT.map(r =>
|
||||
`<tr><td>${r.range}</td><td class="num">${r.count}</td><td class="num">${r.pct}</td></tr>`
|
||||
).join('') +
|
||||
`<tr style="font-weight:700;background:#F0F0F3;border-top:2px solid var(--red)"><td>Total</td><td class="num">${MIS_DATA.reimbTATTotal?.count || 0}</td><td class="num">${MIS_DATA.reimbTATTotal?.pct || 0}</td></tr>`);
|
||||
|
||||
const gender = MIS_DATA.gender || [];
|
||||
const genderOrder = ['Male', 'Female'];
|
||||
setHtml('tblGender', genderOrder.map(label => {
|
||||
const row = gender.find(g => g.label === label) || { label, count: 0, amount: 0 };
|
||||
return `<tr><td>${row.label}</td><td class="num">${row.count}</td><td class="num">${fmt(row.amount)}</td></tr>`;
|
||||
}).join(''));
|
||||
setText('tblGenderCountTotal', MIS_DATA.genderTotal?.count || 0);
|
||||
setText('tblGenderAmtTotal', fmt(MIS_DATA.genderTotal?.incurred_amount || 0));
|
||||
|
||||
const claimType = MIS_DATA.claimType || [];
|
||||
setHtml('tblClaimType', claimType.map(r =>
|
||||
`<tr ${r.header ? 'class="subhead"' : ''}><td>${r.header ? '' : ' '}${r.type}</td><td class="num">${r.count}</td><td class="num">${r.countPct}</td><td class="num">${fmt(r.amount)}</td><td class="num">${r.amtPct}</td><td class="num">${fmt(r.incurred)}</td><td class="num">${r.incPct}</td></tr>`
|
||||
).join(''));
|
||||
const ct = MIS_DATA.claimTypeTotal || {};
|
||||
setText('tblClaimTypeCount', ct.count || 0);
|
||||
setText('tblClaimTypeAmt', fmt(ct.amount || 0));
|
||||
setText('tblClaimTypeInc', fmt(ct.incurred || 0));
|
||||
|
||||
const relation = MIS_DATA.relation || [];
|
||||
setHtml('tblRelation', relation.map(r =>
|
||||
`<tr><td>${r.label}</td><td class="num">${r.count}</td><td class="num">${r.cpct}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.apct}</td></tr>`
|
||||
).join(''));
|
||||
setText('tblRelationCount', MIS_DATA.relationTotal?.count || 0);
|
||||
setText('tblRelationAmt', fmt(MIS_DATA.relationTotal?.incurred_amount || 0));
|
||||
|
||||
const memberType = MIS_DATA.memberType || [];
|
||||
setHtml('tblMemberType', memberType.map(r =>
|
||||
`<tr><td>${r.label}</td><td class="num">${r.count}</td><td class="num">${r.cpct}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.apct}</td></tr>`
|
||||
).join(''));
|
||||
setText('tblMemberCount', MIS_DATA.memberTypeTotal?.count || 0);
|
||||
setText('tblMemberAmt', fmt(MIS_DATA.memberTypeTotal?.incurred_amount || 0));
|
||||
|
||||
const age = MIS_DATA.age || [];
|
||||
setHtml('tblAge', age.map(r =>
|
||||
`<tr><td>${r.band}</td><td class="num">${r.count}</td><td class="num">${r.cpct}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.apct}</td></tr>`
|
||||
).join(''));
|
||||
setText('tblAgeCount', MIS_DATA.ageTotal?.count || 0);
|
||||
setText('tblAgeAmt', fmt(MIS_DATA.ageTotal?.incurred_amount || 0));
|
||||
|
||||
const diagnosis = MIS_DATA.diagnosis || [];
|
||||
setHtml('tblDiagnosis', diagnosis.map(r =>
|
||||
`<tr><td>${r.name}</td><td class="num">${r.count}</td><td class="num">${r.cpct}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.apct}</td><td class="num">${r.acs ? fmt(r.acs) : ''}</td><td class="num">${r.lr}</td></tr>`
|
||||
).join(''));
|
||||
const dt = MIS_DATA.diagnosisTotal || {};
|
||||
setText('tblDiagCount', dt.count || 0);
|
||||
setText('tblDiagAmt', fmt(dt.amt || 0));
|
||||
setText('tblDiagAcs', fmt(dt.acs || 0));
|
||||
setText('tblDiagLr', dt.lr || 0);
|
||||
|
||||
function renderUtil(id, data, prefix) {
|
||||
const rows = data?.rows || [];
|
||||
setHtml(id, rows.map(r =>
|
||||
`<tr><td>${r.claims}</td><td class="num">${r.beneficiaries}</td><td class="num">${r.incurredCount}</td><td class="num">${r.countPct || dash(0)}</td><td class="num">${fmt(r.amount)}</td><td class="num">${r.amountPct || dash(0)}</td></tr>`
|
||||
).join(''));
|
||||
const t = data?.total || {};
|
||||
setText(prefix + 'Ben', t.beneficiaries || 0);
|
||||
setText(prefix + 'Cnt', t.incurredCount || 0);
|
||||
setText(prefix + 'Amt', fmt(t.amount || 0));
|
||||
}
|
||||
renderUtil('tblUtilEmp', MIS_DATA.utilizationEmployees, 'tblUtilEmp');
|
||||
renderUtil('tblUtilDep', MIS_DATA.utilizationDependents, 'tblUtilDep');
|
||||
|
||||
const hospitals = MIS_DATA.hospitals || [];
|
||||
setHtml('tblHospital', hospitals.map(r =>
|
||||
`<tr><td>${r.name}</td><td class="num">${r.count}</td><td class="num">${r.cpct}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.apct}</td></tr>`
|
||||
).join(''));
|
||||
const ht = MIS_DATA.hospitalTotal || {};
|
||||
setText('tblHospitalCount', ht.count || 0);
|
||||
setText('tblHospitalAmt', fmt(ht.incurred_amount || 0));
|
||||
|
||||
const cities = MIS_DATA.cities || [];
|
||||
setHtml('tblCity', cities.map(r =>
|
||||
`<tr><td>${r.city}</td><td class="num">${r.count}</td><td class="num">${fmt(r.amt)}</td><td class="num">${r.paidCount}</td><td class="num">${r.paidAmt !== '' && r.paidAmt !== null ? Number(r.paidAmt).toFixed(2) : ''}</td><td class="num">${r.outCount}</td><td class="num">${r.outAmt !== '' && r.outAmt !== null ? fmt(r.outAmt) : ''}</td><td class="num">${r.acs !== '' && r.acs !== null ? fmt(r.acs) : ''}</td></tr>`
|
||||
).join(''));
|
||||
const ct2 = MIS_DATA.cityTotals || {};
|
||||
setText('tblCityCount', ct2.count || 0);
|
||||
setText('tblCityAmt', fmt(ct2.amt || 0));
|
||||
setText('tblCityPaidCnt', ct2.paidCount || 0);
|
||||
setText('tblCityPaidAmt', Number(ct2.paidAmt || 0).toFixed(2));
|
||||
setText('tblCityOutCnt', ct2.outCount || 0);
|
||||
setText('tblCityOutAmt', fmt(ct2.outAmt || 0));
|
||||
setText('tblCityAcs', fmt(ct2.acs || 0));
|
||||
|
||||
if (embedMode || typeof Chart === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
Chart.defaults.font.family = 'Segoe UI, Arial, sans-serif';
|
||||
Chart.defaults.font.size = 11;
|
||||
Chart.defaults.color = '#333';
|
||||
|
||||
const statusColors = [COLORS.blue, COLORS.navy, COLORS.orange, COLORS.purple];
|
||||
const statusLabels = ['Reported', 'Settled', 'Outstanding', 'Rejected'];
|
||||
const amountData = claimStatus.map(r => r.reported || 0);
|
||||
const countData = claimStatus.map(r => r.number || 0);
|
||||
|
||||
/* Nested donut: outer = Claim Amount, inner = Claim Count */
|
||||
const donutLeaderPlugin = {
|
||||
id: 'donutLeaderPlugin',
|
||||
afterDatasetsDraw(chart) {
|
||||
const { ctx } = chart;
|
||||
const outerMeta = chart.getDatasetMeta(0);
|
||||
const innerMeta = chart.getDatasetMeta(1);
|
||||
if (!outerMeta || !innerMeta) return;
|
||||
|
||||
outerMeta.data.forEach((arc, i) => {
|
||||
const val = amountData[i];
|
||||
if (!val) return;
|
||||
const pos = arc.tooltipPosition();
|
||||
const angle = (arc.startAngle + arc.endAngle) / 2;
|
||||
const r = arc.outerRadius + 18;
|
||||
const x = arc.x + Math.cos(angle) * r;
|
||||
const y = arc.y + Math.sin(angle) * r;
|
||||
const lx = arc.x + Math.cos(angle) * (arc.outerRadius + 4);
|
||||
const ly = arc.y + Math.sin(angle) * (arc.outerRadius + 4);
|
||||
ctx.save();
|
||||
ctx.strokeStyle = '#999';
|
||||
ctx.lineWidth = 0.8;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lx, ly);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.stroke();
|
||||
ctx.font = 'bold 11px Segoe UI, Arial';
|
||||
ctx.fillStyle = '#222';
|
||||
ctx.textAlign = x < arc.x ? 'right' : 'left';
|
||||
ctx.fillText(fmt(val), x + (x < arc.x ? -4 : 4), y + 4);
|
||||
ctx.restore();
|
||||
});
|
||||
|
||||
innerMeta.data.forEach((arc, i) => {
|
||||
const val = countData[i];
|
||||
if (!val) return;
|
||||
const pos = arc.tooltipPosition();
|
||||
ctx.save();
|
||||
ctx.font = 'bold 12px Segoe UI, Arial';
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(String(val), pos.x, pos.y);
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(el('chartDonut1'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: statusLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Claim Amount',
|
||||
data: amountData,
|
||||
backgroundColor: statusColors,
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff',
|
||||
weight: 1.1
|
||||
},
|
||||
{
|
||||
label: 'Claim Count',
|
||||
data: countData,
|
||||
backgroundColor: statusColors,
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff',
|
||||
weight: 0.55
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
cutout: '42%',
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (c) => c.dataset.label + ': ' + (c.datasetIndex === 1 ? c.raw : fmt(c.raw))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [donutLeaderPlugin]
|
||||
});
|
||||
|
||||
/* Pre-Auth TAT — column chart with % labels above bars */
|
||||
const preAuth = MIS_DATA.preAuthTAT || { within: 100, above: 0 };
|
||||
const preAuthValues = [preAuth.within, preAuth.above];
|
||||
|
||||
function maxInDatasets(datasets) {
|
||||
let max = 0;
|
||||
(datasets || []).forEach(ds => {
|
||||
(ds.data || []).forEach(v => { max = Math.max(max, Number(v) || 0); });
|
||||
});
|
||||
return max;
|
||||
}
|
||||
|
||||
const barTopLabelsPlugin = {
|
||||
id: 'barTopLabels',
|
||||
afterDatasetsDraw(chart) {
|
||||
const cfg = chart.options.plugins?.barTopLabels || {};
|
||||
if (cfg.enabled === false || chart.options.indexAxis === 'y') return;
|
||||
|
||||
const formatType = cfg.format || 'number';
|
||||
const formatters = {
|
||||
percent: v => Number(v).toFixed(2) + '%',
|
||||
count: v => String(Math.round(Number(v))),
|
||||
amount: v => fmt(v),
|
||||
number: v => fmt(v)
|
||||
};
|
||||
const labelFn = formatters[formatType] || formatters.number;
|
||||
const hideZero = cfg.hideZero !== false;
|
||||
const { ctx } = chart;
|
||||
const dsCount = chart.data.datasets.length;
|
||||
|
||||
chart.data.datasets.forEach((ds, di) => {
|
||||
chart.getDatasetMeta(di).data.forEach((bar, i) => {
|
||||
const val = Number(ds.data[i]);
|
||||
if (hideZero && (!val || val === 0)) return;
|
||||
const pos = bar.tooltipPosition();
|
||||
const offsetX = dsCount > 1 ? (di - (dsCount - 1) / 2) * 14 : 0;
|
||||
ctx.save();
|
||||
ctx.font = 'bold 10px Segoe UI, Arial';
|
||||
ctx.fillStyle = '#222';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.fillText(labelFn(val), pos.x + offsetX, pos.y - 5);
|
||||
ctx.restore();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const chartLayout = { padding: { top: 28, left: 4, right: 4 } };
|
||||
|
||||
const hBarValuePlugin = {
|
||||
id: 'hBarValuePlugin',
|
||||
afterDatasetsDraw(chart) {
|
||||
const cfg = chart.options.plugins?.hBarLabels || {};
|
||||
if (cfg.enabled === false || chart.options.indexAxis !== 'y') return;
|
||||
|
||||
const formatType = cfg.format || 'amount';
|
||||
const formatters = {
|
||||
count: v => String(Math.round(Number(v))),
|
||||
amount: v => fmt(v)
|
||||
};
|
||||
const labelFn = formatters[formatType] || formatters.amount;
|
||||
|
||||
const { ctx } = chart;
|
||||
chart.getDatasetMeta(0).data.forEach((bar, i) => {
|
||||
const val = chart.data.datasets[0].data[i];
|
||||
if (!val && val !== 0) return;
|
||||
const props = bar.getProps(['x', 'y', 'base', 'width'], true);
|
||||
const barLeft = Math.min(props.x, props.base);
|
||||
const barRight = Math.max(props.x, props.base);
|
||||
const barW = Math.abs(barRight - barLeft);
|
||||
const midX = barLeft + barW / 2;
|
||||
const text = labelFn(val);
|
||||
ctx.save();
|
||||
ctx.font = 'bold 11px Segoe UI, Arial';
|
||||
const tw = ctx.measureText(text).width + 14;
|
||||
const th = 20;
|
||||
const rx = midX - tw / 2;
|
||||
const ry = props.y - th / 2;
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.92)';
|
||||
ctx.strokeStyle = '#d1d5db';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.fillRect(rx, ry, tw, th);
|
||||
ctx.strokeRect(rx, ry, tw, th);
|
||||
ctx.fillStyle = '#222';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(text, midX, props.y);
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Chart.register(barTopLabelsPlugin, hBarValuePlugin);
|
||||
|
||||
new Chart(el('chartPreAuth'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['With in 2 Hours %', 'Above 2 Hours %'],
|
||||
datasets: [{ data: preAuthValues, backgroundColor: [COLORS.blue, COLORS.navy], barPercentage: 0.45, categoryPercentage: 0.7 }]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { display: false }, barTopLabels: { format: 'percent', hideZero: false } },
|
||||
scales: {
|
||||
x: { grid: { display: false }, border: { display: false } },
|
||||
y: { display: false, suggestedMax: 115 }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Premium / Earn Premium / Claim Incurred — horizontal bars with value pills */
|
||||
const premiumBars = MIS_DATA.premiumBars || [0, 0, 0];
|
||||
const premiumLabels = ['Premium', 'Earn Premium', 'Claim Incurred Amount'];
|
||||
|
||||
new Chart(el('chartPremiumBars'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: premiumLabels,
|
||||
datasets: [{ data: premiumBars, backgroundColor: [COLORS.navy, COLORS.blue, COLORS.orange], barThickness: 28 }]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: { padding: { top: 8, right: 16, bottom: 8, left: 8 } },
|
||||
plugins: { legend: { display: false }, barTopLabels: { enabled: false }, hBarLabels: { format: 'amount' } },
|
||||
scales: {
|
||||
x: { display: false, grid: { display: false }, suggestedMax: Math.max(...premiumBars, 1) * 1.15 },
|
||||
y: { grid: { display: false }, border: { display: false }, ticks: { font: { weight: 'bold', size: 12 } } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Gender — horizontal bar charts (Male purple, Female navy) */
|
||||
const genderSorted = genderOrder.map(label => {
|
||||
const found = gender.find(g => g.label === label);
|
||||
return found || { label, count: 0, amount: 0 };
|
||||
});
|
||||
|
||||
function genderBar(canvasId, valueKey) {
|
||||
const labels = genderSorted.map(g => g.label);
|
||||
const values = genderSorted.map(g => g[valueKey] || 0);
|
||||
const colors = labels.map(l => l.toLowerCase() === 'female' ? COLORS.female : COLORS.male);
|
||||
const maxVal = Math.max(...values, 1);
|
||||
|
||||
new Chart(el(canvasId), {
|
||||
type: 'bar',
|
||||
data: { labels, datasets: [{ data: values, backgroundColor: colors, barThickness: 32 }] },
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: { padding: { top: 10, right: 20, bottom: 10, left: 4 } },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
barTopLabels: { enabled: false },
|
||||
hBarLabels: { format: valueKey === 'count' ? 'count' : 'amount' }
|
||||
},
|
||||
scales: {
|
||||
x: { display: false, grid: { display: false }, suggestedMax: maxVal * 1.2 },
|
||||
y: { grid: { display: false }, border: { display: false }, ticks: { font: { weight: 'bold', size: 12 } } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
genderBar('chartGenderCount', 'count');
|
||||
genderBar('chartGenderAmt', 'amount');
|
||||
|
||||
const pc = MIS_DATA.paidCharts || {};
|
||||
const cbt = pc.countByType || {};
|
||||
const paidCountData = pc.paidCount || [0, 0];
|
||||
const paidAmtData = pc.paidAmt || [0, 0];
|
||||
const acsData = pc.acs || [0, 0];
|
||||
const amtStatusData = pc.amtByStatus || [0, 0, 0];
|
||||
const countStatusCashless = [cbt.Settled?.Cashless || 0, cbt.Outstanding?.Cashless || 0, cbt.Rejected?.Cashless || 0];
|
||||
const countStatusReimb = [cbt.Settled?.Reimbursement || 0, cbt.Outstanding?.Reimbursement || 0, cbt.Rejected?.Reimbursement || 0];
|
||||
|
||||
/* Claim Paid Count By Claim Type */
|
||||
new Chart(el('chartPaidCount'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Cashless', 'Reimbursement'],
|
||||
datasets: [{ data: paidCountData, backgroundColor: [COLORS.blue, COLORS.navy], barPercentage: 0.5 }]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { display: false }, barTopLabels: { format: 'count' } },
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 1, precision: 0 },
|
||||
suggestedMax: Math.max(1, maxInDatasets([{ data: paidCountData }]) * 1.25)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Claim Paid Amount and ACS */
|
||||
const paidAmtDatasets = [
|
||||
{ label: 'Claim Paid Amount', data: paidAmtData, backgroundColor: COLORS.blue },
|
||||
{ label: 'ACS', data: acsData, backgroundColor: COLORS.orange }
|
||||
];
|
||||
new Chart(el('chartPaidAmtACS'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Cashless', 'Reimbursement'],
|
||||
datasets: paidAmtDatasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, font: { size: 10 } } }, barTopLabels: { format: 'amount' } },
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { callback: v => fmt(v) },
|
||||
suggestedMax: maxInDatasets(paidAmtDatasets) * 1.2 || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Claim Count By Claim Status */
|
||||
const countStatusDatasets = [
|
||||
{ label: 'Cashless', data: countStatusCashless, backgroundColor: COLORS.blue },
|
||||
{ label: 'Reimbursement', data: countStatusReimb, backgroundColor: COLORS.navy }
|
||||
];
|
||||
new Chart(el('chartCountStatus'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Settled', 'Outstanding', 'Rejected'],
|
||||
datasets: countStatusDatasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, font: { size: 10 } } }, barTopLabels: { format: 'count' } },
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 1, precision: 0 },
|
||||
suggestedMax: Math.max(1, maxInDatasets(countStatusDatasets) * 1.25)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Claim Amount By Claim Status */
|
||||
new Chart(el('chartAmtStatus'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Settled', 'Outstanding', 'Rejected'],
|
||||
datasets: [{ data: amtStatusData, backgroundColor: [COLORS.blue, COLORS.orange, COLORS.purple], barPercentage: 0.55 }]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { display: false }, barTopLabels: { format: 'amount' } },
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { callback: v => fmt(v) },
|
||||
suggestedMax: maxInDatasets([{ data: amtStatusData }]) * 1.2 || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Monthly registration charts */
|
||||
const months = MIS_DATA.months || [];
|
||||
const paidCountMonth = MIS_DATA.paidCountByMonth || [];
|
||||
const outCountMonth = MIS_DATA.outCountByMonth || [];
|
||||
const paidAmtMonth = MIS_DATA.paidAmtByMonth || [];
|
||||
const outAmtMonth = MIS_DATA.outAmtByMonth || [];
|
||||
|
||||
const monthCountDatasets = [
|
||||
{ label: 'Claim Paid Count', data: paidCountMonth, backgroundColor: COLORS.blue, order: 2, barPercentage: 0.7, categoryPercentage: 0.8 },
|
||||
{ label: 'Claim Outstanding Count', data: outCountMonth, backgroundColor: 'rgba(46,134,222,0.12)', borderColor: COLORS.navy, borderWidth: 2, order: 1, barPercentage: 0.95, categoryPercentage: 0.8 }
|
||||
];
|
||||
const monthAmtDatasets = [
|
||||
{ label: 'Claim Paid Amount', data: paidAmtMonth, backgroundColor: COLORS.blue, order: 2, barPercentage: 0.7, categoryPercentage: 0.8 },
|
||||
{ label: 'Claim Outstanding Amount', data: outAmtMonth, backgroundColor: 'rgba(46,134,222,0.12)', borderColor: COLORS.navy, borderWidth: 2, order: 1, barPercentage: 0.95, categoryPercentage: 0.8 }
|
||||
];
|
||||
|
||||
const monthCountMax = maxInDatasets(monthCountDatasets);
|
||||
const monthAmtMax = maxInDatasets(monthAmtDatasets);
|
||||
|
||||
new Chart(el('chartMonthCount'), {
|
||||
type: 'bar',
|
||||
data: { labels: months, datasets: monthCountDatasets },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, font: { size: 10 } } }, barTopLabels: { format: 'count' } },
|
||||
scales: {
|
||||
y: { position: 'right', beginAtZero: true, suggestedMax: monthCountMax * 1.2 || undefined, ticks: { precision: 0 } },
|
||||
x: { grid: { display: false } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Chart(el('chartMonthAmt'), {
|
||||
type: 'bar',
|
||||
data: { labels: months, datasets: monthAmtDatasets },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: chartLayout,
|
||||
plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, font: { size: 10 } } }, barTopLabels: { format: 'amount' } },
|
||||
scales: {
|
||||
y: { position: 'right', beginAtZero: true, suggestedMax: monthAmtMax * 1.2 || undefined, ticks: { callback: v => fmt(v) } },
|
||||
x: { grid: { display: false } }
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
401
app/Views/tpa_mis_reports_dashboard.php
Normal file
401
app/Views/tpa_mis_reports_dashboard.php
Normal file
@ -0,0 +1,401 @@
|
||||
<style>
|
||||
.tpa-mis-dashboard .filter-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px 20px;
|
||||
align-items: flex-end;
|
||||
background: #eef5fa;
|
||||
border-radius: 10px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field {
|
||||
flex: 1 1 140px;
|
||||
min-width: 120px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
color: #111;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field .select2-container {
|
||||
width: 100% !important;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field .select2-selection--single {
|
||||
height: 32px !important;
|
||||
min-height: 32px;
|
||||
border: 1px solid #d8e2ea !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field .select2-selection__rendered {
|
||||
line-height: 30px !important;
|
||||
padding-left: 10px !important;
|
||||
color: #333;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field .select2-selection__arrow {
|
||||
height: 30px !important;
|
||||
}
|
||||
.tpa-mis-dashboard .filter-field .select2-container--disabled .select2-selection--single {
|
||||
background-color: #f5f7f9 !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.tpa-mis-dashboard .dashboard-status {
|
||||
font-size: 12px;
|
||||
color: #5c6b7a;
|
||||
padding: 4px 0 10px;
|
||||
min-height: 20px;
|
||||
}
|
||||
.tpa-mis-dashboard .dashboard-status.is-error { color: #c0392b; }
|
||||
.tpa-mis-dashboard .dashboard-status.is-loading { color: #0a8794; }
|
||||
.tpa-mis-dashboard .dashboard-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
color: #7a8a99;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
border: 1px dashed #d0dce6;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.tpa-mis-dashboard .mis-toolbar {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.tpa-mis-dashboard .mis-toolbar.is-visible {
|
||||
display: flex;
|
||||
}
|
||||
.tpa-mis-dashboard .mis-toolbar h5 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #152A54;
|
||||
}
|
||||
.tpa-mis-dashboard .mis-frame-wrap {
|
||||
display: none;
|
||||
background: #fff;
|
||||
border: 1px solid #d0dce6;
|
||||
border-radius: 8px;
|
||||
overflow: visible;
|
||||
}
|
||||
.tpa-mis-dashboard .mis-frame-wrap.is-visible {
|
||||
display: block;
|
||||
}
|
||||
.tpa-mis-dashboard #mis-report-frame {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
border: 0;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row tpa-mis-dashboard">
|
||||
<div class="col-xl-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="filter-bar">
|
||||
<div class="filter-field">
|
||||
<label for="filter-client">Client</label>
|
||||
<select id="filter-client" class="form-control form-control-sm tpa-mis-select2">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="filter-branch">Branch</label>
|
||||
<select id="filter-branch" class="form-control form-control-sm tpa-mis-select2" disabled>
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="filter-policy">Policy</label>
|
||||
<select id="filter-policy" class="form-control form-control-sm tpa-mis-select2" disabled>
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-status" class="dashboard-status"></div>
|
||||
|
||||
<div id="mis-toolbar" class="mis-toolbar">
|
||||
<h5 id="mis-title">TPA MIS Report</h5>
|
||||
<a id="mis-pdf-link" class="btn btn-sm btn-outline-primary" href="#" target="_blank" rel="noopener" style="display:none;">Download PDF</a>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-container">
|
||||
<div class="dashboard-placeholder" id="dashboard-placeholder">
|
||||
Select client, branch, and policy to load the report.
|
||||
</div>
|
||||
<div id="mis-frame-wrap" class="mis-frame-wrap">
|
||||
<iframe id="mis-report-frame" title="TPA MIS Report" scrolling="no"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
const CLIENTS_API = <?= json_encode(base_url('/util/getClientAndBranchAndPolicy')) ?>;
|
||||
const MIS_API = <?= json_encode($misApiUrl ?? base_url('/util/tpa-reports/mis')) ?>;
|
||||
|
||||
const select2Options = {
|
||||
placeholder: 'Select',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
minimumResultsForSearch: 0,
|
||||
dropdownParent: $(document.body)
|
||||
};
|
||||
|
||||
let branchByClient = {};
|
||||
let policyByClient = {};
|
||||
|
||||
const $elClient = $('#filter-client');
|
||||
const $elBranch = $('#filter-branch');
|
||||
const $elPolicy = $('#filter-policy');
|
||||
const elStatus = document.getElementById('dashboard-status');
|
||||
const elPlaceholder = document.getElementById('dashboard-placeholder');
|
||||
const elMisToolbar = document.getElementById('mis-toolbar');
|
||||
const elMisFrameWrap = document.getElementById('mis-frame-wrap');
|
||||
const elMisFrame = document.getElementById('mis-report-frame');
|
||||
const elMisTitle = document.getElementById('mis-title');
|
||||
const elMisPdf = document.getElementById('mis-pdf-link');
|
||||
|
||||
function setStatus(message, type) {
|
||||
elStatus.textContent = message || '';
|
||||
elStatus.className = 'dashboard-status' + (type ? ' is-' + type : '');
|
||||
}
|
||||
|
||||
function initSelect2($select, disabled) {
|
||||
if ($select.hasClass('select2-hidden-accessible')) {
|
||||
$select.select2('destroy');
|
||||
}
|
||||
$select.prop('disabled', false);
|
||||
$select.select2(select2Options);
|
||||
if (disabled) {
|
||||
$select.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildSelect($select, placeholder, disabled, items, getValue, getLabel) {
|
||||
if ($select.hasClass('select2-hidden-accessible')) {
|
||||
$select.select2('destroy');
|
||||
}
|
||||
$select.empty().append($('<option>', { value: '', text: placeholder }));
|
||||
(items || []).forEach(function (item) {
|
||||
$select.append($('<option>', {
|
||||
value: String(getValue(item)),
|
||||
text: getLabel(item)
|
||||
}));
|
||||
});
|
||||
$select.val('');
|
||||
initSelect2($select, disabled);
|
||||
}
|
||||
|
||||
function filtersComplete() {
|
||||
return !!($elClient.val() && $elBranch.val() && $elPolicy.val());
|
||||
}
|
||||
|
||||
function clearMisReport() {
|
||||
if (elMisToolbar) elMisToolbar.classList.remove('is-visible');
|
||||
if (elMisFrameWrap) elMisFrameWrap.classList.remove('is-visible');
|
||||
if (elMisFrame) {
|
||||
elMisFrame.onload = null;
|
||||
elMisFrame.removeAttribute('src');
|
||||
elMisFrame.style.height = '0px';
|
||||
}
|
||||
if (elMisPdf) {
|
||||
elMisPdf.style.display = 'none';
|
||||
elMisPdf.setAttribute('href', '#');
|
||||
}
|
||||
if (elPlaceholder) elPlaceholder.style.display = '';
|
||||
}
|
||||
|
||||
function prepareMisDocument(doc) {
|
||||
if (!doc || doc.getElementById('tpa-mis-embed-style')) return;
|
||||
const style = doc.createElement('style');
|
||||
style.id = 'tpa-mis-embed-style';
|
||||
style.textContent = [
|
||||
'html, body { height: auto !important; min-height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; background: #fff !important; }',
|
||||
'.page, .wrap { margin: 0 auto !important; box-shadow: none !important; }',
|
||||
'body { padding: 0 !important; }'
|
||||
].join('\n');
|
||||
(doc.head || doc.documentElement).appendChild(style);
|
||||
}
|
||||
|
||||
/** Fit iframe to report content only — avoids leftover grey scroll after load. */
|
||||
function resizeMisFrame() {
|
||||
if (!elMisFrame || !elMisFrame.contentWindow) return;
|
||||
try {
|
||||
const doc = elMisFrame.contentDocument || elMisFrame.contentWindow.document;
|
||||
if (!doc || !doc.body) return;
|
||||
|
||||
prepareMisDocument(doc);
|
||||
|
||||
const roots = doc.querySelectorAll('.page, .wrap');
|
||||
let height = 0;
|
||||
if (roots.length) {
|
||||
roots.forEach(function (el) {
|
||||
const bottom = el.offsetTop + el.offsetHeight;
|
||||
if (bottom > height) height = bottom;
|
||||
});
|
||||
} else {
|
||||
height = doc.body.scrollHeight || doc.body.offsetHeight || 0;
|
||||
}
|
||||
|
||||
// Prefer content box over inflated html/body min-heights.
|
||||
height = Math.ceil(height);
|
||||
if (height < 200) {
|
||||
height = Math.max(
|
||||
doc.body.scrollHeight || 0,
|
||||
(doc.documentElement && doc.documentElement.scrollHeight) || 0
|
||||
);
|
||||
}
|
||||
|
||||
if (height > 0) {
|
||||
const next = height + 8;
|
||||
const prev = parseInt(elMisFrame.style.height, 10) || 0;
|
||||
// Only grow when content actually needs more room; shrink when excess grey space.
|
||||
if (Math.abs(next - prev) > 4) {
|
||||
elMisFrame.style.height = next + 'px';
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Same-origin expected; leave height alone on failure.
|
||||
}
|
||||
}
|
||||
|
||||
function bindMisFrameResize() {
|
||||
if (!elMisFrame) return;
|
||||
elMisFrame.onload = function () {
|
||||
resizeMisFrame();
|
||||
// Charts/images can settle after first paint (ABHI Chart.js).
|
||||
[200, 600, 1200, 2000].forEach(function (ms) {
|
||||
setTimeout(resizeMisFrame, ms);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function loadFilterData() {
|
||||
setStatus('Loading clients…', 'loading');
|
||||
try {
|
||||
const res = await fetch(CLIENTS_API, { credentials: 'same-origin' });
|
||||
const json = await res.json();
|
||||
if (!json.status) {
|
||||
setStatus('Could not load client list.', 'error');
|
||||
return;
|
||||
}
|
||||
branchByClient = json.branch_data || {};
|
||||
policyByClient = json.policyListByClient || {};
|
||||
|
||||
rebuildSelect($elClient, 'Select', false, json.client_data || [], function (c) { return c.id; }, function (c) {
|
||||
return c.client_name || c.client_short_name || ('Client ' + c.id);
|
||||
});
|
||||
rebuildSelect($elBranch, 'Select', true);
|
||||
rebuildSelect($elPolicy, 'Select', true);
|
||||
setStatus('');
|
||||
} catch (e) {
|
||||
setStatus('Failed to load filter options.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchMisReport(policyId) {
|
||||
clearMisReport();
|
||||
if (!filtersComplete() || !policyId) {
|
||||
setStatus('Select client, branch, and policy to load the report.');
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus('Loading MIS report…', 'loading');
|
||||
try {
|
||||
const url = MIS_API + '?client_policy=' + encodeURIComponent(policyId);
|
||||
const res = await fetch(url, { credentials: 'same-origin' });
|
||||
const json = await res.json();
|
||||
if (json.status !== 'success' || !json.data || !json.data.preview_url) {
|
||||
setStatus(json.message || 'No MIS report for this TPA.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (elMisTitle) elMisTitle.textContent = json.data.title || 'TPA MIS Report';
|
||||
if (elMisFrame) {
|
||||
bindMisFrameResize();
|
||||
elMisFrame.src = json.data.preview_url;
|
||||
}
|
||||
if (elMisPdf && json.data.pdf_url) {
|
||||
elMisPdf.href = json.data.pdf_url;
|
||||
elMisPdf.style.display = '';
|
||||
}
|
||||
if (elPlaceholder) elPlaceholder.style.display = 'none';
|
||||
if (elMisToolbar) elMisToolbar.classList.add('is-visible');
|
||||
if (elMisFrameWrap) elMisFrameWrap.classList.add('is-visible');
|
||||
setStatus('');
|
||||
} catch (e) {
|
||||
setStatus('Failed to load MIS report.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
$(window).on('resize', function () {
|
||||
// Parent width change may reflow report; re-measure after layout.
|
||||
setTimeout(resizeMisFrame, 100);
|
||||
});
|
||||
|
||||
$elClient.on('change', function () {
|
||||
const clientId = $(this).val();
|
||||
rebuildSelect($elBranch, 'Select', !clientId);
|
||||
rebuildSelect($elPolicy, 'Select', true);
|
||||
clearMisReport();
|
||||
setStatus(clientId ? '' : 'Select client, branch, and policy to load the report.');
|
||||
|
||||
if (!clientId) return;
|
||||
const branches = branchByClient[clientId] || [];
|
||||
rebuildSelect($elBranch, 'Select', branches.length === 0, branches, function (b) { return b.id; }, function (b) {
|
||||
return b.branch_name || ('Branch ' + b.id);
|
||||
});
|
||||
});
|
||||
|
||||
$elBranch.on('change', function () {
|
||||
const branchId = $(this).val();
|
||||
const clientId = $elClient.val();
|
||||
rebuildSelect($elPolicy, 'Select', !branchId);
|
||||
clearMisReport();
|
||||
setStatus(branchId ? '' : 'Select branch and policy to load the report.');
|
||||
|
||||
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 () {
|
||||
fetchMisReport($(this).val());
|
||||
});
|
||||
|
||||
initSelect2($elClient, false);
|
||||
initSelect2($elBranch, true);
|
||||
initSelect2($elPolicy, true);
|
||||
loadFilterData();
|
||||
});
|
||||
</script>
|
||||
BIN
public/assets/images/abhi.png
Normal file
BIN
public/assets/images/abhi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
public/assets/images/icici_lambard_new.png
Normal file
BIN
public/assets/images/icici_lambard_new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/images/medi_assist.png
Normal file
BIN
public/assets/images/medi_assist.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
public/assets/images/vidal.png
Normal file
BIN
public/assets/images/vidal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
public/assets/images/vidal_mis.png
Normal file
BIN
public/assets/images/vidal_mis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
20
public/assets/libs/chart.js/chart.umd.min.js
vendored
Normal file
20
public/assets/libs/chart.js/chart.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
72
tests/smoke_abhi_mis_report.php
Normal file
72
tests/smoke_abhi_mis_report.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: ABHI MIS report service.
|
||||
* Run: php tests/smoke_abhi_mis_report.php [policy_id]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
require_once APPPATH . 'Config/Constants.php';
|
||||
|
||||
$policyId = (int) ($argv[1] ?? 0);
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
$row = $db->query(
|
||||
"SELECT cr.client_policy_id, COUNT(*) AS cnt
|
||||
FROM claim_report cr
|
||||
WHERE cr.source_table = 'claims_dump_abhi'
|
||||
AND cr.is_active = 1
|
||||
AND cr.source_row_id IS NOT NULL
|
||||
GROUP BY cr.client_policy_id
|
||||
ORDER BY cnt DESC
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
$policyId = (int) ($row['client_policy_id'] ?? 0);
|
||||
echo "Auto-selected policy_id: {$policyId}\n";
|
||||
}
|
||||
|
||||
if ($policyId <= 0) {
|
||||
echo "FAIL: No policy with ABHI claim_report linkage found.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$service = new \App\Libraries\AbhiMisReportService();
|
||||
$built = $service->build($policyId);
|
||||
|
||||
if (!$built['status']) {
|
||||
echo 'FAIL: ' . ($built['message'] ?? 'unknown') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$data = $built['data'];
|
||||
$status = $data['status_breakdown']['buckets'] ?? [];
|
||||
|
||||
echo "OK policy_id={$policyId}\n";
|
||||
echo 'Policy: ' . ($data['header']['policy_number'] ?? '') . "\n";
|
||||
echo 'Dump rows: ' . ($data['dump_row_count'] ?? 0) . "\n";
|
||||
echo 'Reported: ' . ($status['Reported']['count'] ?? 0) . "\n";
|
||||
echo 'Settled: ' . ($status['Settled']['count'] ?? 0) . "\n";
|
||||
echo 'Outstanding: ' . ($status['Outstanding']['count'] ?? 0) . "\n";
|
||||
echo 'Rejected: ' . ($status['Rejected']['count'] ?? 0) . "\n";
|
||||
echo 'ICR: ' . ($data['summary']['icr_pct'] ?? 0) . "%\n";
|
||||
echo 'Incurred: ' . ($data['summary']['claim_paid_outstanding'] ?? 0) . "\n";
|
||||
|
||||
exit(0);
|
||||
140
tests/smoke_icici_mis_report.php
Normal file
140
tests/smoke_icici_mis_report.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: ICICI Portfolio Analysis MIS (service + HTML + PDF).
|
||||
* Run: php tests/smoke_icici_mis_report.php [policy_id]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
require_once APPPATH . 'Config/Constants.php';
|
||||
|
||||
$policyId = (int) ($argv[1] ?? 0);
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
$row = $db->query(
|
||||
"SELECT cr.client_policy_id, COUNT(*) AS cnt
|
||||
FROM claim_report cr
|
||||
WHERE cr.source_table = 'claims_dump_icici'
|
||||
AND cr.is_active = 1
|
||||
AND cr.source_row_id IS NOT NULL
|
||||
GROUP BY cr.client_policy_id
|
||||
ORDER BY cnt DESC
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
$policyId = (int) ($row['client_policy_id'] ?? 0);
|
||||
echo "Auto-selected policy_id: {$policyId}\n";
|
||||
}
|
||||
|
||||
if ($policyId <= 0) {
|
||||
echo "FAIL: No policy with ICICI claim_report linkage found.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$service = new \App\Libraries\IciciMisReportService();
|
||||
$built = $service->build($policyId);
|
||||
|
||||
if (!$built['status']) {
|
||||
echo 'FAIL: ' . ($built['message'] ?? 'unknown') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$data = $built['data'];
|
||||
$vm = $data['view_model'] ?? [];
|
||||
|
||||
$requiredSections = [
|
||||
'meta', 'premium_lr', 'claims_summary', 'iltc_summary', 'enrollment',
|
||||
'age_analysis', 'relation_acs', 'amount_bands', 'si_bands', 'iltc_app',
|
||||
'utilization_employees', 'utilization_dependents', 'disease',
|
||||
'hospitals', 'cities', 'tat', 'takeaways',
|
||||
];
|
||||
|
||||
foreach ($requiredSections as $key) {
|
||||
if (!array_key_exists($key, $vm)) {
|
||||
echo "FAIL: missing view_model.{$key}\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo "OK policy_id={$policyId}\n";
|
||||
echo 'Policy: ' . ($data['header']['policy_number'] ?? '') . "\n";
|
||||
echo 'Client: ' . ($vm['meta']['client_name'] ?? '') . "\n";
|
||||
echo 'Dump rows: ' . ($data['dump_row_count'] ?? 0) . "\n";
|
||||
echo 'Claims summary rows: ' . count($vm['claims_summary']['rows'] ?? []) . "\n";
|
||||
echo 'Age rows: ' . count($vm['age_analysis']['rows'] ?? []) . "\n";
|
||||
echo 'Disease rows: ' . count($vm['disease']['rows'] ?? []) . "\n";
|
||||
echo 'Hospitals: ' . count($vm['hospitals']['rows'] ?? []) . "\n";
|
||||
echo 'Cities: ' . count($vm['cities']['rows'] ?? []) . "\n";
|
||||
echo 'TAT months: ' . count($vm['tat'] ?? []) . "\n";
|
||||
|
||||
$html = view('claims_mis_icici', [
|
||||
'report' => $data,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
$sectionMarkers = [
|
||||
'Portfolio Analysis',
|
||||
'Enrollment Summary',
|
||||
'Age-wise Analysis',
|
||||
'Relation ACS',
|
||||
'Amount Band-wise Analysis',
|
||||
'SI Band-wise Analysis',
|
||||
'IL Take Care',
|
||||
'Disease-Analysis',
|
||||
'Claim Utilization report',
|
||||
'Top Hospitals',
|
||||
'Top Cities',
|
||||
'TAT Analysis',
|
||||
];
|
||||
|
||||
foreach ($sectionMarkers as $marker) {
|
||||
if (stripos($html, $marker) === false) {
|
||||
echo "FAIL: HTML missing section '{$marker}'\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos($html, 'icici_lambard_new') === false && stripos($html, 'data:image') === false) {
|
||||
if (stripos($html, 'icici_lambard_new.png') === false) {
|
||||
echo "FAIL: HTML missing ICICI logo reference\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo "OK HTML sections present (" . strlen($html) . " bytes)\n";
|
||||
|
||||
$pdf = (new \App\Libraries\IciciMisPdfService())->generateFromReport($data, $policyId);
|
||||
if (!$pdf['status']) {
|
||||
echo 'FAIL PDF: ' . ($pdf['message'] ?? 'unknown') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$outDir = WRITEPATH . 'uploads/';
|
||||
if (!is_dir($outDir)) {
|
||||
@mkdir($outDir, 0775, true);
|
||||
}
|
||||
$outFile = $outDir . ($pdf['filename'] ?? 'ICICI_MIS_smoke.pdf');
|
||||
file_put_contents($outFile, $pdf['content']);
|
||||
echo 'OK PDF: ' . $outFile . ' (' . strlen((string) $pdf['content']) . " bytes)\n";
|
||||
|
||||
exit(0);
|
||||
128
tests/smoke_medi_assist_mis_report.php
Normal file
128
tests/smoke_medi_assist_mis_report.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: Medi Assist Portfolio Analysis MIS.
|
||||
* Run: php tests/smoke_medi_assist_mis_report.php [policy_id]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
require_once APPPATH . 'Config/Constants.php';
|
||||
|
||||
$policyId = (int) ($argv[1] ?? 0);
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
$row = $db->query(
|
||||
"SELECT cr.client_policy_id, COUNT(*) AS cnt
|
||||
FROM claim_report cr
|
||||
WHERE cr.source_table = 'claims_dump_medi_assist'
|
||||
AND cr.is_active = 1
|
||||
AND cr.source_row_id IS NOT NULL
|
||||
GROUP BY cr.client_policy_id
|
||||
ORDER BY cnt DESC
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
$policyId = (int) ($row['client_policy_id'] ?? 0);
|
||||
echo "Auto-selected policy_id: {$policyId}\n";
|
||||
}
|
||||
|
||||
if ($policyId <= 0) {
|
||||
echo "FAIL: No policy with Medi Assist claim_report linkage found.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$service = new \App\Libraries\MediAssistMisReportService();
|
||||
$built = $service->build($policyId);
|
||||
|
||||
if (!$built['status']) {
|
||||
echo 'FAIL: ' . ($built['message'] ?? 'unknown') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$data = $built['data'];
|
||||
$vm = $data['view_model'] ?? [];
|
||||
|
||||
$required = [
|
||||
'meta', 'header', 'index_policies', 'portfolio_summary', 'policy_lives', 'policy_premium',
|
||||
'claim_status_rows', 'claim_type_sections', 'savings', 'top_providers', 'top_ailments',
|
||||
'beneficiary', 'age_bands', 'utilization_employees', 'utilization_dependents',
|
||||
'amount_bands_cashless', 'amount_bands_reimbursement', 'glossary',
|
||||
];
|
||||
|
||||
foreach ($required as $key) {
|
||||
if (!array_key_exists($key, $vm)) {
|
||||
echo "FAIL: missing view_model.{$key}\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo "OK policy_id={$policyId}\n";
|
||||
echo 'Policy: ' . ($data['header']['policy_number'] ?? '') . "\n";
|
||||
echo 'Dump rows: ' . ($data['dump_row_count'] ?? 0) . "\n";
|
||||
echo 'Claims: ' . ($vm['portfolio_summary']['claims_count'] ?? 0) . "\n";
|
||||
|
||||
$html = view('claims_mis_medi_assist', [
|
||||
'report' => $data,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
$markers = [
|
||||
'Portfolio Analysis Report',
|
||||
'Portfolio Report',
|
||||
'Savings Summary',
|
||||
'Top 10 Distribution Across Providers',
|
||||
'Distribution Across Beneficiary',
|
||||
'Utilization Report for Employees',
|
||||
'Distribution Across Amount Bands',
|
||||
'Glossary',
|
||||
];
|
||||
|
||||
foreach ($markers as $m) {
|
||||
if (stripos($html, $m) === false) {
|
||||
echo "FAIL: HTML missing '{$m}'\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos($html, 'medi_assist.png') === false && stripos($html, 'data:image') === false) {
|
||||
echo "FAIL: logo missing\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo 'OK HTML (' . strlen($html) . " bytes)\n";
|
||||
|
||||
$pdf = (new \App\Libraries\MediAssistMisPdfService())->generateFromReport($data, $policyId);
|
||||
if (!$pdf['status']) {
|
||||
echo 'FAIL PDF: ' . ($pdf['message'] ?? '') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$outDir = WRITEPATH . 'uploads/';
|
||||
if (!is_dir($outDir)) {
|
||||
@mkdir($outDir, 0775, true);
|
||||
}
|
||||
$outFile = $outDir . ($pdf['filename'] ?? 'MA_MIS_smoke.pdf');
|
||||
file_put_contents($outFile, $pdf['content']);
|
||||
echo 'OK PDF: ' . $outFile . ' (' . strlen((string) $pdf['content']) . " bytes)\n";
|
||||
|
||||
exit(0);
|
||||
133
tests/smoke_vidal_mis_report.php
Normal file
133
tests/smoke_vidal_mis_report.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: Vidal Corporate Analysis MIS.
|
||||
* Run: php tests/smoke_vidal_mis_report.php [policy_id]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
require_once APPPATH . 'Config/Constants.php';
|
||||
|
||||
$policyId = (int) ($argv[1] ?? 0);
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
$row = $db->query(
|
||||
"SELECT cr.client_policy_id, COUNT(*) AS cnt
|
||||
FROM claim_report cr
|
||||
WHERE cr.source_table = 'claims_dump_vidal'
|
||||
AND cr.is_active = 1
|
||||
AND cr.source_row_id IS NOT NULL
|
||||
GROUP BY cr.client_policy_id
|
||||
ORDER BY cnt DESC
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
$policyId = (int) ($row['client_policy_id'] ?? 0);
|
||||
echo "Auto-selected policy_id: {$policyId}\n";
|
||||
}
|
||||
|
||||
if ($policyId <= 0) {
|
||||
echo "FAIL: No policy with Vidal claim_report linkage found.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$service = new \App\Libraries\VidalMisReportService();
|
||||
$built = $service->build($policyId);
|
||||
|
||||
if (!$built['status']) {
|
||||
echo 'FAIL: ' . ($built['message'] ?? 'unknown') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$data = $built['data'];
|
||||
$vm = $data['view_model'] ?? [];
|
||||
|
||||
$required = [
|
||||
'meta', 'header', 'toc', 'icr', 'hospitalization', 'member_gender', 'member_age',
|
||||
'claims_age', 'claims_amount', 'ailments', 'hospitals', 'cashless_member_summary',
|
||||
'tat', 'month_on_month', 'payout', 'chart_gender', 'chart_age',
|
||||
];
|
||||
|
||||
foreach ($required as $key) {
|
||||
if (!array_key_exists($key, $vm)) {
|
||||
echo "FAIL: missing view_model.{$key}\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo "OK policy_id={$policyId}\n";
|
||||
echo 'Policy: ' . ($data['header']['policy_number'] ?? '') . "\n";
|
||||
echo 'Dump rows: ' . ($data['dump_row_count'] ?? 0) . "\n";
|
||||
|
||||
$html = view('claims_mis_vidal', [
|
||||
'report' => $data,
|
||||
'view_model' => $vm,
|
||||
'policy_id' => $policyId,
|
||||
'pdf_url' => '',
|
||||
'embed' => true,
|
||||
]);
|
||||
|
||||
$markers = [
|
||||
'Corporate Analysis Report',
|
||||
'Incurred Claims Ratio',
|
||||
'Hospitalisation Type Details',
|
||||
'Member Details - Relationship',
|
||||
'Claims Approved - Age Band',
|
||||
'Claims Approved - Amount Band',
|
||||
'Ailment',
|
||||
'Hospital',
|
||||
'Turn Around Time',
|
||||
'Month on Month',
|
||||
'Payout Ratio',
|
||||
'DISCLAIMER',
|
||||
];
|
||||
|
||||
foreach ($markers as $m) {
|
||||
if (stripos($html, $m) === false) {
|
||||
echo "FAIL: HTML missing '{$m}'\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
stripos($html, 'vidal.png') === false
|
||||
&& stripos($html, 'vidal_mis.png') === false
|
||||
&& stripos($html, 'data:image') === false
|
||||
) {
|
||||
echo "FAIL: logo missing\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo 'OK HTML (' . strlen($html) . " bytes)\n";
|
||||
|
||||
$pdf = (new \App\Libraries\VidalMisPdfService())->generateFromReport($data, $policyId);
|
||||
if (!$pdf['status']) {
|
||||
echo 'FAIL PDF: ' . ($pdf['message'] ?? '') . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$outDir = WRITEPATH . 'uploads/';
|
||||
if (!is_dir($outDir)) {
|
||||
@mkdir($outDir, 0775, true);
|
||||
}
|
||||
$outFile = $outDir . ($pdf['filename'] ?? 'VIDAL_MIS_smoke.pdf');
|
||||
file_put_contents($outFile, $pdf['content']);
|
||||
echo 'OK PDF: ' . $outFile . ' (' . strlen((string) $pdf['content']) . " bytes)\n";
|
||||
|
||||
exit(0);
|
||||
Loading…
Reference in New Issue
Block a user