MERGE_LIVE_BUG_FIXES
This commit is contained in:
commit
88098b881a
319
app/Commands/RecreateOrphanDumpTickets.php
Normal file
319
app/Commands/RecreateOrphanDumpTickets.php
Normal 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;
|
||||
}
|
||||
}
|
||||
549
app/Commands/TestTpaE2ePipeline.php
Normal file
549
app/Commands/TestTpaE2ePipeline.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -116,6 +116,8 @@ $routes->group("/user", ["filter" => "authMVC"], function ($routes) {
|
||||
|
||||
$routes->group("/dashboard", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("view", "DashboardController::dashboard");
|
||||
$routes->get("claims-dash", "DashboardController::claimsDashFragment");
|
||||
$routes->get("leads-dash", "DashboardController::leadsDashFragment");
|
||||
$routes->get('get-notification', 'DashboardController::getDashboardNotifications');
|
||||
$routes->get('acknowledge-notification/(:segment)', 'DashboardController::acknowledgeMessage/$1');
|
||||
$routes->get('get-pending-action', 'PendingActionsController::getPendingActions');
|
||||
@ -444,6 +446,9 @@ $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->match(['get', 'post'], 'reprocessClaimDumpPending', 'TicketController::reprocessClaimDumpPending');
|
||||
$routes->match(['get', 'post'], 'getClaimDumpPendingRows', 'TicketController::getClaimDumpPendingRows');
|
||||
$routes->post('uploadMultiFileFromRfq', 'LeadsController::uploadMultiFileFromRfq');
|
||||
$routes->get('downloadMemberFile/(:any)', 'LeadsController::downloadMemberFile/$1');
|
||||
$routes->get('downloadFullMemberDataExcelErrorFile/(:any)', 'LeadsController::downloadFullMemberDataExcelErrorFile/$1');
|
||||
|
||||
@ -2994,20 +2994,20 @@ class ClientController extends AdminController
|
||||
$base_policy = $sanitized_post_data['base_policy'] ?? null;
|
||||
|
||||
|
||||
$insurerValue = (string) $sanitized_post_data['insurer'] ?? null;
|
||||
$insurerValue = (string) ($sanitized_post_data['insurer'] ?? '');
|
||||
list($insurerBranchId, $insurerId) = explode('-', $insurerValue);
|
||||
|
||||
|
||||
$sanitized_post_data['insurer_branch_id'] = $insurerBranchId;
|
||||
$sanitized_post_data['insurer_id'] = $insurerId;
|
||||
|
||||
$tpaValue = (string) $sanitized_post_data['tpa'] ?? null;
|
||||
$tpaValue = (string) ($sanitized_post_data['tpa'] ?? '');
|
||||
|
||||
if ($tpaValue === null || $tpaValue === '') {
|
||||
if ($tpaValue === '') {
|
||||
$tpaBranchId = null;
|
||||
$tpaId = null;
|
||||
} else {
|
||||
list($tpaBranchId, $tpaId) = explode('-', $tpaValue);
|
||||
list($tpaBranchId, $tpaId) = array_pad(explode('-', $tpaValue, 2), 2, null);
|
||||
}
|
||||
|
||||
|
||||
@ -3197,15 +3197,15 @@ class ClientController extends AdminController
|
||||
$base_policy = $sanitized_post_data['base_policy'] ?? null;
|
||||
|
||||
|
||||
$insurerValue = (string) $sanitized_post_data['insurer'];
|
||||
$insurerValue = (string) ($sanitized_post_data['insurer'] ?? '');
|
||||
list($insurerBranchId, $insurerId) = explode('-', $insurerValue);
|
||||
|
||||
$sanitized_post_data['insurer_branch_id'] = $insurerBranchId ?? null;
|
||||
$sanitized_post_data['insurer_id'] = $insurerId ?? null;
|
||||
|
||||
$tpaValue = (string) $sanitized_post_data['tpa'] ?? null;
|
||||
if (!empty($tpaValue) || $tpaValue !== '') {
|
||||
list($tpaBranchId, $tpaId) = explode('-', $tpaValue);
|
||||
$tpaValue = (string) ($sanitized_post_data['tpa'] ?? '');
|
||||
if ($tpaValue !== '') {
|
||||
list($tpaBranchId, $tpaId) = array_pad(explode('-', $tpaValue, 2), 2, null);
|
||||
} else {
|
||||
$tpaBranchId = null;
|
||||
$tpaId = null;
|
||||
|
||||
@ -191,105 +191,101 @@ class DashboardController extends AdminController
|
||||
|
||||
public function dashboard()
|
||||
{
|
||||
|
||||
$data = [];
|
||||
$roleId = get_role_id();
|
||||
$teams = user_team();
|
||||
|
||||
if (in_array(get_role_id(), [1, 2, 3, 5]) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()))) {
|
||||
$showPending = in_array($roleId, [1, 2, 3, 5]);
|
||||
$showClaims = ($roleId == STAFF_ROLE_ID && in_array(CLAIMS_TEAM_ID, $teams)) || in_array($roleId, [1, 5]);
|
||||
$showLeads = ($roleId == STAFF_ROLE_ID && in_array(ENROLLMENT_TEAM_ID, $teams)) || in_array($roleId, [1, 5]);
|
||||
|
||||
$db = db_connect();
|
||||
$sql = "SELECT
|
||||
clients.id AS client_id,
|
||||
clients.client_name,
|
||||
clients.short_name,
|
||||
client_branch.id AS client_branch_id,
|
||||
client_branch.branch_name,
|
||||
client_branch.branch_code,
|
||||
|
||||
COUNT(employees.id) AS total_employees,
|
||||
SUM(CASE WHEN employees.emp_status = 'draft' THEN 1 ELSE 0 END) AS draft_count,
|
||||
SUM(CASE WHEN employees.emp_status IN ('enrolled', 'active') THEN 1 ELSE 0 END) AS enrolled_count,
|
||||
|
||||
SUM(CASE WHEN auth_history.user_id IS NOT NULL THEN 1 ELSE 0 END) AS logged_in_count,
|
||||
SUM(CASE WHEN auth_history.user_id IS NULL THEN 1 ELSE 0 END) AS not_logged_in_count,
|
||||
|
||||
CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM client_policy
|
||||
WHERE client_policy.client_branch_id = client_branch.id
|
||||
AND client_policy.is_active = 1
|
||||
AND client_policy.open_for_enrollment = 1
|
||||
) THEN 1
|
||||
ELSE 0
|
||||
END AS open_or_close_enrollment
|
||||
|
||||
FROM clients
|
||||
LEFT JOIN client_branch ON clients.id = client_branch.client_id
|
||||
LEFT JOIN employees ON client_branch.id = employees.client_branch_id
|
||||
LEFT JOIN (
|
||||
SELECT user_id, user_type
|
||||
FROM auth_history
|
||||
WHERE user_type = 'employee'
|
||||
GROUP BY user_id
|
||||
) AS auth_history ON employees.id = auth_history.user_id
|
||||
|
||||
WHERE employees.relationship = 'Self'
|
||||
AND employees.emp_status IN ('draft', 'enrolled', 'active')
|
||||
AND employees.is_active = 1
|
||||
AND clients.is_active = 1
|
||||
AND client_branch.is_active = 1
|
||||
|
||||
GROUP BY clients.id, client_branch.id";
|
||||
|
||||
$query = $db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
|
||||
$pendingActionsController = new PendingActionsController;
|
||||
$pendingActionsData = $pendingActionsController->getPendingActionsForDashBoard();
|
||||
|
||||
//BDS dashboard data
|
||||
// $businessTeamData = $this->policyTransactionModel->getBusinessReportList();
|
||||
// $financeTeamData = $this->policyTransactionModel->getFinanceReportList();
|
||||
// $businessTeamStatusData = $this->data_construct_for_bds($businessTeamData);
|
||||
// $financeTeamStatusData = $this->data_construct_for_bds($financeTeamData);
|
||||
$businessTeamData = [];
|
||||
$financeTeamData = [];
|
||||
$businessTeamStatusData = [];
|
||||
$financeTeamStatusData = [];
|
||||
|
||||
$data['client_branch_emp_list'] = $results;
|
||||
$session = \Config\Services::session();
|
||||
// $session->set('enrollment_data', json_encode($data));
|
||||
|
||||
$data['pendingActionsData'] = $pendingActionsData;
|
||||
$data['businessTeamCount'] = count($businessTeamData) ?? 0;
|
||||
$data['financeTeamCount'] = count($financeTeamData) ?? 0;
|
||||
$data['businessTeamStatusData'] = $businessTeamStatusData;
|
||||
$data['financeTeamStatusData'] = $financeTeamStatusData;
|
||||
$data['policyStatus'] = $this->policyStatus;
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
if ($showPending) {
|
||||
$defaultPane = 'pending';
|
||||
} elseif ($showClaims) {
|
||||
$defaultPane = 'claims';
|
||||
} elseif ($showLeads) {
|
||||
$defaultPane = 'leads';
|
||||
} else {
|
||||
$defaultPane = null;
|
||||
}
|
||||
if ((get_role_id() == STAFF_ROLE_ID && in_array(CLAIMS_TEAM_ID,user_team())) || in_array(get_role_id(),[1,5])){
|
||||
|
||||
if ($showPending) {
|
||||
$pendingActionsController = new PendingActionsController();
|
||||
$data['pendingActionsData'] = $pendingActionsController->getPendingActionsForDashBoard();
|
||||
$data['businessTeamCount'] = 0;
|
||||
$data['financeTeamCount'] = 0;
|
||||
$data['businessTeamStatusData'] = [];
|
||||
$data['financeTeamStatusData'] = [];
|
||||
$data['policyStatus'] = $this->policyStatus;
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
}
|
||||
|
||||
// Only the default tab loads with the page; other tabs fetch on click.
|
||||
$data['lazy_load_claims'] = $showClaims && $defaultPane !== 'claims';
|
||||
$data['lazy_load_leads'] = $showLeads && $defaultPane !== 'leads';
|
||||
|
||||
if ($showClaims && !$data['lazy_load_claims']) {
|
||||
$data['claim_data'] = $this->getClaimData();
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
}
|
||||
if ((get_role_id() == STAFF_ROLE_ID && in_array(ENROLLMENT_TEAM_ID,user_team())) || in_array(get_role_id(),[1,5])){
|
||||
|
||||
if ($showLeads && !$data['lazy_load_leads']) {
|
||||
$data['lead_data'] = $this->leadModel->getDashData();
|
||||
$data['bds_renewal'] = $this->policyTransactionModel->getBDSRenewalData();
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
|
||||
$data['colorShades'] = $this->colorShades;
|
||||
}
|
||||
|
||||
// dd(get_role_id(),user_team());
|
||||
// dd($data);
|
||||
$data['tab_name'] = 'Dashboard';
|
||||
$data['page_name'] = 'Dashboard';
|
||||
|
||||
echo view('layout/header', $data);
|
||||
echo view('layout/header', $data);
|
||||
echo view('DashBoard', $data);
|
||||
echo view('layout/footer');
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML fragment for Claims dashboard tab (loaded on tab click).
|
||||
*/
|
||||
public function claimsDashFragment()
|
||||
{
|
||||
$roleId = get_role_id();
|
||||
$teams = user_team();
|
||||
$allowed = ($roleId == STAFF_ROLE_ID && in_array(CLAIMS_TEAM_ID, $teams)) || in_array($roleId, [1, 5]);
|
||||
if (!$allowed) {
|
||||
return $this->response->setStatusCode(403)->setBody('Forbidden');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'claim_data' => $this->getClaimData(),
|
||||
'colorShades' => $this->colorShades,
|
||||
'dash_claims_pane_active' => 'active show',
|
||||
];
|
||||
|
||||
return $this->response->setBody(view('claims_dash', $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML fragment for Leads / BDS Renewals dashboard tab (loaded on tab click).
|
||||
*/
|
||||
public function leadsDashFragment()
|
||||
{
|
||||
$roleId = get_role_id();
|
||||
$teams = user_team();
|
||||
$allowed = ($roleId == STAFF_ROLE_ID && in_array(ENROLLMENT_TEAM_ID, $teams)) || in_array($roleId, [1, 5]);
|
||||
if (!$allowed) {
|
||||
return $this->response->setStatusCode(403)->setBody('Forbidden');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'lead_data' => $this->leadModel->getDashData(),
|
||||
'bds_renewal' => $this->policyTransactionModel->getBDSRenewalData(),
|
||||
'colorShades' => $this->colorShades,
|
||||
'dash_leads_pane_active' => 'active show',
|
||||
];
|
||||
|
||||
return $this->response->setBody(view('leads_dash', $data));
|
||||
}
|
||||
|
||||
|
||||
public function getClaimData()
|
||||
{
|
||||
|
||||
@ -97,115 +97,292 @@ class PendingActionsController extends AdminController
|
||||
return $data;
|
||||
}
|
||||
|
||||
//for only COUNT
|
||||
/**
|
||||
* Dashboard counts only — avoids loading full pending-action rowsets.
|
||||
* Cached briefly so repeated dashboard hits don't re-run heavy aggregations.
|
||||
*/
|
||||
public function getPendingActionsForDashBoard()
|
||||
{
|
||||
$tpa = $this->getPendingActionForTPAIDEmpty();
|
||||
$uhid = $this->getPendingActionForUHIDEmpty();
|
||||
$deletion = $this->getPendingActionForDeletion();
|
||||
$inception = $this->getPendingActionForInception();
|
||||
$ticketData = $this->getTicketsDataForDashBoard();
|
||||
$correction = $this->getPendingActionForCorrection();
|
||||
$si_enhancement = $this->getPendingActionForSIEnhancement();
|
||||
$PolicyRenewalData = $this->getPolicyRenewalDataForAllClient();
|
||||
|
||||
$uhid_export_count = [];
|
||||
$uhid_not_export_count = [];
|
||||
foreach ($uhid as $value) {
|
||||
if($value['batch_export_count'] == 1){
|
||||
$uhid_export_count[] = $value['batch_export_count'];
|
||||
}else{
|
||||
$uhid_not_export_count[] = $value['batch_export_count'];
|
||||
}
|
||||
}
|
||||
$tpa_export_count = [];
|
||||
$tpa_not_export_count = [];
|
||||
foreach ($tpa as $value) {
|
||||
if($value['batch_export_count'] == 1){
|
||||
$tpa_export_count[] = $value['batch_export_count'];
|
||||
}else{
|
||||
$tpa_not_export_count[] = $value['batch_export_count'];
|
||||
}
|
||||
}
|
||||
$deletion_export_count = [];
|
||||
$deletion_not_export_count = [];
|
||||
foreach ($deletion as $value) {
|
||||
if($value['batch_export_count'] == 1){
|
||||
$deletion_export_count[] = $value['batch_export_count'];
|
||||
}else{
|
||||
$deletion_not_export_count[] = $value['batch_export_count'];
|
||||
}
|
||||
}
|
||||
$correction_export_count = [];
|
||||
$correction_not_export_count = [];
|
||||
foreach ($correction as $value) {
|
||||
if($value['batch_export_count'] == 1){
|
||||
$correction_export_count[] = $value['batch_export_count'];
|
||||
}else{
|
||||
$correction_not_export_count[] = $value['batch_export_count'];
|
||||
}
|
||||
}
|
||||
$si_export_count = [];
|
||||
$si_not_export_count = [];
|
||||
foreach ($si_enhancement as $value) {
|
||||
if($value['batch_export_count'] == 1){
|
||||
$si_export_count[] = $value['batch_export_count'];
|
||||
}else{
|
||||
$si_not_export_count[] = $value['batch_export_count'];
|
||||
}
|
||||
$cache = \Config\Services::cache();
|
||||
$cacheKey = 'dashboard_pending_actions_counts_v2';
|
||||
$cached = $cache->get($cacheKey);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// dd($uhid_export_count, $tpa_export_count ,$deletion_export_count, $correction_export_count, $si_export_count, $ticketData, $inception, $si_enhancement, $correction, $deletion, $tpa, $uhid, $PolicyRenewalData);
|
||||
|
||||
$inception = count($inception);
|
||||
$correction = count($correction);
|
||||
$si_enhancement = count($si_enhancement);
|
||||
$deletion = count($deletion);
|
||||
$tpa = count($tpa);
|
||||
$uhid = count($uhid);
|
||||
$PolicyRenewalData = count($PolicyRenewalData);
|
||||
$ticketData = count($ticketData);
|
||||
|
||||
$uhid_export_count = count($uhid_export_count);
|
||||
$tpa_export_count = count($tpa_export_count);
|
||||
$deletion_export_count = count($deletion_export_count);
|
||||
$correction_export_count = count($correction_export_count);
|
||||
$si_export_count = count($si_export_count);
|
||||
|
||||
$uhid_not_export_count = count($uhid_not_export_count);
|
||||
$tpa_not_export_count = count($tpa_not_export_count);
|
||||
$deletion_not_export_count = count($deletion_not_export_count);
|
||||
$correction_not_export_count = count($correction_not_export_count);
|
||||
$si_not_export_count = count($si_not_export_count);
|
||||
|
||||
|
||||
// dd($inception, $si_enhancement, $correction, $deletion, $tpa, $uhid, $PolicyRenewalData);
|
||||
$uhidCounts = $this->countPendingUhidForDashboard();
|
||||
$tpaCounts = $this->countPendingTpaForDashboard();
|
||||
$correctionCounts = $this->countPendingCorrectionForDashboard();
|
||||
$deletionCounts = $this->countPendingDeletionForDashboard();
|
||||
$siCounts = $this->countPendingSiForDashboard();
|
||||
|
||||
$data = [
|
||||
'inception' => $inception,
|
||||
'correction' => $correction,
|
||||
'si_enhancement' => $si_enhancement,
|
||||
'deletion' => $deletion,
|
||||
'tpa' => $tpa,
|
||||
'uhid' => $uhid,
|
||||
'PolicyRenewalData' => $PolicyRenewalData,
|
||||
'ticketData' => $ticketData,
|
||||
|
||||
'uhid_export_count' => $uhid_export_count,
|
||||
'tpa_export_count' => $tpa_export_count,
|
||||
'deletion_export_count' => $deletion_export_count,
|
||||
'correction_export_count' => $correction_export_count,
|
||||
'si_export_count' => $si_export_count,
|
||||
|
||||
'uhid_not_export_count' => $uhid_not_export_count,
|
||||
'tpa_not_export_count' => $tpa_not_export_count,
|
||||
'deletion_not_export_count' => $deletion_not_export_count,
|
||||
'correction_not_export_count' => $correction_not_export_count,
|
||||
'si_not_export_count' => $si_not_export_count,
|
||||
'inception' => $this->countPendingInceptionForDashboard(),
|
||||
'correction' => $correctionCounts['total'],
|
||||
'si_enhancement' => $siCounts['total'],
|
||||
'deletion' => $deletionCounts['total'],
|
||||
'tpa' => $tpaCounts['total'],
|
||||
'uhid' => $uhidCounts['total'],
|
||||
'PolicyRenewalData' => $this->countPolicyRenewalForDashboard(),
|
||||
'ticketData' => $this->countTicketsForDashboard(),
|
||||
'uhid_export_count' => $uhidCounts['export_count'],
|
||||
'tpa_export_count' => $tpaCounts['export_count'],
|
||||
'deletion_export_count' => $deletionCounts['export_count'],
|
||||
'correction_export_count' => $correctionCounts['export_count'],
|
||||
'si_export_count' => $siCounts['export_count'],
|
||||
'uhid_not_export_count' => $uhidCounts['not_export_count'],
|
||||
'tpa_not_export_count' => $tpaCounts['not_export_count'],
|
||||
'deletion_not_export_count' => $deletionCounts['not_export_count'],
|
||||
'correction_not_export_count' => $correctionCounts['not_export_count'],
|
||||
'si_not_export_count' => $siCounts['not_export_count'],
|
||||
];
|
||||
|
||||
$cache->save($cacheKey, $data, 60);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function countPendingInceptionForDashboard(): int
|
||||
{
|
||||
// NOT EXISTS avoids scanning employee_polices for every open policy via LEFT JOIN + HAVING.
|
||||
$sql = "
|
||||
SELECT COUNT(*) AS cnt
|
||||
FROM client_policy cp
|
||||
INNER JOIN clients ON cp.client_id = clients.id
|
||||
INNER JOIN client_branch cb ON cb.id = cp.client_branch_id
|
||||
INNER JOIN policy_type ON cp.policy_type_id = policy_type.id
|
||||
WHERE cp.is_active = 1
|
||||
AND cp.open_for_enrollment = 1
|
||||
AND cp.is_addon = 1
|
||||
AND cp.policy_status = 1
|
||||
AND cp.inception_type = 1
|
||||
AND cp.policy_type_id IN (1, 2, 3)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM employee_polices ep
|
||||
WHERE ep.client_policy_id = cp.id
|
||||
AND ep.is_active = 1
|
||||
)
|
||||
";
|
||||
|
||||
return (int) ($this->db->query($sql)->getRowArray()['cnt'] ?? 0);
|
||||
}
|
||||
|
||||
private function countPendingUhidForDashboard(): array
|
||||
{
|
||||
// Aggregate batch_files once, then join — correlated COUNT per policy was acceptable but JOIN scales better.
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) = 1 THEN 1 ELSE 0 END) AS export_count,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) != 1 THEN 1 ELSE 0 END) AS not_export_count
|
||||
FROM (
|
||||
SELECT ep.client_policy_id
|
||||
FROM employee_polices ep
|
||||
INNER JOIN client_policy cp
|
||||
ON ep.client_policy_id = cp.id
|
||||
AND cp.is_active = 1
|
||||
AND cp.policy_status = 1
|
||||
INNER JOIN clients c ON c.id = cp.client_id
|
||||
INNER JOIN client_branch cb ON cb.id = cp.client_branch_id
|
||||
INNER JOIN policy_type ON policy_type.id = cp.policy_type_id
|
||||
WHERE ep.uhid IS NULL
|
||||
AND ep.status = 'active'
|
||||
AND ep.is_active = 1
|
||||
GROUP BY ep.client_policy_id
|
||||
) AS pending
|
||||
LEFT JOIN (
|
||||
SELECT client_policy_id, COUNT(*) AS export_cnt
|
||||
FROM batch_files
|
||||
WHERE actions = 'export'
|
||||
AND event_type = 'inception'
|
||||
AND insurer_or_tpa = 'insurer'
|
||||
GROUP BY client_policy_id
|
||||
) AS bf ON bf.client_policy_id = pending.client_policy_id
|
||||
";
|
||||
|
||||
return $this->normalizeExportCounts($this->db->query($sql)->getRowArray());
|
||||
}
|
||||
|
||||
private function countPendingTpaForDashboard(): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) = 1 THEN 1 ELSE 0 END) AS export_count,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) != 1 THEN 1 ELSE 0 END) AS not_export_count
|
||||
FROM (
|
||||
SELECT employee_polices.client_policy_id
|
||||
FROM employee_polices
|
||||
INNER JOIN client_policy
|
||||
ON employee_polices.client_policy_id = client_policy.id
|
||||
AND client_policy.is_active = 1
|
||||
AND client_policy.policy_status = 1
|
||||
INNER JOIN clients ON clients.id = client_policy.client_id
|
||||
INNER JOIN client_branch ON client_branch.id = client_policy.client_branch_id
|
||||
INNER JOIN policy_type ON policy_type.id = client_policy.policy_type_id
|
||||
WHERE employee_polices.tpa_id IS NULL
|
||||
AND employee_polices.uhid IS NOT NULL
|
||||
AND employee_polices.status = 'active'
|
||||
AND employee_polices.is_active = 1
|
||||
GROUP BY employee_polices.client_policy_id
|
||||
) AS pending
|
||||
LEFT JOIN (
|
||||
SELECT client_policy_id, COUNT(*) AS export_cnt
|
||||
FROM batch_files
|
||||
WHERE actions = 'export'
|
||||
AND event_type = 'inception'
|
||||
AND insurer_or_tpa = 'tpa'
|
||||
GROUP BY client_policy_id
|
||||
) AS bf ON bf.client_policy_id = pending.client_policy_id
|
||||
";
|
||||
|
||||
return $this->normalizeExportCounts($this->db->query($sql)->getRowArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start from pending emp_endorsement rows — NOT from all clients with EXISTS
|
||||
* (EXISTS over every client was hanging for minutes on staging ~500k policies).
|
||||
*/
|
||||
private function countPendingCorrectionForDashboard(): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) = 1 THEN 1 ELSE 0 END) AS export_count,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) != 1 THEN 1 ELSE 0 END) AS not_export_count
|
||||
FROM (
|
||||
SELECT e.client_id
|
||||
FROM emp_endorsement ee
|
||||
INNER JOIN employees e
|
||||
ON ee.pk = e.id
|
||||
AND e.is_active = 1
|
||||
AND e.emp_status = 'active'
|
||||
WHERE ee.actions = 'c'
|
||||
AND ee.is_active = 1
|
||||
AND ee.status = 'pending'
|
||||
AND ee.endorsement_id IS NULL
|
||||
GROUP BY e.client_id
|
||||
) AS pending
|
||||
LEFT JOIN (
|
||||
SELECT client_id, COUNT(*) AS export_cnt
|
||||
FROM batch_files
|
||||
WHERE actions = 'export'
|
||||
AND event_type = 'correction'
|
||||
AND insurer_or_tpa = 'tpa'
|
||||
GROUP BY client_id
|
||||
) AS bf ON bf.client_id = pending.client_id
|
||||
";
|
||||
|
||||
return $this->normalizeExportCounts($this->db->query($sql)->getRowArray());
|
||||
}
|
||||
|
||||
private function countPendingDeletionForDashboard(): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) = 1 THEN 1 ELSE 0 END) AS export_count,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) != 1 THEN 1 ELSE 0 END) AS not_export_count
|
||||
FROM (
|
||||
SELECT cp.client_id
|
||||
FROM emp_endorsement ee
|
||||
INNER JOIN employee_polices ep
|
||||
ON ee.pk = ep.id
|
||||
AND ep.is_active = 1
|
||||
AND ep.status = 'active'
|
||||
INNER JOIN client_policy cp
|
||||
ON ep.client_policy_id = cp.id
|
||||
AND cp.is_active = 1
|
||||
AND cp.policy_status = 1
|
||||
WHERE ee.actions = 'd'
|
||||
AND ee.is_active = 1
|
||||
AND ee.status = 'pending'
|
||||
AND ee.endorsement_id IS NULL
|
||||
AND ee.table_name = 'employee_polices'
|
||||
GROUP BY cp.client_id
|
||||
) AS pending
|
||||
LEFT JOIN (
|
||||
SELECT client_id, COUNT(*) AS export_cnt
|
||||
FROM batch_files
|
||||
WHERE actions = 'export'
|
||||
AND event_type = 'deletion'
|
||||
AND insurer_or_tpa = 'insurer'
|
||||
GROUP BY client_id
|
||||
) AS bf ON bf.client_id = pending.client_id
|
||||
";
|
||||
|
||||
return $this->normalizeExportCounts($this->db->query($sql)->getRowArray());
|
||||
}
|
||||
|
||||
private function countPendingSiForDashboard(): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) = 1 THEN 1 ELSE 0 END) AS export_count,
|
||||
SUM(CASE WHEN COALESCE(bf.export_cnt, 0) != 1 THEN 1 ELSE 0 END) AS not_export_count
|
||||
FROM (
|
||||
SELECT cp.client_id
|
||||
FROM emp_endorsement ee
|
||||
INNER JOIN employee_polices ep
|
||||
ON ee.pk = ep.id
|
||||
AND ep.is_active = 1
|
||||
AND ep.status = 'active'
|
||||
INNER JOIN client_policy cp
|
||||
ON ep.client_policy_id = cp.id
|
||||
AND cp.is_active = 1
|
||||
AND cp.policy_status = 1
|
||||
WHERE ee.actions = 'si'
|
||||
AND ee.is_active = 1
|
||||
AND ee.status = 'pending'
|
||||
AND ee.endorsement_id IS NULL
|
||||
GROUP BY cp.client_id
|
||||
) AS pending
|
||||
LEFT JOIN (
|
||||
SELECT client_id, COUNT(*) AS export_cnt
|
||||
FROM batch_files
|
||||
WHERE actions = 'export'
|
||||
AND event_type = 'si_enhancement'
|
||||
AND insurer_or_tpa = 'tpa'
|
||||
GROUP BY client_id
|
||||
) AS bf ON bf.client_id = pending.client_id
|
||||
";
|
||||
|
||||
return $this->normalizeExportCounts($this->db->query($sql)->getRowArray());
|
||||
}
|
||||
|
||||
private function countPolicyRenewalForDashboard(): int
|
||||
{
|
||||
return (int) $this->clientPolicyModel
|
||||
->join('clients', 'clients.id = client_policy.client_id')
|
||||
->join('client_branch', 'client_branch.id = client_policy.client_branch_id')
|
||||
->where('client_policy.is_active', 1)
|
||||
->where('client_policy.policy_status', 0)
|
||||
->where('clients.is_active', 1)
|
||||
->where('client_branch.is_active', 1)
|
||||
->where('client_policy.policy_end_date < DATE_ADD(CURDATE(), INTERVAL 2 MONTH)', null, false)
|
||||
->countAllResults();
|
||||
}
|
||||
|
||||
private function countTicketsForDashboard(): int
|
||||
{
|
||||
return (int) $this->db->table('hdz_tickets')
|
||||
->join('hdz_status', 'hdz_status.id = hdz_tickets.status')
|
||||
->where('hdz_status.active', 1)
|
||||
->where('hdz_tickets.status !=', 5)
|
||||
->countAllResults();
|
||||
}
|
||||
|
||||
private function normalizeExportCounts(?array $row): array
|
||||
{
|
||||
return [
|
||||
'total' => (int) ($row['total'] ?? 0),
|
||||
'export_count' => (int) ($row['export_count'] ?? 0),
|
||||
'not_export_count' => (int) ($row['not_export_count'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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,
|
||||
@ -4329,6 +4360,10 @@ class TicketController extends BaseController
|
||||
->orderBy('claim_dump_files.id', 'desc')
|
||||
->findAll();
|
||||
|
||||
$data['claim_dump_file_data'] = $this->attachPendingDumpTicketCounts(
|
||||
$data['claim_dump_file_data'] ?? []
|
||||
);
|
||||
|
||||
return $this->loadLayout('claim_dump_file_list', $data);
|
||||
|
||||
}else{
|
||||
@ -4391,15 +4426,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 +4514,414 @@ 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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* List dump rows for a file where ticket_id is still NULL (not moved to ticket_master).
|
||||
*/
|
||||
public function getClaimDumpPendingRows()
|
||||
{
|
||||
$fileId = (int) ($this->request->getGet('file_id') ?? $this->request->getPost('file_id') ?? 0);
|
||||
if ($fileId <= 0) {
|
||||
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)) {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'File not found'], 200);
|
||||
}
|
||||
|
||||
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
|
||||
$tableMap = $this->getTpaDumpTableMap();
|
||||
$tpaTable = $tableMap[$tpaId] ?? null;
|
||||
if ($tpaTable === null) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Pending dump view is only supported for TPA claim dumps',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$displayMap = $this->getPendingDumpDisplayColumns($tpaId);
|
||||
$selectCols = array_values(array_unique(array_merge(['id'], array_keys($displayMap))));
|
||||
|
||||
$db = db_connect();
|
||||
if (!$db->tableExists($tpaTable)) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Dump table not found'], 200);
|
||||
}
|
||||
|
||||
// Only select columns that exist on the table.
|
||||
$existingCols = array_column($db->query('SHOW COLUMNS FROM `' . $tpaTable . '`')->getResultArray(), 'Field');
|
||||
$selectCols = array_values(array_intersect($selectCols, $existingCols));
|
||||
if ($selectCols === []) {
|
||||
$selectCols = ['id'];
|
||||
}
|
||||
|
||||
$rows = $db->table($tpaTable)
|
||||
->select(implode(', ', $selectCols))
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->orderBy('id', 'ASC')
|
||||
->limit(500)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$headers = [];
|
||||
foreach ($selectCols as $col) {
|
||||
$headers[] = [
|
||||
'key' => $col,
|
||||
'label' => $displayMap[$col] ?? $col,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'Pending dump rows loaded',
|
||||
'data' => [
|
||||
'file_id' => $fileId,
|
||||
'file_name' => $fileData['file_name'] ?? '',
|
||||
'tpa_id' => $tpaId,
|
||||
'count' => count($rows),
|
||||
'headers' => $headers,
|
||||
'rows' => $rows,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display columns for pending dump modal (db_column => label).
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function getPendingDumpDisplayColumns(int $tpaId): array
|
||||
{
|
||||
$byTpa = [
|
||||
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'employee_member_id' => 'Emp Code',
|
||||
'main_member_name' => 'Employee Name',
|
||||
'insured_name' => 'Insured Name',
|
||||
'uhid' => 'UHID / TPA No',
|
||||
'claimed_amount' => 'Claim Amount',
|
||||
'doa' => 'DOA',
|
||||
'updated_status' => 'Status',
|
||||
'relation' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
(int) env('ABHI_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'member_code' => 'Emp Code',
|
||||
'proposer_name' => 'Employee Name',
|
||||
'patient_name' => 'Insured Name',
|
||||
'healthcard_id' => 'UHID / TPA No',
|
||||
'claimed_amount' => 'Claim Amount',
|
||||
'doa' => 'DOA',
|
||||
'claim_status' => 'Status',
|
||||
'relation' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
(int) env('MEDI_ASSIST_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'pribenef_employee_code' => 'Emp Code',
|
||||
'pribenef_name' => 'Employee Name',
|
||||
'benef_name' => 'Insured Name',
|
||||
'event_id' => 'UHID / TPA No',
|
||||
'claim_amount' => 'Claim Amount',
|
||||
'date_of_admission' => 'DOA',
|
||||
'claim_status' => 'Status',
|
||||
'benef_relation' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
(int) env('FHPL_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'employee_id' => 'Emp Code',
|
||||
'main_member_name' => 'Employee Name',
|
||||
'member_name' => 'Insured Name',
|
||||
'uhid_no' => 'UHID / TPA No',
|
||||
'claim_amount' => 'Claim Amount',
|
||||
'admission_date' => 'DOA',
|
||||
'current_claim_status' => 'Status',
|
||||
'relationship' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
(int) env('R_CARE_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'employee_member_id' => 'Emp Code',
|
||||
'insured_name' => 'Employee Name',
|
||||
'patient_name' => 'Insured Name',
|
||||
'uhid' => 'UHID / TPA No',
|
||||
'claimed_amount' => 'Claim Amount',
|
||||
'doa_opd_treatment_from' => 'DOA',
|
||||
'final_status' => 'Status',
|
||||
'relation' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
(int) env('VIDAL_PRIMARY_KEY_CONSTANT') => [
|
||||
'id' => 'Dump ID',
|
||||
'employee_number' => 'Emp Code',
|
||||
'primary_policy_holder_name' => 'Employee Name',
|
||||
'patient_name' => 'Insured Name',
|
||||
'primary_policy_holder_card_id' => 'UHID / TPA No',
|
||||
'claim_amount' => 'Claim Amount',
|
||||
'date_of_admission' => 'DOA',
|
||||
'claim_status' => 'Status',
|
||||
'relation' => 'Relation',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
],
|
||||
];
|
||||
|
||||
return $byTpa[$tpaId] ?? [
|
||||
'id' => 'Dump ID',
|
||||
'master_reject_reason' => 'Reject Reason',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-run Job 2 for dump rows that never got a ticket_master link (ticket_id IS NULL).
|
||||
*/
|
||||
public function reprocessClaimDumpPending()
|
||||
{
|
||||
$fileId = (int) ($this->request->getGet('file_id') ?? $this->request->getPost('file_id') ?? 0);
|
||||
log_message('error', '[CLAIM_DUMP][CTRL_REPROCESS_PENDING_START] file_id=' . $fileId);
|
||||
|
||||
if ($fileId <= 0) {
|
||||
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)) {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'File not found'], 200);
|
||||
}
|
||||
|
||||
if (($fileData['status'] ?? '') === 'processing') {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Cannot reprocess while import is processing',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$tpaId = (int) ($fileData['tpa_id'] ?? 0);
|
||||
if ($tpaId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Reprocess is only supported for TPA claim dumps',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$tableMap = $this->getTpaDumpTableMap();
|
||||
$tpaTable = $tableMap[$tpaId] ?? null;
|
||||
if ($tpaTable === null) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Unsupported TPA'], 200);
|
||||
}
|
||||
|
||||
try {
|
||||
$db = db_connect();
|
||||
$pendingBefore = (int) $db->table($tpaTable)
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->countAllResults();
|
||||
|
||||
if ($pendingBefore <= 0) {
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'No pending dump rows to process',
|
||||
'data' => ['pending_before' => 0],
|
||||
], 200);
|
||||
}
|
||||
|
||||
// Allow previously rejected unlinked rows to be tried again.
|
||||
$db->table($tpaTable)
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->where('master_reject_reason IS NOT NULL', null, false)
|
||||
->set(['master_reject_reason' => null])
|
||||
->update();
|
||||
|
||||
$handler = \App\Libraries\TpaClaimsImportFactory::make($tpaId);
|
||||
$result = $handler->runTicketMasterInsert(['file_id' => $fileId]);
|
||||
|
||||
$pendingAfter = (int) $db->table($tpaTable)
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->countAllResults();
|
||||
|
||||
if (!empty($result['status'])) {
|
||||
$this->claimDumpFileModel->update($fileId, [
|
||||
'status' => 'success',
|
||||
'reason' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
log_message(
|
||||
'error',
|
||||
'[CLAIM_DUMP][CTRL_REPROCESS_PENDING_DONE] file_id=' . $fileId
|
||||
. ' pending_before=' . $pendingBefore
|
||||
. ' pending_after=' . $pendingAfter
|
||||
. ' status=' . (!empty($result['status']) ? 'true' : 'false')
|
||||
. ' message=' . ($result['message'] ?? '')
|
||||
);
|
||||
|
||||
$moved = max(0, $pendingBefore - $pendingAfter);
|
||||
$message = !empty($result['status'])
|
||||
? "Processed pending dump rows. Moved {$moved} of {$pendingBefore} to ticket master."
|
||||
: ($result['message'] ?? 'Reprocess failed');
|
||||
|
||||
if (!empty($result['status']) && $pendingAfter > 0) {
|
||||
$message .= " {$pendingAfter} row(s) still pending (e.g. employee not found).";
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => !empty($result['status']),
|
||||
'code' => !empty($result['status']) ? 200 : 400,
|
||||
'message' => $message,
|
||||
'data' => [
|
||||
'pending_before' => $pendingBefore,
|
||||
'pending_after' => $pendingAfter,
|
||||
'moved' => $moved,
|
||||
'job2' => $result,
|
||||
],
|
||||
], 200);
|
||||
} catch (\Throwable $th) {
|
||||
$this->myLogger->logme('error', 'reprocessClaimDumpPending: ' . $th->getMessage());
|
||||
log_message('error', '[CLAIM_DUMP][CTRL_REPROCESS_PENDING_EXCEPTION] file_id=' . $fileId . ' error=' . $th->getMessage());
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Reprocess failed: ' . $th->getMessage(),
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function getTpaDumpTableMap(): array
|
||||
{
|
||||
return [
|
||||
(int) env('VIDAL_PRIMARY_KEY_CONSTANT') => 'claims_dump_vidal',
|
||||
(int) env('ABHI_PRIMARY_KEY_CONSTANT') => 'claims_dump_abhi',
|
||||
(int) env('MEDI_ASSIST_PRIMARY_KEY_CONSTANT') => 'claims_dump_medi_assist',
|
||||
(int) env('FHPL_PRIMARY_KEY_CONSTANT') => 'claims_dump_fhpl',
|
||||
(int) env('R_CARE_PRIMARY_KEY_CONSTANT') => 'claims_dump_reliance',
|
||||
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => 'claims_dump_icici',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach pending dump row counts (ticket_id IS NULL) for list UI.
|
||||
*
|
||||
* @param list<array<string, mixed>> $files
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function attachPendingDumpTicketCounts(array $files): array
|
||||
{
|
||||
if ($files === []) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
$tableMap = $this->getTpaDumpTableMap();
|
||||
$byTpa = [];
|
||||
foreach ($files as $file) {
|
||||
$tpaId = (int) ($file['tpa_id'] ?? 0);
|
||||
$fileId = (int) ($file['file_id'] ?? 0);
|
||||
if ($tpaId > 0 && $fileId > 0 && isset($tableMap[$tpaId])) {
|
||||
$byTpa[$tpaId][] = $fileId;
|
||||
}
|
||||
}
|
||||
|
||||
$counts = [];
|
||||
$db = db_connect();
|
||||
foreach ($byTpa as $tpaId => $fileIds) {
|
||||
$fileIds = array_values(array_unique($fileIds));
|
||||
if ($fileIds === []) {
|
||||
continue;
|
||||
}
|
||||
$table = $tableMap[$tpaId];
|
||||
if (!$db->tableExists($table)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows = $db->table($table)
|
||||
->select('file_id, COUNT(*) AS pending_count', false)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->whereIn('file_id', $fileIds)
|
||||
->groupBy('file_id')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$counts[(int) $row['file_id']] = (int) $row['pending_count'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($files as &$file) {
|
||||
$fileId = (int) ($file['file_id'] ?? 0);
|
||||
$file['pending_ticket_count'] = $counts[$fileId] ?? 0;
|
||||
}
|
||||
unset($file);
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
// -------- END CLAIM DUMP UPLOAD ----------------------------------------------------------------------------------------------
|
||||
public function saveIRDocsJson()
|
||||
{
|
||||
|
||||
@ -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) {
|
||||
|
||||
55
app/Database/bds_report_performance_indexes.sql
Normal file
55
app/Database/bds_report_performance_indexes.sql
Normal file
@ -0,0 +1,55 @@
|
||||
-- BDS report performance indexes
|
||||
-- Run manually on the application database (e.g. nhance_live).
|
||||
-- Safe to re-run: each statement checks information_schema before creating.
|
||||
|
||||
-- policy_transaction: default 90-day filter + active flag
|
||||
SET @idx := (
|
||||
SELECT COUNT(1) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'policy_transaction'
|
||||
AND index_name = 'idx_pt_active_created'
|
||||
);
|
||||
SET @sql := IF(@idx = 0,
|
||||
'CREATE INDEX idx_pt_active_created ON policy_transaction (is_active, created_at)',
|
||||
'SELECT ''idx_pt_active_created already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- pt_co_share_details: join from policy_transaction
|
||||
SET @idx := (
|
||||
SELECT COUNT(1) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'pt_co_share_details'
|
||||
AND index_name = 'idx_pcsd_pt_active'
|
||||
);
|
||||
SET @sql := IF(@idx = 0,
|
||||
'CREATE INDEX idx_pcsd_pt_active ON pt_co_share_details (pt_id, is_active)',
|
||||
'SELECT ''idx_pcsd_pt_active already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- co_share_stmt_details: billed/reward aggregate joins
|
||||
SET @idx := (
|
||||
SELECT COUNT(1) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'co_share_stmt_details'
|
||||
AND index_name = 'idx_cssd_coshare_stmt_active'
|
||||
);
|
||||
SET @sql := IF(@idx = 0,
|
||||
'CREATE INDEX idx_cssd_coshare_stmt_active ON co_share_stmt_details (co_share_id, statement_id, is_active)',
|
||||
'SELECT ''idx_cssd_coshare_stmt_active already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- insurer_statements: month + invoice filters used by aggregates
|
||||
SET @idx := (
|
||||
SELECT COUNT(1) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'insurer_statements'
|
||||
AND index_name = 'idx_insq_active_month_invoice'
|
||||
);
|
||||
SET @sql := IF(@idx = 0,
|
||||
'CREATE INDEX idx_insq_active_month_invoice ON insurer_statements (is_active, month, invoice_status)',
|
||||
'SELECT ''idx_insq_active_month_invoice already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -2186,42 +2186,44 @@
|
||||
// Increase GROUP_CONCAT limit
|
||||
$this->db->query("SET SESSION group_concat_max_len = 1000000;");
|
||||
|
||||
$builder = $this->db->table('policy_transaction pt');
|
||||
// NOT EXISTS avoids a self-join that balloons on large policy_transaction tables.
|
||||
$sql = "
|
||||
SELECT
|
||||
SUM(CASE WHEN pt.policy_end_date < CURDATE() THEN 1 ELSE 0 END) AS Expired,
|
||||
SUM(CASE WHEN pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH) THEN 1 ELSE 0 END) AS `Renewal Pending`,
|
||||
GROUP_CONCAT(CASE WHEN pt.policy_end_date < CURDATE() THEN pt.id ELSE NULL END) AS Expired_ids,
|
||||
GROUP_CONCAT(CASE WHEN pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH) THEN pt.id ELSE NULL END) AS Renewal_Pending_ids
|
||||
FROM policy_transaction pt
|
||||
WHERE pt.policy_end_date IS NOT NULL
|
||||
AND pt.is_active = 1
|
||||
AND (
|
||||
pt.policy_end_date < CURDATE()
|
||||
OR pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM policy_transaction renewed
|
||||
WHERE renewed.source_client_policy_id = pt.client_policy_id
|
||||
AND renewed.client_id = pt.client_id
|
||||
AND renewed.client_branch_id = pt.client_branch_id
|
||||
)
|
||||
";
|
||||
|
||||
$builder->select([
|
||||
'SUM(CASE WHEN pt.policy_end_date < CURDATE() THEN 1 ELSE 0 END) AS Expired',
|
||||
'SUM(CASE WHEN pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH) THEN 1 ELSE 0 END) AS `Renewal Pending`',
|
||||
'GROUP_CONCAT(CASE WHEN pt.policy_end_date < CURDATE() THEN pt.id ELSE NULL END) AS Expired_ids',
|
||||
'GROUP_CONCAT(CASE WHEN pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH) THEN pt.id ELSE NULL END) AS Renewal_Pending_ids'
|
||||
]);
|
||||
$builder->where("pt.policy_end_date IS NOT NULL", null, false);
|
||||
$builder->where('pt.is_active', 1);
|
||||
$result = $this->db->query($sql)->getResultArray();
|
||||
$row = $result[0] ?? [
|
||||
'Expired' => 0,
|
||||
'Renewal Pending' => 0,
|
||||
'Expired_ids' => null,
|
||||
'Renewal_Pending_ids' => null,
|
||||
];
|
||||
|
||||
// Self join to check if a policy has been renewed
|
||||
$builder->join('policy_transaction renewed', 'renewed.source_client_policy_id = pt.client_policy_id and renewed.client_id = pt.client_id and renewed.client_branch_id = pt.client_branch_id', 'left');
|
||||
$total = (int) ($row['Expired'] ?? 0) + (int) ($row['Renewal Pending'] ?? 0);
|
||||
|
||||
// Filter for expired or expiring policies
|
||||
$builder->where("(pt.policy_end_date < CURDATE() OR pt.policy_end_date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 1 MONTH))", null, false);
|
||||
$row['Expired_ids'] = $row['Expired_ids'] ?: [];
|
||||
$row['Renewal Pending_ids'] = $row['Renewal_Pending_ids'] ?: [];
|
||||
$row['total'] = $total;
|
||||
|
||||
$builder->where('renewed.id IS NULL', null, false);
|
||||
$query = $builder->get();
|
||||
$result = $query->getResultArray();
|
||||
|
||||
$total = 0;
|
||||
foreach ($result[0] as $key => $value) {
|
||||
if ($key !== 'Expired_ids' && $key !== 'Renewal_Pending_ids') {
|
||||
$total += $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure ID fields are arrays (not null)
|
||||
$result[0]['Expired_ids'] = $result[0]['Expired_ids'] ? $result[0]['Expired_ids'] : [];
|
||||
$result[0]['Renewal Pending_ids'] = $result[0]['Renewal_Pending_ids'] ? $result[0]['Renewal_Pending_ids'] : [];
|
||||
|
||||
// Add total count
|
||||
$result[0]['total'] = $total;
|
||||
|
||||
return $result[0];
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function reportBDSNew(
|
||||
@ -3423,31 +3425,30 @@
|
||||
}
|
||||
|
||||
$main_conditions = '';
|
||||
function addCondition(&$main_conditions, &$whereAdded, $condition)
|
||||
{
|
||||
$addCondition = static function (&$main_conditions, &$whereAdded, $condition) {
|
||||
if (!$whereAdded) {
|
||||
$main_conditions .= " WHERE $condition ";
|
||||
$whereAdded = true;
|
||||
} else {
|
||||
$main_conditions .= " AND $condition ";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 4. Common filters
|
||||
if ($client_id != 0) {
|
||||
addCondition($main_conditions, $whereAdded, "client_id = $client_id");
|
||||
$addCondition($main_conditions, $whereAdded, "client_id = $client_id");
|
||||
}
|
||||
|
||||
if ($insurer_id != 0) {
|
||||
addCondition($main_conditions, $whereAdded, "insurer_id = $insurer_id");
|
||||
$addCondition($main_conditions, $whereAdded, "insurer_id = $insurer_id");
|
||||
}
|
||||
|
||||
if ($client_branch_id != 0) {
|
||||
addCondition($main_conditions, $whereAdded, "client_branch_id = $client_branch_id");
|
||||
$addCondition($main_conditions, $whereAdded, "client_branch_id = $client_branch_id");
|
||||
}
|
||||
|
||||
if ($insurer_branch_id != 0) {
|
||||
addCondition($main_conditions, $whereAdded, "insurer_branch_id = $insurer_branch_id");
|
||||
$addCondition($main_conditions, $whereAdded, "insurer_branch_id = $insurer_branch_id");
|
||||
}
|
||||
|
||||
$sql = "
|
||||
@ -3590,19 +3591,15 @@
|
||||
LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
|
||||
JOIN clients c ON c.id = pt.client_id
|
||||
LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
|
||||
LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
|
||||
LEFT JOIN user_profiles up ON pt.created_by = up.id
|
||||
LEFT JOIN vehicle v ON pt.vehicle_id = v.id
|
||||
LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
|
||||
LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
|
||||
LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
|
||||
LEFT JOIN tpa ON pt.tpa_id = tpa.id
|
||||
LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
|
||||
LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
|
||||
LEFT JOIN user_profiles se ON pt.serviced_by = se.id
|
||||
LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
|
||||
LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
|
||||
|
||||
LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
|
||||
LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
|
||||
LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
|
||||
@ -3698,21 +3695,7 @@
|
||||
|
||||
0 AS reward,
|
||||
|
||||
COALESCE((
|
||||
SELECT SUM(
|
||||
COALESCE(cs.actual_bp_brokerage_amt,0) +
|
||||
COALESCE(cs.actual_tp_brokerage_amt,0) +
|
||||
COALESCE(cs.actual_tep_brokerage_amt,0)
|
||||
)
|
||||
FROM co_share_stmt_details cs
|
||||
JOIN insurer_statements i ON cs.statement_id = i.id
|
||||
WHERE cs.co_share_id = pcsd.id
|
||||
AND i.invoice_status IS NOT NULL
|
||||
AND i.month = insq.month
|
||||
AND i.is_active = 1
|
||||
AND cs.is_active = 1
|
||||
GROUP BY pt.policy_no, i.month, pcsd.insurer_id
|
||||
),0) AS billed_amt,
|
||||
COALESCE(MAX(bds_billed.billed_amt), 0) AS billed_amt,
|
||||
|
||||
CASE
|
||||
WHEN pcsd.co_share_type IN (0,1) THEN pt.policy_no
|
||||
@ -3774,27 +3757,40 @@
|
||||
LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
|
||||
LEFT JOIN co_share_stmt_details cssd ON pcsd.id = cssd.co_share_id
|
||||
LEFT JOIN insurer_statements insq ON cssd.statement_id = insq.id
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
cs.co_share_id,
|
||||
i.month,
|
||||
SUM(
|
||||
COALESCE(cs.actual_bp_brokerage_amt, 0) +
|
||||
COALESCE(cs.actual_tp_brokerage_amt, 0) +
|
||||
COALESCE(cs.actual_tep_brokerage_amt, 0)
|
||||
) AS billed_amt
|
||||
FROM co_share_stmt_details cs
|
||||
JOIN insurer_statements i ON cs.statement_id = i.id
|
||||
WHERE i.invoice_status IS NOT NULL
|
||||
AND i.is_active = 1
|
||||
AND cs.is_active = 1
|
||||
GROUP BY cs.co_share_id, i.month
|
||||
HAVING billed_amt <> 0
|
||||
) bds_billed ON bds_billed.co_share_id = pcsd.id
|
||||
AND bds_billed.month = insq.month
|
||||
JOIN clients c ON c.id = pt.client_id
|
||||
LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
|
||||
LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
|
||||
LEFT JOIN user_profiles up ON pt.created_by = up.id
|
||||
LEFT JOIN vehicle v ON pt.vehicle_id = v.id
|
||||
LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
|
||||
LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
|
||||
LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
|
||||
LEFT JOIN tpa ON pt.tpa_id = tpa.id
|
||||
LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
|
||||
LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
|
||||
LEFT JOIN user_profiles se ON pt.serviced_by = se.id
|
||||
LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
|
||||
LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
|
||||
|
||||
LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
|
||||
LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
|
||||
LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
|
||||
LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
|
||||
|
||||
|
||||
WHERE pt.is_active = 1
|
||||
AND pcsd.is_active = 1
|
||||
AND cssd.is_active = 1
|
||||
@ -3885,16 +3881,7 @@
|
||||
|
||||
0 AS total_irda_amt_2,
|
||||
|
||||
COALESCE((
|
||||
SELECT SUM(cs.reward)
|
||||
FROM co_share_stmt_details cs
|
||||
JOIN insurer_statements i ON cs.statement_id = i.id
|
||||
WHERE cs.co_share_id = pcsd.id
|
||||
AND i.month = insq.month
|
||||
AND i.is_active = 1
|
||||
AND cs.is_active = 1
|
||||
GROUP BY pt.policy_no, i.month, pcsd.insurer_id
|
||||
),0) AS reward,
|
||||
COALESCE(bds_reward.reward, 0) AS reward,
|
||||
|
||||
0 AS billed_amt,
|
||||
|
||||
@ -3958,21 +3945,30 @@
|
||||
LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
|
||||
LEFT JOIN co_share_stmt_details cssd ON pcsd.id = cssd.co_share_id
|
||||
LEFT JOIN insurer_statements insq ON cssd.statement_id = insq.id
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
cs.co_share_id,
|
||||
i.month,
|
||||
SUM(cs.reward) AS reward
|
||||
FROM co_share_stmt_details cs
|
||||
JOIN insurer_statements i ON cs.statement_id = i.id
|
||||
WHERE i.is_active = 1
|
||||
AND cs.is_active = 1
|
||||
GROUP BY cs.co_share_id, i.month
|
||||
HAVING reward <> 0
|
||||
) bds_reward ON bds_reward.co_share_id = pcsd.id
|
||||
AND bds_reward.month = insq.month
|
||||
JOIN clients c ON c.id = pt.client_id
|
||||
LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
|
||||
LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
|
||||
LEFT JOIN user_profiles up ON pt.created_by = up.id
|
||||
LEFT JOIN vehicle v ON pt.vehicle_id = v.id
|
||||
LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
|
||||
LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
|
||||
LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
|
||||
LEFT JOIN tpa ON pt.tpa_id = tpa.id
|
||||
LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
|
||||
LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
|
||||
LEFT JOIN user_profiles se ON pt.serviced_by = se.id
|
||||
LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
|
||||
LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
|
||||
|
||||
LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
|
||||
LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
|
||||
LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
|
||||
@ -4102,17 +4098,19 @@
|
||||
|
||||
/**
|
||||
* Cached processed BDS report list for a given filter set.
|
||||
* Returns ['rows' => array, 'totals' => array].
|
||||
*/
|
||||
public function getCachedBDSReportList(array $filters): array
|
||||
{
|
||||
$cacheKey = 'bds_report_v1_' . md5(json_encode($filters));
|
||||
$cacheKey = 'bds_report_v2_' . md5(json_encode($filters));
|
||||
$cache = \Config\Services::cache();
|
||||
$cached = $cache->get($cacheKey);
|
||||
|
||||
if (is_array($cached)) {
|
||||
if (is_array($cached) && isset($cached['rows']) && isset($cached['totals'])) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// Legacy cache entries stored raw row arrays — ignore and rebuild.
|
||||
$processed = $this->getBDSReportList(
|
||||
$filters['start_date'] ?? 0,
|
||||
$filters['end_date'] ?? 0,
|
||||
@ -4128,9 +4126,14 @@
|
||||
$filters['where'] ?? []
|
||||
);
|
||||
|
||||
$cache->save($cacheKey, $processed, 300);
|
||||
$payload = [
|
||||
'rows' => $processed,
|
||||
'totals' => $this->calculateBDSReportTotals($processed),
|
||||
];
|
||||
|
||||
return $processed;
|
||||
$cache->save($cacheKey, $payload, 300);
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4138,11 +4141,14 @@
|
||||
*/
|
||||
public function getBDSReportListDataTable(int $draw, int $start, int $length, string $searchValue, array $filters): array
|
||||
{
|
||||
$allRows = $this->getCachedBDSReportList($filters);
|
||||
$cached = $this->getCachedBDSReportList($filters);
|
||||
$allRows = $cached['rows'];
|
||||
$recordsTotal = count($allRows);
|
||||
$totals = $cached['totals'];
|
||||
|
||||
if ($searchValue !== '') {
|
||||
$allRows = $this->filterBDSReportRowsBySearch($allRows, $searchValue);
|
||||
$totals = $this->calculateBDSReportTotals($allRows);
|
||||
}
|
||||
|
||||
$recordsFiltered = count($allRows);
|
||||
@ -4160,7 +4166,7 @@
|
||||
'recordsTotal' => $recordsTotal,
|
||||
'recordsFiltered' => $recordsFiltered,
|
||||
'data' => $pageRows,
|
||||
'totals' => $this->calculateBDSReportTotals($allRows),
|
||||
'totals' => $totals,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -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',
|
||||
@ -874,25 +878,37 @@ class TicketMasterModel extends Model
|
||||
public function getDashData($claim_statuses, $limit)
|
||||
{
|
||||
$finalResults = [];
|
||||
if (empty($claim_statuses)) {
|
||||
return $finalResults;
|
||||
}
|
||||
|
||||
foreach ($claim_statuses as $typeId => $statuses) {
|
||||
// Get all active, non-null claim status tickets for this type
|
||||
$builder = $this->db->table('ticket_master tm')
|
||||
->select('tm.id, tm.ticket_type_id, tcs.claim_status, th.last_claim_status_change')
|
||||
->join('ticket_claim_status tcs', 'tm.claim_status_id = tcs.id', 'left')
|
||||
->join(
|
||||
'(SELECT ticket_id, MAX(created_at) AS last_claim_status_change
|
||||
$typeIds = array_map('intval', array_keys($claim_statuses));
|
||||
|
||||
// One history aggregate + one ticket pull for all types (was 4 full scans).
|
||||
$builder = $this->db->table('ticket_master tm')
|
||||
->select('tm.id, tm.ticket_type_id, tcs.claim_status, th.last_claim_status_change')
|
||||
->join('ticket_claim_status tcs', 'tm.claim_status_id = tcs.id', 'left')
|
||||
->join(
|
||||
'(SELECT ticket_id, MAX(created_at) AS last_claim_status_change
|
||||
FROM ticket_history
|
||||
WHERE field_name = \'claim_status_id\'
|
||||
GROUP BY ticket_id) th',
|
||||
'tm.id = th.ticket_id',
|
||||
'left'
|
||||
)
|
||||
->where('tm.ticket_type_id', $typeId)
|
||||
->where('tm.is_active', 1)
|
||||
->where('tm.claim_status_id IS NOT NULL');
|
||||
'tm.id = th.ticket_id',
|
||||
'left'
|
||||
)
|
||||
->whereIn('tm.ticket_type_id', $typeIds)
|
||||
->where('tm.is_active', 1)
|
||||
->where('tm.claim_status_id IS NOT NULL');
|
||||
|
||||
$results = $builder->get()->getResultArray();
|
||||
$allRows = $builder->get()->getResultArray();
|
||||
|
||||
$rowsByType = [];
|
||||
foreach ($allRows as $row) {
|
||||
$rowsByType[$row['ticket_type_id']][] = $row;
|
||||
}
|
||||
|
||||
foreach ($claim_statuses as $typeId => $statuses) {
|
||||
$results = $rowsByType[$typeId] ?? [];
|
||||
|
||||
$summary = [
|
||||
'ticket_type_id' => $typeId,
|
||||
@ -908,7 +924,6 @@ class TicketMasterModel extends Model
|
||||
$threshold = date('Y-m-d H:i:s', strtotime("-{$dateLimit} days"));
|
||||
|
||||
$ticketIds = [];
|
||||
// dd($results);
|
||||
foreach ($results as $row) {
|
||||
if (
|
||||
$row['claim_status'] === $status &&
|
||||
@ -924,17 +939,14 @@ class TicketMasterModel extends Model
|
||||
}
|
||||
|
||||
$total++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ticket IDs array to a comma-separated string
|
||||
$summary[$alias . '_ids'] = implode(',', $ticketIds);
|
||||
}
|
||||
|
||||
$summary['total'] = $total;
|
||||
|
||||
// Add approved but not settled IDs and count
|
||||
$approvedNotSettled = $this->getNotSettledbutApprovedCount($typeId);
|
||||
$summary['approved_not_settled'] = $approvedNotSettled['count'];
|
||||
$summary['approved_not_settled_ids'] = $approvedNotSettled['ticket_ids'];
|
||||
@ -942,7 +954,6 @@ class TicketMasterModel extends Model
|
||||
$finalResults[$typeId] = $summary;
|
||||
}
|
||||
|
||||
// dd($finalResults);
|
||||
return $finalResults;
|
||||
}
|
||||
|
||||
|
||||
@ -259,7 +259,7 @@
|
||||
// $isActive = get_role_id() == STAFF_ROLE_ID && in_array(ENROLLMENT_TEAM_ID,user_team()) ? 'active show' : '';
|
||||
?> -->
|
||||
<li class="nav-item d-flex justify-content-center align-items-center ">
|
||||
<a href="#leads-dash-tab " data-toggle="tab" aria-expanded="true" class="nav-link px-3 py-1 dash-anchor nav-dash" id="leads_tab" >
|
||||
<a href="#leads-dash-tab" data-toggle="tab" aria-expanded="true" class="nav-link px-3 py-1 dash-anchor nav-dash" id="leads_tab" >
|
||||
<img src="<?= base_url() . "public"; ?>/assets/images/inactive_leads_and_bds_renewal.png" alt="Logo" height="14" class="inactive_leads">
|
||||
<img src="<?= base_url() . "public"; ?>/assets/images/active_leads_and_bds_renewal.png" alt="Logo" height="14"
|
||||
class="active_leads " style="display:none;">
|
||||
@ -277,14 +277,28 @@
|
||||
<div class="tab-content tab-content-styles">
|
||||
|
||||
<?php if ((get_role_id() == STAFF_ROLE_ID && in_array(CLAIMS_TEAM_ID,user_team())) || in_array(get_role_id(),[1,5])) :?>
|
||||
<?php include("claims_dash.php") ?>
|
||||
<?php if (!empty($lazy_load_claims)) : ?>
|
||||
<div class="tab-pane fade <?= $dash_claims_pane_active ?>" id="claims-dash-tab"
|
||||
data-dash-lazy-url="<?= esc(base_url('dashboard/claims-dash'), 'attr') ?>">
|
||||
<div class="text-center p-5 text-muted dash-lazy-placeholder">Click to load claims…</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<?php include("claims_dash.php") ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(in_array(get_role_id(), [1,2,3,5]) || (get_role_id() == 4 && in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()))) { ?>
|
||||
<!-- <?php //include('bds_dash.php'); ?> -->
|
||||
<?php } ?>
|
||||
<?php if ((get_role_id() == STAFF_ROLE_ID && in_array(ENROLLMENT_TEAM_ID,user_team())) || in_array(get_role_id(),[1,5])) :?>
|
||||
<?php include("leads_dash.php") ?>
|
||||
<?php if (!empty($lazy_load_leads)) : ?>
|
||||
<div class="tab-pane fade <?= $dash_leads_pane_active ?>" id="leads-dash-tab"
|
||||
data-dash-lazy-url="<?= esc(base_url('dashboard/leads-dash'), 'attr') ?>">
|
||||
<div class="text-center p-5 text-muted dash-lazy-placeholder">Click to load opportunities…</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<?php include("leads_dash.php") ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(in_array(get_role_id(), [1,2,3,5])) { ?>
|
||||
@ -307,6 +321,64 @@
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
function injectDashTabHtml($pane, html) {
|
||||
var nodes = $.parseHTML(html, document, true) || [];
|
||||
var $nodes = $(nodes);
|
||||
var styles = $nodes.filter('style').add($nodes.find('style'));
|
||||
styles.each(function () {
|
||||
var css = this.textContent || '';
|
||||
if (css && !$('style[data-dash-lazy-css="' + $pane.attr('id') + '"]').length) {
|
||||
$('<style>', { 'data-dash-lazy-css': $pane.attr('id'), text: css }).appendTo('head');
|
||||
}
|
||||
});
|
||||
var $newPane = $nodes.filter('.tab-pane').add($nodes.find('.tab-pane')).first();
|
||||
var innerHtml = $newPane.length ? $newPane.html() : html;
|
||||
$pane
|
||||
.removeAttr('data-dash-lazy-url')
|
||||
.html(innerHtml)
|
||||
.addClass('active show');
|
||||
}
|
||||
|
||||
function loadDashTabOnDemand($pane) {
|
||||
var url = $pane.attr('data-dash-lazy-url');
|
||||
if (!url || $pane.data('dash-lazy-loading')) {
|
||||
return;
|
||||
}
|
||||
$pane.data('dash-lazy-loading', true);
|
||||
$pane.html('<div class="text-center p-5 text-muted dash-lazy-placeholder">Loading…</div>');
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
dataType: 'html',
|
||||
timeout: 120000
|
||||
}).done(function (html) {
|
||||
injectDashTabHtml($pane, html);
|
||||
}).fail(function (xhr) {
|
||||
$pane.data('dash-lazy-loading', false);
|
||||
$pane.attr('data-dash-lazy-url', url);
|
||||
var msg = 'Failed to load (' + (xhr.status || 'timeout') + '). Click the tab again to retry.';
|
||||
$pane.html('<div class="text-center p-5 text-danger dash-lazy-placeholder">' + msg + '</div>');
|
||||
});
|
||||
}
|
||||
|
||||
// Load tab data when the user clicks Claims / Opportunities.
|
||||
$(document).on('click', '#claims_tab, #leads_tab', function () {
|
||||
var target = ($(this).attr('href') || '').trim();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
var $pane = $(target);
|
||||
if ($pane.length && $pane.attr('data-dash-lazy-url')) {
|
||||
loadDashTabOnDemand($pane);
|
||||
}
|
||||
});
|
||||
|
||||
// If Claims/Leads is the default visible pane, load it once after paint.
|
||||
$('.tab-pane.active[data-dash-lazy-url]').each(function () {
|
||||
loadDashTabOnDemand($(this));
|
||||
});
|
||||
|
||||
$('.nav-link').click(function(){
|
||||
|
||||
//image of tabs by default inactive before particular click
|
||||
|
||||
@ -163,17 +163,25 @@
|
||||
</td>
|
||||
<td><?php echo change_date_format($file['created_at'], 'Y-m-d H:i:s', 'd/m/Y h:i A') . ' by <strong>' . $file['user_name'] . '</strong>' ?> </td>
|
||||
<td>
|
||||
<?php
|
||||
$pendingCount = (int) ($file['pending_ticket_count'] ?? 0);
|
||||
$hasPendingTickets = !empty($file['tpa_id']) && $pendingCount > 0;
|
||||
?>
|
||||
<?php if ($file['status'] == "failed") { ?>
|
||||
<span style="color : #BD0707 ;"> <?= $file['status'] ?> </span>
|
||||
<span class='col-xl-3 col-lg-4 col-sm-6'>
|
||||
<!-- <a href="<?= base_url('util/claim_dump_excel_error/') . $file['file_id'] ?>" target="_blank" class='fe-alert-circle' data-err="<?= $file['file_id'] ?>"></a> -->
|
||||
<a href="#" class='fe-alert-circle' onclick="fetchFileError(<?= $file['file_id'] ?>)"></a>
|
||||
</span>
|
||||
<?php } else if ($file['status'] == "inprogress") { ?>
|
||||
<?php } else if ($file['status'] == "inprogress" || $file['status'] == "processing") { ?>
|
||||
<a data-id="<?= $file['status'] ?>" class="reload" href="#"
|
||||
style="color : #938e04ff ;">
|
||||
<?= $file['status'] ?>
|
||||
</a>
|
||||
<?php } else if ($hasPendingTickets) { ?>
|
||||
<span style="color:#D97706;" title="<?= $pendingCount ?> dump row(s) not moved to ticket master">
|
||||
partial
|
||||
</span>
|
||||
<div class="text-muted small mt-1"><?= $pendingCount ?> pending ticket(s)</div>
|
||||
<?php } else { ?>
|
||||
<span style="color : #34A853 ;"> <?= $file['status'] ?> </span>
|
||||
<?php } ?>
|
||||
@ -195,6 +203,30 @@
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($hasPendingTickets && $file['status'] !== 'processing') : ?>
|
||||
<a href="javascript:void(0);"
|
||||
class="dropdown-item"
|
||||
onclick="viewClaimDumpPendingData(<?= (int) $file['file_id']; ?>)">
|
||||
<i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>
|
||||
View Data (<?= $pendingCount; ?>)
|
||||
</a>
|
||||
<a href="javascript:void(0);"
|
||||
class="dropdown-item text-warning"
|
||||
onclick="reprocessClaimDumpPending(<?= (int) $file['file_id']; ?>, <?= $pendingCount; ?>)">
|
||||
<i class="mdi mdi-reload mr-2 text-warning font-18 vertical-middle"></i>
|
||||
Process Pending (<?= $pendingCount; ?>)
|
||||
</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>
|
||||
@ -209,6 +241,56 @@
|
||||
</div>
|
||||
</div><!-- end col -->
|
||||
|
||||
<!-- Pending dump rows (ticket_id NULL) modal -->
|
||||
<div class="modal fade" id="claim-dump-pending-modal" tabindex="-1" role="dialog" aria-labelledby="claim-dump-pending-title" aria-hidden="true" data-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="claim-dump-pending-title">Pending Dump Rows (ticket_id empty)</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="claim-dump-pending-loading" class="text-center py-4">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
<div id="claim-dump-pending-content" style="display:none;">
|
||||
<p class="mb-2 text-muted" id="claim-dump-pending-summary"></p>
|
||||
<div class="table-responsive" style="max-height: 420px; overflow:auto;">
|
||||
<table class="table table-sm table-bordered table-hover mb-0" id="claim-dump-pending-table">
|
||||
<thead class="thead-light" id="claim-dump-pending-thead"></thead>
|
||||
<tbody id="claim-dump-pending-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="claim-dump-pending-empty" class="text-center text-muted py-4" style="display:none;">
|
||||
No pending rows found.
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-warning" id="claim-dump-pending-process-btn" style="display:none;">
|
||||
Process Pending
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#claim-dump-pending-modal {
|
||||
z-index: 1060;
|
||||
}
|
||||
#claim-dump-pending-modal .modal-content {
|
||||
background: #fff;
|
||||
opacity: 1;
|
||||
}
|
||||
#claim-dump-pending-modal .modal-body {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Center modal content -->
|
||||
<div class="modal fade" id="cliam-dump-file-err-modal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
@ -709,6 +791,162 @@
|
||||
});
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function viewClaimDumpPendingData(file_id) {
|
||||
if (!file_id) {
|
||||
toastr.error('Invalid file reference', 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
var $loading = $('#claim-dump-pending-loading');
|
||||
var $content = $('#claim-dump-pending-content');
|
||||
var $empty = $('#claim-dump-pending-empty');
|
||||
var $processBtn = $('#claim-dump-pending-process-btn');
|
||||
|
||||
$('#claim-dump-pending-title').text('Pending Dump Rows');
|
||||
$('#claim-dump-pending-summary').text('');
|
||||
$('#claim-dump-pending-thead').empty();
|
||||
$('#claim-dump-pending-tbody').empty();
|
||||
$loading.show();
|
||||
$content.hide();
|
||||
$empty.hide();
|
||||
$processBtn.hide().off('click');
|
||||
|
||||
var myModal = new bootstrap.Modal(document.getElementById('claim-dump-pending-modal'));
|
||||
myModal.show();
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url("util/getClaimDumpPendingRows"); ?>',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
data: { file_id: file_id },
|
||||
success: function(response) {
|
||||
$loading.hide();
|
||||
if (!response || response.status !== true || !response.data) {
|
||||
toastr.error((response && response.message) ? response.message : 'Unable to load pending rows', 'Error');
|
||||
$empty.show().text((response && response.message) ? response.message : 'Unable to load pending rows');
|
||||
return;
|
||||
}
|
||||
|
||||
var data = response.data;
|
||||
var rows = data.rows || [];
|
||||
var headers = data.headers || [];
|
||||
|
||||
$('#claim-dump-pending-title').text(
|
||||
'Pending Dump Rows — ' + (data.file_name || ('File #' + file_id))
|
||||
);
|
||||
$('#claim-dump-pending-summary').text(
|
||||
rows.length + ' row(s) with ticket_id empty (not moved to ticket master).'
|
||||
);
|
||||
|
||||
if (!rows.length) {
|
||||
$empty.show();
|
||||
return;
|
||||
}
|
||||
|
||||
var headHtml = '<tr>';
|
||||
headHtml += '<th>#</th>';
|
||||
headers.forEach(function(h) {
|
||||
headHtml += '<th>' + (h.label || h.key) + '</th>';
|
||||
});
|
||||
headHtml += '</tr>';
|
||||
$('#claim-dump-pending-thead').html(headHtml);
|
||||
|
||||
var bodyHtml = '';
|
||||
rows.forEach(function(row, idx) {
|
||||
bodyHtml += '<tr>';
|
||||
bodyHtml += '<td>' + (idx + 1) + '</td>';
|
||||
headers.forEach(function(h) {
|
||||
var val = row[h.key];
|
||||
if (val === null || typeof val === 'undefined' || val === '') {
|
||||
val = '-';
|
||||
}
|
||||
bodyHtml += '<td>' + $('<div>').text(String(val)).html() + '</td>';
|
||||
});
|
||||
bodyHtml += '</tr>';
|
||||
});
|
||||
$('#claim-dump-pending-tbody').html(bodyHtml);
|
||||
$content.show();
|
||||
|
||||
$processBtn.show().on('click', function() {
|
||||
myModal.hide();
|
||||
reprocessClaimDumpPending(file_id, rows.length);
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
$loading.hide();
|
||||
$empty.show().text('Failed to load pending rows');
|
||||
toastr.error('Failed to load pending rows', 'Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function reprocessClaimDumpPending(file_id, pending_count) {
|
||||
if (!file_id) {
|
||||
toastr.error('Invalid file reference', 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Process pending dump rows?',
|
||||
text: (pending_count || 0) + ' row(s) have no ticket yet. This will try to create/link tickets again.',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Process',
|
||||
cancelButtonText: 'Cancel',
|
||||
}).then(function(result) {
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = '<?= base_url("util/reprocessClaimDumpPending"); ?>';
|
||||
sendAjaxRequestForGlobal(url, 'GET', { file_id: file_id }, function(response) {
|
||||
if (response && response.status === true) {
|
||||
toastr.success(response.message || 'Pending rows processed', 'Success');
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 800);
|
||||
} else {
|
||||
toastr.error((response && response.message) ? response.message : 'Reprocess failed', 'Error');
|
||||
}
|
||||
}, function() {
|
||||
toastr.error('Reprocess 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>`
|
||||
|
||||
@ -197,12 +197,9 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row d-flex align-items-center">
|
||||
<div class="goBack" style="padding-left: 25px;">
|
||||
<a href="#" onclick="hideAndShowTile('2')" ><b><i class="mdi mdi-chevron-left mdi-36px chevron-left-dash"></i></b> </a>
|
||||
</div>
|
||||
<div class="goBack" style="display: none;padding-left: 15px;">
|
||||
<a href="#" id="current_tile">Current Tile : </a>
|
||||
<div class="row d-flex align-items-center">
|
||||
<div class="goBack" style="display: none; padding-left: 25px;">
|
||||
<a href="#" onclick="hideAndShowTile('2')" title="Back"><b><i class="mdi mdi-chevron-left mdi-36px chevron-left-dash"></i></b></a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
@ -317,16 +314,6 @@
|
||||
|
||||
$('.claimTypeTile').hide();
|
||||
$('.claimStatusTitle_'+claimType).show();
|
||||
|
||||
if (claimType == 1) {
|
||||
$('#current_tile').text(" GMC");
|
||||
} else if (claimType == 2) {
|
||||
$('#current_tile').text(" GPA");
|
||||
} else if (claimType == 3) {
|
||||
$('#current_tile').text(" EDLI");
|
||||
} else if (claimType == 4) {
|
||||
$('#current_tile').text(" GTLI");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -751,6 +751,8 @@ input:checked + .slider_blue::before {
|
||||
$('.loader-mask').fadeIn();
|
||||
|
||||
var formData = new FormData($('#policy_form')[0]);
|
||||
// TPA is optional for GPA and some other policy types; ensure the key is always posted
|
||||
formData.set('tpa', $('#tpa').val() || '');
|
||||
['policy_start_date', 'policy_end_date', 'open_date', 'close_date'].forEach(function(fieldName) {
|
||||
var val = formData.get(fieldName);
|
||||
if (val) {
|
||||
@ -2056,7 +2058,11 @@ input:checked + .slider_blue::before {
|
||||
if(res.status == true){
|
||||
|
||||
$('#insurer').val(res.data.insurer_branch_id + '-' + res.data.insurer_id).change();
|
||||
$('#tpa').val(res.data.tpa_branch_id + '-' + res.data.tpa_id).change();
|
||||
var baseTpaValue = '';
|
||||
if (res.data.tpa_branch_id && res.data.tpa_id) {
|
||||
baseTpaValue = res.data.tpa_branch_id + '-' + res.data.tpa_id;
|
||||
}
|
||||
$('#tpa').val(baseTpaValue).change();
|
||||
$('#policy_no').val(res.data.policy_no).change();
|
||||
// $('#open_date').val(rearrangeDateFormat(res.data.open_date)).change();
|
||||
// $('#close_date').val(rearrangeDateFormat(res.data.close_date)).change();
|
||||
|
||||
@ -188,12 +188,9 @@
|
||||
|
||||
|
||||
|
||||
<div class="row d-flex align-items-center">
|
||||
<div class="status_tile" style="padding-left: 25px;">
|
||||
<a href="#" onclick="hide_and_show_tile('2')" ><b><i class="mdi mdi-chevron-left mdi-36px chevron-left-dash"></i></b> </a>
|
||||
</div>
|
||||
<div class="status_tile" style="padding-left: 15px;">
|
||||
<a href="#" id="main_tile">Current Tile : </a>
|
||||
<div class="row d-flex align-items-center">
|
||||
<div class="status_tile" style="display: none; padding-left: 25px;">
|
||||
<a href="#" onclick="hide_and_show_tile('2')" title="Back"><b><i class="mdi mdi-chevron-left mdi-36px chevron-left-dash"></i></b></a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
@ -344,13 +341,9 @@
|
||||
|
||||
if (type == 1 && leadType != null && leadType == 1) {
|
||||
$('.leadStatusTitle_1').show();
|
||||
$('#main_tile').text("Opportunities");
|
||||
|
||||
}
|
||||
if (type == 1 && leadType != null && leadType == 2) {
|
||||
$('.leadStatusTitle_2').show();
|
||||
$("#main_tile").text(" BDS Renewals");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
|
||||
<div class="badge-container">
|
||||
Total Policy Count: <span class="text-primary"><?= $policy_count ?? 0 ?></span>
|
||||
Total Policy Count: <span class="text-primary" id="total_policy_count"><?= $policy_count ?? 0 ?></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user