320 lines
12 KiB
PHP
320 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Commands;
|
|
|
|
use App\Libraries\TpaClaimsImportFactory;
|
|
use CodeIgniter\CLI\BaseCommand;
|
|
use CodeIgniter\CLI\CLI;
|
|
|
|
/**
|
|
* Find TPA dump rows whose ticket_id is missing/inactive in ticket_master,
|
|
* clear the orphan link, then run Job 2 to insert tickets again.
|
|
*
|
|
* Usage:
|
|
* php spark tpa:recreate-orphan-tickets
|
|
* php spark tpa:recreate-orphan-tickets --tpa=icici
|
|
* php spark tpa:recreate-orphan-tickets --tpa-id=6 --apply
|
|
* php spark tpa:recreate-orphan-tickets --file-id=84 --apply
|
|
* php spark tpa:recreate-orphan-tickets --tpa=all --apply
|
|
*/
|
|
class RecreateOrphanDumpTickets extends BaseCommand
|
|
{
|
|
protected $group = 'TPA';
|
|
protected $name = 'tpa:recreate-orphan-tickets';
|
|
protected $description = 'Recreate ticket_master rows for dump ticket_id orphans';
|
|
protected $usage = 'tpa:recreate-orphan-tickets [--tpa=NAME|all] [--tpa-id=ID] [--file-id=ID] [--include-pending] [--apply]';
|
|
protected $options = [
|
|
'--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)',
|
|
'--tpa-id' => 'Numeric TPA primary key (overrides --tpa)',
|
|
'--file-id' => 'Limit to one claim_dump_files.id',
|
|
'--include-pending' => 'Also process dump rows with ticket_id IS NULL (never linked)',
|
|
'--apply' => 'Clear orphan links and run Job 2 (default is dry-run list only)',
|
|
];
|
|
|
|
private function tpaConfigs(): array
|
|
{
|
|
return [
|
|
'icici' => ['env' => 'ICICI_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_icici'],
|
|
'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'],
|
|
'vidal' => ['env' => 'VIDAL_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_vidal'],
|
|
];
|
|
}
|
|
|
|
public function run(array $params)
|
|
{
|
|
helper('utility_helper');
|
|
|
|
$db = db_connect();
|
|
$apply = $this->hasFlag('apply');
|
|
$includePending = $this->hasFlag('include-pending');
|
|
$fileIdFilter = (int) $this->resolveOptionValue('file-id', '0');
|
|
$configs = $this->tpaConfigs();
|
|
$selected = $this->resolveSelectedTpas($configs);
|
|
|
|
if ($selected === []) {
|
|
return;
|
|
}
|
|
|
|
$summary = [];
|
|
|
|
foreach ($selected as $tpaKey) {
|
|
$cfg = $configs[$tpaKey];
|
|
$tpaId = (int) env($cfg['env']);
|
|
$table = $cfg['table'];
|
|
|
|
CLI::newLine();
|
|
CLI::write(str_repeat('=', 64), 'yellow');
|
|
CLI::write(strtoupper($tpaKey) . " (tpa_id={$tpaId}, table={$table})", 'yellow');
|
|
CLI::write(str_repeat('=', 64), 'yellow');
|
|
|
|
if (!$this->tableExists($db, $table)) {
|
|
CLI::error("Table {$table} missing — skipped");
|
|
$summary[$tpaKey] = ['ok' => false, 'error' => 'table missing'];
|
|
continue;
|
|
}
|
|
|
|
$orphans = $this->findOrphans($db, $table, $fileIdFilter);
|
|
CLI::write('Orphan dump rows (ticket_id missing in ticket_master): ' . count($orphans));
|
|
|
|
$pending = [];
|
|
if ($includePending) {
|
|
$pending = $this->findPending($db, $table, $fileIdFilter);
|
|
CLI::write('Pending dump rows (ticket_id IS NULL): ' . count($pending));
|
|
}
|
|
|
|
if ($orphans === [] && $pending === []) {
|
|
$summary[$tpaKey] = ['ok' => true, 'orphans' => 0, 'pending' => 0, 'files' => 0];
|
|
continue;
|
|
}
|
|
|
|
foreach (array_slice($orphans, 0, 20) as $row) {
|
|
CLI::write(sprintf(
|
|
' [orphan] dump_id=%s file_id=%s ticket_id=%s',
|
|
$row['id'],
|
|
$row['file_id'],
|
|
$row['ticket_id']
|
|
));
|
|
}
|
|
if (count($orphans) > 20) {
|
|
CLI::write(' ... and ' . (count($orphans) - 20) . ' more orphans');
|
|
}
|
|
|
|
foreach (array_slice($pending, 0, 10) as $row) {
|
|
CLI::write(sprintf(
|
|
' [pending] dump_id=%s file_id=%s',
|
|
$row['id'],
|
|
$row['file_id']
|
|
));
|
|
}
|
|
|
|
$fileIds = array_values(array_unique(array_map(
|
|
'intval',
|
|
array_merge(array_column($orphans, 'file_id'), array_column($pending, 'file_id'))
|
|
)));
|
|
CLI::write('Affected file_ids: ' . implode(', ', $fileIds), 'cyan');
|
|
|
|
if (!$apply) {
|
|
CLI::write("Dry-run only. Apply with: php spark tpa:recreate-orphan-tickets --tpa={$tpaKey} --apply", 'yellow');
|
|
$summary[$tpaKey] = [
|
|
'ok' => true,
|
|
'orphans' => count($orphans),
|
|
'pending' => count($pending),
|
|
'files' => count($fileIds),
|
|
'dry_run' => true,
|
|
];
|
|
continue;
|
|
}
|
|
|
|
$fileResults = [];
|
|
foreach ($fileIds as $fileId) {
|
|
CLI::write("Processing file_id={$fileId} ...", 'light_red');
|
|
try {
|
|
$service = TpaClaimsImportFactory::make($tpaId);
|
|
$result = $service->recreateOrphanDumpTickets($fileId);
|
|
CLI::write(' result: ' . json_encode($result));
|
|
|
|
$stillOrphan = $this->countOrphansForFile($db, $table, $fileId);
|
|
$linked = $db->table($table)
|
|
->where('file_id', $fileId)
|
|
->where('is_active', 1)
|
|
->where('ticket_id IS NOT NULL', null, false)
|
|
->countAllResults();
|
|
|
|
$ok = !empty($result['status']) && $stillOrphan === 0;
|
|
$fileResults[] = [
|
|
'file_id' => $fileId,
|
|
'ok' => $ok,
|
|
'still_orphan' => $stillOrphan,
|
|
'linked' => $linked,
|
|
'message' => $result['message'] ?? null,
|
|
];
|
|
|
|
if (!$ok) {
|
|
CLI::error(" file_id={$fileId} still has {$stillOrphan} orphan(s) or Job2 failed");
|
|
} else {
|
|
CLI::write(" file_id={$fileId} OK (linked={$linked})", 'green');
|
|
}
|
|
|
|
if (!empty($result['status'])) {
|
|
$db->table('claim_dump_files')->where('id', $fileId)->update([
|
|
'status' => 'success',
|
|
'reason' => null,
|
|
]);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
CLI::error(" file_id={$fileId} ERROR: " . $e->getMessage());
|
|
$fileResults[] = [
|
|
'file_id' => $fileId,
|
|
'ok' => false,
|
|
'error' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
$allOk = !in_array(false, array_column($fileResults, 'ok'), true);
|
|
$summary[$tpaKey] = [
|
|
'ok' => $allOk,
|
|
'orphans' => count($orphans),
|
|
'pending' => count($pending),
|
|
'files' => count($fileIds),
|
|
'results' => $fileResults,
|
|
];
|
|
}
|
|
|
|
CLI::newLine();
|
|
CLI::write(str_repeat('=', 64), 'cyan');
|
|
CLI::write('SUMMARY', 'cyan');
|
|
CLI::write(str_repeat('=', 64), 'cyan');
|
|
foreach ($summary as $tpaKey => $row) {
|
|
$line = sprintf(
|
|
'%-12s %s orphans=%s pending=%s files=%s%s',
|
|
strtoupper($tpaKey),
|
|
!empty($row['ok']) ? 'OK' : 'FAIL',
|
|
$row['orphans'] ?? 0,
|
|
$row['pending'] ?? 0,
|
|
$row['files'] ?? 0,
|
|
!empty($row['dry_run']) ? ' (dry-run)' : (!empty($row['error']) ? ' | ' . $row['error'] : '')
|
|
);
|
|
CLI::write($line, !empty($row['ok']) ? 'green' : 'red');
|
|
}
|
|
|
|
if (!$apply) {
|
|
CLI::newLine();
|
|
CLI::write('No DB changes made. Re-run with --apply to clear orphans and insert tickets.', 'yellow');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dump rows with ticket_id set but no active ticket_master row.
|
|
*/
|
|
private function findOrphans($db, string $table, int $fileIdFilter = 0): array
|
|
{
|
|
$sql = "SELECT d.id, d.file_id, d.ticket_id
|
|
FROM `{$table}` d
|
|
LEFT JOIN ticket_master tm ON tm.id = d.ticket_id AND tm.is_active = 1
|
|
WHERE d.is_active = 1
|
|
AND d.ticket_id IS NOT NULL
|
|
AND tm.id IS NULL";
|
|
$binds = [];
|
|
if ($fileIdFilter > 0) {
|
|
$sql .= ' AND d.file_id = ?';
|
|
$binds[] = $fileIdFilter;
|
|
}
|
|
$sql .= ' ORDER BY d.file_id ASC, d.id ASC';
|
|
|
|
return $db->query($sql, $binds)->getResultArray();
|
|
}
|
|
|
|
/**
|
|
* Dump rows never linked / rejected (eligible for Job 2 insert).
|
|
*/
|
|
private function findPending($db, string $table, int $fileIdFilter = 0): array
|
|
{
|
|
$builder = $db->table($table)
|
|
->select('id, file_id, ticket_id')
|
|
->where('is_active', 1)
|
|
->where('ticket_id IS NULL', null, false)
|
|
->where('master_reject_reason IS NULL', null, false);
|
|
if ($fileIdFilter > 0) {
|
|
$builder->where('file_id', $fileIdFilter);
|
|
}
|
|
|
|
return $builder->orderBy('file_id', 'ASC')->orderBy('id', 'ASC')->get()->getResultArray();
|
|
}
|
|
|
|
private function countOrphansForFile($db, string $table, int $fileId): int
|
|
{
|
|
$sql = "SELECT COUNT(*) AS cnt
|
|
FROM `{$table}` d
|
|
LEFT JOIN ticket_master tm ON tm.id = d.ticket_id AND tm.is_active = 1
|
|
WHERE d.is_active = 1
|
|
AND d.file_id = ?
|
|
AND d.ticket_id IS NOT NULL
|
|
AND tm.id IS NULL";
|
|
$row = $db->query($sql, [$fileId])->getRowArray();
|
|
|
|
return (int) ($row['cnt'] ?? 0);
|
|
}
|
|
|
|
private function resolveSelectedTpas(array $configs): array
|
|
{
|
|
$tpaIdOpt = (int) $this->resolveOptionValue('tpa-id', '0');
|
|
if ($tpaIdOpt > 0) {
|
|
foreach ($configs as $key => $cfg) {
|
|
if ((int) env($cfg['env']) === $tpaIdOpt) {
|
|
return [$key];
|
|
}
|
|
}
|
|
CLI::error("No TPA config matches --tpa-id={$tpaIdOpt}");
|
|
return [];
|
|
}
|
|
|
|
$tpaOpt = strtolower((string) ($this->resolveOptionValue('tpa', '') ?: 'all'));
|
|
if ($tpaOpt === '' || $tpaOpt === 'all') {
|
|
return array_keys($configs);
|
|
}
|
|
if (!isset($configs[$tpaOpt])) {
|
|
CLI::error("Unknown --tpa={$tpaOpt}. Use: " . implode('|', array_keys($configs)) . '|all');
|
|
return [];
|
|
}
|
|
|
|
return [$tpaOpt];
|
|
}
|
|
|
|
private function tableExists($db, string $table): bool
|
|
{
|
|
return !empty($db->query('SHOW TABLES LIKE ' . $db->escape($table))->getResultArray());
|
|
}
|
|
|
|
private function hasFlag(string $name): bool
|
|
{
|
|
if (CLI::getOption($name) !== null) {
|
|
return true;
|
|
}
|
|
return in_array('--' . $name, $_SERVER['argv'] ?? [], true);
|
|
}
|
|
|
|
private function resolveOptionValue(string $name, string $default): string
|
|
{
|
|
$opt = CLI::getOption($name);
|
|
if (is_string($opt) && $opt !== '') {
|
|
return $opt;
|
|
}
|
|
|
|
$argv = $_SERVER['argv'] ?? [];
|
|
foreach ($argv as $i => $arg) {
|
|
if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) {
|
|
return trim($m[1]);
|
|
}
|
|
if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) {
|
|
return (string) $argv[$i + 1];
|
|
}
|
|
}
|
|
|
|
return $default;
|
|
}
|
|
}
|