FEAT_VIEW_PARTIALLY_SUCCESS_DATA

This commit is contained in:
VENKATESHWARAN 2026-07-13 12:30:07 +05:30
parent bec7e9b76d
commit 67684a55ec
3 changed files with 560 additions and 2 deletions

View File

@ -445,6 +445,8 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
$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');

View File

@ -4360,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{
@ -4562,6 +4566,362 @@ class TicketController extends BaseController
], 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()
{

View File

@ -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,21 @@
</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"
@ -218,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">
@ -751,6 +824,129 @@
});
}
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>`