$nilClaimFlags
+ */
+ private function getClaimHistoryRowCount(array $postData, array $nilClaimFlags): int
+ {
+ $counts = [
+ count((array) ($postData['first_year'] ?? [])),
+ count($nilClaimFlags),
+ ];
+
+ $counts[] = count($this->getFinyearRowsFromPost($postData));
+
+ return max(0, ...$counts);
+ }
+
public function getLastFiveFinancialYears(): array
{
$year = (int) date('Y');
@@ -4921,8 +5156,11 @@ class LeadsController extends BaseController
$data['lead_edit_data']['multi_file_html'] = trim($html) !== '' ? $html : null;
}
- if (! in_array((int) $data['lead_edit_data']['lead_type'], [1, 3], true) && $data['lead_edit_data']['lead_form_type'] == 2) {
- $data['lead_edit_data']['claims_details_html'] = view('rfq/claims_details_non_eb', $data['lead_edit_data']);
+ if (in_array((int) $data['lead_edit_data']['lead_type'], [1, 2, 3], true) && $data['lead_edit_data']['lead_form_type'] == 2) {
+ $data['lead_edit_data']['claims_details_html'] = view('rfq/claims_details_non_eb', [
+ 'lead_edit_data' => $data['lead_edit_data'],
+ 'lastFiveYears' => $data['lastFiveYears'] ?? $this->getLastFiveFinancialYears(),
+ ]);
} else {
$data['lead_edit_data']['claims_details_html'] = "";
}
@@ -5372,6 +5610,16 @@ class LeadsController extends BaseController
{
helper('excel_util_helper');
+ $finyear = array_map(static function ($record) {
+ if (! is_array($record)) {
+ return $record;
+ }
+
+ unset($record['nil_claims']);
+
+ return $record;
+ }, $finyear);
+
$first = $finyear[0] ?? null;
if (! is_array($first) || $first === []) {
@@ -5844,8 +6092,11 @@ class LeadsController extends BaseController
public function generateViewPageHtml($policy_type_id, $data = [])
{
- $data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
- $data['tpa'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
+ $data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
+ $data['tpa'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
+ $data['lastFiveYears'] = $data['lastFiveYears'] ?? $this->getLastFiveFinancialYears();
+ $data['gpaClaimType'] = $data['gpaClaimType'] ?? $this->claim_type_for_gpa;
+ $data['causeOfDeath'] = $data['causeOfDeath'] ?? $this->cause_of_death;
$viewMap = [
1 => 'rfq/gpa',
@@ -6483,7 +6734,9 @@ class LeadsController extends BaseController
if (! empty($rfq_data['fin_years_claims']) && $claim_history == 1) {
- $claim_details = json_decode($rfq_data['fin_years_claims'], true) ?? [];
+ $claim_details = $this->sanitizeClaimDetailsForExport(
+ json_decode($rfq_data['fin_years_claims'], true) ?? []
+ );
if (! empty($claim_details['finyear'])) {
diff --git a/app/Controllers/TicketServiceController.php b/app/Controllers/TicketServiceController.php
index 24e5e463..ffa6f154 100644
--- a/app/Controllers/TicketServiceController.php
+++ b/app/Controllers/TicketServiceController.php
@@ -1297,6 +1297,7 @@ class TicketServiceController extends AdminController
} else {
//check the employee and insured
+ $params['is_from'] = 'claim_dump';
$employee_data = $ticketMasterModal->getEmployeeAndEmployeePolicyDetails($params) ?? [];
// dd($employee_data);
if (count($employee_data) == 0) {
diff --git a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php
index 566bb703..467fceba 100644
--- a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php
+++ b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php
@@ -19,6 +19,7 @@ abstract class BaseTpaClaimImportService
protected $claimDumpFileModel;
protected $clientPolicyModel;
protected $policyNumberMapping;
+ protected $tpaTableMapping;
public function __construct()
{
@@ -33,6 +34,14 @@ abstract class BaseTpaClaimImportService
(int) env('R_CARE_PRIMARY_KEY_CONSTANT') => 'Policy Number',
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => 'POLICY_NO',
];
+ $this->tpaTableMapping = [
+ (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',
+ ];
}
/**
@@ -58,8 +67,7 @@ abstract class BaseTpaClaimImportService
}
if (empty($rows)) {
- $this->db->transRollback(); // ROLLBACK BEFORE RETURN
- return ['status' => false, 'message' => 'Excel file contains no data or wrong file upload'];
+ return $this->failTpaClaimDumpInsert($fileId, 'Excel file contains no data or wrong file upload');
}
if(isset($this->policyNumberMapping[$fileData['tpa_id']]) && !empty($this->policyNumberMapping[$fileData['tpa_id']])){
@@ -70,22 +78,19 @@ abstract class BaseTpaClaimImportService
if($client_policy_data['policy_no'] != ($rows[0][$policy_number_column] ?? '')){
- $this->db->transRollback();
- return ['status' => false, 'message' => 'Policy number mismatch in the file and in the system'];
+ return $this->failTpaClaimDumpInsert($fileId, 'Policy number mismatch in the file and in the system');
}
$tpaInsertData = $this->mapTPAData($rows, $fileId);
if (empty($tpaInsertData)) {
- $this->db->transRollback(); // ROLLBACK BEFORE RETURN
- return ['status' => false, 'message' => 'These records already exist in the system.'];
+ return $this->failTpaClaimDumpInsert($fileId, 'These records already exist in the system.');
}
$return_res = $this->bulkInsertTPATable($tpaInsertData);
if ($return_res !== true) {
- $this->db->transRollback(); // ROLLBACK BEFORE RETURN
- return ['status' => false, 'message' => 'TPA Import bulk insert failed'];
+ return $this->failTpaClaimDumpInsert($fileId, 'TPA Import bulk insert failed');
}
// 2. Commit if everything is fine
@@ -93,9 +98,7 @@ abstract class BaseTpaClaimImportService
return ['status' => true, 'message' => 'File uploaded successfully', 'record_count' => count($tpaInsertData)];
} catch (\Throwable $e) {
- // 3. Rollback on any crash/exception
- $this->db->transRollback();
- return ['status' => false, 'message' => 'System error : ' . $e->getMessage()];
+ return $this->failTpaClaimDumpInsert($fileId, 'System error : ' . $e->getMessage());
}
}
@@ -113,7 +116,7 @@ abstract class BaseTpaClaimImportService
// Check if mapping failed
if (!$ticketMasterData['status']) {
- $this->db->transRollback(); // ALWAYS rollback before early return
+ $this->rollbackAndCleanupClaimDumpData($file_id);
return $ticketMasterData;
}
@@ -125,13 +128,11 @@ abstract class BaseTpaClaimImportService
if (!empty($ticketMasterData['mapped_array'])) {
$insert_res = $this->importClaimMaster($ticketMasterData['mapped_array']);
if (!$insert_res) {
- $this->db->transRollback();
- return ['status' => false, 'message' => 'Ticket Master Claim bulk insert failed'];
+ return $this->failTicketMasterInsert($file_id, 'Ticket Master Claim bulk insert failed');
}
// Map newly created ticket IDs back to the TPA staging table
if (!$this->updateTicketIdInTPATable($file_id)) {
- $this->db->transRollback();
- return ['status' => false, 'message' => 'Updating ticket_id in TPA table failed'];
+ return $this->failTicketMasterInsert($file_id, 'Updating ticket_id in TPA table failed');
}
$message .= 'Ticket Master Claim bulk insert success. ';
@@ -144,8 +145,7 @@ abstract class BaseTpaClaimImportService
if (!empty($ticketMasterData['rejected_reason_array'])) {
$update_res = $this->updateTicketMasterRejectedReasonInTPATable($ticketMasterData['rejected_reason_array']);
if (!$update_res) {
- $this->db->transRollback();
- return ['status' => false, 'message' => 'Updating rejected reasons failed'];
+ return $this->failTicketMasterInsert($file_id, 'Updating rejected reasons failed');
}
$message .= empty($ticketMasterData['mapped_array'])
@@ -156,24 +156,69 @@ abstract class BaseTpaClaimImportService
// If nothing was processed but no error occurred
if (!$hasExecutedTask) {
- $this->db->transRollback();
- return ['status' => false, 'message' => 'No data found to process.'];
+ return $this->failTicketMasterInsert($file_id, 'No data found to process.');
}
// 2. Commit the transaction
$this->db->transCommit();
+
+ if (!$status) {
+ $this->cleanupClaimDumpData($file_id);
+ }
+
return ['status' => $status, 'message' => trim($message)];
} catch (\Throwable $th) {
- // 3. Rollback on crash
- $this->db->transRollback();
- return [
- 'status' => false,
- 'message' => 'System error during Ticket Master Insert: ' . $th->getMessage()
- ];
+ $fileId = (int) ($params['file_id'] ?? 0);
+ return $this->failTicketMasterInsert(
+ $fileId,
+ 'System error during Ticket Master Insert: ' . $th->getMessage()
+ );
}
}
+ /**
+ * Remove TPA staging rows and ticket_master rows created for a failed claim dump upload.
+ */
+ protected function cleanupClaimDumpData(int $fileId): void
+ {
+ if ($fileId <= 0) {
+ return;
+ }
+
+ $fileData = $this->claimDumpFileModel->where('id', $fileId)->first();
+ if (empty($fileData)) {
+ return;
+ }
+
+ $tpaId = (int) ($fileData['tpa_id'] ?? 0);
+ $tpaTable = $this->tpaTableMapping[$tpaId] ?? null;
+
+ if ($tpaTable !== null) {
+ $this->db->table($tpaTable)->where('file_id', $fileId)->delete();
+ }
+
+ $this->db->table('ticket_master')->where('file_id', $fileId)->delete();
+ }
+
+ protected function rollbackAndCleanupClaimDumpData(int $fileId): void
+ {
+ $this->db->transRollback();
+ $this->cleanupClaimDumpData($fileId);
+ }
+
+ protected function failTpaClaimDumpInsert(int $fileId, string $message): array
+ {
+ $this->rollbackAndCleanupClaimDumpData($fileId);
+ return ['status' => false, 'message' => $message];
+ }
+
+ protected function failTicketMasterInsert(int $fileId, string $message): array
+ {
+ $this->rollbackAndCleanupClaimDumpData($fileId);
+ return ['status' => false, 'message' => $message];
+ }
+
/**
* Read Excel and return associative rows (header based)
*/
@@ -324,7 +369,15 @@ abstract class BaseTpaClaimImportService
* Dublicate check in the TPA specific table records
*/
public function getEmployeeDetails(int $client_id, int $client_policy_id, string $emp_code, string $relation): array
- {
+ {
+
+ $client_policy_data = $this->clientPolicyModel->where('id', $client_policy_id)->first();
+ if(!empty($client_policy_data)){
+ if(!empty($client_policy_data['base_policy'])){
+ $client_policy_id = $client_policy_data['base_policy'];
+ }
+ }
+
$EmployeeModel = new EmployeeModel();
$employeeData = $EmployeeModel
->select([
diff --git a/app/Models/ClaimFilesModel.php b/app/Models/ClaimFilesModel.php
index 055b9b3b..66bd25ed 100644
--- a/app/Models/ClaimFilesModel.php
+++ b/app/Models/ClaimFilesModel.php
@@ -59,4 +59,18 @@ class ClaimFilesModel extends Model
return $data;
}
+
+ /**
+ * Whether the claim (ticket) has at least one active PDF in claim_files.
+ */
+ public function hasPdfFileForTicket($ticketId): bool
+ {
+ return $this->where('ticket_id', $ticketId)
+ ->where('is_active', 1)
+ ->groupStart()
+ ->where('mime_type', 'pdf')
+ ->orWhere('mime_type', 'application/pdf')
+ ->groupEnd()
+ ->countAllResults(false) > 0;
+ }
}
diff --git a/app/Models/EmployeePolicyModel.php b/app/Models/EmployeePolicyModel.php
index dac8f8d6..474221b2 100755
--- a/app/Models/EmployeePolicyModel.php
+++ b/app/Models/EmployeePolicyModel.php
@@ -756,14 +756,17 @@ class EmployeePolicyModel extends Model
LEFT JOIN (
SELECT
emp_code,
+ group_key,
CAST(MAX(CASE WHEN field_name = 'basic_cover_si' THEN new_value END) AS UNSIGNED) AS new_basic_cover_si,
CAST(MAX(CASE WHEN field_name = 'premium' THEN new_value END) AS DECIMAL(10,2)) AS new_si_premium,
CAST(MAX(CASE WHEN field_name = 'premium' THEN old_value END) AS DECIMAL(10,2)) AS old_si_premium,
MAX(CASE WHEN field_name = 'si_enhancement_date' THEN new_value END) AS date_of_coverage
FROM emp_endorsement
$subquery_endorsement_condition
- GROUP BY emp_code
- ) AS sidata ON a.emp_code = sidata.emp_code
+ AND actions = 'si'
+ AND status != 'truncated'
+ GROUP BY emp_code, group_key
+ ) AS sidata ON a.emp_code = sidata.emp_code AND a.group_key = sidata.group_key
WHERE employee_polices.client_policy_id = '{$client_policy_id}'
AND employees.client_branch_id = '{$client_branch_id}'
$endorsement_condition
@@ -2252,7 +2255,7 @@ class EmployeePolicyModel extends Model
}
else if(count($emp_codes) > 0 && $all == false)
{
- $result->whereNotIn('emp.emp_code',$emp_codes);
+ $result->whereNotIn('employee_polices.id',$emp_codes);
}
else if(count($emp_codes) == 0 && $all == true)
{
diff --git a/app/Models/TicketMasterModel.php b/app/Models/TicketMasterModel.php
index 8a6128ca..f6ea661d 100644
--- a/app/Models/TicketMasterModel.php
+++ b/app/Models/TicketMasterModel.php
@@ -1118,12 +1118,23 @@ class TicketMasterModel extends Model
$policy_no = $params['policy_no'] ?? null;
$relationship = $params['relationship'] ?? null;
$insured_name = $params['insured_name'] ?? null;
+ $is_from = $params['is_from'] ?? null;
// $client_name = "6-Eleven";
// $emp_code = "EMP0020K12";
// $emp_name = "Lokesh";
// $policy_no = "GMC-1999/2000/2021/2022";
+ $base_policy_id = null;
+ if($is_from == "claim_dump"){
+ $client_policy_data = $this->db->table('client_policy')->where('policy_no', trim($policy_no))->where('is_active', 1)->get()->getRowArray();
+ if(isset($client_policy_data) && !empty($client_policy_data)){
+ if(!empty($client_policy_data['base_policy'])) {
+ $base_policy_id = $client_policy_data['base_policy'];
+ }
+ }
+ }
+
$builder = $this->db->table('employees e');
$builder->select("
e.id as emp_id,
@@ -1161,7 +1172,13 @@ class TicketMasterModel extends Model
$builder->where('e.emp_code', trim($emp_code));
$builder->where('e.name', trim($emp_name));
$builder->where('c.client_name', trim($client_name));
- $builder->where('cp.policy_no', trim($policy_no));
+
+ if(!empty($base_policy_id)) {
+ $builder->where('cp.id', trim($base_policy_id));
+ } else {
+ $builder->where('cp.policy_no', trim($policy_no));
+ }
+
$builder->where('LOWER(e.relationship)', strtolower('self'));
$query = $builder->get();
diff --git a/app/Views/batch_list.php b/app/Views/batch_list.php
index e63140ef..5ebdebc5 100755
--- a/app/Views/batch_list.php
+++ b/app/Views/batch_list.php
@@ -112,18 +112,44 @@ for ($i = 0; $i < $batch_col_count; $i++) {
max-width: 95%;
}
+/* Make the modal content a flex column so only the body scrolls */
#tpa_variation_modal .modal-content {
max-height: 90vh;
+ display: flex;
+ flex-direction: column;
}
+/* Internal vertical scroll for large tables, keep header/footer fixed */
#tpa_variation_modal .modal-body {
- max-height: calc(85vh - 50px);
+ flex: 1 1 auto;
overflow-y: auto;
direction: ltr;
}
+/* Tab panes must shrink inside flex modal-body so scrollX width is correct */
+#tpa_variation_modal .tab-content,
+#tpa_variation_modal .tab-pane {
+ min-width: 0;
+}
+
+/* Outer .table-responsive must not scroll when DataTables scrollX is active (see custom.css) */
#tpa_variation_modal .table-responsive {
- overflow-x: auto;
+ width: 100%;
+ max-width: 100%;
+}
+
+#tpa_variation_modal .dataTables_wrapper {
+ width: 100%;
+ max-width: 100%;
+}
+
+#tpa_variation_modal .dataTables_scrollHead {
+ overflow: hidden !important;
+}
+
+#tpa_variation_modal .dataTables_scrollBody {
+ overflow-x: auto !important;
+ -webkit-overflow-scrolling: touch;
}
#tpa_variation_modal table {
@@ -548,6 +574,10 @@ for ($i = 0; $i < $batch_col_count; $i++) {
let tpaNotInNhanceProceedEnabled = true;
/** From API `not_in_nhance_proceed_button_text` — label for Proceed on "Not in Nhance" tab. */
let tpaNotInNhanceProceedButtonText = 'Proceed - Not in Nhance';
+ /** From API `not_matched_count` — when 0, Proceed is disabled on "Need to Review" tab. */
+ let tpaNeedToReviewProceedEnabled = true;
+ /** From API `need_to_review_proceed_button_text` — label for Proceed on "Need to Review" tab. */
+ let tpaNeedToReviewProceedButtonText = 'Proceed - Need to Review ( 0 )';
const tpaVariationColumns = {
not_in_nhance: [
@@ -694,6 +724,16 @@ for ($i = 0; $i < $batch_col_count; $i++) {
tpaNotInNhanceProceedButtonText = 'Proceed - Not in Nhance';
}
+ const notMatchedCount = parseInt(data.not_matched_count, 10);
+ tpaNeedToReviewProceedEnabled = !isNaN(notMatchedCount) && notMatchedCount > 0;
+ if (typeof data.need_to_review_proceed_button_text === 'string' && data.need_to_review_proceed_button_text.trim() !== '') {
+ tpaNeedToReviewProceedButtonText = data.need_to_review_proceed_button_text.trim();
+ } else if (!isNaN(notMatchedCount)) {
+ tpaNeedToReviewProceedButtonText = 'Proceed - Need to Review ( ' + notMatchedCount + ' )';
+ } else {
+ tpaNeedToReviewProceedButtonText = 'Proceed - Need to Review ( 0 )';
+ }
+
showTPAVariationTabByLinkId('not-in-nhance-tab');
updateTPAProceedButton('not-in-nhance-tab');
adjustVariationTableByTabId('not-in-nhance-tab');
@@ -711,6 +751,20 @@ for ($i = 0; $i < $batch_col_count; $i++) {
updateTPAProceedButton($activeTab.attr('id'));
}
+ function bindVariationScrollHeadSync($wrap) {
+ const $body = $wrap.find('.dataTables_scrollBody');
+ const $head = $wrap.find('.dataTables_scrollHead');
+ if (!$body.length || !$head.length) {
+ return;
+ }
+ $body.off('scroll.nhTpaVarHScroll').on('scroll.nhTpaVarHScroll', function () {
+ $head.scrollLeft($(this).scrollLeft());
+ });
+ $head.off('scroll.nhTpaVarHScroll').on('scroll.nhTpaVarHScroll', function () {
+ $body.scrollLeft($(this).scrollLeft());
+ });
+ }
+
function getVariationDataTableConfig() {
return {
dom: "<'dt-top'lf>" +
@@ -722,7 +776,15 @@ for ($i = 0; $i < $batch_col_count; $i++) {
paging: true,
ordering: false,
info: false,
+ scrollX: true,
+ scrollY: false,
+ scrollCollapse: false,
autoWidth: false,
+ initComplete: function () {
+ const $wrap = $(this.api().table().container());
+ $wrap.closest('.table-responsive').addClass('nh-dt-no-outer-scroll');
+ bindVariationScrollHeadSync($wrap);
+ },
language: {
search: `
@@ -773,8 +835,17 @@ for ($i = 0; $i < $batch_col_count; $i++) {
}
const dt = $table.DataTable();
+ const $wrap = $(dt.table().container());
+ bindVariationScrollHeadSync($wrap);
dt.columns.adjust().draw(false);
bindVariationSearchIcons($table);
+
+ /* Re-measure after tab/modal becomes visible — hidden tables mis-size columns */
+ setTimeout(function () {
+ if ($table.is(':visible')) {
+ dt.columns.adjust().draw(false);
+ }
+ }, 0);
}
function adjustVariationTableByTabId(tabId) {
@@ -814,7 +885,10 @@ for ($i = 0; $i < $batch_col_count; $i++) {
$button.prop('disabled', true);
}
} else if (activeId === 'need-to-review-tab') {
- $button.text('Proceed - Need to Review');
+ $button.text(tpaNeedToReviewProceedButtonText);
+ if (!tpaNeedToReviewProceedEnabled) {
+ $button.prop('disabled', true);
+ }
} else if (activeId === 'not-in-tpa-tab') {
$button.addClass('d-none');
} else {
@@ -829,12 +903,32 @@ for ($i = 0; $i < $batch_col_count; $i++) {
const activeId = $(e.target).attr('id');
updateTPAProceedButton(activeId);
adjustVariationTableByTabId(activeId);
+ setTimeout(function () {
+ const tabToTableMap = {
+ 'not-in-nhance-tab': '#not_in_nhance_table',
+ 'not-in-tpa-tab': '#not_in_tpa_table',
+ 'need-to-review-tab': '#need_to_review_table'
+ };
+ const sel = tabToTableMap[activeId];
+ const $t = sel ? $(sel) : $();
+ if ($.fn.DataTable.isDataTable($t)) {
+ $t.DataTable().columns.adjust().draw(false);
+ }
+ }, 50);
});
$('#tpa_variation_modal').on('shown.bs.modal', function () {
showTPAVariationTabByLinkId('not-in-nhance-tab');
updateTPAProceedButton('not-in-nhance-tab');
adjustVariationTableByTabId('not-in-nhance-tab');
+ setTimeout(function () {
+ ['#not_in_nhance_table', '#not_in_tpa_table', '#need_to_review_table'].forEach(function (sel) {
+ const $t = $(sel);
+ if ($.fn.DataTable.isDataTable($t) && $t.is(':visible')) {
+ $t.DataTable().columns.adjust().draw(false);
+ }
+ });
+ }, 50);
});
$('#tpa_variation_modal').on('hidden.bs.modal', function () {
@@ -1155,6 +1249,10 @@ for ($i = 0; $i < $batch_col_count; $i++) {
} else if (activeId === 'not-in-tpa-tab') {
proceedNotInTPA();
} else if (activeId === 'need-to-review-tab') {
+ if (!tpaNeedToReviewProceedEnabled) {
+ toastr.warning('No mismatched records to proceed.', 'WARNING');
+ return;
+ }
proceedNeedToReview();
} else {
toastr.warning('Unknown tab selected.', 'WARNING');
diff --git a/app/Views/leads_form.php b/app/Views/leads_form.php
index 36d930c8..d6942deb 100644
--- a/app/Views/leads_form.php
+++ b/app/Views/leads_form.php
@@ -163,7 +163,8 @@
-->