From 0435eeac6bc624cda5cc9db3a536a6a4dddbee7c Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Thu, 23 Jul 2026 14:09:43 +0530 Subject: [PATCH] FIX_CLAIM_DUMP_STATUS_MAPPING --- app/Commands/TestTpaE2ePipeline.php | 294 ++++++++++++++---- .../BaseTpaClaimImportService.php | 15 +- 2 files changed, 241 insertions(+), 68 deletions(-) diff --git a/app/Commands/TestTpaE2ePipeline.php b/app/Commands/TestTpaE2ePipeline.php index 51c4c5e6..f349edbd 100644 --- a/app/Commands/TestTpaE2ePipeline.php +++ b/app/Commands/TestTpaE2ePipeline.php @@ -17,19 +17,21 @@ use ReflectionClass; * php spark tpa:e2e-pipeline --tpa=icici --apply * php spark tpa:e2e-pipeline --tpa-id=6 --apply * php spark tpa:e2e-pipeline --tpa=all --apply --truncate + * php spark tpa:e2e-pipeline --tpa=icici --status-cases --apply */ class TestTpaE2ePipeline extends BaseCommand { protected $group = 'TPA'; protected $name = 'tpa:e2e-pipeline'; protected $description = 'Generate TPA Excel and run Job1 + Job2 end-to-end'; - protected $usage = 'tpa:e2e-pipeline [--tpa=NAME|all] [--tpa-id=ID] [--apply] [--truncate] [--new-status=STATUS]'; + protected $usage = 'tpa:e2e-pipeline [--tpa=NAME|all] [--tpa-id=ID] [--apply] [--truncate] [--new-status=STATUS] [--status-cases]'; protected $options = [ - '--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)', - '--tpa-id' => 'Numeric TPA primary key (overrides --tpa)', - '--apply' => 'Run Job 1 + Job 2 (default is generate Excel + file row only)', - '--truncate' => 'Soft-truncate the test file after a successful apply', - '--new-status' => 'Override dump status string written into Excel', + '--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)', + '--tpa-id' => 'Numeric TPA primary key (overrides --tpa)', + '--apply' => 'Run Job 1 + Job 2 (default is generate Excel + file row only)', + '--truncate' => 'Soft-truncate the test file after a successful apply', + '--new-status' => 'Override dump status string written into Excel', + '--status-cases' => 'Write 3 rows: empty status, unmapped status, mapped status (expect 61/61/mapped id)', ]; /** @@ -40,12 +42,15 @@ class TestTpaE2ePipeline extends BaseCommand { return [ 'icici' => [ - 'env' => 'ICICI_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_icici', - 'sheet' => null, - 'default_status'=> 'REJECTED', - 'status_header' => 'Updated_status', - 'excel_fields' => [ + 'env' => 'ICICI_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_icici', + 'sheet' => null, + 'default_status' => 'REJECTED', + 'status_header' => 'Updated_status', + 'status_db_column' => 'updated_status', + 'mapped_status' => 'PAID', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'POLICY_NO' => 'policy_no', 'UHID' => 'tpa_no', 'EMPLOYEE_MEMBER_ID' => 'emp_code', @@ -56,12 +61,15 @@ class TestTpaE2ePipeline extends BaseCommand ], ], 'abhi' => [ - 'env' => 'ABHI_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_abhi', - 'sheet' => null, - 'default_status'=> 'Rejected', - 'status_header' => 'Claim Status', - 'excel_fields' => [ + 'env' => 'ABHI_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_abhi', + 'sheet' => null, + 'default_status' => 'Rejected', + 'status_header' => 'Claim Status', + 'status_db_column' => 'claim_status', + 'mapped_status' => 'Paid', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'Policy Number' => 'policy_no', 'HEALTHCARD_ID' => 'tpa_no', 'Member Code' => 'emp_code', @@ -72,12 +80,15 @@ class TestTpaE2ePipeline extends BaseCommand ], ], 'mediassist' => [ - 'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_medi_assist', - 'sheet' => null, - 'default_status'=> 'Rejected', - 'status_header' => 'claim_status', - 'excel_fields' => [ + 'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_medi_assist', + 'sheet' => null, + 'default_status' => 'Rejected', + 'status_header' => 'claim_status', + 'status_db_column' => 'claim_status', + 'mapped_status' => 'Paid', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'policy_no' => 'policy_no', 'event_id' => 'tpa_no', 'pribenef_employee_code' => 'emp_code', @@ -88,12 +99,15 @@ class TestTpaE2ePipeline extends BaseCommand ], ], 'fhpl' => [ - 'env' => 'FHPL_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_fhpl', - 'sheet' => 'Claims&Preauth', - 'default_status'=> 'Rejected', - 'status_header' => 'Current Claim Status', - 'excel_fields' => [ + 'env' => 'FHPL_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_fhpl', + 'sheet' => 'Claims&Preauth', + 'default_status' => 'Rejected', + 'status_header' => 'Current Claim Status', + 'status_db_column' => 'current_claim_status', + 'mapped_status' => 'Paid', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'Policy No' => 'policy_no', 'UHIDNO' => 'tpa_no', 'employeeid' => 'emp_code', @@ -104,12 +118,15 @@ class TestTpaE2ePipeline extends BaseCommand ], ], 'rcare' => [ - 'env' => 'R_CARE_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_reliance', - 'sheet' => 'CL', - 'default_status'=> 'Rejected', - 'status_header' => 'Final Status', - 'excel_fields' => [ + 'env' => 'R_CARE_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_reliance', + 'sheet' => 'CL', + 'default_status' => 'Rejected', + 'status_header' => 'Final Status', + 'status_db_column' => 'final_status', + 'mapped_status' => 'Paid', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'Policy Number' => 'policy_no', 'UHID' => 'tpa_no', 'Employee/Member Id' => 'emp_code', @@ -120,12 +137,15 @@ class TestTpaE2ePipeline extends BaseCommand ], ], 'vidal' => [ - 'env' => 'VIDAL_PRIMARY_KEY_CONSTANT', - 'table' => 'claims_dump_vidal', - 'sheet' => null, - 'default_status'=> 'Rejected', - 'status_header' => 'Claim Status', - 'excel_fields' => [ + 'env' => 'VIDAL_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_vidal', + 'sheet' => null, + 'default_status' => 'Rejected', + 'status_header' => 'Claim Status', + 'status_db_column' => 'claim_status', + 'mapped_status' => 'Paid', + 'mapped_status_id' => 11, + 'excel_fields' => [ 'Insurer Policy Number' => 'policy_no', 'Primary Policy Holder Card ID' => 'tpa_no', 'Employee Number' => 'emp_code', @@ -145,6 +165,7 @@ class TestTpaE2ePipeline extends BaseCommand $db = db_connect(); $apply = $this->hasFlag('apply'); $truncate = $this->hasFlag('truncate'); + $statusCases = $this->hasFlag('status-cases'); $statusOverride = $this->resolveOptionValue('new-status', ''); $configs = $this->tpaConfigs(); @@ -157,7 +178,7 @@ class TestTpaE2ePipeline extends BaseCommand foreach ($selected as $tpaKey) { CLI::newLine(); CLI::write(str_repeat('=', 64), 'yellow'); - CLI::write('E2E TPA: ' . strtoupper($tpaKey), 'yellow'); + CLI::write('E2E TPA: ' . strtoupper($tpaKey) . ($statusCases ? ' [status-cases]' : ''), 'yellow'); CLI::write(str_repeat('=', 64), 'yellow'); try { @@ -167,7 +188,8 @@ class TestTpaE2ePipeline extends BaseCommand $configs[$tpaKey], $apply, $truncate, - $statusOverride + $statusOverride, + $statusCases ); } catch (\Throwable $e) { CLI::error("[{$tpaKey}] " . $e->getMessage()); @@ -194,6 +216,19 @@ class TestTpaE2ePipeline extends BaseCommand !empty($row['error']) ? ' | ' . $row['error'] : '' ); CLI::write($line, $ok ? 'green' : 'red'); + if (!empty($row['status_cases'])) { + foreach ($row['status_cases'] as $case) { + $caseOk = !empty($case['ok']); + CLI::write(sprintf( + ' %-10s dump=%s expected=%s got=%s %s', + $case['label'], + $case['dump_status'] === '' ? '(empty)' : $case['dump_status'], + $case['expected_id'], + $case['actual_id'] ?? 'null', + $caseOk ? 'PASS' : 'FAIL' + ), $caseOk ? 'green' : 'red'); + } + } } if (!$apply) { @@ -233,7 +268,8 @@ class TestTpaE2ePipeline extends BaseCommand array $cfg, bool $apply, bool $truncate, - string $statusOverride + string $statusOverride, + bool $statusCases = false ): array { $tpaId = (int) env($cfg['env']); CLI::write("{$cfg['env']} = {$tpaId}", 'cyan'); @@ -262,15 +298,36 @@ class TestTpaE2ePipeline extends BaseCommand throw new \RuntimeException('Could not read Excel headers from service mapping'); } - $rowValues = $this->buildExcelRow($headers, $cfg, $context); - $fileName = "{$tpaKey}_e2e_" . date('Ymd_His') . '.xlsx'; + $scenarios = $statusCases + ? $this->buildStatusCaseScenarios($cfg) + : [['label' => 'default', 'status_value' => $context['status_value'], 'claim_amount' => $context['claim_amount'], 'expected_id' => null]]; + + $excelRows = []; + foreach ($scenarios as $i => $scenario) { + $rowContext = $context; + $rowContext['status_value'] = $scenario['status_value']; + $rowContext['claim_amount'] = $scenario['claim_amount']; + $excelRows[] = $this->buildExcelRow($headers, $cfg, $rowContext); + CLI::write(sprintf( + 'Row %d [%s]: status=%s amount=%s expected_id=%s', + $i + 1, + $scenario['label'], + $scenario['status_value'] === '' ? '(empty)' : $scenario['status_value'], + $scenario['claim_amount'], + $scenario['expected_id'] ?? 'n/a' + ), 'cyan'); + } + + $fileName = $statusCases + ? "{$tpaKey}_status_cases_" . date('Ymd_His') . '.xlsx' + : "{$tpaKey}_e2e_" . date('Ymd_His') . '.xlsx'; $uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0775, true); } $filePath = $uploadDir . $fileName; - $this->writeExcel($filePath, $headers, $rowValues, $cfg['sheet'] ?? null); - CLI::write("Excel written: {$filePath}", 'green'); + $this->writeExcel($filePath, $headers, $excelRows, $cfg['sheet'] ?? null); + CLI::write("Excel written: {$filePath} (" . count($excelRows) . ' data rows)', 'green'); $fileId = $this->createClaimDumpFile($db, [ 'tpa_id' => $tpaId, @@ -293,13 +350,15 @@ class TestTpaE2ePipeline extends BaseCommand ]; if (!$apply) { - CLI::write("Dry-run ready. Apply with: php spark tpa:e2e-pipeline --tpa={$tpaKey} --apply", 'yellow'); - // Point user at existing file_id for apply-by-id later if needed + $applyHint = $statusCases + ? "php spark tpa:e2e-pipeline --tpa={$tpaKey} --status-cases --apply" + : "php spark tpa:e2e-pipeline --tpa={$tpaKey} --apply"; + CLI::write("Dry-run ready. Apply with: {$applyHint}", 'yellow'); return $result; } // Clear claim_dump_ref_id so link/update path can re-bind cleanly. - if (!empty($context['ticket']['id']) && !empty($context['ticket']['claim_dump_ref_id'])) { + if (!$statusCases && !empty($context['ticket']['id']) && !empty($context['ticket']['claim_dump_ref_id'])) { $db->table('ticket_master') ->where('id', $context['ticket']['id']) ->update(['claim_dump_ref_id' => null]); @@ -343,9 +402,15 @@ class TestTpaE2ePipeline extends BaseCommand return $result; } + $statusCol = $cfg['status_db_column'] ?? null; + $selectCols = 'id, ticket_id, master_reject_reason, is_active'; + if ($statusCol) { + $selectCols .= ', ' . $statusCol; + } $afterDump = $db->table($cfg['table']) - ->select('id, ticket_id, master_reject_reason, is_active') + ->select($selectCols) ->where('file_id', $fileId) + ->orderBy('id', 'ASC') ->get()->getResultArray(); CLI::write('Dump AFTER Job2: ' . json_encode($afterDump, JSON_PRETTY_PRINT)); @@ -363,6 +428,17 @@ class TestTpaE2ePipeline extends BaseCommand return $result; } + if ($statusCases) { + $caseResults = $this->assertStatusCases($db, $cfg, $afterDump, $scenarios); + $result['status_cases'] = $caseResults; + foreach ($caseResults as $case) { + if (empty($case['ok'])) { + $result['ok'] = false; + $result['error'] = 'Status case assertion failed for: ' . $case['label']; + } + } + } + if ($truncate) { CLI::write('Running soft truncate...', 'light_red'); $trunc = $service->softTruncateClaimDump($fileId); @@ -377,6 +453,101 @@ class TestTpaE2ePipeline extends BaseCommand return $result; } + /** + * Three rows: empty status → 61, unmapped → 61, mapped → mapped_status_id. + * Unique claim amounts so Job 2 creates/links distinct tickets. + */ + private function buildStatusCaseScenarios(array $cfg): array + { + $suffix = (string) (time() % 100000); + $mappedStatus = (string) ($cfg['mapped_status'] ?? 'Paid'); + $mappedId = (int) ($cfg['mapped_status_id'] ?? 11); + + return [ + [ + 'label' => 'empty', + 'status_value' => '', + 'claim_amount' => '9' . $suffix . '.11', + 'expected_id' => 61, + ], + [ + 'label' => 'unmapped', + 'status_value' => 'ZZZ_UNMAPPED_STATUS', + 'claim_amount' => '9' . $suffix . '.22', + 'expected_id' => 61, + ], + [ + 'label' => 'mapped', + 'status_value' => $mappedStatus, + 'claim_amount' => '9' . $suffix . '.33', + 'expected_id' => $mappedId, + ], + ]; + } + + /** + * Match dump rows to scenarios by amount (and status text) and assert ticket claim_status_id. + */ + private function assertStatusCases($db, array $cfg, array $afterDump, array $scenarios): array + { + $statusCol = $cfg['status_db_column'] ?? null; + $results = []; + + // Resolve amount column from excel_fields source key claim_amount → need dump amount column. + // Prefer joining via ticket_id and comparing expected_id on ticket_master. + foreach ($scenarios as $scenario) { + $match = null; + foreach ($afterDump as $drow) { + $dumpStatus = $statusCol !== null ? trim((string) ($drow[$statusCol] ?? '')) : ''; + $expectedStatus = trim((string) $scenario['status_value']); + if ($dumpStatus === $expectedStatus) { + $match = $drow; + break; + } + } + + $actualId = null; + $ticketId = $match['ticket_id'] ?? null; + if (!empty($ticketId)) { + $ticket = $db->table('ticket_master') + ->select('id, claim_status_id') + ->where('id', $ticketId) + ->get()->getRowArray(); + $actualId = isset($ticket['claim_status_id']) ? (int) $ticket['claim_status_id'] : null; + } + + $ok = $match !== null + && !empty($ticketId) + && $actualId !== null + && $actualId === (int) $scenario['expected_id']; + + $results[] = [ + 'label' => $scenario['label'], + 'dump_status' => $scenario['status_value'], + 'expected_id' => (int) $scenario['expected_id'], + 'actual_id' => $actualId, + 'ticket_id' => $ticketId, + 'dump_id' => $match['id'] ?? null, + 'reject' => $match['master_reject_reason'] ?? null, + 'ok' => $ok, + ]; + + CLI::write(sprintf( + 'ASSERT [%s] status=%s expected=%d actual=%s ticket_id=%s dump_id=%s %s%s', + $scenario['label'], + $scenario['status_value'] === '' ? '(empty)' : $scenario['status_value'], + (int) $scenario['expected_id'], + $actualId === null ? 'null' : (string) $actualId, + $ticketId ?? 'null', + $match['id'] ?? 'null', + $ok ? 'PASS' : 'FAIL', + !empty($match['master_reject_reason']) ? ' reject=' . $match['master_reject_reason'] : '' + ), $ok ? 'green' : 'red'); + } + + return $results; + } + private function resolveSeedContext($db, int $tpaId, string $tpaKey, array $cfg, string $statusOverride): array { $ticket = $db->table('ticket_master') @@ -482,8 +653,13 @@ class TestTpaE2ePipeline extends BaseCommand return $row; } - private function writeExcel(string $filePath, array $headers, array $rowValues, ?string $sheetName): void + private function writeExcel(string $filePath, array $headers, array $rows, ?string $sheetName): void { + // Backward compatible: single row flat array becomes one data row. + if ($rows !== [] && !is_array($rows[0] ?? null)) { + $rows = [$rows]; + } + $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); if ($sheetName) { @@ -491,9 +667,13 @@ class TestTpaE2ePipeline extends BaseCommand } foreach ($headers as $i => $header) { - $col = $i + 1; - $sheet->setCellValue([$col, 1], $header); - $sheet->setCellValue([$col, 2], $rowValues[$i] ?? ''); + $sheet->setCellValue([$i + 1, 1], $header); + } + + foreach ($rows as $r => $rowValues) { + foreach ($headers as $i => $header) { + $sheet->setCellValue([$i + 1, $r + 2], $rowValues[$i] ?? ''); + } } $writer = new Xlsx($spreadsheet); diff --git a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php index 9f8f5b4c..f256bc6e 100644 --- a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php @@ -1116,7 +1116,8 @@ abstract class BaseTpaClaimImportService } /** - * Map dump status text to claim_status_id, or quarantine the dump row when unknown/empty. + * Map dump status text to claim_status_id. + * Falls back to 61 when status is empty or not found in the mapping. */ protected function resolveClaimStatusId( array $statusMapping, @@ -1127,20 +1128,12 @@ abstract class BaseTpaClaimImportService $statusString = trim((string) ($statusString ?? '')); if ($statusString === '') { - $rejectedReasonArray[] = [ - 'id' => $dumpRowId, - 'master_reject_reason' => 'Claim status is empty in the dump file.', - ]; - return null; + return 61; } $statusId = $this->checkStatusMapping($statusMapping, $statusString); if ($statusId === null) { - $rejectedReasonArray[] = [ - 'id' => $dumpRowId, - 'master_reject_reason' => 'Unknown claim status: ' . $statusString, - ]; - return null; + return 61; } return (int) $statusId;