MERGE_UAT_BUG_FIXES

This commit is contained in:
Ubuntu 2026-07-22 11:05:12 +05:30
commit 44d6301f0a
6 changed files with 671 additions and 109 deletions

View File

@ -871,6 +871,7 @@ $routes->group("/bdsReport", ["filter" => "authMVC"], function ($routes) {
//New Tickets
$routes->group("/ticket", ["filter" => "authMVC"], function ($routes) {
$routes->match( ['get', 'post'], 'list','TicketController::ticketList');
$routes->post('export','TicketController::exportTickets');
$routes->get('feedback-list','TicketController::feedbackList');
$routes->match(['get', 'post'], 'claim-upload','TicketController::claimDumpUpload');
$routes->get('remove','TicketController::removeTicket');

View File

@ -463,19 +463,31 @@ class LeadsController extends BaseController
// If an existing client is selected from the dropdown, `client_id` will be present.
if (empty($postData['client_id'])) {
$ignoreClientId = null;
$lead = null;
if ($id) { // This is an update of a lead
$lead = $this->leadsModel->find($id);
if ($lead && !empty($lead['is_client_created'])) {
$ignoreClientId = $lead['is_client_created'];
if ($lead && ! empty($lead['is_client_created'])) {
$ignoreClientId = (int) $lead['is_client_created'];
}
}
$submittedName = trim((string) ($postData['client_name'] ?? ''));
$submittedShort = trim((string) ($postData['client_short_name'] ?? ''));
$nameUnchanged = $lead && strcasecmp(trim((string) ($lead['client_name'] ?? '')), $submittedName) === 0;
$shortUnchanged = $lead && strcasecmp(trim((string) ($lead['client_short_name'] ?? '')), $submittedShort) === 0;
if ($ignoreClientId) {
$clientNameRules .= '|is_unique[clients.client_name,id,' . $ignoreClientId . ']';
$clientShortNameRules .= '|is_unique[clients.short_name,id,' . $ignoreClientId . ']';
} else {
$clientNameRules .= '|is_unique[clients.client_name]';
$clientShortNameRules .= '|is_unique[clients.short_name]';
// On edit, unchanged names belong to this opportunity — do not treat as duplicates.
// (Client row may already exist even when is_client_created is unset.)
if (! $nameUnchanged) {
$clientNameRules .= '|is_unique[clients.client_name]';
}
if (! $shortUnchanged) {
$clientShortNameRules .= '|is_unique[clients.short_name]';
}
}
}
@ -768,6 +780,15 @@ class LeadsController extends BaseController
$data['client_id'] = 0;
$data['client_branch_id'] = 0;
$data['source_policy_id'] = 0;
// Keep existing client link on update when "Existing Client" is off.
// Otherwise is_client_created is wiped to null and later edits fail uniqueness checks.
if (! empty($data['id'])) {
$existingLead = $this->leadsModel->find($data['id']);
if ($existingLead && ! empty($existingLead['is_client_created'])) {
$data['is_client_created'] = $existingLead['is_client_created'];
}
}
}
}
@ -4168,7 +4189,7 @@ class LeadsController extends BaseController
$data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first();
if (! $data) {
$this->myLogger->logme('featchLeadDataAndInsertClient', "Lead not found or inactive", ['lead_id' => $lead_id]);
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Lead not found or inactive', ['lead_id' => $lead_id]);
return $this->respond(['status' => false, 'message' => 'Failed to create Client', 'data' => null], 200);
}
@ -4186,10 +4207,10 @@ class LeadsController extends BaseController
], 200);
}
$this->myLogger->logme('featchLeadDataAndInsertClient', "Client creation failed after processing", ['lead_id' => $lead_id]);
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Client creation failed after processing', ['lead_id' => $lead_id]);
return $this->respond(['status' => false, 'message' => 'Failed to create client', 'data' => $data], 200);
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient failed');
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient failed: ' . $e->getMessage());
return $this->respond(['status' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()], 500);
}
}
@ -4210,17 +4231,40 @@ class LeadsController extends BaseController
return false;
}
$client_id = $this->clientModel->insert($this->prepareClientData($data));
if (! $client_id) {
return false;
}
// Reuse client/branch from a previous partial attempt (client created, policy failed).
$existingClientId = (int) ($data['is_client_created'] ?? 0);
if ($existingClientId > 0) {
$client_id = $existingClientId;
$branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id));
if (! $branch_id) {
return false;
}
$existingBranch = $this->clientBranchModel
->where('client_id', $client_id)
->where('branch_name', $data['branch_name'] ?? null)
->where('branch_code', $data['branch_code'] ?? null)
->where('is_active', 1)
->first();
$this->levelContactModel->insert($this->prepareContactData($data, $branch_id));
if ($existingBranch) {
$branch_id = (int) $existingBranch['id'];
} else {
$branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id));
if (! $branch_id) {
return false;
}
$this->levelContactModel->insert($this->prepareContactData($data, $branch_id));
}
} else {
$client_id = $this->clientModel->insert($this->prepareClientData($data));
if (! $client_id) {
return false;
}
$branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id));
if (! $branch_id) {
return false;
}
$this->levelContactModel->insert($this->prepareContactData($data, $branch_id));
}
$policy_ids = [];
$lead_ids = [];
@ -4258,7 +4302,7 @@ class LeadsController extends BaseController
'lead_ids' => $lead_ids,
];
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientWithMatchingLeadsPolicies failed');
$this->myLogger->logme('error', 'createClientWithMatchingLeadsPolicies failed: ' . $e->getMessage());
return false;
}
}
@ -4349,7 +4393,7 @@ class LeadsController extends BaseController
return $client_policy_id;
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientPolicyWithLeadData failed');
$this->myLogger->logme('error', 'createClientPolicyWithLeadData failed: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine());
return false;
}
}
@ -4607,9 +4651,20 @@ class LeadsController extends BaseController
//Function for convert the RFQ and QCR Json to Policy Terms Json
public function convertQCRJsonToPolicyTerms($data, $policy_type, $proposel_name, $insurer_name)
{
// Extract special conditions from original QCR data first.
// transformProposelData drops the Quote Asked column, which is the label source.
$specialConditionTerms = $this->extractSpecialConditionsFromQCR(
$data,
$policy_type,
$proposel_name,
$insurer_name
);
//transform the data into the currernt proposel and insurere ( get single proposel )
$data = $this->transformProposelData($data, $proposel_name, $insurer_name);
$terms_array = $specialConditionTerms;
// Initialize age_ratio based on policy type
$age_ratio = $policy_type == 2 ? [
'self' => ['min' => '18', 'max' => '60'],
@ -4641,21 +4696,14 @@ class LeadsController extends BaseController
continue;
}
// Special conditions already extracted above.
if (str_starts_with((string) $item, 'special_condition')) {
continue;
}
switch (true) {
// CASE 1: Handle special conditions
case str_starts_with($item, 'special_condition') && $parentth === $proposel_name && $subth === $insurer_name:
// log_message("error","INPUT".json_encode($input_value));
$parts = explode('-', $input_value);
$labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label';
$inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input';
$terms_array[$labelKey][] = $parts[0] ?? '';
$terms_array[$inputKey][] = $parts[1] ?? '';
break;
// CASE 2: Handle sum insured
// CASE 1: Handle sum insured
case in_array($item, ['sum_insured', 'sumInsured2']):
$si_amt = explode(',', $value);
$terms_array[$item] = $si_amt[0] ?? '';
@ -4668,7 +4716,7 @@ class LeadsController extends BaseController
break;
// CASE 3: Handle family floaters
// CASE 2: Handle family floaters
case $item === 'family_composition':
$terms_array['family_floaters'] = isJsonString($input_value) ? json_decode($input_value, true) : $input_value;
@ -4691,6 +4739,138 @@ class LeadsController extends BaseController
return json_encode($terms_array);
}
/**
* Build special_condition_label/input arrays from QCR rows.
*
* Mapping:
* - Canonical RFQ hidden format "label--value" (or display "label - value") on insurer cell
* - Otherwise Quote Asked = label, insurer cell = value
*
* @return array<string, list<string>>
*/
private function extractSpecialConditionsFromQCR($data, $policy_type, $proposel_name, $insurer_name): array
{
$labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label';
$inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input';
$result = [
$labelKey => [],
$inputKey => [],
];
if (empty($data['table_data']['data']) || ! is_array($data['table_data']['data'])) {
return [];
}
foreach ($data['table_data']['data'] as $dataRow) {
$item = $dataRow['items'] ?? '';
if (! str_starts_with((string) $item, 'special_condition')) {
continue;
}
$quoteAsked = '';
$insurerDisplay = '';
$insurerInput = '';
foreach ($dataRow['data'] ?? [] as $cellData) {
$parentth = $cellData['parentth'] ?? '';
$subth = $cellData['subth'] ?? '';
if ($parentth !== $proposel_name) {
continue;
}
$cellInput = isset($cellData['input_value']) && $cellData['input_value'] !== ''
? $cellData['input_value']
: ($cellData['value'] ?? '');
$cellValue = $cellData['value'] ?? '';
if ($subth === 'Quote Asked') {
$quoteAsked = $this->normalizeSpecialConditionCellText($cellValue, $cellInput);
}
if ($subth === $insurer_name) {
$insurerDisplay = is_string($cellValue) ? trim($cellValue) : '';
$insurerInput = is_string($cellInput) ? trim((string) $cellInput) : '';
}
}
[$label, $input] = $this->parseSpecialConditionParts($insurerInput, $insurerDisplay, $quoteAsked);
if ($label === '' && $input === '') {
continue;
}
$result[$labelKey][] = $label;
$result[$inputKey][] = $input;
}
if ($result[$labelKey] === [] && $result[$inputKey] === []) {
return [];
}
return $result;
}
/**
* Prefer visible cell text; fall back to hidden input_value.
*/
private function normalizeSpecialConditionCellText($display_value, $input_value): string
{
$display = is_string($display_value) ? trim($display_value) : '';
if ($display !== '') {
return $display;
}
return is_string($input_value) ? trim($input_value) : '';
}
/**
* Parse RFQ/QCR special-condition cells into [label, value].
*
* @return array{0: string, 1: string}
*/
private function parseSpecialConditionParts($input_value, $display_value = '', $quote_asked = ''): array
{
// Prefer canonical hidden "label--value"; otherwise prefer visible insurer text
// (QCR edits often update the cell text but leave the hidden input stale/empty).
$raw = '';
if (is_string($input_value) && str_contains($input_value, '--')) {
$raw = trim($input_value);
} elseif (is_string($display_value) && trim($display_value) !== '') {
$raw = trim($display_value);
} elseif (is_string($input_value)) {
$raw = trim($input_value);
}
$quoteAsked = is_string($quote_asked) ? trim($quote_asked) : '';
if ($raw === '' && $quoteAsked === '') {
return ['', ''];
}
if ($raw !== '' && str_contains($raw, '--')) {
$parts = explode('--', $raw, 2);
return [trim($parts[0] ?? ''), trim($parts[1] ?? '')];
}
if ($raw !== '' && str_contains($raw, ' - ')) {
$parts = explode(' - ', $raw, 2);
return [trim($parts[0] ?? ''), trim($parts[1] ?? '')];
}
// QCR columns: Quote Asked = label, insurer response = value
if ($quoteAsked !== '' && $raw !== '') {
return [$quoteAsked, $raw];
}
if ($quoteAsked !== '') {
return [$quoteAsked, $quoteAsked];
}
// Single insurer cell with no Quote Asked / separator: use as label and value
return [$raw, $raw];
}
public function convertNonEbQCRJsonToPolicyTerms($allTableData)
{
@ -4747,6 +4927,14 @@ class LeadsController extends BaseController
public function getPlacementJson(array $data): ?string
{
$proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? [];
$proposelName = $proposel_data['proposel_name'] ?? '';
$insurerName = $proposel_data['insurer_name'] ?? '';
// Non-EB placement JSON is filtered by the won proposal/insurer selection.
// Without that selection, skip placement terms rather than throwing.
if ($proposelName === '' || $insurerName === '') {
return null;
}
$QCRData = $this->RFQModel
->where('is_active', 1)
@ -4764,26 +4952,26 @@ class LeadsController extends BaseController
return null;
}
$placement_json_data = array_map(function ($jsonData) use ($proposel_data) {
$placement_json_data = array_map(function ($jsonData) use ($proposelName, $insurerName) {
return $this->transformNonEbProposelData(
$jsonData,
$proposel_data['proposel_name'] ?? '',
$proposel_data['insurer_name'] ?? '',
$proposelName,
$insurerName,
null,
1
);
}, $jsonArray);
if (! empty($proposalData)) {
if (! empty($proposalData['proposal_data']['over_all_column_data']) && is_array($proposalData['proposal_data']['over_all_column_data'])) {
foreach ($proposalData['proposal_data']['over_all_column_data'] as $key => &$proposal) {
// Keep only the required proposal
if ($key !== $proposel_data['proposel_name']) {
if ($key !== $proposelName) {
unset($proposalData['proposal_data']['over_all_column_data'][$key]);
} else {
// Within the matched proposal, filter insurers
if (! empty($proposal['insurers'])) {
$proposal['insurers'] = array_values(array_filter($proposal['insurers'], function ($insurer) use ($proposel_data) {
return $insurer['display_name'] === $proposel_data['insurer_name'];
$proposal['insurers'] = array_values(array_filter($proposal['insurers'], function ($insurer) use ($insurerName) {
return ($insurer['display_name'] ?? null) === $insurerName;
}));
}
}

View File

@ -461,6 +461,155 @@ class TicketController extends BaseController
}
}
public function exportTickets()
{
$format = $this->request->getPost('export_format') ?? 'csv';
$rows = $this->ticketSearch(); // uses same POST filters as the list
// Restrict to DataTables-visible rows when client sends export_ids
if ($this->request->getPost('export_ids') !== null) {
$exportIds = array_values(array_filter(array_map('intval', explode(',', (string) $this->request->getPost('export_ids')))));
if (empty($exportIds)) {
$rows = [];
} else {
$idOrder = array_flip($exportIds);
$rows = array_values(array_filter($rows, function ($row) use ($idOrder) {
return isset($idOrder[(int) ($row['id'] ?? 0)]);
}));
usort($rows, function ($a, $b) use ($idOrder) {
return ($idOrder[(int) $a['id']] ?? 0) <=> ($idOrder[(int) $b['id']] ?? 0);
});
}
}
$headers = [
'Created At', 'Updated At', 'Status', 'Policy Type', 'Claim Number', 'TPA ID', 'Emp ID', 'Emp Name',
'Insured Name', 'Corporate Name', 'ACM', 'Insurer', 'TPA', 'Policy No',
'Priority', 'Relationship', 'Emp Mobile', 'Emp Email', 'Emp Personal Email',
'Mode of Intimation', 'Claim Type', 'Claim Category', 'Hospital Name', 'DOA', 'DOD',
'Claim Amount', 'POD No.', 'Date of Join', 'Date of Inception', 'DOB',
'Date of Accident', 'Date of Death', 'Date of Intimation', 'Sum Insured',
'Raised Date', 'Registration Date', 'Query Received Date', 'Denial Date',
'Approved Date', 'Settled Date', 'Denial Reason', 'Approved Letter',
'Approved Amount', 'UTR Details', 'Settle Letter', 'Return Remark',
'Cancel Remark', 'AWB No & Courier Name', 'Non ID Reason',
'Payment Initiate Date', 'Approved Description', 'TAT', 'Claim Created By',
];
$claimTypeMap = $this->claimType;
$priorityMap = $this->priorityType;
$modeMap = $this->modeOFIntimate;
$ticketTypeMap = $this->ticketType;
$buildRow = function (array $row) use ($claimTypeMap, $priorityMap, $modeMap, $ticketTypeMap): array {
$typeId = (int) ($row['ticket_type_id'] ?? 0);
$claimKey = $row['claim_type'] ?? '';
$claimTypeLabel = $claimTypeMap[$typeId][$claimKey] ?? '';
$claimCreatedBy = strtoupper((string) ($row['claim_created_by'] ?? ''));
$claimCreatedByLabel = $claimCreatedBy === 'CRM' ? 'STAFF' : $claimCreatedBy;
$claimCategory = trim((string) ($row['tpa_claim_type'] ?? ''));
return [
$row['ticket_created_date'] ?? '',
$row['ticket_updated_date'] ?? '',
$row['status'] ?? '',
str_replace('Claim-', '', $ticketTypeMap[$typeId] ?? ''),
$row['claim_no'] ?? '',
$row['tpa_no'] ?? '',
$row['emp_code'] ?? '',
$row['emp_name'] ?? '',
$row['insured_name'] ?? '',
$row['short_name'] ?? '',
$row['acm_name'] ?? '',
$row['insurer_name'] ?? '',
$row['tpa_name'] ?? '',
$row['policy_no'] ?? '',
$priorityMap[$row['priority'] ?? 0] ?? '',
$row['relationship'] ?? '',
$row['emp_mobile'] ?? '',
$row['emp_mail'] ?? '',
$row['emp_personal_mail'] ?? '',
$modeMap[$row['mode_of_intimation'] ?? ''] ?? '',
$claimTypeLabel,
$claimCategory,
$row['hospital_name'] ?? '',
$row['doa'] ?? '',
$row['dod'] ?? '',
$row['claim_amount'] ?? '',
$row['pod_no'] ?? '',
$row['date_of_join'] ?? '',
$row['date_of_incep'] ?? '',
$row['dob'] ?? '',
$row['date_of_accident'] ?? '',
$row['date_of_death'] ?? '',
$row['date_of_intimat'] ?? '',
$row['si_amt'] ?? '',
$row['raised_date'] ?? '',
$row['registration_date'] ?? '',
$row['query_received_date'] ?? '',
$row['denial_date'] ?? '',
$row['approved_date'] ?? '',
$row['settled_date'] ?? '',
$row['denial_reason'] ?? '',
$row['approved_letter'] ?? '',
$row['approved_amount'] ?? '',
$row['utr_details'] ?? '',
$row['settle_letter'] ?? '',
$row['return_remark'] ?? '',
$row['cancel_remark'] ?? '',
$row['awb_no_courier_name'] ?? '',
$row['non_id_reason'] ?? '',
$row['pay_initiate_date'] ?? '',
$row['approved_description'] ?? '',
$row['tat'] ?? '',
$claimCreatedByLabel,
];
};
$filename = 'Claim-List-' . date('Y-m-d');
if ($format === 'excel') {
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Claim List');
$sheet->fromArray([$headers], null, 'A1');
$rowIndex = 2;
foreach ($rows as $row) {
$sheet->fromArray([$buildRow($row)], null, 'A' . $rowIndex);
$rowIndex++;
}
// Bold header row
$lastCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex(count($headers));
$sheet->getStyle('A1:' . $lastCol . '1')->getFont()->setBold(true);
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . $filename . '.xlsx"');
header('Cache-Control: max-age=0');
ob_end_clean();
$writer->save('php://output');
exit;
}
// Default: CSV
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $filename . '.csv"');
header('Cache-Control: max-age=0');
ob_end_clean();
$out = fopen('php://output', 'w');
// UTF-8 BOM for Excel compatibility
fwrite($out, "\xEF\xBB\xBF");
fputcsv($out, $headers);
foreach ($rows as $row) {
fputcsv($out, $buildRow($row));
}
fclose($out);
exit;
}
public function ticketSearch($action = null)
{
$db = db_connect();
@ -620,6 +769,12 @@ class TicketController extends BaseController
// print_r($search_data); die()
$where = [];
foreach ($search_data as $search_objects => $key) {
if (stripos((string)$search_objects, 'csrf') !== false) {
continue;
}
if ($search_objects === 'export_format' || $search_objects === 'export_ids') {
continue;
}
if ($key != null && $key != '' && $key != 0 && $search_objects != 'is_dashboard') {
if($search_objects == "date_type" && $search_data[$search_objects] === 'ticket_created_date'){
@ -730,6 +885,15 @@ class TicketController extends BaseController
}
}
$exportIdsRaw = $this->request->getPost('export_ids');
if ($exportIdsRaw !== null) {
$exportIds = array_values(array_filter(array_map('intval', explode(',', (string) $exportIdsRaw))));
if (empty($exportIds)) {
return [];
}
$query->whereIn('tm.id', $exportIds);
}
$data = $query->get()->getResultArray();
// dd($db->getLastQuery()->getQuery());die;
// dd($data);

View File

@ -113,7 +113,7 @@ class VidalApiController extends BaseController
}
if ($httpCode !== 200 && $httpCode !== 201) {
tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse");
tpa_claim_push_log($claimId, "VIDAL - Claim Push | File upload failed with HTTP Code: $httpCode | URL: $signedUrl | Request Body: $payload | Response: $uploadResponse");
return [
"status" => false,
"message" => "File upload failed",

View File

@ -30,6 +30,15 @@ table.dataTable tbody td {
color: #6c757d !important;
}
.emp-name-cell {
font-weight: 500;
}
.tpa-id-truncate {
cursor: default;
white-space: nowrap;
}
.policy-type-icon {
display: inline-flex;
align-items: center;
@ -93,6 +102,38 @@ table.dataTable tbody td {
white-space: nowrap;
}
#scroll-horizontal-datatable_wrapper .dt-buttons {
position: relative;
}
#scroll-horizontal-datatable_wrapper .dt-button-collection {
z-index: 9999 !important;
}
#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu {
display: none;
background: #00999E;
border: 1px solid #00999E;
border-radius: .25rem;
box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15);
padding: .25rem 0;
}
#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu .dropdown-item {
display: block;
width: 100%;
padding: .4rem .9rem;
clear: both;
color: #fff;
text-decoration: none;
white-space: nowrap;
}
#scroll-horizontal-datatable_wrapper .ticket-backend-export-menu .dropdown-item:hover {
background-color: #007A7E;
color: #fff;
}
</style>
<style>
.table th:nth-child(1),
@ -130,10 +171,10 @@ table.dataTable tbody td {
<th>Policy Type&nbsp;</th>
<th>Claim number&nbsp;</th>
<th>TPA ID&nbsp;</th>
<th>Emp ID&nbsp;</th>
<th>Emp name&nbsp;</th>
<th>Emp Name&nbsp;</th>
<th>Insured Name&nbsp;</th>
<th>Corporate name&nbsp;</th>
<th>Claim Type&nbsp;</th>
<th style="display: none;">ACM</th>
<th style="display: none;">Insurer</th>
@ -145,7 +186,6 @@ table.dataTable tbody td {
<th style="display: none;">Emp Email</th>
<th style="display: none;">Emp Personal Email</th>
<th style="display: none;">Mode of Intimation</th>
<th style="display: none;">Claim Type</th>
<th style="display: none;">Hospital Name</th>
<th style="display: none;">DOA</th>
<th style="display: none;">DOD</th>
@ -244,19 +284,20 @@ table.dataTable tbody td {
];
$claimSrcClass = $claimSrcClassMap[$claimSrc] ?? null;
$policyTypeLabel = str_replace('Claim-', '', $ticket_type[$row['ticket_type_id']] ?? 'N/A');
$tpaClaimTypeRaw = trim((string)($row['tpa_claim_type'] ?? ''));
$tpaClaimType = strtolower($tpaClaimTypeRaw);
$policyTypeIconHtml = '';
// $tpaClaimTypeRaw = trim((string)($row['tpa_claim_type'] ?? ''));
// $tpaClaimType = strtolower($tpaClaimTypeRaw);
// $policyTypeIconHtml = '';
if ($tpaClaimType !== '') {
if (strpos($tpaClaimType, 'cash') !== false) {
$policyTypeIconHtml = '<span class="policy-type-icon policy-type-icon--cashless" title="Cashless"><i class="mdi mdi-hospital-box-outline" aria-hidden="true"></i></span>';
} elseif (strpos($tpaClaimType, 'reimburse') !== false) {
$policyTypeIconHtml = '<span class="policy-type-icon policy-type-icon--reimbursement" title="Reimbursement"><i class="mdi mdi-cash-refund" aria-hidden="true"></i></span>';
}
}
// if ($tpaClaimType !== '') {
// if (strpos($tpaClaimType, 'cash') !== false) {
// $policyTypeIconHtml = '<span class="policy-type-icon policy-type-icon--cashless" title="Cashless"><i class="mdi mdi-hospital-box-outline" aria-hidden="true"></i></span>';
// } elseif (strpos($tpaClaimType, 'reimburse') !== false) {
// $policyTypeIconHtml = '<span class="policy-type-icon policy-type-icon--reimbursement" title="Reimbursement"><i class="mdi mdi-cash-refund" aria-hidden="true"></i></span>';
// }
// }
echo $policyTypeIconHtml . esc($policyTypeLabel);
// echo $policyTypeIconHtml . esc($policyTypeLabel);
echo esc($policyTypeLabel);
if ($claimSrcClass !== null) {
echo '<br><span class="claim-source-badge ' . esc($claimSrcClass, 'attr') . '">' . esc($claimSrc == "CRM" ? "STAFF" : $claimSrc) . '</span>';
}
@ -266,11 +307,31 @@ table.dataTable tbody td {
<span class="text-custom-grey"><br><?= "Created at ".$row['ticket_created_date']; ?></span>
<?php endif; ?>
</td>
<td><?php echo $row['tpa_no'] ?: 'N/A'; ?></td>
<td><?php echo $row['emp_code'] ?: 'N/A'; ?></td>
<td><?php echo $row['emp_name'] ?: 'N/A'; ?></td>
<td><?php
$tpaNo = $row['tpa_no'] ?: 'N/A';
$tpaNoMaxLen = 10;
if ($tpaNo !== 'N/A' && strlen($tpaNo) > $tpaNoMaxLen) {
echo '<span class="tpa-id-truncate" data-toggle="tooltip" data-placement="top" title="' . esc($tpaNo, 'attr') . '">' . esc(substr($tpaNo, 0, $tpaNoMaxLen) . '...') . '</span>';
} else {
echo esc($tpaNo);
}
?></td>
<td class="emp-name-cell"><?php echo $row['emp_name'] ?: 'N/A'; ?>
<span class="text-custom-grey"><br><?php echo $row['emp_code'] ?: 'N/A'; ?></span>
</td>
<td><?php echo $row['insured_name'] ?: 'N/A'; ?></td>
<td><?php echo $row['short_name'] ?: 'N/A'; ?></td>
<td class="emp-name-cell"><?php
$typeId = $row['ticket_type_id'] ?? 0;
$claimKey = $row['claim_type'] ?? '';
if ($typeId == 1) {
echo isset($claimType[1][$claimKey]) ? $claimType[1][$claimKey] : 'N/A';
} else {
echo isset($claimType[2][$claimKey]) ? $claimType[2][$claimKey] : 'N/A';
}
?>
<span class="text-custom-grey"><br><?php echo !empty($row['tpa_claim_type']) ? esc($row['tpa_claim_type']) : 'N/A'; ?></span>
</td>
<td style="display: none;"><?php echo $row['acm_name']; ?></td>
<td style="display: none;"><?php echo $row['insurer_name']; ?></td>
@ -282,17 +343,6 @@ table.dataTable tbody td {
<td style="display: none;"><?php echo $row['emp_mail']; ?></td>
<td style="display: none;"><?php echo $row['emp_personal_mail']; ?></td>
<td style="display: none;"><?php echo ($modeOFIntimate ?? [])[$row['mode_of_intimation'] ?? ''] ?? ''; ?></td>
<td style="display: none;">
<?php
$typeId = $row['ticket_type_id'] ?? 0;
$claimKey = $row['claim_type'] ?? '';
if ($typeId == 1) {
echo isset($claimType[1][$claimKey]) ? $claimType[1][$claimKey] : '';
} else {
echo isset($claimType[2][$claimKey]) ? $claimType[2][$claimKey] : '';
}
?>
</td>
<td style="display: none;"><?php echo $row['hospital_name']; ?></td>
<td style="display: none;"><?php echo change_date_format($row['doa'], null, 'd-m-Y'); ?></td>
<td style="display: none;"><?php echo $row['dod']; ?></td>
@ -385,6 +435,17 @@ function asText(value) {
return escapeHtml(value);
}
function getTruncatedTpaIdHtml(tpaNo) {
var value = (tpaNo === null || tpaNo === undefined || tpaNo === '') ? 'N/A' : String(tpaNo);
var maxLen = 10;
if (value === 'N/A' || value.length <= maxLen) {
return escapeHtml(value);
}
return '<span class="tpa-id-truncate" data-toggle="tooltip" data-placement="top" title="' + escapeHtml(value) + '">' +
escapeHtml(value.substring(0, maxLen) + '...') +
'</span>';
}
function getClaimTypeLabel(row) {
var typeId = Number(row.ticket_type_id || 0);
var claimKey = row.claim_type || '';
@ -478,7 +539,7 @@ function buildTicketRowHtml(row) {
var statusUpdated = row.ticket_updated_date ? '<span class="text-custom-grey"><br>Updated at ' + escapeHtml(row.ticket_updated_date) + '</span>' : '';
var claimCreated = row.ticket_created_date ? '<span class="text-custom-grey"><br>Created at ' + escapeHtml(row.ticket_created_date) + '</span>' : '';
var policyType = (window.ticketTypeMap[row.ticket_type_id] || '').replace('Claim-', '');
var policyTypeIcon = getPolicyTypeIconHtml(row.tpa_claim_type);
// var policyTypeIcon = getPolicyTypeIconHtml(row.tpa_claim_type);
var claimSourceBadge = getClaimSourceBadgeHtml(row.claim_created_by);
var modeOfIntimation = window.modeOfIntimateMap[row.mode_of_intimation] || '';
var priority = window.priorityTypeMap[row.priority || 0] || '';
@ -489,13 +550,14 @@ function buildTicketRowHtml(row) {
'<span class="status-tooltip">' + getStatusDisplay(row) + '</span>' +
statusUpdated +
'</td>' +
'<td>' + policyTypeIcon + asText(policyType) + (claimSourceBadge ? '<br>' + claimSourceBadge : '') + '</td>' +
// '<td>' + policyTypeIcon + asText(policyType) + (claimSourceBadge ? '<br>' + claimSourceBadge : '') + '</td>' +
'<td>' + asText(policyType) + (claimSourceBadge ? '<br>' + claimSourceBadge : '') + '</td>' +
'<td>' + asText(row.claim_no) + claimCreated + '</td>' +
'<td>' + asText(row.tpa_no) + '</td>' +
'<td>' + asText(row.emp_code) + '</td>' +
'<td>' + asText(row.emp_name) + '</td>' +
'<td>' + getTruncatedTpaIdHtml(row.tpa_no) + '</td>' +
'<td class="emp-name-cell">' + asText(row.emp_name) + '<span class="text-custom-grey"><br>' + asText(row.emp_code) + '</span></td>' +
'<td>' + asText(row.insured_name) + '</td>' +
'<td>' + asText(row.short_name) + '</td>' +
'<td class="emp-name-cell">' + asText(getClaimTypeLabel(row)) + '<span class="text-custom-grey"><br>' + asText(row.tpa_claim_type) + '</span></td>' +
'<td style="display: none;">' + escapeHtml(row.acm_name || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(row.insurer_name || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(row.tpa_name || '') + '</td>' +
@ -506,7 +568,6 @@ function buildTicketRowHtml(row) {
'<td style="display: none;">' + escapeHtml(row.emp_mail || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(row.emp_personal_mail || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(modeOfIntimation) + '</td>' +
'<td style="display: none;">' + escapeHtml(getClaimTypeLabel(row)) + '</td>' +
'<td style="display: none;">' + escapeHtml(row.hospital_name || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(row.doa || '') + '</td>' +
'<td style="display: none;">' + escapeHtml(row.dod || '') + '</td>' +
@ -553,7 +614,8 @@ function applyTicketRows(rows) {
if (window.ticketTableInstance) {
window.ticketTableInstance.clear();
tbody.html(html);
window.ticketTableInstance.rows.add(tbody.find('tr')).draw();
window.ticketTableInstance.rows.add(tbody.find('tr')).draw(false);
window.ticketTableInstance.columns.adjust();
} else {
tbody.html(html);
}
@ -618,27 +680,59 @@ $(document).ready(function() {
}
},
{
extend: 'collection',
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
className: 'btn app-btn-secondary ',
buttons: [
{
extend: 'csv',
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
className: 'app-btn-primary ',
title: 'Claim-List',
},
{
extend: 'excel',
text: '<i class="mdi mdi-file-excel " ></i><span class=" btn-custom"> EXCEL </span>',
title: 'claim-List',
sheetName: 'claim-List',
exportOptions: {
orthogonal: 'sort'
},
className: 'app-btn-primary ',
}
]
text: '<span class="btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
className: 'btn app-btn-secondary ticket-backend-export-btn',
name: 'ticketBackendExport',
action: function(e, dt, node, config) {
e.preventDefault();
e.stopPropagation();
var $btn = $(node);
var $wrap = $btn.closest('.dt-buttons');
var $menu = $wrap.find('.ticket-backend-export-menu');
if (!$menu.length) {
$menu = $(
'<div class="dropdown-menu ticket-backend-export-menu show" style="position:absolute;z-index:9999;min-width:140px;display:block;">' +
'<a class="dropdown-item ticket-export-csv" href="javascript:void(0);"><i class="mdi mdi-file-delimited mr-1"></i> CSV</a>' +
'<a class="dropdown-item ticket-export-excel" href="javascript:void(0);"><i class="mdi mdi-file-excel mr-1"></i> Excel</a>' +
'</div>'
);
$wrap.css('position', 'relative').append($menu);
$menu.css({
top: ($btn.position().top + $btn.outerHeight()) + 'px',
left: $btn.position().left + 'px'
});
$menu.on('click', '.ticket-export-csv', function(ev) {
ev.preventDefault();
ev.stopPropagation();
$menu.hide();
triggerTicketExport('csv');
});
$menu.on('click', '.ticket-export-excel', function(ev) {
ev.preventDefault();
ev.stopPropagation();
$menu.hide();
triggerTicketExport('excel');
});
} else {
$menu.toggle();
if ($menu.is(':visible')) {
$menu.css({
top: ($btn.position().top + $btn.outerHeight()) + 'px',
left: $btn.position().left + 'px',
display: 'block'
});
}
}
setTimeout(function() {
$(document).one('click.ticketExportMenu', function() {
$menu.hide();
});
}, 0);
}
}
],
language: {
@ -657,6 +751,7 @@ $(document).ready(function() {
pageLength: 10, // Set default number of rows per page (optional)
ordering: false,
});
window.ticketTableInstance.columns.adjust();
} else if (!ticketsTable.length) {
console.error("Table not found.");
}
@ -827,6 +922,48 @@ function removeClaim(input, ticket_id) {
}
function triggerTicketExport(format) {
var exportUrl = '<?= base_url('ticket/export') ?>';
// Reuse the same filter parameters that were last applied to the table
var stored = localStorage.getItem('filterData');
var filterData = stored ? JSON.parse(stored) : {};
filterData['export_format'] = format;
// Export only rows currently visible in DataTables (respects search box)
if (window.ticketTableInstance) {
var ids = window.ticketTableInstance
.rows({ search: 'applied' })
.nodes()
.to$()
.map(function () {
return $(this).data('id');
})
.get()
.filter(function (id) {
return id !== null && id !== undefined && id !== '';
});
filterData['export_ids'] = ids.join(',');
}
var $form = $('<form method="POST" style="display:none;"></form>').attr('action', exportUrl);
$.each(filterData, function(name, value) {
if (String(name).toLowerCase().indexOf('csrf') !== -1) {
return;
}
if (name === 'export_ids' || (value !== null && value !== undefined && value !== '')) {
$form.append($('<input type="hidden">').attr('name', name).val(value));
}
});
$('body').append($form);
$form.submit();
setTimeout(function() { $form.remove(); }, 3000);
}
$(document).on('click', 'tbody tr', function (e) {
// Exclude clicks on any elements inside the last column (actions)
if ($(e.target).closest('td').index() !== $(this).children('td').length - 1) {

View File

@ -484,6 +484,12 @@
max-width: none;
}
}
/* Keep Bootstrap modals visible above body backdrop (theme uses overflow:hidden on #wrapper) */
body.modal-open #wrapper,
body.modal-open .content-page {
overflow: visible !important;
}
</style>
@ -1150,7 +1156,79 @@
placeholder: "Start typing here...", // Optional placeholder text
};
var RFQ_MODAL_IDS = [
'list_model',
'internal_mail_modal',
'placement_mail_modal',
'view_demography_modal',
'other_docs_upload_modal'
];
function relocateRfqModalsToBody() {
RFQ_MODAL_IDS.forEach(function (modalId) {
var modalEl = document.getElementById(modalId);
if (modalEl && modalEl.parentElement !== document.body) {
document.body.appendChild(modalEl);
}
});
}
function clearOrphanedModalBackdrops() {
document.querySelectorAll('.modal-backdrop').forEach(function (el) {
el.remove();
});
document.body.classList.remove('modal-open');
document.body.style.removeProperty('padding-right');
}
/**
* Open a Bootstrap modal reliably on this page:
* - moves it under <body> (escapes #wrapper overflow:hidden)
* - prefers Bootstrap 4 window.bootstrap.Modal from vendor.min.js
* - reuses one instance per element
*/
function openBootstrapModal(modalId) {
var modalEl = document.getElementById(modalId);
if (!modalEl) {
console.error('Modal not found:', modalId);
return null;
}
if (modalEl.parentElement !== document.body) {
document.body.appendChild(modalEl);
}
clearOrphanedModalBackdrops();
if (typeof bootstrap !== 'undefined' && bootstrap.Modal) {
var existing = (typeof jQuery !== 'undefined') ? jQuery(modalEl).data('bs.modal') : null;
if (existing && typeof existing.show === 'function') {
existing.show();
return existing;
}
var modal = new bootstrap.Modal(modalEl);
if (typeof jQuery !== 'undefined') {
jQuery(modalEl).data('bs.modal', modal);
}
modal.show();
return modal;
}
if (typeof jQuery !== 'undefined' && jQuery.fn && typeof jQuery.fn.modal === 'function') {
jQuery(modalEl).modal('show');
// BS3 uses .in while theme CSS expects .show
modalEl.classList.add('show');
modalEl.style.display = 'block';
modalEl.removeAttribute('aria-hidden');
return null;
}
console.error('No compatible Bootstrap modal API found for', modalId);
return null;
}
$(document).ready(function() {
relocateRfqModalsToBody();
const path = window.location.pathname; // Get the current URL path
const segments = path.split('/'); // Split the path into segments
@ -4027,8 +4105,7 @@ function showModal(mail_type) {
}else if(mail_type == 2){
var myModal = new bootstrap.Modal(document.getElementById('internal_mail_modal'));
myModal.show();
openBootstrapModal('internal_mail_modal');
// getMailContent('internal_mail_content');
@ -4057,8 +4134,7 @@ function insurerAndClientMailPopUp(){
ajaxRequestForGetMailData(url);
var myModal = new bootstrap.Modal(document.getElementById('list_model'));
myModal.show();
openBootstrapModal('list_model');
}
function placementMailPopUp() {
@ -4093,8 +4169,7 @@ function placementMailPopUp() {
}
// Show the modal
const myModal = new bootstrap.Modal(document.getElementById('placement_mail_modal'));
myModal.show();
openBootstrapModal('placement_mail_modal');
}
function ajaxRequestForGetMailData(url) {
@ -7354,10 +7429,9 @@ function appendMultiFileData(data) {
// }
function viewDocs(){
// Show the modal
const myModal = new bootstrap.Modal(document.getElementById('other_docs_upload_modal'));
myModal.show();
openBootstrapModal('other_docs_upload_modal');
}
window.viewDocs = viewDocs;
</script>
@ -7846,8 +7920,7 @@ function appendMultiFileData(data) {
}
// Show the modal
const myModal = new bootstrap.Modal(document.getElementById('view_demography_modal'));
myModal.show();
openBootstrapModal('view_demography_modal');
// Hide loader
$('.loader').fadeOut();
@ -7857,8 +7930,7 @@ function appendMultiFileData(data) {
$('#demography_modal_body').append("<p style='text-align: center; color: #6c757d; font-style: italic;'>No Demography Data Found or Wrong File</p>")
// Show the modal
const myModal = new bootstrap.Modal(document.getElementById('view_demography_modal'));
myModal.show();
openBootstrapModal('view_demography_modal');
console.error('Error fetching data:', error);
console.error(xhr.responseText);