353 lines
11 KiB
PHP
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,
|
|
];
|
|
}
|
|
}
|