'Optional client_policy_id', '--tpa' => 'icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)', '--limit' => 'Batch size per dump query (default 500)', '--ticket-only' => 'Skip dump tables; copy only from ticket_master', ]; /** * @return array */ private function tpaConfigs(): array { return [ 'vidal' => [ 'env' => 'VIDAL_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_vidal', ], 'abhi' => [ 'env' => 'ABHI_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_abhi', ], 'mediassist' => [ 'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_medi_assist', ], 'fhpl' => [ 'env' => 'FHPL_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_fhpl', ], 'rcare' => [ 'env' => 'R_CARE_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_reliance', ], 'icici' => [ 'env' => 'ICICI_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_icici', ], ]; } public function run(array $params) { $db = db_connect(); if (!$db->tableExists('claim_report')) { CLI::error('Table claim_report does not exist. Run: php spark migrate'); return EXIT_ERROR; } // Prefer $params (works from HTTP command() helper) then CLI options (spark). $policyId = (int) ($params['policy'] ?? CLI::getOption('policy') ?? 0); $limit = (int) ($params['limit'] ?? CLI::getOption('limit') ?? 500); $ticketOnly = array_key_exists('ticket-only', $params) || CLI::getOption('ticket-only') !== null; $tpaOpt = strtolower(trim((string) ($params['tpa'] ?? CLI::getOption('tpa') ?? 'all'))); if ($limit <= 0) { $limit = 500; } $totalMapped = 0; $totalSkipped = 0; $errors = 0; if (!$ticketOnly) { CLI::write('Phase 1: sync from TPA dump tables (linked ticket_id rows)...', 'yellow'); $configs = $this->tpaConfigs(); if ($tpaOpt !== '' && $tpaOpt !== 'all') { if (!isset($configs[$tpaOpt])) { CLI::error('Unknown --tpa=' . $tpaOpt . '. Use: ' . implode('|', array_keys($configs)) . '|all'); return EXIT_ERROR; } $configs = [$tpaOpt => $configs[$tpaOpt]]; } foreach ($configs as $key => $cfg) { $tpaId = (int) env($cfg['env']); $table = $cfg['table']; if ($tpaId <= 0) { CLI::write(" [SKIP] {$key}: env {$cfg['env']} not set", 'light_gray'); continue; } if (!$db->tableExists($table)) { CLI::write(" [SKIP] {$key}: table {$table} missing", 'light_gray'); continue; } try { $service = TpaClaimsImportFactory::make($tpaId); } catch (InvalidArgumentException $e) { CLI::write(' [SKIP] ' . $key . ': ' . $e->getMessage(), 'light_gray'); continue; } $offset = 0; $tpaMapped = 0; while (true) { $builder = $db->table($table) ->select('id, file_id, ticket_id, client_policy_id') ->where('is_active', 1) ->where('ticket_id IS NOT NULL', null, false) ->orderBy('id', 'ASC') ->limit($limit, $offset); if ($policyId > 0) { $builder->where('client_policy_id', $policyId); } $dumpRows = $builder->get()->getResultArray(); if ($dumpRows === []) { break; } // Group dump IDs by file_id for mapClaimReportData $byFile = []; foreach ($dumpRows as $row) { $fileId = (int) ($row['file_id'] ?? 0); $dumpId = (int) ($row['id'] ?? 0); if ($fileId <= 0 || $dumpId <= 0) { $totalSkipped++; continue; } $byFile[$fileId][] = $dumpId; } foreach ($byFile as $fileId => $dumpIds) { $result = $service->backfillClaimReportByDumpIds((int) $fileId, $dumpIds); if (!$result['status']) { CLI::error(" [FAIL] {$key} file_id={$fileId} upsert failed"); $errors++; continue; } $tpaMapped += (int) $result['count']; $totalMapped += (int) $result['count']; } $offset += count($dumpRows); CLI::write(" {$key}: processed {$offset} dump rows (mapped so far {$tpaMapped})", 'green'); if (count($dumpRows) < $limit) { break; } } CLI::write(" [DONE] {$key} mapped≈{$tpaMapped}", 'green'); } } else { CLI::write('Skipping dump tables (--ticket-only).', 'yellow'); } CLI::write('Phase 2: fill gaps from ticket_master dump-sourced claims...', 'yellow'); $tmResult = $this->syncFromTicketMaster($db, $policyId, $limit); if ($tmResult === false) { return EXIT_ERROR; } $totalMapped += $tmResult['mapped']; $totalSkipped += $tmResult['skipped']; CLI::write( "Done. dump+ticket mapped≈{$totalMapped}, skipped={$totalSkipped}, errors={$errors}", $errors > 0 ? 'red' : 'green' ); return $errors > 0 ? EXIT_ERROR : EXIT_SUCCESS; } /** * @return array{mapped:int,skipped:int}|false */ private function syncFromTicketMaster($db, int $policyId, int $limit) { if (!$db->tableExists('ticket_master')) { CLI::error('ticket_master missing'); return false; } $offset = 0; $mapped = 0; $skipped = 0; $now = date('Y-m-d H:i:s'); while (true) { $builder = $db->table('ticket_master') ->where('is_active', 1) ->groupStart() ->where('claim_dump_ref_id IS NOT NULL', null, false) ->orWhere('file_id IS NOT NULL', null, false) ->groupEnd() ->where("claim_number IS NOT NULL AND TRIM(claim_number) != ''", null, false) ->orderBy('id', 'ASC') ->limit($limit, $offset); if ($policyId > 0) { $builder->where('client_policy_id', $policyId); } $tickets = $builder->get()->getResultArray(); if ($tickets === []) { break; } $rows = []; foreach ($tickets as $tm) { $claimNumber = trim((string) ($tm['claim_number'] ?? '')); $clientPolicyId = (int) ($tm['client_policy_id'] ?? 0); if ($claimNumber === '' || $clientPolicyId <= 0) { $skipped++; continue; } // Resolve dump provenance when possible $sourceTable = null; $tpaId = (int) ($tm['tpa_id'] ?? 0); foreach ($this->tpaConfigs() as $cfg) { if ($tpaId === (int) env($cfg['env'])) { $sourceTable = $cfg['table']; break; } } $rows[] = [ 'tpa_id' => $tm['tpa_id'] ?? null, 'client_id' => $tm['client_id'] ?? null, 'client_policy_id' => $clientPolicyId, 'file_id' => $tm['file_id'] ?? null, 'ticket_id' => $tm['id'] ?? null, 'source_table' => $sourceTable, 'source_row_id' => $tm['claim_dump_ref_id'] ?? null, 'claim_number' => $claimNumber, 'emp_code' => $tm['emp_code'] ?? null, 'tpa_no' => $tm['tpa_no'] ?? null, 'emp_id' => $tm['emp_id'] ?? null, 'insured_emp_id' => $tm['insured_emp_id'] ?? null, 'claim_amount' => $tm['claim_amount'] ?? null, 'approved_amount' => $tm['approved_amount'] ?? null, 'incurred_amount' => $tm['approved_amount'] ?? $tm['claim_amount'] ?? null, 'si_amt' => $tm['si_amt'] ?? null, 'tpa_claim_status' => $tm['tpa_claim_status'] ?? null, 'claim_status_id' => $tm['claim_status_id'] ?? null, 'tpa_claim_type' => $tm['tpa_claim_type'] ?? null, 'tpa_ailments' => $tm['tpa_ailments'] ?? null, 'doa' => $tm['doa'] ?? null, 'dod' => $tm['dod'] ?? null, 'date_of_intimat' => $tm['date_of_intimat'] ?? null, 'settled_date' => $tm['settled_date'] ?? null, 'approved_date' => $tm['approved_date'] ?? null, 'claim_dump_date' => $tm['claim_dump_date'] ?? null, 'hospital_name' => $tm['hospital_name'] ?? null, 'hospital_city' => $tm['hospital_city'] ?? null, 'hospital_state' => $tm['hospital_state'] ?? null, 'hospital_pin_code' => $tm['hospital_pin_code'] ?? null, 'hospital_address' => $tm['hospital_address'] ?? null, 'gender' => null, 'age' => null, 'relation' => $tm['relationship'] ?? null, 'is_active' => 1, 'created_at' => $now, 'updated_at' => $now, ]; } if ($rows !== [] && $this->upsertRows($db, $rows) === false) { CLI::error('ticket_master upsert failed at offset ' . $offset); return false; } $mapped += count($rows); $offset += count($tickets); CLI::write(" ticket_master: processed {$offset} rows (mapped {$mapped})", 'green'); if (count($tickets) < $limit) { break; } } return ['mapped' => $mapped, 'skipped' => $skipped]; } /** * @param list> $rows */ private function upsertRows($db, array $rows): bool { $columns = [ 'tpa_id', 'client_id', 'client_policy_id', 'file_id', 'ticket_id', 'source_table', 'source_row_id', 'claim_number', 'emp_code', 'tpa_no', 'emp_id', 'insured_emp_id', 'claim_amount', 'approved_amount', 'incurred_amount', 'si_amt', 'tpa_claim_status', 'claim_status_id', 'tpa_claim_type', 'tpa_ailments', 'doa', 'dod', 'date_of_intimat', 'settled_date', 'approved_date', 'claim_dump_date', 'hospital_name', 'hospital_city', 'hospital_state', 'hospital_pin_code', 'hospital_address', 'gender', 'age', 'relation', 'is_active', 'created_at', 'updated_at', ]; $updateCols = array_values(array_diff($columns, ['client_policy_id', 'claim_number', 'created_at'])); foreach (array_chunk($rows, 100) as $chunk) { $placeholders = []; $binds = []; foreach ($chunk as $row) { $rowPlaceholders = []; foreach ($columns as $col) { $rowPlaceholders[] = '?'; $binds[] = $row[$col] ?? null; } $placeholders[] = '(' . implode(', ', $rowPlaceholders) . ')'; } $updates = []; foreach ($updateCols as $col) { // Prefer non-empty dump enrichment already written in phase 1: // only overwrite when VALUES has a non-null value. if (in_array($col, ['gender', 'age', 'relation', 'approved_amount', 'incurred_amount', 'tpa_ailments', 'tpa_claim_type', 'source_table', 'source_row_id'], true)) { $updates[] = '`' . $col . '` = COALESCE(VALUES(`' . $col . '`), `' . $col . '`)'; } else { $updates[] = '`' . $col . '` = VALUES(`' . $col . '`)'; } } $sql = 'INSERT INTO `claim_report` (`' . implode('`, `', $columns) . '`) VALUES ' . implode(', ', $placeholders) . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates); if ($db->query($sql, $binds) === false) { return false; } } return true; } }