nhance/app/Controllers/ClaimReportDashboardController.php
2026-07-28 15:52:47 +05:30

353 lines
11 KiB
PHP

<?php
namespace App\Controllers;
use App\Models\ClaimReportDashboardModel;
use App\Models\ClaimsCollectionV2DashboardModel;
use App\Models\ClaimDumpFileModel;
use CodeIgniter\API\ResponseTrait;
/**
* Claims Collection dashboard API using claim_report (falls back to ticket_master).
*/
class ClaimReportDashboardController extends BaseController
{
use ResponseTrait;
/**
* Resolve policy id from request; optional $fallback (e.g. route default for debug only).
*/
protected function resolvePolicyId(?int $fallback = null): int
{
$id = (int) (
$this->request->getGet('client_policy')
?? $this->request->getGet('client_policy_id')
?? $this->request->getPost('client_policy')
?? $this->request->getPost('client_policy_id')
?? 0
);
if ($id > 0) {
return $id;
}
if ($fallback !== null && $fallback > 0) {
return $fallback;
}
return 0;
}
/**
* Resolve KPI by Metabase numeric id or method slug.
*/
protected function resolveKpiMethod(string $kpiKey): ?string
{
$kpiKey = trim($kpiKey);
if ($kpiKey === '') {
return null;
}
if (in_array($kpiKey, ClaimsCollectionV2DashboardModel::KPI_MAP, true)) {
return $kpiKey;
}
if (ctype_digit($kpiKey)) {
$id = (int) $kpiKey;
return ClaimsCollectionV2DashboardModel::KPI_MAP[$id] ?? null;
}
return null;
}
/**
* JSON: single KPI by slug or Metabase id.
*/
public function kpi(string $kpiMethod = '')
{
$policyId = $this->resolvePolicyId();
if ($policyId <= 0) {
return $this->respond([
'status' => false,
'message' => 'client_policy or client_policy_id is required.',
], 422);
}
$kpiMethod = $this->resolveKpiMethod($kpiMethod);
if ($kpiMethod === null) {
return $this->respond([
'status' => false,
'message' => 'Unknown KPI. Pass Metabase id or method slug.',
'allowed' => ClaimsCollectionV2DashboardModel::KPI_MAP,
], 404);
}
$model = new ClaimReportDashboardModel();
$metabaseId = array_search($kpiMethod, ClaimsCollectionV2DashboardModel::KPI_MAP, true);
return $this->respond([
'status' => true,
'policy_id' => $policyId,
'claims_source' => $model->resolveClaimsTable($policyId),
'kpi_id' => $metabaseId !== false ? (int) $metabaseId : null,
'kpi' => $kpiMethod,
'label' => ClaimsCollectionV2DashboardModel::KPI_LABELS[$kpiMethod] ?? $kpiMethod,
'rows' => $model->getKpi($kpiMethod, $policyId),
]);
}
/**
* JSON: all KPIs.
*/
public function all()
{
$policyId = $this->resolvePolicyId();
if ($policyId <= 0) {
return $this->respond([
'status' => false,
'message' => 'client_policy or client_policy_id is required.',
], 422);
}
$generatedAt = (new ClaimDumpFileModel())->getGeneratedAtForPolicy($policyId);
if ($generatedAt === null) {
return $this->respond([
'status' => false,
'message' => 'claim_dump_date is mandatory. No claim dump date found for this policy.',
], 422);
}
$model = new ClaimReportDashboardModel();
$data = $model->getAllKpis($policyId);
$source = $data['_meta']['claims_source'] ?? $model->resolveClaimsTable($policyId);
unset($data['_meta']);
return $this->respond([
'status' => true,
'policy_id' => $policyId,
'claims_source' => $source,
'generated_at' => $generatedAt,
'data' => $data,
]);
}
/**
* Admin preview UI — KPI grid for manual testing.
*/
public function preview(int $policyId = 4687)
{
$policyId = $this->resolvePolicyId($policyId);
$path = $this->request->getUri()->getPath();
$isJwt = stripos($path, 'employeeRest') !== false;
$prefix = $isJwt ? 'employeeRest/claims-collection-report' : 'util/claims-collection-report';
return view('claims_collection_v2_dashboard', [
'policy_id' => $policyId,
'kpi_map' => ClaimsCollectionV2DashboardModel::KPI_MAP,
'kpi_labels' => ClaimsCollectionV2DashboardModel::KPI_LABELS,
'api_all_url' => base_url($prefix . '/all'),
'api_kpi_url' => base_url($prefix . '/kpi'),
]);
}
/**
* Admin check only: raw JSON on screen (no dashboard UI).
*/
public function debug(int $policyId = 4687)
{
$policyId = $this->resolvePolicyId($policyId);
if ($policyId <= 0) {
return $this->response
->setStatusCode(422)
->setBody('client_policy or client_policy_id is required.');
}
$model = new ClaimReportDashboardModel();
$data = $model->getAllKpis($policyId);
$source = $data['_meta']['claims_source'] ?? $model->resolveClaimsTable($policyId);
unset($data['_meta']);
$body = json_encode([
'status' => true,
'policy_id' => $policyId,
'claims_source' => $source,
'data' => $data,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
return $this->response
->setHeader('Content-Type', 'application/json; charset=UTF-8')
->setBody($body);
}
/**
* Run claim:sync-report via URL (authMVC / JWT).
*
* Query params (all optional):
* client_policy / client_policy_id → --policy=
* tpa=icici|abhi|mediassist|fhpl|rcare|vidal|all
* limit=500
* ticket_only=1
*
* Examples:
* /util/claims-collection-report/sync
* /util/claims-collection-report/sync?client_policy=12
* /util/claims-collection-report/sync?client_policy=12&tpa=icici&limit=200
*/
public function sync()
{
// Avoid request/proxy timeouts on large syncs
@set_time_limit(0);
@ini_set('max_execution_time', '0');
$policyId = $this->resolvePolicyId();
$tpa = strtolower(trim((string) ($this->request->getGet('tpa') ?? $this->request->getPost('tpa') ?? 'all')));
$limit = (int) ($this->request->getGet('limit') ?? $this->request->getPost('limit') ?? 500);
$ticketOnly = (string) ($this->request->getGet('ticket_only') ?? $this->request->getPost('ticket_only') ?? '') !== '';
$allowedTpa = ['all', 'vidal', 'abhi', 'mediassist', 'fhpl', 'rcare', 'icici'];
if ($tpa === '' || ! in_array($tpa, $allowedTpa, true)) {
return $this->respond([
'status' => false,
'message' => 'Invalid tpa. Allowed: ' . implode(', ', $allowedTpa),
], 422);
}
if ($limit <= 0) {
$limit = 500;
}
if ($limit > 5000) {
$limit = 5000;
}
$parts = ['claim:sync-report', '--tpa', $tpa, '--limit', (string) $limit];
if ($policyId > 0) {
$parts[] = '--policy';
$parts[] = (string) $policyId;
}
if ($ticketOnly) {
$parts[] = '--ticket-only';
}
$cmd = implode(' ', $parts);
$startedAt = date('Y-m-d H:i:s');
try {
$output = command($cmd);
} catch (\Throwable $e) {
return $this->respond([
'status' => false,
'message' => 'Sync failed: ' . $e->getMessage(),
'command' => $cmd,
'started_at' => $startedAt,
], 500);
}
$output = is_string($output) ? trim($output) : '';
$failed = stripos($output, '[FAIL]') !== false
|| stripos($output, 'does not exist') !== false
|| stripos($output, 'upsert failed') !== false;
$parsed = $this->parseSyncOutput($output);
return $this->respond([
'status' => ! $failed,
'message' => $failed ? 'Sync completed with errors. See summary.' : 'Sync completed.',
'command' => $cmd,
'policy_id' => $policyId > 0 ? $policyId : null,
'tpa' => $tpa,
'limit' => $limit,
'ticket_only' => $ticketOnly,
'started_at' => $startedAt,
'finished_at' => date('Y-m-d H:i:s'),
'summary' => $parsed['summary'],
'phases' => $parsed['phases'],
'tpa_results' => $parsed['tpa_results'],
'logs' => $parsed['logs'],
], $failed ? 500 : 200);
}
/**
* Turn CLI sync text into a readable structured payload.
*
* @return array{
* summary: array<string, mixed>,
* phases: list<string>,
* tpa_results: list<array<string, mixed>>,
* logs: list<string>
* }
*/
protected function parseSyncOutput(string $output): array
{
$lines = preg_split('/\R+/', $output) ?: [];
$logs = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line !== '') {
$logs[] = $line;
}
}
$phases = [];
$tpaResults = [];
$summary = [
'mapped' => null,
'skipped' => null,
'errors' => null,
'message' => null,
];
foreach ($logs as $line) {
if (stripos($line, 'Phase 1:') === 0 || stripos($line, 'Phase 2:') === 0) {
$phases[] = $line;
continue;
}
// [DONE] icici mapped≈14
if (preg_match('/\[DONE\]\s+(\w+)\s+mapped≈(\d+)/i', $line, $m)) {
$tpaResults[] = [
'tpa' => strtolower($m[1]),
'mapped' => (int) $m[2],
'status' => 'done',
'detail' => $line,
];
continue;
}
// [SKIP] abhi: table missing
if (preg_match('/\[SKIP\]\s+(.+)/i', $line, $m)) {
$tpaResults[] = [
'tpa' => null,
'mapped' => 0,
'status' => 'skipped',
'detail' => trim($m[1]),
];
continue;
}
// Done. dump+ticket mapped≈16, skipped=14, errors=0
if (preg_match(
'/Done\.\s*dump\+ticket mapped≈(\d+),\s*skipped=(\d+),\s*errors=(\d+)/i',
$line,
$m
)) {
$summary = [
'mapped' => (int) $m[1],
'skipped' => (int) $m[2],
'errors' => (int) $m[3],
'message' => $line,
];
}
}
return [
'summary' => $summary,
'phases' => $phases,
'tpa_results' => $tpaResults,
'logs' => $logs,
];
}
}