MERGE_TEST_BUG_FIXES

This commit is contained in:
Ubuntu 2026-07-13 10:16:21 +05:30
commit d255fca519
15 changed files with 1884 additions and 109 deletions

View File

@ -0,0 +1,319 @@
<?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;
}
}

View File

@ -0,0 +1,549 @@
<?php
namespace App\Commands;
use App\Libraries\TpaClaimsImportFactory;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use ReflectionClass;
/**
* End-to-end TPA claim dump test: generate Excel Job 1 Job 2.
*
* Usage:
* php spark tpa:e2e-pipeline --tpa=all
* 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
*/
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 $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',
];
/**
* Per-TPA Excel seed config.
* excel_fields: excel header => source key from ticket/policy context
*/
private function tpaConfigs(): array
{
return [
'icici' => [
'env' => 'ICICI_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_icici',
'sheet' => null,
'default_status'=> 'REJECTED',
'status_header' => 'Updated_status',
'excel_fields' => [
'POLICY_NO' => 'policy_no',
'UHID' => 'tpa_no',
'EMPLOYEE_MEMBER_ID' => 'emp_code',
'RELATION' => 'relation',
'CLAIMED_AMOUNT' => 'claim_amount',
'DOA' => 'doa',
'Updated_status' => 'status_value',
],
],
'abhi' => [
'env' => 'ABHI_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_abhi',
'sheet' => null,
'default_status'=> 'Rejected',
'status_header' => 'Claim Status',
'excel_fields' => [
'Policy Number' => 'policy_no',
'HEALTHCARD_ID' => 'tpa_no',
'Member Code' => 'emp_code',
'Relation' => 'relation',
'Claimed Amount'=> 'claim_amount',
'DOA' => 'doa',
'Claim Status' => 'status_value',
],
],
'mediassist' => [
'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_medi_assist',
'sheet' => null,
'default_status'=> 'Rejected',
'status_header' => 'claim_status',
'excel_fields' => [
'policy_no' => 'policy_no',
'event_id' => 'tpa_no',
'pribenef_employee_code' => 'emp_code',
'benef_relation' => 'relation',
'claim_amount' => 'claim_amount',
'date_of_admission' => 'doa',
'claim_status' => 'status_value',
],
],
'fhpl' => [
'env' => 'FHPL_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_fhpl',
'sheet' => 'Claims&Preauth',
'default_status'=> 'Rejected',
'status_header' => 'Current Claim Status',
'excel_fields' => [
'Policy No' => 'policy_no',
'UHIDNO' => 'tpa_no',
'employeeid' => 'emp_code',
'relationship' => 'relation',
'claimamount' => 'claim_amount',
'Admdate' => 'doa',
'Current Claim Status' => 'status_value',
],
],
'rcare' => [
'env' => 'R_CARE_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_reliance',
'sheet' => 'CL',
'default_status'=> 'Rejected',
'status_header' => 'Final Status',
'excel_fields' => [
'Policy Number' => 'policy_no',
'UHID' => 'tpa_no',
'Employee/Member Id' => 'emp_code',
'Relation' => 'relation',
'Claimed Amount' => 'claim_amount',
'DOA/OPD Treatment From' => 'doa',
'Final Status' => 'status_value',
],
],
'vidal' => [
'env' => 'VIDAL_PRIMARY_KEY_CONSTANT',
'table' => 'claims_dump_vidal',
'sheet' => null,
'default_status'=> 'Rejected',
'status_header' => 'Claim Status',
'excel_fields' => [
'Insurer Policy Number' => 'policy_no',
'Primary Policy Holder Card ID' => 'tpa_no',
'Employee Number' => 'emp_code',
'Relation' => 'relation',
'Claim Amount' => 'claim_amount',
'Date of Admission' => 'doa',
'Claim Status' => 'status_value',
],
],
];
}
public function run(array $params)
{
helper('utility_helper');
$db = db_connect();
$apply = $this->hasFlag('apply');
$truncate = $this->hasFlag('truncate');
$statusOverride = $this->resolveOptionValue('new-status', '');
$configs = $this->tpaConfigs();
$selected = $this->resolveSelectedTpas($configs);
if ($selected === []) {
return;
}
$summary = [];
foreach ($selected as $tpaKey) {
CLI::newLine();
CLI::write(str_repeat('=', 64), 'yellow');
CLI::write('E2E TPA: ' . strtoupper($tpaKey), 'yellow');
CLI::write(str_repeat('=', 64), 'yellow');
try {
$summary[$tpaKey] = $this->runForTpa(
$db,
$tpaKey,
$configs[$tpaKey],
$apply,
$truncate,
$statusOverride
);
} catch (\Throwable $e) {
CLI::error("[{$tpaKey}] " . $e->getMessage());
$summary[$tpaKey] = [
'ok' => false,
'error' => $e->getMessage(),
];
}
}
CLI::newLine();
CLI::write(str_repeat('=', 64), 'cyan');
CLI::write('E2E SUMMARY', 'cyan');
CLI::write(str_repeat('=', 64), 'cyan');
foreach ($summary as $tpaKey => $row) {
$ok = !empty($row['ok']);
$line = sprintf(
'%-12s %s file_id=%s job1=%s job2=%s%s',
strtoupper($tpaKey),
$ok ? 'OK' : 'FAIL',
$row['file_id'] ?? '-',
$row['job1'] ?? '-',
$row['job2'] ?? '-',
!empty($row['error']) ? ' | ' . $row['error'] : ''
);
CLI::write($line, $ok ? 'green' : 'red');
}
if (!$apply) {
CLI::newLine();
CLI::write('Dry-run only (Excel + claim_dump_files created). Re-run with --apply to execute Job1+Job2.', 'yellow');
}
}
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 runForTpa(
$db,
string $tpaKey,
array $cfg,
bool $apply,
bool $truncate,
string $statusOverride
): array {
$tpaId = (int) env($cfg['env']);
CLI::write("{$cfg['env']} = {$tpaId}", 'cyan');
CLI::write("table={$cfg['table']}, sheet=" . ($cfg['sheet'] ?? 'active'), 'cyan');
if (!$this->tableExists($db, $cfg['table'])) {
throw new \RuntimeException("Table {$cfg['table']} does not exist");
}
$context = $this->resolveSeedContext($db, $tpaId, $tpaKey, $cfg, $statusOverride);
CLI::write('Seed ticket: ' . json_encode([
'id' => $context['ticket']['id'] ?? null,
'client_id' => $context['client_id'],
'client_policy_id' => $context['client_policy_id'],
'policy_no' => $context['policy_no'],
'emp_code' => $context['emp_code'],
'tpa_no' => $context['tpa_no'],
'claim_amount' => $context['claim_amount'],
'doa' => $context['doa'],
'status_value' => $context['status_value'],
]));
$service = TpaClaimsImportFactory::make($tpaId);
$headers = $this->extractExcelHeaders($service);
if ($headers === []) {
throw new \RuntimeException('Could not read Excel headers from service mapping');
}
$rowValues = $this->buildExcelRow($headers, $cfg, $context);
$fileName = "{$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');
$fileId = $this->createClaimDumpFile($db, [
'tpa_id' => $tpaId,
'client_id' => $context['client_id'],
'client_policy_id' => $context['client_policy_id'],
'file_name' => $fileName,
'status' => 'inprogress',
'created_by' => 1,
'created_at' => date('Y-m-d H:i:s'),
'is_active' => 1,
]);
CLI::write("claim_dump_files.id = {$fileId}", 'green');
$result = [
'ok' => true,
'file_id' => $fileId,
'file_name' => $fileName,
'job1' => 'skipped',
'job2' => 'skipped',
];
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
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'])) {
$db->table('ticket_master')
->where('id', $context['ticket']['id'])
->update(['claim_dump_ref_id' => null]);
CLI::write('Cleared seed ticket.claim_dump_ref_id for retest.', 'yellow');
}
CLI::write('Running Job 1 (Excel → dump)...', 'light_red');
$job1 = $service->runTpaClaimDumpInsert($filePath, $fileId);
CLI::write('Job1 result: ' . json_encode($job1));
$result['job1'] = !empty($job1['status']) ? 'ok' : 'fail';
if (empty($job1['status'])) {
$db->table('claim_dump_files')->where('id', $fileId)->update([
'status' => 'failed',
'reason' => json_encode(['error_data' => $job1['message'] ?? 'Job1 failed']),
]);
$result['ok'] = false;
$result['error'] = $job1['message'] ?? 'Job1 failed';
return $result;
}
$dumpCount = $db->table($cfg['table'])->where('file_id', $fileId)->where('is_active', 1)->countAllResults();
CLI::write("Dump rows after Job1: {$dumpCount}", 'cyan');
CLI::write('Running Job 2 (dump → ticket_master)...', 'light_red');
$job2 = $service->runTicketMasterInsert(['file_id' => $fileId]);
CLI::write('Job2 result: ' . json_encode($job2));
$result['job2'] = !empty($job2['status']) ? 'ok' : 'fail';
if (!empty($job2['status'])) {
$db->table('claim_dump_files')->where('id', $fileId)->update([
'status' => 'success',
'reason' => null,
]);
} else {
$db->table('claim_dump_files')->where('id', $fileId)->update([
'status' => 'failed',
'reason' => json_encode(['error_data' => $job2['message'] ?? 'Job2 failed']),
]);
$result['ok'] = false;
$result['error'] = $job2['message'] ?? 'Job2 failed';
return $result;
}
$afterDump = $db->table($cfg['table'])
->select('id, ticket_id, master_reject_reason, is_active')
->where('file_id', $fileId)
->get()->getResultArray();
CLI::write('Dump AFTER Job2: ' . json_encode($afterDump, JSON_PRETTY_PRINT));
$linked = 0;
foreach ($afterDump as $drow) {
if (!empty($drow['ticket_id'])) {
$linked++;
}
}
$result['linked'] = $linked;
if ($linked === 0 && empty($afterDump[0]['master_reject_reason'])) {
$result['ok'] = false;
$result['error'] = 'Job2 reported success but dump ticket_id is still null';
CLI::error($result['error']);
return $result;
}
if ($truncate) {
CLI::write('Running soft truncate...', 'light_red');
$trunc = $service->softTruncateClaimDump($fileId);
CLI::write('Truncate: ' . json_encode($trunc));
$result['truncated'] = !empty($trunc['status']);
if (empty($trunc['status'])) {
$result['ok'] = false;
$result['error'] = $trunc['message'] ?? 'Truncate failed';
}
}
return $result;
}
private function resolveSeedContext($db, int $tpaId, string $tpaKey, array $cfg, string $statusOverride): array
{
$ticket = $db->table('ticket_master')
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
->where('tpa_id', $tpaId)
->where('is_active', 1)
->where('emp_code IS NOT NULL', null, false)
->where('tpa_no IS NOT NULL', null, false)
->where('claim_amount IS NOT NULL', null, false)
->where('doa IS NOT NULL', null, false)
->orderBy('id', 'DESC')
->get()->getRowArray();
if (empty($ticket)) {
CLI::write("[{$tpaKey}] No ticket for this tpa_id; falling back to any ticket with identity keys.", 'yellow');
$ticket = $db->table('ticket_master')
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
->where('is_active', 1)
->where('client_policy_id IS NOT NULL', null, false)
->where('emp_code IS NOT NULL', null, false)
->where('tpa_no IS NOT NULL', null, false)
->where('claim_amount IS NOT NULL', null, false)
->where('doa IS NOT NULL', null, false)
->orderBy('id', 'DESC')
->get()->getRowArray();
}
if (empty($ticket)) {
throw new \RuntimeException('No suitable ticket_master row found to seed Excel');
}
$policy = $db->table('client_policy')
->select('id, client_id, policy_no')
->where('id', $ticket['client_policy_id'])
->get()->getRowArray();
if (empty($policy) || trim((string) ($policy['policy_no'] ?? '')) === '') {
throw new \RuntimeException('client_policy / policy_no missing for seed ticket');
}
$statusValue = $statusOverride !== '' ? $statusOverride : $cfg['default_status'];
return [
'ticket' => $ticket,
'client_id' => (int) ($ticket['client_id'] ?? $policy['client_id']),
'client_policy_id' => (int) $ticket['client_policy_id'],
'policy_no' => trim((string) $policy['policy_no']),
'emp_code' => (string) $ticket['emp_code'],
'tpa_no' => (string) $ticket['tpa_no'],
'claim_amount' => (string) $ticket['claim_amount'],
'doa' => (string) $ticket['doa'],
'relation' => 'SELF',
'status_value' => $statusValue,
];
}
private function extractExcelHeaders(object $service): array
{
$ref = new ReflectionClass($service);
if (!$ref->hasProperty('mapping')) {
return [];
}
$prop = $ref->getProperty('mapping');
$prop->setAccessible(true);
$mapping = $prop->getValue($service);
if (!is_array($mapping)) {
return [];
}
$headers = [];
foreach ($mapping as $map) {
$excel = $map['excel_column'] ?? null;
if (is_array($excel)) {
$name = trim((string) ($excel['col_name'] ?? ''));
} else {
$name = trim((string) $excel);
}
if ($name !== '') {
$headers[] = $name;
}
}
return $headers;
}
private function buildExcelRow(array $headers, array $cfg, array $context): array
{
$row = array_fill(0, count($headers), '');
$headerIndex = array_flip($headers);
foreach ($cfg['excel_fields'] as $excelHeader => $sourceKey) {
if (!isset($headerIndex[$excelHeader])) {
CLI::write("Warning: Excel header '{$excelHeader}' not in mapping; skipped.", 'yellow');
continue;
}
$row[$headerIndex[$excelHeader]] = $context[$sourceKey] ?? '';
}
// Ensure status header is filled even if excel_fields key differs.
if (isset($headerIndex[$cfg['status_header']])) {
$row[$headerIndex[$cfg['status_header']]] = $context['status_value'];
}
return $row;
}
private function writeExcel(string $filePath, array $headers, array $rowValues, ?string $sheetName): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
if ($sheetName) {
$sheet->setTitle($sheetName);
}
foreach ($headers as $i => $header) {
$col = $i + 1;
$sheet->setCellValue([$col, 1], $header);
$sheet->setCellValue([$col, 2], $rowValues[$i] ?? '');
}
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
}
private function createClaimDumpFile($db, array $data): int
{
$cols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field');
$data = array_intersect_key($data, array_flip($cols));
$db->table('claim_dump_files')->insert($data);
$id = (int) $db->insertID();
if ($id <= 0) {
throw new \RuntimeException('Failed to insert claim_dump_files');
}
return $id;
}
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;
}
}

View File

@ -261,6 +261,24 @@ class TestTpaStatusUpdate extends BaseCommand
$result = $service->runTicketMasterInsert(['file_id' => $currentFileId]);
CLI::write('Result: ' . json_encode($result, JSON_PRETTY_PRINT));
// Mirror TicketServiceController Job 2 success/fail file status update.
if (!empty($result['status'])) {
$db->table('claim_dump_files')->where('id', $currentFileId)->update([
'status' => 'success',
'reason' => null,
]);
CLI::write("claim_dump_files.status => success", 'green');
} else {
$db->table('claim_dump_files')->where('id', $currentFileId)->update([
'status' => 'failed',
'reason' => json_encode([
'error_summary' => [5 => 1],
'error_data' => $result['message'] ?? 'Job 2 failed',
]),
]);
CLI::write("claim_dump_files.status => failed", 'red');
}
$afterDump = $db->table($cfg['table'])
->select($this->dumpSelectColumns($cfg))
->where('file_id', $currentFileId)
@ -489,6 +507,15 @@ class TestTpaStatusUpdate extends BaseCommand
if (in_array($arg, ['--file_id', '--file-id'], true) && isset($argv[$i + 1])) {
return (int) $argv[$i + 1];
}
// Bare numeric arg (e.g. `tpa:test-status --tpa=icici 70 --apply`)
if (is_numeric($arg) && (int) $arg > 0 && $i > 0 && strpos((string) $argv[$i - 1], 'spark') === false) {
$prev = (string) ($argv[$i - 1] ?? '');
// Skip values belonging to options like --tpa=... or --new-status STATUS
if ($prev === '--tpa' || $prev === '--new-status' || $prev === '--file_id' || $prev === '--file-id') {
continue;
}
return (int) $arg;
}
}
return (int) (CLI::getOption('file_id') ?? CLI::getOption('file-id') ?? 0);

View File

@ -444,6 +444,7 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
$routes->get('getClaimDumpFileErrorData', 'TicketController::getClaimDumpFileErrorData');
$routes->get("claim_dump_excel_error/(:any)", "TicketController::getClaimDumpExcelFileErrors/$1");
$routes->get("download_claim_dump_file/(:any)", "TicketController::downloadClaimDumpFile/$1");
$routes->match(['get', 'post'], 'truncateClaimDumpFile', 'TicketController::truncateClaimDumpFile');
$routes->post('uploadMultiFileFromRfq', 'LeadsController::uploadMultiFileFromRfq');
$routes->get('downloadMemberFile/(:any)', 'LeadsController::downloadMemberFile/$1');
$routes->get('downloadFullMemberDataExcelErrorFile/(:any)', 'LeadsController::downloadFullMemberDataExcelErrorFile/$1');

View File

@ -2527,14 +2527,44 @@ class TicketController extends BaseController
public function removeTicket()
{
$ticket_id = $this->request->getGet('ticket_id');
if(!empty($ticket_id)){
$data['is_active'] = 0;
if (!empty($ticket_id)) {
$data['is_active'] = 0;
$this->ticketMasterModel->where('id', $ticket_id)->set($data)->update();
$this->clearDumpTicketLinksForTicket((int) $ticket_id);
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Claim removed successfully'], 200);
}else{
} else {
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to remove Claim'], 200);
}
}
/**
* Clear TPA dump ticket_id links when a ticket is soft-deleted (avoids orphan dump refs).
*/
private function clearDumpTicketLinksForTicket(int $ticketId): void
{
if ($ticketId <= 0) {
return;
}
$tables = [
'claims_dump_vidal',
'claims_dump_abhi',
'claims_dump_medi_assist',
'claims_dump_fhpl',
'claims_dump_reliance',
'claims_dump_icici',
];
$db = db_connect();
foreach ($tables as $table) {
if (!$db->tableExists($table)) {
continue;
}
$db->table($table)
->where('ticket_id', $ticketId)
->set(['ticket_id' => null])
->update();
}
}
public function getTpaClaimPushLogs()
@ -4317,6 +4347,7 @@ class TicketController extends BaseController
claim_dump_files.id as file_id,
claim_dump_files.file_name,
claim_dump_files.status,
claim_dump_files.tpa_id,
claim_dump_files.created_at,
up.first_name as user_name,
c.client_name,
@ -4391,15 +4422,22 @@ class TicketController extends BaseController
$file_id = $this->claimDumpFileModel->insert($insert_data);
$this->myLogger->logme("error", 'claim_dumb_file_id : {file_id}, uploaded success', ['file_id' => $file_id]);
log_message('error', '[CLAIM_DUMP][UPLOAD_SAVED] file_id=' . $file_id
. ' file_name=' . $filename
. ' client_id=' . ($insert_data['client_id'] ?? 'null')
. ' client_policy_id=' . ($insert_data['client_policy_id'] ?? 'null')
. ' tpa_id=' . ($insert_data['tpa_id'] ?? 'null'));
//after file upload success than call the file formate validation in service controller
// $ticketServiceController = new TicketServiceController();
if(!empty($insert_data['tpa_id'])){
$r = Jobs::addJob(['job_name' => 'tpaClaimDumpImporter', 'payload' => ['file_id' => $file_id]]);
log_message('error', '[CLAIM_DUMP][UPLOAD_QUEUED_JOB1] file_id=' . $file_id . ' job=tpaClaimDumpImporter');
// $response = $ticketServiceController->claimDumpExcelFileFormatValidation(["file_id" => $file_id]);
}else{
$r = Jobs::addJob(['job_name' => 'claimDumpExcelFileFormatValidation', 'payload' => ['file_id' => $file_id]]);
log_message('error', '[CLAIM_DUMP][UPLOAD_QUEUED_LEGACY] file_id=' . $file_id . ' job=claimDumpExcelFileFormatValidation');
// $response = $ticketServiceController->claimDumpExcelFileFormatValidation(["file_id" => $file_id]);
}
return $this->respond(['status' => true, 'code' => 200, 'message' => 'File uploaded successfully. File being validated'], 200);
@ -4472,6 +4510,58 @@ class TicketController extends BaseController
}
}
/**
* Soft-delete a TPA claim dump upload: dump rows + tickets created by that file_id.
*/
public function truncateClaimDumpFile()
{
$fileId = (int) ($this->request->getGet('file_id') ?? $this->request->getPost('file_id') ?? 0);
log_message('error', '[CLAIM_DUMP][CTRL_TRUNCATE_START] file_id=' . $fileId);
if ($fileId <= 0) {
log_message('error', '[CLAIM_DUMP][CTRL_TRUNCATE_INVALID] file_id=' . $fileId);
return $this->respond(['status' => false, 'code' => 400, 'message' => 'file_id is required'], 200);
}
$fileData = $this->claimDumpFileModel->where('id', $fileId)->where('is_active', 1)->first();
if (empty($fileData)) {
log_message('error', '[CLAIM_DUMP][CTRL_TRUNCATE_NOT_FOUND] file_id=' . $fileId);
return $this->respond(['status' => false, 'code' => 404, 'message' => 'File not found or already truncated'], 200);
}
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
if ($tpaId <= 0) {
log_message('error', '[CLAIM_DUMP][CTRL_TRUNCATE_NO_TPA] file_id=' . $fileId);
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Truncate is only supported for TPA claim dumps'], 200);
}
try {
$handler = \App\Libraries\TpaClaimsImportFactory::make($tpaId);
$result = $handler->softTruncateClaimDump($fileId);
log_message(
!empty($result['status']) ? 'info' : 'error',
'[CLAIM_DUMP][CTRL_TRUNCATE_DONE] file_id=' . $fileId
. ' status=' . (!empty($result['status']) ? 'true' : 'false')
. ' message=' . ($result['message'] ?? '')
);
return $this->respond([
'status' => !empty($result['status']),
'code' => !empty($result['status']) ? 200 : 400,
'message' => $result['message'] ?? 'Truncate failed',
'data' => $result,
], 200);
} catch (\Throwable $th) {
$this->myLogger->logme('error', 'truncateClaimDumpFile: ' . $th->getMessage());
log_message('error', '[CLAIM_DUMP][CTRL_TRUNCATE_EXCEPTION] file_id=' . $fileId . ' error=' . $th->getMessage());
return $this->respond([
'status' => false,
'code' => 500,
'message' => 'Truncate failed: ' . $th->getMessage(),
], 200);
}
}
// -------- END CLAIM DUMP UPLOAD ----------------------------------------------------------------------------------------------
public function saveIRDocsJson()
{

View File

@ -1847,12 +1847,13 @@ class TicketServiceController extends AdminController
// --------------------------------------------------------------------------------------------------------------------------------
/**
* Resolve and validate Claim Dump file metadata and physical file for TPA imports.
* Resolve Claim Dump file metadata (and optionally the physical Excel) for TPA imports.
*
* @param int|null $fileId
* @param bool $requirePhysicalFile Job 1 needs the Excel; Job 2 only needs DB metadata.
* @return array{status:bool,message?:string,fileData?:array,filePath?:string}
*/
private function resolveTpaClaimDumpFile(?int $fileId): array
private function resolveTpaClaimDumpFile(?int $fileId, bool $requirePhysicalFile = true): array
{
if (empty($fileId)) {
return [
@ -1871,7 +1872,7 @@ class TicketServiceController extends AdminController
$filePath = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR . $fileData['file_name'];
if (!is_file($filePath)) {
if ($requirePhysicalFile && !is_file($filePath)) {
return [
'status' => false,
'message' => 'Claim dump file not found',
@ -1881,7 +1882,7 @@ class TicketServiceController extends AdminController
return [
'status' => true,
'fileData' => $fileData,
'filePath' => $filePath,
'filePath' => is_file($filePath) ? $filePath : null,
];
}
@ -1894,30 +1895,63 @@ class TicketServiceController extends AdminController
public function tpaClaimDumpImporter(array $params)
{
$file_id = isset($params['file_id']) ? (int) $params['file_id'] : null;
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_START] file_id=' . ($file_id ?? 'null'));
try {
$resolved = $this->resolveTpaClaimDumpFile($file_id);
if ($resolved['status'] === false) {
$message = $resolved['message'] ?? 'Unable to resolve claim dump file';
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_RESOLVE_FAILED] file_id=' . ($file_id ?? 'null') . ' message=' . $message);
if (!empty($file_id)) {
$this->markAsFailed($file_id, $message);
}
return [
'status' => false,
'message' => $resolved['message'] ?? 'Unable to resolve claim dump file',
'message' => $message,
];
}
$fileData = $resolved['fileData'];
$filePath = $resolved['filePath'];
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_RESOLVED] file_id=' . $file_id . ' tpa_id=' . ($fileData['tpa_id'] ?? 'null') . ' file=' . ($fileData['file_name'] ?? ''));
// Prevent concurrent Job 1 runs for the same file.
$db = \Config\Database::connect();
$db->table('claim_dump_files')
->where('id', $file_id)
->where('status', 'inprogress')
->update([
'status' => 'processing',
'updated_at' => date('Y-m-d H:i:s'),
]);
if ($db->affectedRows() === 0) {
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_LOCK_SKIP] file_id=' . $file_id);
return [
'status' => true,
'message' => 'Claim dump import already in progress or completed for this file.',
];
}
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_LOCK_ACQUIRED] file_id=' . $file_id);
$handler = TpaClaimsImportFactory::make($fileData['tpa_id'] ?? 0);
$result = $handler->runTpaClaimDumpInsert($filePath, $file_id);
if (!empty($result['status']) && $result['status'] === true) {
// Ready for Job 2 (release Job 1 lock back to inprogress).
$this->claimDumpFileModel->update($file_id, [
'status' => 'inprogress',
]);
Jobs::addJob([
'job_name' => 'tpaClaimDumpToTicketMasterImporters',
'payload' => ['file_id' => $file_id],
]);
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_SUCCESS_QUEUED_JOB2] file_id=' . $file_id . ' message=' . ($result['message'] ?? '') . ' record_count=' . ($result['record_count'] ?? 0));
} else {
// FORCE FAIL LOGIC
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_FAILED] file_id=' . $file_id . ' message=' . ($result['message'] ?? 'unknown'));
$this->markAsFailed(
$file_id,
$result['message'] ?? 'System error contact admin',
@ -1928,6 +1962,7 @@ class TicketServiceController extends AdminController
return $result;
} catch (\Throwable $th) {
$this->myLogger->logme("error", 'TPA_CLAIM_IMPORTER_JOB : ' . $th->getMessage());
log_message('error', '[CLAIM_DUMP][CTRL_JOB1_EXCEPTION] file_id=' . ($file_id ?? 'null') . ' error=' . $th->getMessage());
if (!empty($file_id)) {
$this->markAsFailed($file_id, 'System error contact admin');
@ -1968,30 +2003,60 @@ class TicketServiceController extends AdminController
{
$file_id = isset($params['file_id']) ? (int) $params['file_id'] : null;
$fileData = null;
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_START] file_id=' . ($file_id ?? 'null'));
try {
$resolved = $this->resolveTpaClaimDumpFile($file_id);
// Job 2 reads staging tables only — physical Excel is not required.
$resolved = $this->resolveTpaClaimDumpFile($file_id, false);
if ($resolved['status'] === false) {
$message = $resolved['message'] ?? 'Unable to resolve claim dump file';
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_RESOLVE_FAILED] file_id=' . ($file_id ?? 'null') . ' message=' . $message);
if (!empty($file_id)) {
$this->markAsFailed($file_id, $message);
}
return [
'status' => false,
'message' => $resolved['message'] ?? 'Unable to resolve claim dump file',
'message' => $message,
];
}
$fileData = $resolved['fileData'];
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_RESOLVED] file_id=' . $file_id . ' tpa_id=' . ($fileData['tpa_id'] ?? 'null'));
// Atomic lock: skip if another worker is already processing this file.
$db = \Config\Database::connect();
$db->table('claim_dump_files')
->where('id', $file_id)
->where('status !=', 'processing')
->update([
'status' => 'processing',
'updated_at' => date('Y-m-d H:i:s'),
]);
if ($db->affectedRows() === 0) {
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_LOCK_SKIP] file_id=' . $file_id);
return [
'status' => true,
'message' => 'Ticket master import already in progress for this file.',
];
}
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_LOCK_ACQUIRED] file_id=' . $file_id);
$handler = TpaClaimsImportFactory::make($fileData['tpa_id'] ?? 0);
$result = $handler->runTicketMasterInsert($params);
if (!empty($result['status']) && $result['status'] === true) {
// Success: Update the status to success
// Success (including rejection-only): dump staging is kept for error export / retry.
$this->claimDumpFileModel->update($file_id, [
'status' => 'success',
'reason' => null,
]);
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_SUCCESS] file_id=' . $file_id . ' message=' . ($result['message'] ?? ''));
} else {
// Logic failure: The runTicketMasterInsert returned status false
// Job 2 failed but Job 1 dump rows are preserved for retry.
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_FAILED] file_id=' . $file_id . ' message=' . ($result['message'] ?? 'unknown'));
$this->markAsFailed(
$file_id,
$result['message'] ?? 'System error contact admin',
@ -2003,6 +2068,7 @@ class TicketServiceController extends AdminController
} catch (\Throwable $th) {
// Log the full error
$this->myLogger->logme("error", 'TICKET_MASTER_CLAIM_IMPORTER_JOB :' . $th->getMessage() . ' at line ' . $th->getLine());
log_message('error', '[CLAIM_DUMP][CTRL_JOB2_EXCEPTION] file_id=' . ($file_id ?? 'null') . ' error=' . $th->getMessage() . ' line=' . $th->getLine());
// CRITICAL: Even if the code crashes, try to mark the file as failed
if ($file_id) {

View File

@ -108,10 +108,8 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
'rejection_category' => 'denial_reason',
// Misc
'diagnosis' => 'claim_description',
'healthcard_id' => 'tpa_no',
'claim_type' => 'tpa_claim_type',
'diagnosis' => 'tpa_ailments',
];
@ -211,7 +209,7 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_abhi');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -235,7 +233,7 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
$dbColumn = $map['db_column'];
$value = $row[$excelColumn] ?? null;
$item[$dbColumn] = trim($value);
$item[$dbColumn] = $value === null ? null : trim((string) $value);
}
foreach ($this->dateColumns as $key => $value) {
@ -296,10 +294,20 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
'doa' => change_date_format($row['doa'] ?? '') ?? null,
'emp_code' => $row['member_code'] ?? null,
'claim_amount' => $row['claimed_amount'] ?? null,
'tpa_no' => $row['healthcard_id'] ?? null
'tpa_no' => $row['healthcard_id'] ?? null,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['claim_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -328,8 +336,8 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
if (!empty($employee_data['insured_emp_id'])) {
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;
@ -355,7 +363,10 @@ class AbhiClaimImportService extends BaseTpaClaimImportService
$item['claim_dump_ref_id'] = $row['id'];
$item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null;
$item['created_by'] = $file_data['created_by'] ?? null;
$item['claim_type'] = isset($row['mainclaim_prepost_type']) && !empty($row['mainclaim_prepost_type']) && strtolower($row['mainclaim_prepost_type']) == 'normal' ? 1 : 3;
$item['claim_description'] = $row['diagnosis'] ?? null;
$item['tpa_ailments'] = $row['diagnosis'] ?? null;
$claimTypeRaw = strtolower(trim((string) ($row['claim_type'] ?? '')));
$item['claim_type'] = (str_contains($claimTypeRaw, 'pre') || str_contains($claimTypeRaw, 'post')) ? 3 : 1;
$item['priority'] = 1;
$item['mode_of_intimation'] = 5;
$item['ticket_type_id'] = 1;

View File

@ -44,60 +44,154 @@ abstract class BaseTpaClaimImportService
];
}
/**
* Structured claim-dump process logging. Search logs with: [CLAIM_DUMP]
* Uses error level so entries appear under production logger threshold.
*/
protected function logClaimDump(string $level, string $step, array $context = []): void
{
$parts = [];
foreach ($context as $key => $value) {
if (is_bool($value)) {
$parts[] = $key . '=' . ($value ? 'true' : 'false');
} elseif (is_scalar($value) || $value === null) {
$parts[] = $key . '=' . ($value === null ? 'null' : $value);
} else {
$parts[] = $key . '=' . json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
}
$suffix = $parts === [] ? '' : ' ' . implode(' ', $parts);
// Keep severity in the message; always write as error for production visibility.
log_message('error', '[CLAIM_DUMP][' . strtoupper($level) . '][' . $step . ']' . $suffix);
}
/**
* First JOB for insert TPA wise Bulk Upload
*/
public function runTpaClaimDumpInsert(string $filePath, int $fileId): array
{
$this->logClaimDump('info', 'JOB1_START', [
'file_id' => $fileId,
'file_path' => $filePath,
]);
// 1. Start Transaction
$this->db->transBegin();
try {
$fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
if (empty($fileData)) {
$this->logClaimDump('error', 'JOB1_FILE_NOT_FOUND', ['file_id' => $fileId]);
return $this->failTpaClaimDumpInsert($fileId, 'Claim dump file record not found');
}
$client_policy_data = $this->clientPolicyModel->where('id', $fileData['client_policy_id'])->first();
if (empty($client_policy_data)) {
$this->logClaimDump('error', 'JOB1_POLICY_NOT_FOUND', [
'file_id' => $fileId,
'client_policy_id' => $fileData['client_policy_id'] ?? null,
]);
return $this->failTpaClaimDumpInsert($fileId, 'Client policy not found');
}
$this->logClaimDump('info', 'JOB1_FILE_LOADED', [
'file_id' => $fileId,
'tpa_id' => $fileData['tpa_id'] ?? null,
'client_id' => $fileData['client_id'] ?? null,
'client_policy_id' => $fileData['client_policy_id'] ?? null,
'policy_no' => $client_policy_data['policy_no'] ?? null,
]);
// Determine sheet name logic...
if (env('FHPL_PRIMARY_KEY_CONSTANT') == $fileData['tpa_id']) {
$rows = $this->readExcelBySheetName($filePath, 'Claims&Preauth');
$this->logClaimDump('info', 'JOB1_EXCEL_READ', ['file_id' => $fileId, 'sheet' => 'Claims&Preauth', 'rows' => count($rows)]);
} else if (env('R_CARE_PRIMARY_KEY_CONSTANT') == $fileData['tpa_id']) {
$rows = $this->readExcelBySheetName($filePath, 'CL');
$this->logClaimDump('info', 'JOB1_EXCEL_READ', ['file_id' => $fileId, 'sheet' => 'CL', 'rows' => count($rows)]);
} else {
$rows = $this->readExcel($filePath);
$this->logClaimDump('info', 'JOB1_EXCEL_READ', ['file_id' => $fileId, 'sheet' => 'active', 'rows' => count($rows)]);
}
if (empty($rows)) {
$this->logClaimDump('error', 'JOB1_EMPTY_EXCEL', ['file_id' => $fileId]);
return $this->failTpaClaimDumpInsert($fileId, 'Excel file contains no data or wrong file upload');
}
if ($this->fileHasStagingRows($fileId)) {
$this->db->transRollback();
$this->logClaimDump('info', 'JOB1_STAGING_EXISTS_SKIP', ['file_id' => $fileId]);
return [
'status' => true,
'message' => 'Claim dump staging already exists for this file; skipped re-insert.',
];
}
if(isset($this->policyNumberMapping[$fileData['tpa_id']]) && !empty($this->policyNumberMapping[$fileData['tpa_id']])){
$policy_number_column = $this->policyNumberMapping[$fileData['tpa_id']];
}else{
$policy_number_column = 'policy_no';
}
if($client_policy_data['policy_no'] != ($rows[0][$policy_number_column] ?? '')){
return $this->failTpaClaimDumpInsert($fileId, 'Policy number mismatch in the file and in the system');
$expectedPolicy = trim((string) ($client_policy_data['policy_no'] ?? ''));
foreach ($rows as $index => $row) {
$filePolicy = trim((string) ($row[$policy_number_column] ?? ''));
if ($filePolicy === '' || $filePolicy !== $expectedPolicy) {
$rowNumber = $index + 2; // header is row 1
$this->logClaimDump('error', 'JOB1_POLICY_MISMATCH', [
'file_id' => $fileId,
'excel_row' => $rowNumber,
'expected' => $expectedPolicy,
'found' => $filePolicy,
'column' => $policy_number_column,
]);
return $this->failTpaClaimDumpInsert(
$fileId,
"Policy number mismatch at Excel row {$rowNumber}. Expected '{$expectedPolicy}', found '{$filePolicy}'."
);
}
}
$this->logClaimDump('info', 'JOB1_POLICY_VALIDATED', [
'file_id' => $fileId,
'policy_no' => $expectedPolicy,
'rows_checked' => count($rows),
]);
$tpaInsertData = $this->mapTPAData($rows, $fileId);
$this->logClaimDump('info', 'JOB1_MAPPED', [
'file_id' => $fileId,
'mapped_count' => count($tpaInsertData),
]);
if (empty($tpaInsertData)) {
$this->logClaimDump('error', 'JOB1_NO_MAPPED_ROWS', ['file_id' => $fileId]);
return $this->failTpaClaimDumpInsert($fileId, 'These records already exist in the system.');
}
$return_res = $this->bulkInsertTPATable($tpaInsertData);
if ($return_res !== true) {
$this->logClaimDump('error', 'JOB1_BULK_INSERT_FAILED', ['file_id' => $fileId]);
return $this->failTpaClaimDumpInsert($fileId, 'TPA Import bulk insert failed');
}
// 2. Commit if everything is fine
$this->db->transCommit();
$this->logClaimDump('info', 'JOB1_SUCCESS', [
'file_id' => $fileId,
'record_count' => count($tpaInsertData),
]);
return ['status' => true, 'message' => 'File uploaded successfully', 'record_count' => count($tpaInsertData)];
} catch (\Throwable $e) {
$this->logClaimDump('error', 'JOB1_EXCEPTION', [
'file_id' => $fileId,
'error' => $e->getMessage(),
'line' => $e->getLine(),
'file' => $e->getFile(),
]);
return $this->failTpaClaimDumpInsert($fileId, 'System error : ' . $e->getMessage());
}
}
@ -107,29 +201,57 @@ abstract class BaseTpaClaimImportService
*/
public function runTicketMasterInsert(array $params): array
{
$file_id = (int) ($params['file_id'] ?? 0);
$this->logClaimDump('info', 'JOB2_START', ['file_id' => $file_id]);
// Outside the Job 2 transaction so a later rollback cannot restore orphan links.
$orphansCleared = $this->clearOrphanDumpTicketLinks($file_id);
if ($orphansCleared > 0) {
$this->logClaimDump('info', 'JOB2_ORPHANS_CLEARED', [
'file_id' => $file_id,
'count' => $orphansCleared,
]);
}
// 1. Start manual transaction
$this->db->transBegin();
try {
$file_id = $params['file_id'];
$ticketMasterData = $this->mapClaimMasterData($file_id);
$this->logClaimDump('info', 'JOB2_MAPPED', [
'file_id' => $file_id,
'mapping_status' => !empty($ticketMasterData['status']),
'inserts' => count($ticketMasterData['mapped_array'] ?? []),
'status_updates' => count($ticketMasterData['status_update_array'] ?? []),
'rejects_or_links' => count($ticketMasterData['rejected_reason_array'] ?? []),
'already_processed' => !empty($ticketMasterData['already_processed']),
'message' => $ticketMasterData['message'] ?? null,
]);
// Check if mapping failed
if (!$ticketMasterData['status']) {
// Already-processed dump rows (ticket_id linked) must not be deleted as a "failure".
if (!empty($ticketMasterData['already_processed'])) {
$this->db->transCommit();
$this->logClaimDump('info', 'JOB2_ALREADY_PROCESSED', ['file_id' => $file_id]);
return [
'status' => true,
'message' => $ticketMasterData['message'] ?? 'Claim dump already processed for this file.',
];
}
$this->rollbackAndCleanupClaimDumpData($file_id);
// Job 1 staging is already committed — roll back Job 2 only; keep dump for retry/export.
$this->db->transRollback();
$this->logClaimDump('error', 'JOB2_MAPPING_FAILED', [
'file_id' => $file_id,
'message' => $ticketMasterData['message'] ?? 'mapping failed',
]);
return $ticketMasterData;
}
$message = '';
$message = $orphansCleared > 0
? "Cleared {$orphansCleared} orphan dump ticket_id link(s). "
: '';
$hasExecutedTask = false;
$hasInserts = !empty($ticketMasterData['mapped_array']);
$hasExistingTicketUpdates = !empty($ticketMasterData['status_update_array']);
@ -138,6 +260,10 @@ abstract class BaseTpaClaimImportService
// Process Mapped Data
if ($hasInserts) {
$this->logClaimDump('info', 'JOB2_INSERT_TICKETS_START', [
'file_id' => $file_id,
'count' => count($ticketMasterData['mapped_array']),
]);
$insert_res = $this->importClaimMaster($ticketMasterData['mapped_array']);
if (!$insert_res) {
return $this->failTicketMasterInsert($file_id, 'Ticket Master Claim bulk insert failed');
@ -146,26 +272,45 @@ abstract class BaseTpaClaimImportService
if (!$this->updateTicketIdInTPATable($file_id)) {
return $this->failTicketMasterInsert($file_id, 'Updating ticket_id in TPA table failed');
}
if (!$this->recordHistoryForNewDumpTickets($ticketMasterData['mapped_array'], $file_id)) {
return $this->failTicketMasterInsert($file_id, 'Recording ticket history for dump claims failed');
}
$message .= 'Ticket Master Claim bulk insert success. ';
$hasExecutedTask = true;
$this->logClaimDump('info', 'JOB2_INSERT_TICKETS_DONE', ['file_id' => $file_id]);
}
// Update existing tickets: status and/or missing claim_dump_ref_id
if ($hasExistingTicketUpdates) {
$this->logClaimDump('info', 'JOB2_UPDATE_EXISTING_START', [
'file_id' => $file_id,
'count' => count($ticketMasterData['status_update_array']),
]);
$update_status_res = $this->updateExistingTicketStatuses($ticketMasterData['status_update_array']);
if (!$update_status_res) {
return $this->failTicketMasterInsert($file_id, 'Updating existing tickets failed');
}
if (!$this->recordHistoryForExistingTicketUpdates($ticketMasterData['status_update_array'])) {
return $this->failTicketMasterInsert($file_id, 'Recording ticket history for status updates failed');
}
$message .= 'Existing ticket updated successfully. ';
$hasExecutedTask = true;
$this->logClaimDump('info', 'JOB2_UPDATE_EXISTING_DONE', ['file_id' => $file_id]);
}
// Process Rejected Reasons (also writes ticket_id onto TPA dump rows for existing tickets)
// Process dump link / reject updates (ticket_id and/or master_reject_reason)
if ($hasRejectedReasons) {
$update_res = $this->updateTicketMasterRejectedReasonInTPATable($ticketMasterData['rejected_reason_array']);
if (!$update_res) {
$this->logClaimDump('info', 'JOB2_DUMP_LINK_REJECT_START', [
'file_id' => $file_id,
'count' => count($ticketMasterData['rejected_reason_array']),
'has_links' => $hasExistingTicketLinks,
]);
$update_res = $this->updateTicketMasterRejectedReasonInTPATable(
$this->normalizeDumpLinkOrRejectUpdates($ticketMasterData['rejected_reason_array'])
);
if ($update_res === false) {
return $this->failTicketMasterInsert($file_id, 'Updating rejected reasons failed');
}
@ -177,6 +322,7 @@ abstract class BaseTpaClaimImportService
$message .= 'Ticket Master Claim rejected reason updated successfully. ';
}
$hasExecutedTask = true;
$this->logClaimDump('info', 'JOB2_DUMP_LINK_REJECT_DONE', ['file_id' => $file_id]);
}
// If nothing was processed but no error occurred
@ -187,15 +333,29 @@ abstract class BaseTpaClaimImportService
// 2. Commit the transaction
$this->db->transCommit();
$status = $hasInserts || $hasExistingTicketUpdates || $hasExistingTicketLinks;
if (!$status) {
$this->cleanupClaimDumpData($file_id);
}
// Rejection-only runs are still a completed Job 2 — keep dump rows for error export.
$status = $hasInserts || $hasExistingTicketUpdates || $hasExistingTicketLinks || $hasRejectedReasons;
$this->logClaimDump('info', 'JOB2_SUCCESS', [
'file_id' => $file_id,
'status' => $status,
'has_inserts' => $hasInserts,
'has_existing_updates' => $hasExistingTicketUpdates,
'has_links' => $hasExistingTicketLinks,
'has_rejects' => $hasRejectedReasons,
'message' => trim($message),
]);
return ['status' => $status, 'message' => trim($message)];
} catch (\Throwable $th) {
$fileId = (int) ($params['file_id'] ?? 0);
$this->logClaimDump('error', 'JOB2_EXCEPTION', [
'file_id' => $fileId,
'error' => $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
]);
return $this->failTicketMasterInsert(
$fileId,
'System error during Ticket Master Insert: ' . $th->getMessage()
@ -204,7 +364,220 @@ abstract class BaseTpaClaimImportService
}
/**
* Remove TPA staging rows and ticket_master rows created for a failed claim dump upload.
* Clear dump ticket_id (and reject reason) when the linked ticket is missing or inactive.
* Makes orphan rows pending again so Job 2 can recreate tickets.
*
* @return int Number of dump rows reset
*/
protected function clearOrphanDumpTicketLinks(int $fileId): int
{
if ($fileId <= 0) {
return 0;
}
$fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
if (empty($fileData)) {
return 0;
}
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
$tpaTable = $this->tpaTableMapping[$tpaId] ?? null;
if ($tpaTable === null || !in_array($tpaTable, $this->tpaTableMapping, true)) {
return 0;
}
$sql = "UPDATE `{$tpaTable}` d
LEFT JOIN ticket_master tm ON tm.id = d.ticket_id AND tm.is_active = 1
SET d.ticket_id = NULL,
d.master_reject_reason = NULL
WHERE d.file_id = ?
AND d.is_active = 1
AND d.ticket_id IS NOT NULL
AND tm.id IS NULL";
$this->db->query($sql, [$fileId]);
return $this->db->affectedRows();
}
/**
* Clear orphan dump ticket_id links (ticket missing/inactive), then run Job 2
* so those dump rows are inserted into ticket_master again.
*/
public function recreateOrphanDumpTickets(int $fileId): array
{
$this->logClaimDump('info', 'RECREATE_ORPHANS_START', ['file_id' => $fileId]);
if ($fileId <= 0) {
return ['status' => false, 'message' => 'Invalid file id'];
}
$fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
if (empty($fileData)) {
return ['status' => false, 'message' => 'claim_dump_files row not found'];
}
$cleared = $this->clearOrphanDumpTicketLinks($fileId);
$this->logClaimDump('info', 'RECREATE_ORPHANS_CLEARED', [
'file_id' => $fileId,
'cleared' => $cleared,
]);
// Job 2 also clears orphans; running insert/link for now-pending dump rows.
$result = $this->runTicketMasterInsert(['file_id' => $fileId]);
$result['orphans_cleared'] = $cleared;
$this->logClaimDump(
!empty($result['status']) ? 'info' : 'error',
'RECREATE_ORPHANS_DONE',
[
'file_id' => $fileId,
'cleared' => $cleared,
'status' => !empty($result['status']),
'message' => $result['message'] ?? null,
]
);
return $result;
}
/**
* Soft-delete dump staging + tickets created by this upload (admin truncate / undo).
* Does not deactivate existing tickets that were only linked from this dump (different file_id).
*/
public function softTruncateClaimDump(int $fileId): array
{
$this->logClaimDump('info', 'TRUNCATE_START', ['file_id' => $fileId]);
if ($fileId <= 0) {
$this->logClaimDump('error', 'TRUNCATE_INVALID_FILE_ID', ['file_id' => $fileId]);
return ['status' => false, 'message' => 'Invalid file id'];
}
$fileData = $this->claimDumpFileModel->where('id', $fileId)->where('is_active', 1)->first();
if (empty($fileData)) {
$this->logClaimDump('error', 'TRUNCATE_FILE_NOT_FOUND', ['file_id' => $fileId]);
return ['status' => false, 'message' => 'File not found or already truncated'];
}
if (($fileData['status'] ?? '') === 'processing') {
$this->logClaimDump('error', 'TRUNCATE_BLOCKED_PROCESSING', ['file_id' => $fileId]);
return ['status' => false, 'message' => 'Cannot truncate while import is processing'];
}
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
$tpaTable = $this->tpaTableMapping[$tpaId] ?? null;
if ($tpaTable === null || !in_array($tpaTable, $this->tpaTableMapping, true)) {
$this->logClaimDump('error', 'TRUNCATE_UNSUPPORTED_TPA', [
'file_id' => $fileId,
'tpa_id' => $tpaId,
]);
return ['status' => false, 'message' => 'Unsupported TPA for truncate'];
}
$this->db->transBegin();
try {
$ticketIds = $this->db->table('ticket_master')
->select('id, claim_status_id, created_by')
->where('file_id', $fileId)
->where('is_active', 1)
->get()
->getResultArray();
$this->logClaimDump('info', 'TRUNCATE_TICKETS_FOUND', [
'file_id' => $fileId,
'tpa_table' => $tpaTable,
'ticket_count' => count($ticketIds),
]);
// Soft-deactivate dump rows only — keep ticket_id for audit/traceability.
$this->db->table($tpaTable)
->where('file_id', $fileId)
->where('is_active', 1)
->set(['is_active' => 0])
->update();
$dumpAffected = $this->db->affectedRows();
$this->db->table('ticket_master')
->where('file_id', $fileId)
->where('is_active', 1)
->set(['is_active' => 0])
->update();
$ticketAffected = $this->db->affectedRows();
// Avoid Model callbacks that call get_session_userid() (breaks CLI / after output).
$actorId = null;
try {
if (function_exists('get_session_userid')) {
$actorId = get_session_userid();
}
} catch (\Throwable $e) {
$actorId = null;
}
if ($ticketIds !== []) {
$historyRows = [];
$now = date('Y-m-d H:i:s');
foreach ($ticketIds as $ticket) {
$historyRows[] = [
'ticket_id' => $ticket['id'],
'field_name' => 'claim_status_id',
'display_name' => 'Claim Removed (TPA Dump Truncate)',
'old_value' => $ticket['claim_status_id'],
'new_value' => null,
'created_by' => $ticket['created_by'] ?? $actorId,
'created_at' => $now,
'is_active' => 1,
];
}
if ($this->db->table('ticket_history')->insertBatch($historyRows) === false) {
throw new RuntimeException('Failed to write truncate ticket history');
}
}
$this->db->table('claim_dump_files')
->where('id', $fileId)
->update([
'is_active' => 0,
'status' => 'truncated',
'updated_by' => $actorId,
'updated_at' => date('Y-m-d H:i:s'),
]);
if ($this->db->transStatus() === false) {
$this->db->transRollback();
$this->logClaimDump('error', 'TRUNCATE_DB_FAILED', ['file_id' => $fileId]);
return ['status' => false, 'message' => 'Truncate failed'];
}
$this->db->transCommit();
$this->logClaimDump('info', 'TRUNCATE_SUCCESS', [
'file_id' => $fileId,
'dump_rows_deactivated' => $dumpAffected,
'tickets_deactivated' => $ticketAffected,
]);
return [
'status' => true,
'message' => 'Claim dump truncated successfully',
'tickets_deactivated' => count($ticketIds),
];
} catch (\Throwable $e) {
$this->db->transRollback();
$this->logClaimDump('error', 'TRUNCATE_EXCEPTION', [
'file_id' => $fileId,
'error' => $e->getMessage(),
]);
return ['status' => false, 'message' => 'Truncate failed: ' . $e->getMessage()];
}
}
/**
* Soft-delete TPA staging rows and ticket_master rows for a failed Job 1 dump upload.
* Never touches tickets unless the TPA dump table is resolved (avoids orphan ticket_id links).
*/
protected function cleanupClaimDumpData(int $fileId): void
{
@ -214,34 +587,74 @@ abstract class BaseTpaClaimImportService
$fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
if (empty($fileData)) {
$this->logClaimDump('warning', 'CLEANUP_FILE_NOT_FOUND', ['file_id' => $fileId]);
return;
}
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
$tpaTable = $this->tpaTableMapping[$tpaId] ?? null;
if ($tpaTable !== null) {
$this->db->table($tpaTable)->where('file_id', $fileId)->delete();
if ($tpaTable === null) {
$this->logClaimDump('error', 'CLEANUP_UNKNOWN_TPA', [
'file_id' => $fileId,
'tpa_id' => $tpaId,
]);
return;
}
$this->db->table('ticket_master')->where('file_id', $fileId)->delete();
$this->db->table($tpaTable)
->where('file_id', $fileId)
->where('is_active', 1)
->set([
'is_active' => 0,
'ticket_id' => null,
])
->update();
$dumpAffected = $this->db->affectedRows();
$this->db->table('ticket_master')
->where('file_id', $fileId)
->where('is_active', 1)
->set(['is_active' => 0])
->update();
$ticketAffected = $this->db->affectedRows();
$this->logClaimDump('info', 'CLEANUP_DONE', [
'file_id' => $fileId,
'tpa_table' => $tpaTable,
'dump_rows_deactivated' => $dumpAffected,
'tickets_deactivated' => $ticketAffected,
]);
}
protected function rollbackAndCleanupClaimDumpData(int $fileId): void
{
$this->logClaimDump('warning', 'JOB1_ROLLBACK_AND_CLEANUP', ['file_id' => $fileId]);
$this->db->transRollback();
$this->cleanupClaimDumpData($fileId);
}
protected function failTpaClaimDumpInsert(int $fileId, string $message): array
{
$this->logClaimDump('error', 'JOB1_FAIL', [
'file_id' => $fileId,
'message' => $message,
]);
$this->rollbackAndCleanupClaimDumpData($fileId);
return ['status' => false, 'message' => $message];
}
/**
* Job 2 failure: roll back this transaction only.
* Do not delete committed Job 1 dump rows so the file can be retried.
*/
protected function failTicketMasterInsert(int $fileId, string $message): array
{
$this->rollbackAndCleanupClaimDumpData($fileId);
$this->logClaimDump('error', 'JOB2_FAIL', [
'file_id' => $fileId,
'message' => $message,
]);
$this->db->transRollback();
return ['status' => false, 'message' => $message];
}
@ -275,8 +688,8 @@ abstract class BaseTpaClaimImportService
foreach ($rows as $row) {
if(check_row_is_empty_or_null($row)){
break;
if (check_row_is_empty_or_null($row)) {
continue;
}
$item = [];
@ -354,17 +767,26 @@ abstract class BaseTpaClaimImportService
/**
* Fetch existing ticket_master record matching claim identity keys.
* Scoped by client_id / client_policy_id when provided to avoid cross-tenant matches.
*/
protected function getExistingTicketMasterClaim(array $param): ?array
{
$ticketMaster = new TicketMasterModel();
$ticket = $ticketMaster
$builder = $ticketMaster
->where('doa', $param['doa'])
->where('tpa_no', $param['tpa_no'])
->where('claim_amount', $param['claim_amount'])
->where('emp_code', $param['emp_code'])
->where('is_active', 1)
->first();
->where('is_active', 1);
if (!empty($param['client_id'])) {
$builder->where('client_id', $param['client_id']);
}
if (!empty($param['client_policy_id'])) {
$builder->where('client_policy_id', $param['client_policy_id']);
}
$ticket = $builder->orderBy('id', 'DESC')->first();
return $ticket ?: null;
}
@ -378,8 +800,98 @@ abstract class BaseTpaClaimImportService
return true;
}
$payload = [];
foreach ($statusUpdates as $update) {
$row = $update;
unset($row['old_claim_status_id']);
$payload[] = $row;
}
$ticketMasterModel = new TicketMasterModel();
return $ticketMasterModel->updateBatch($statusUpdates, 'id') !== false;
return $ticketMasterModel->updateBatch($payload, 'id') !== false;
}
/**
* Write ticket_history for newly created dump tickets (by claim_dump_ref_id + file_id).
*/
protected function recordHistoryForNewDumpTickets(array $mappedTickets, int $fileId): bool
{
if (empty($mappedTickets) || $fileId <= 0) {
return true;
}
$refIds = [];
foreach ($mappedTickets as $row) {
if (!empty($row['claim_dump_ref_id'])) {
$refIds[] = (int) $row['claim_dump_ref_id'];
}
}
$refIds = array_values(array_unique($refIds));
if ($refIds === []) {
return true;
}
$tickets = $this->db->table('ticket_master')
->select('id, claim_status_id, created_by')
->where('file_id', $fileId)
->whereIn('claim_dump_ref_id', $refIds)
->where('is_active', 1)
->get()
->getResultArray();
if ($tickets === []) {
return true;
}
$historyRows = [];
foreach ($tickets as $ticket) {
$historyRows[] = [
'ticket_id' => $ticket['id'],
'field_name' => 'claim_status_id',
'display_name' => 'Claim Created (TPA Dump)',
'old_value' => null,
'new_value' => $ticket['claim_status_id'],
'created_by' => $ticket['created_by'] ?? null,
'is_active' => 1,
];
}
$historyModel = new \App\Models\TicketHistoryModel();
return $historyModel->insertBatch($historyRows) !== false;
}
/**
* Write ticket_history when dump import updates an existing ticket's claim_status_id.
*/
protected function recordHistoryForExistingTicketUpdates(array $statusUpdates): bool
{
if (empty($statusUpdates)) {
return true;
}
$historyRows = [];
foreach ($statusUpdates as $update) {
if (empty($update['id']) || !array_key_exists('claim_status_id', $update)) {
continue;
}
$historyRows[] = [
'ticket_id' => $update['id'],
'field_name' => 'claim_status_id',
'display_name' => 'Claim Status Updated (TPA Dump)',
'old_value' => $update['old_claim_status_id'] ?? null,
'new_value' => $update['claim_status_id'],
'created_by' => $update['updated_by'] ?? null,
'is_active' => 1,
];
}
if ($historyRows === []) {
return true;
}
$historyModel = new \App\Models\TicketHistoryModel();
return $historyModel->insertBatch($historyRows) !== false;
}
/**
@ -396,12 +908,38 @@ abstract class BaseTpaClaimImportService
return false;
}
/**
* Ensure link and reject dump updates share the same columns for updateBatch.
*/
protected function normalizeDumpLinkOrRejectUpdates(array $rows): array
{
$normalized = [];
foreach ($rows as $row) {
if (empty($row['id'])) {
continue;
}
$normalized[] = [
'id' => $row['id'],
'ticket_id' => $row['ticket_id'] ?? null,
'master_reject_reason' => array_key_exists('master_reject_reason', $row)
? $row['master_reject_reason']
: null,
];
}
return $normalized;
}
/**
* If ticket already exists:
* - update claim_status_id when changed
* - set claim_dump_ref_id on ticket_master when null
* - set ticket_id on TPA dump row when null (via rejected_reason_array)
*
* Link updates do not write master_reject_reason (that field is for true rejects only).
*
* Returns true when the row was handled as an existing ticket (caller should continue).
*/
protected function handleExistingTicketStatusUpdate(
@ -421,33 +959,24 @@ abstract class BaseTpaClaimImportService
$claimDumpRefIdMissing = empty($existingTicket['claim_dump_ref_id']);
$ticketUpdate = ['id' => $ticketId];
$reasons = [];
if ($statusChanged) {
$ticketUpdate['claim_status_id'] = $newStatusId;
$reasons[] = 'status updated';
$ticketUpdate['old_claim_status_id'] = $currentStatusId;
}
if ($claimDumpRefIdMissing) {
$ticketUpdate['claim_dump_ref_id'] = $dumpRowId;
$reasons[] = 'claim_dump_ref_id linked';
}
if (count($ticketUpdate) > 1) {
$statusUpdateArray[] = $ticketUpdate;
}
if ($statusChanged || $claimDumpRefIdMissing) {
$reason = 'Existing claim ' . implode(' and ', $reasons) . '.';
} else {
$reason = 'This claim already exists in our system.';
}
// Dump rows reaching here already have ticket_id NULL; link them to the existing ticket.
$rejectedReasonArray[] = [
'id' => $dumpRowId,
'master_reject_reason' => $reason,
'ticket_id' => $ticketId,
'id' => $dumpRowId,
'ticket_id' => $ticketId,
];
return true;
@ -481,7 +1010,8 @@ abstract class BaseTpaClaimImportService
/**
* Response when no pending dump rows are left for ticket_master mapping.
* Marks already-processed files so cleanup does not delete linked dump rows.
* Orphan ticket_id links are cleared before mapping (see clearOrphanDumpTicketLinks).
* Remaining linked/rejected rows are treated as already processed (not a wipe failure).
*/
protected function emptyClaimMasterMappingResponse(string $table, int $fileId): array
{
@ -506,19 +1036,36 @@ abstract class BaseTpaClaimImportService
}
/**
* Dublicate check in the TPA specific table records
* Resolve employee (self) and insured member for a claim dump row.
*/
public function getEmployeeDetails(int $client_id, int $client_policy_id, string $emp_code, string $relation): array
{
public function getEmployeeDetails(int $client_id, int $client_policy_id, ?string $emp_code, ?string $relation = null): array
{
if ($emp_code === null || trim($emp_code) === '') {
return [];
}
$emp_code = trim($emp_code);
$relation = $relation !== null ? strtolower(trim($relation)) : '';
$client_policy_data = $this->clientPolicyModel->where('id', $client_policy_id)->first();
if(!empty($client_policy_data)){
if(!empty($client_policy_data['base_policy'])){
if (!empty($client_policy_data)) {
if (!empty($client_policy_data['base_policy'])) {
$client_policy_id = $client_policy_data['base_policy'];
}
}
$EmployeeModel = new EmployeeModel();
// When relation is unknown, still resolve the employee (self); insured join is skipped.
$insuredJoin = "insured.emp_code = employees.emp_code
AND insured.client_id = employees.client_id";
if ($relation !== '') {
$insuredJoin .= " AND LOWER(insured.relationship) = " . $EmployeeModel->db->escape($relation);
} else {
// No usable relation — do not match any insured row.
$insuredJoin .= " AND 1 = 0";
}
$employeeData = $EmployeeModel
->select([
'employees.id AS id',
@ -537,9 +1084,7 @@ abstract class BaseTpaClaimImportService
)
->join(
'employees AS insured',
"insured.emp_code = employees.emp_code
AND insured.client_id = employees.client_id
AND LOWER(insured.relationship) = " . $EmployeeModel->db->escape(strtolower($relation)),
$insuredJoin,
'left'
)
->where('employees.is_active', 1)
@ -556,13 +1101,75 @@ abstract class BaseTpaClaimImportService
public function checkStatusMapping($statusArray, $statusString)
{
$statusString = trim((string) $statusString);
if ($statusString === '') {
return null;
}
foreach ($statusArray as $key => $value) {
if (strtolower($statusString) == strtolower($key) || strtolower($statusString) == strtolower(trim($key))) {
if (strtolower($statusString) === strtolower(trim((string) $key))) {
return $value;
}
}
return 61;
return null;
}
/**
* Map dump status text to claim_status_id, or quarantine the dump row when unknown/empty.
*/
protected function resolveClaimStatusId(
array $statusMapping,
?string $statusString,
int $dumpRowId,
array &$rejectedReasonArray
): ?int {
$statusString = trim((string) ($statusString ?? ''));
if ($statusString === '') {
$rejectedReasonArray[] = [
'id' => $dumpRowId,
'master_reject_reason' => 'Claim status is empty in the dump file.',
];
return null;
}
$statusId = $this->checkStatusMapping($statusMapping, $statusString);
if ($statusId === null) {
$rejectedReasonArray[] = [
'id' => $dumpRowId,
'master_reject_reason' => 'Unknown claim status: ' . $statusString,
];
return null;
}
return (int) $statusId;
}
/**
* True when this file already has staging rows in its TPA dump table.
*/
protected function fileHasStagingRows(int $fileId): bool
{
if ($fileId <= 0) {
return false;
}
$fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
if (empty($fileData)) {
return false;
}
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
$tpaTable = $this->tpaTableMapping[$tpaId] ?? null;
if ($tpaTable === null || !in_array($tpaTable, $this->tpaTableMapping, true)) {
return false;
}
return $this->db->table($tpaTable)
->where('file_id', $fileId)
->where('is_active', 1)
->countAllResults() > 0;
}
/**

View File

@ -155,7 +155,6 @@ class FhplClaimImportService extends BaseTpaClaimImportService
// Amounts
'claim_amount' => 'claim_amount',
'settled_amount' => 'settled_amount',
'disallowed_amount' => 'denial_reason',
'coverage_amount' => 'si_amt',
@ -167,14 +166,12 @@ class FhplClaimImportService extends BaseTpaClaimImportService
'provider_pincode' => 'hospital_pin_code',
// Remarks / Description
'diagnosis' => 'claim_description',
'rejection_remarks' => 'return_remark',
// Payment
'cheque_no' => 'utr_details',
'uhid_no' => 'tpa_no',
'claim_type' => 'tpa_claim_type',
'diagnosis' => 'tpa_ailments',
];
@ -277,7 +274,7 @@ class FhplClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_fhpl');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -301,11 +298,12 @@ class FhplClaimImportService extends BaseTpaClaimImportService
$dbColumn = $map['db_column'];
$value = $row[$excelColumn] ?? null;
$item[$dbColumn] = trim($value);
$item[$dbColumn] = $value === null ? null : trim((string) $value);
}
foreach ($this->dateColumns as $key => $value) {
$item[$value] = change_date_format($item[$value] ?? null, 'd-M-y', 'Y-m-d');
// Auto-detect source format (Excel may be Y-m-d or d-M-y).
$item[$value] = change_date_format($item[$value] ?? null);
}
$item['file_id'] = $file_id ?? null;
@ -362,10 +360,20 @@ class FhplClaimImportService extends BaseTpaClaimImportService
'doa' => change_date_format($row['admission_date'] ?? '') ?? null,
'emp_code' => $row['employee_id'] ?? null,
'claim_amount' => $row['claim_amount'] ?? null,
'tpa_no' => $row['uhid_no'] ?? null
'tpa_no' => $row['uhid_no'] ?? null,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['current_claim_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['current_claim_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -394,8 +402,8 @@ class FhplClaimImportService extends BaseTpaClaimImportService
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
if (!empty($employee_data['insured_emp_id'])) {
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;
@ -421,6 +429,8 @@ class FhplClaimImportService extends BaseTpaClaimImportService
$item['claim_dump_ref_id'] = $row['id'];
$item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null;
$item['created_by'] = $file_data['created_by'] ?? null;
$item['claim_description'] = $row['diagnosis'] ?? null;
$item['tpa_ailments'] = $row['diagnosis'] ?? null;
$item['claim_type'] = 1;
$item['priority'] = 1;
$item['mode_of_intimation'] = 5;

View File

@ -85,7 +85,7 @@ class IciciClaimImportService extends BaseTpaClaimImportService
// Claim
'claim_number' => 'claim_number',
'claimed_amount' => 'claim_amount',
'claim_status' => 'tpa_claim_status',
'updated_status' => 'tpa_claim_status',
// Dates
'doa' => 'doa',
@ -192,7 +192,7 @@ class IciciClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_icici');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -277,10 +277,20 @@ class IciciClaimImportService extends BaseTpaClaimImportService
'doa' => change_date_format($row['doa'] ?? '') ?? null,
'emp_code' => $row['employee_member_id'] ?? null,
'claim_amount' => $row['claimed_amount'] ?? null,
'tpa_no' => $row['uhid'] ?? null
'tpa_no' => $row['uhid'] ?? null,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['updated_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['updated_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -309,8 +319,8 @@ class IciciClaimImportService extends BaseTpaClaimImportService
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
if (!empty($employee_data['insured_emp_id'])) {
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;

View File

@ -119,7 +119,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService
// Policy / Claim identifiers
'claim_id' => 'claim_number',
'event_id' => 'tpa_no',
'event_id' => 'tpa_claim_id',
'claim_pre_auths' => 'tpa_claim_push_reference_no',
// Claim type & status
@ -229,7 +229,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_medi_assist');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -306,14 +306,33 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService
foreach ($tpaClaimDumpData as $row) {
$tpaNo = trim((string) ($row['benef_insurer_id'] ?? ''));
if ($tpaNo === '') {
$tpaNo = trim((string) ($row['benef_maid'] ?? ''));
}
if ($tpaNo === '') {
$tpaNo = trim((string) ($row['event_id'] ?? ''));
}
$tpaNo = $tpaNo !== '' ? $tpaNo : null;
$params = [
'doa' => change_date_format($row['date_of_admission'] ?? '') ?? null,
'emp_code' => $row['pribenef_employee_code'] ?? null,
'claim_amount' => $row['claim_amount'] ?? null,
'tpa_no' => $row['event_id'] ?? null
'tpa_no' => $tpaNo,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['claim_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -342,8 +361,8 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
if (!empty($employee_data['insured_emp_id'])) {
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;
@ -369,6 +388,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService
$item['claim_status_id'] = $newStatusId;
$item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null;
$item['created_by'] = $file_data['created_by'] ?? null;
$item['tpa_no'] = $tpaNo;
$item['claim_type'] = 1;
$item['priority'] = 1;
$item['mode_of_intimation'] = 5;

View File

@ -83,16 +83,13 @@ class RcareClaimImportService extends BaseTpaClaimImportService
'hospital_state' => 'hospital_state',
'hospital_district' => 'hospital_city',
'diagnosis' => 'claim_description',
'diagnosis' => 'tpa_ailments',
// Payment
'cheque_neft_number' => 'utr_details',
'cheque_neft_date' => 'settled_date',
// References
'cl_inward_no' => 'claim_number',
'uhid' => 'tpa_no',
];
protected $statusMapping = [
@ -178,7 +175,7 @@ class RcareClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_reliance');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -262,10 +259,20 @@ class RcareClaimImportService extends BaseTpaClaimImportService
'doa' => change_date_format($row['doa_opd_treatment_from'] ?? '') ?? null,
'emp_code' => $row['employee_member_id'] ?? null,
'claim_amount' => $row['claimed_amount'] ?? null,
'tpa_no' => $row['uhid'] ?? null
'tpa_no' => $row['uhid'] ?? null,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['final_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['final_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -294,8 +301,8 @@ class RcareClaimImportService extends BaseTpaClaimImportService
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
if (!empty($employee_data['insured_emp_id'])) {
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;
@ -321,6 +328,8 @@ class RcareClaimImportService extends BaseTpaClaimImportService
$item['created_by'] = $file_data['created_by'] ?? null;
$item['claim_dump_ref_id'] = $row['id'];
$item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null;
$item['claim_description'] = $row['diagnosis'] ?? null;
$item['tpa_ailments'] = $row['diagnosis'] ?? null;
$item['claim_type'] = 1;
$item['priority'] = 1;
$item['mode_of_intimation'] = 5;

View File

@ -423,7 +423,7 @@ class VidalClaimImportService extends BaseTpaClaimImportService
}
$builder = $this->db->table('claims_dump_vidal');
return $builder->updateBatch($data, 'id');
return $builder->updateBatch($data, 'id') !== false;
}
@ -509,10 +509,20 @@ class VidalClaimImportService extends BaseTpaClaimImportService
'doa' => change_date_format($row['date_of_admission'] ?? '') ?? null,
'emp_code' => $row['employee_number'] ?? null,
'claim_amount' => $row['claim_amount'] ?? null,
'tpa_no' => $row['primary_policy_holder_card_id'] ?? null
'tpa_no' => $row['primary_policy_holder_card_id'] ?? null,
'client_id' => $file_data['client_id'] ?? null,
'client_policy_id' => $file_data['client_policy_id'] ?? null,
];
$newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61;
$newStatusId = $this->resolveClaimStatusId(
$this->statusMapping,
$row['claim_status'] ?? '',
(int) $row['id'],
$rejecetd_reason
);
if ($newStatusId === null) {
continue;
}
$existingTicket = $this->getExistingTicketMasterClaim($params);
if ($this->handleExistingTicketStatusUpdate(
@ -540,9 +550,9 @@ class VidalClaimImportService extends BaseTpaClaimImportService
if (!empty($employee_data)) {
$item['emp_id'] = $employee_data['id'] ?? null;
$item['emp_name'] = $employee_data['name'] ?? null;
$item['emp_mail'] = $employee_data['email_corporate'] ?? null;
$item['emp_mail'] = $employee_data['emp_mail'] ?? null;
$item['emp_code'] = $employee_data['emp_code'] ?? null;
$item['emp_mobile'] = $employee_data['mobile'] ?? null;
$item['emp_mobile'] = $employee_data['emp_mobile'] ?? null;
$item['insured_emp_id'] = $employee_data['insured_emp_id'] ?? null;
$item['insured_name'] = $employee_data['insured_name'] ?? null;
} else {
@ -759,14 +769,14 @@ class VidalClaimImportService extends BaseTpaClaimImportService
}
public function convertRelation(string $relation, string $gender): ?string
public function convertRelation(?string $relation, ?string $gender = null): ?string
{
if (empty($relation)) {
return null;
}
$relation = strtolower($relation);
$gender = strtolower($gender);
$gender = strtolower((string) ($gender ?? ''));
if ($relation == 'self') {
return $relation;

View File

@ -93,6 +93,10 @@ class TicketMasterModel extends Model
'policy_transaction_id',
'tpa_claim_type',
'tpa_ailments',
'tpa_claim_status',
'tpa_claim_id',
'settled_amount',
'tpa_claim_push_reference_no',
'claim_dump_ref_id',
'last_updated_by',
'tpa_shortfall_no',

View File

@ -195,6 +195,15 @@
</a>
<?php endif; ?>
<?php if (!empty($file['tpa_id']) && $file['status'] !== 'processing') : ?>
<a href="javascript:void(0);"
class="dropdown-item text-danger"
onclick="truncateClaimDumpFile(<?= (int) $file['file_id']; ?>)">
<i class="mdi mdi-delete-forever mr-2 text-danger font-18 vertical-middle"></i>
Truncate
</a>
<?php endif; ?>
<!-- <a data-id="<?php echo $file['file_id'] ?>" data-toggle="modal" data-target="#full-width-modal-emp-list" class="dropdown-item view_emp_list" href="#"><i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>View</a> -->
</div>
</div>
@ -709,6 +718,39 @@
});
}
function truncateClaimDumpFile(file_id) {
if (!file_id) {
toastr.error('Invalid file reference', 'Error');
return;
}
Swal.fire({
title: 'Do you want to truncate this?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
}).then(function(result) {
if (!result.isConfirmed) {
return;
}
var url = '<?= base_url("util/truncateClaimDumpFile"); ?>';
sendAjaxRequestForGlobal(url, 'GET', { file_id: file_id }, function(response) {
if (response && response.status === true) {
toastr.success(response.message || 'Claim dump truncated', 'Success');
setTimeout(function() {
window.location.reload();
}, 800);
} else {
toastr.error((response && response.message) ? response.message : 'Truncate failed', 'Error');
}
}, function() {
toastr.error('Truncate request failed', 'Error');
});
});
}
$('.close').click(function(){
$('#modal_body').empty()
let html = `<div class="spinner-border text-primary" role="status" style="position: relative; left: 200px;"></div>`