FIX_TPA_RECON_LIST
This commit is contained in:
parent
eb3d58c69a
commit
cad8a0e758
@ -799,7 +799,7 @@ class ClientController extends AdminController
|
|||||||
],
|
],
|
||||||
|
|
||||||
'cd_ac_no' => [
|
'cd_ac_no' => [
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9\/\-_]+$/]',
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9\/\-_ ]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'CD Account number is required.',
|
'required' => 'CD Account number is required.',
|
||||||
'regex_match' => 'CD Account number can only contain letters, numbers, hyphens(-), underscores(_), and slashes(/).',
|
'regex_match' => 'CD Account number can only contain letters, numbers, hyphens(-), underscores(_), and slashes(/).',
|
||||||
@ -874,12 +874,14 @@ class ClientController extends AdminController
|
|||||||
$record_date = null;
|
$record_date = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$cd_master_data = $this->CDMasterModel->where('id', $cd_ac_pk)->where('is_active', 1)->first();
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'amount' => $sanitized_post_data['amount'] ?? null,
|
'amount' => $sanitized_post_data['amount'] ?? null,
|
||||||
'sub_type_id' => $sanitized_post_data['sub_type_id'] ?? null,
|
'sub_type_id' => $sanitized_post_data['sub_type_id'] ?? null,
|
||||||
'client_id' => $sanitized_post_data['client_id'] ?? null,
|
'client_id' => $sanitized_post_data['client_id'] ?? null,
|
||||||
'client_policy_id' => null,
|
'client_policy_id' => null,
|
||||||
'cd_ac_no' => $cd_ac_no ?? null,
|
'cd_ac_no' => $cd_master_data['cd_ac_no'] ?? null,
|
||||||
'cd_ac_pk' => $cd_ac_pk ?? null,
|
'cd_ac_pk' => $cd_ac_pk ?? null,
|
||||||
'endorsement_no' => null,
|
'endorsement_no' => null,
|
||||||
'insurer_id' => $sanitized_post_data['insurer_id'] ?? null,
|
'insurer_id' => $sanitized_post_data['insurer_id'] ?? null,
|
||||||
|
|||||||
@ -517,7 +517,7 @@ class EmployeeController extends AdminController
|
|||||||
")
|
")
|
||||||
->join('client_policy', 'client_policy.id = batch_files.client_policy_id')
|
->join('client_policy', 'client_policy.id = batch_files.client_policy_id')
|
||||||
->join('insurers', 'client_policy.insurer_id = insurers.id')
|
->join('insurers', 'client_policy.insurer_id = insurers.id')
|
||||||
->join('tpa', 'client_policy.tpa_id = tpa.id')
|
->join('tpa', 'client_policy.tpa_id = tpa.id', 'left')
|
||||||
->join('client_branch', 'client_branch.id = batch_files.client_branch_id')
|
->join('client_branch', 'client_branch.id = batch_files.client_branch_id')
|
||||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
||||||
->join('clients', 'clients.id = client_policy.client_id')
|
->join('clients', 'clients.id = client_policy.client_id')
|
||||||
@ -574,7 +574,6 @@ class EmployeeController extends AdminController
|
|||||||
->orderBy('batch_files.id', 'desc')
|
->orderBy('batch_files.id', 'desc')
|
||||||
->find();
|
->find();
|
||||||
|
|
||||||
|
|
||||||
// dd($data['fileList']);die();
|
// dd($data['fileList']);die();
|
||||||
if ($this->request->getMethod() == "get") {
|
if ($this->request->getMethod() == "get") {
|
||||||
$this->loadLayout('import_export', $data);
|
$this->loadLayout('import_export', $data);
|
||||||
@ -4255,25 +4254,36 @@ class EmployeeController extends AdminController
|
|||||||
$emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
$emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
||||||
|
|
||||||
// Reconcile DB records against TPA records and classify records for rec_type updates.
|
// Reconcile DB records against TPA records and classify records for rec_type updates.
|
||||||
|
$consumedTpaIdsByEmpCode = [];
|
||||||
foreach ($emp_data_wo_tpa_id as $db_key => $db_row) {
|
foreach ($emp_data_wo_tpa_id as $db_key => $db_row) {
|
||||||
$tpa_temp_data = $tpaByEmpCode[$db_row['emp_code']] ?? [];
|
$empCode = (string) ($db_row['emp_code'] ?? '');
|
||||||
$match = $this->reconcileDbWithTpa($db_row, $tpa_temp_data);
|
$empId = (string) ($db_row['id'] ?? '');
|
||||||
|
$empName = (string) ($db_row['name'] ?? '');
|
||||||
|
$tpa_temp_data = $tpaByEmpCode[$empCode] ?? [];
|
||||||
|
$excludeTpaIds = $consumedTpaIdsByEmpCode[$empCode] ?? [];
|
||||||
|
$match = $this->reconcileDbWithTpa($db_row, $tpa_temp_data, $excludeTpaIds);
|
||||||
$emp_data_wo_tpa_id[$db_key]['match'] = $match;
|
$emp_data_wo_tpa_id[$db_key]['match'] = $match;
|
||||||
|
|
||||||
if (($match['status'] ?? '') === 'matched') {
|
if (($match['status'] ?? '') === 'matched') {
|
||||||
$matchedTpaId = (int) ($match['tpa_record']['id'] ?? 0);
|
$matchedTpaId = (int) ($match['tpa_record']['id'] ?? 0);
|
||||||
|
// echo 'matched' . $matchedTpaId . ' for emp_code ' . $empCode . PHP_EOL . '<br>';
|
||||||
if ($matchedTpaId > 0) {
|
if ($matchedTpaId > 0) {
|
||||||
|
$consumedTpaIdsByEmpCode[$empCode][] = $matchedTpaId;
|
||||||
// If compare-fields list has differences, the row must be reviewed.
|
// If compare-fields list has differences, the row must be reviewed.
|
||||||
// Otherwise keep it as matched.
|
// Otherwise keep it as matched.
|
||||||
$recTypeById[$matchedTpaId] = empty($match['not_matching']) ? 'matched' : 'need_to_review';
|
$recTypeById[$matchedTpaId] = empty($match['not_matching']) ? 'matched' : 'need_to_review';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No relation-level match found for this DB member.
|
// echo 'not matched' . ' for emp_code ' . $empCode . PHP_EOL . ' - ' . $empId . ' - ' . $empName . ' - ' . '<br>';
|
||||||
// Mark all candidate TPA rows for the same emp_code as review-required.
|
// No match for this DB member: flag same-relation TPA rows not already paired.
|
||||||
|
$dbRel = strtolower(trim((string) ($db_row['relationship'] ?? '')));
|
||||||
foreach ($tpa_temp_data as $candidate) {
|
foreach ($tpa_temp_data as $candidate) {
|
||||||
|
if (strtolower(trim((string) ($candidate['relation'] ?? ''))) !== $dbRel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$candidateId = (int) ($candidate['id'] ?? 0);
|
$candidateId = (int) ($candidate['id'] ?? 0);
|
||||||
if ($candidateId > 0) {
|
if ($candidateId > 0 && !in_array($candidateId, $excludeTpaIds, true)) {
|
||||||
$recTypeById[$candidateId] = 'need_to_review';
|
$recTypeById[$candidateId] = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4309,73 +4319,72 @@ class EmployeeController extends AdminController
|
|||||||
// this will match tpa api data with emp/emp policy table and update ref in tpa api data once
|
// this will match tpa api data with emp/emp policy table and update ref in tpa api data once
|
||||||
$this->reconTpaApiDataWithEmployeepolicies(['file_id' => $file_id]);
|
$this->reconTpaApiDataWithEmployeepolicies(['file_id' => $file_id]);
|
||||||
} else {
|
} else {
|
||||||
// echo 'else';die;
|
|
||||||
// Cached mode:
|
// Cached mode:
|
||||||
// Read previously classified rows from rec_type, keep response shape compatible
|
// `not_in_nhance` stays sourced from persisted rec_type snapshot.
|
||||||
// with existing UI/export (`mismatch_data` still contains DB row + match payload).
|
// `mismatch_data` must match compute-mode shape: every policy row with
|
||||||
|
// tpa_id NULL gets reconcileDbWithTpa against *all* active TPA rows for
|
||||||
|
// that emp_code (not only rows flagged need_to_review), otherwise second+
|
||||||
|
// loads drop rows / lose not_matching vs the first compute pass.
|
||||||
$not_in_nhance = $tpaApiDataModel->select('*')
|
$not_in_nhance = $tpaApiDataModel->select('*')
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->where('file_id', $file_id)
|
->where('file_id', $file_id)
|
||||||
->where('rec_type', 'not_in_nhance')
|
->where('rec_type', 'not_in_nhance')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$needToReviewRows = $tpaApiDataModel->select('*')
|
$allActiveTpaRowsCached = $tpaApiDataModel->select('*')
|
||||||
->where('is_active', 1)
|
|
||||||
->where('file_id', $file_id)
|
->where('file_id', $file_id)
|
||||||
->where('rec_type', 'need_to_review')
|
->where('is_active', 1)
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$needToReviewByEmpCode = [];
|
$tpaByEmpCodeCached = [];
|
||||||
foreach ($needToReviewRows as $row) {
|
foreach ($allActiveTpaRowsCached as $tpaRow) {
|
||||||
$needToReviewByEmpCode[$row['emp_code']][] = $row;
|
$tpaByEmpCodeCached[$tpaRow['emp_code']][] = $tpaRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
$baseRows = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
$emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
||||||
foreach ($baseRows as $db_row) {
|
$consumedTpaIdsByEmpCodeCached = [];
|
||||||
$candidates = $needToReviewByEmpCode[$db_row['emp_code']] ?? [];
|
foreach ($emp_data_wo_tpa_id as $db_key => $db_row) {
|
||||||
if (empty($candidates)) {
|
$empCode = (string) ($db_row['emp_code'] ?? '');
|
||||||
continue;
|
$tpa_temp_data = $tpaByEmpCodeCached[$empCode] ?? [];
|
||||||
|
$excludeTpaIds = $consumedTpaIdsByEmpCodeCached[$empCode] ?? [];
|
||||||
|
$match = $this->reconcileDbWithTpa($db_row, $tpa_temp_data, $excludeTpaIds);
|
||||||
|
$emp_data_wo_tpa_id[$db_key]['match'] = $match;
|
||||||
|
if (($match['status'] ?? '') === 'matched') {
|
||||||
|
$matchedTpaId = (int) ($match['tpa_record']['id'] ?? 0);
|
||||||
|
if ($matchedTpaId > 0) {
|
||||||
|
$consumedTpaIdsByEmpCodeCached[$empCode][] = $matchedTpaId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$selectedTpa = null;
|
// print_rr($emp_data_wo_tpa_id);die();
|
||||||
foreach ($candidates as $candidate) {
|
|
||||||
// Prefer same-relation row to mimic reconcileDbWithTpa relation matching.
|
|
||||||
if (strtolower((string) ($candidate['relation'] ?? '')) === strtolower((string) ($db_row['relationship'] ?? ''))) {
|
|
||||||
$selectedTpa = $candidate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($selectedTpa === null) {
|
|
||||||
$selectedTpa = $candidates[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$db_row['match'] = [
|
|
||||||
'status' => 'matched',
|
|
||||||
'tpa_record' => $selectedTpa,
|
|
||||||
'not_matching' => [],
|
|
||||||
];
|
|
||||||
$emp_data_wo_tpa_id[] = $db_row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally keep `not_in_tpa` live from current join/query logic
|
// Intentionally keep `not_in_tpa` live from current join/query logic
|
||||||
// (as requested) and do not source it from rec_type snapshot.
|
// (as requested) and do not source it from rec_type snapshot.
|
||||||
$tpa_emp_codes = $tpaApiDataModel->select('emp_code')
|
$tpa_emp_codes = $tpaApiDataModel->select('ref')
|
||||||
->where('file_id', $file_id)
|
->where('file_id', $file_id)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->groupBy('emp_code')
|
->groupBy('ref')
|
||||||
->findAll();
|
->findAll();
|
||||||
$tpa_emp_codes = array_column($tpa_emp_codes, 'emp_code');
|
|
||||||
$not_in_tpa = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id, $tpa_emp_codes);
|
// $tpa_emp_codes = array_column($tpa_emp_codes, 'ref');
|
||||||
|
$tpa_ref = array_column($tpa_emp_codes, 'ref');
|
||||||
|
$not_in_tpa = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id, $tpa_ref);
|
||||||
|
|
||||||
|
// Format dates for UI/export (dd/mm/yyyy). Applied after reconciliation so
|
||||||
|
// reconcileDbWithTpa can still compare raw Y-m-d values from the database.
|
||||||
|
$this->formatVariationReportDataForDisplay($not_in_tpa, $not_in_nhance, $emp_data_wo_tpa_id);
|
||||||
|
|
||||||
if (!empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id)) {
|
if (!empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id)) {
|
||||||
$not_in_nhance_button_enable_status = false;
|
$not_in_nhance_button_enable_status = false;
|
||||||
if(count($not_in_tpa))
|
if (count($not_in_nhance)) {
|
||||||
{
|
foreach ($not_in_nhance as $nih) {
|
||||||
foreach($not_in_nhance as $nih)
|
$hasRefKey = array_key_exists('ref', $nih);
|
||||||
{
|
$ref = $hasRefKey ? $nih['ref'] : null;
|
||||||
if($nih['ref'] === '' || empty($nih['ref']))
|
$refIsEmpty = $ref === null || $ref === '';
|
||||||
{
|
if ($refIsEmpty) {
|
||||||
$not_in_nhance_button_enable_status = true;
|
$not_in_nhance_button_enable_status = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4410,6 +4419,21 @@ class EmployeeController extends AdminController
|
|||||||
$not_in_nhance_deletion_count
|
$not_in_nhance_deletion_count
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$not_matched_count = 0;
|
||||||
|
$not_matched_data = [];
|
||||||
|
foreach ($emp_data_wo_tpa_id as $key => $row) {
|
||||||
|
$notMatching = $row['match']['not_matching'] ?? [];
|
||||||
|
if (isset($row['match']) && is_array($notMatching) && $notMatching !== []) {
|
||||||
|
$not_matched_count++;
|
||||||
|
$not_matched_data[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$need_to_review_proceed_button_text = sprintf(
|
||||||
|
'Proceed - Need to Review ( %d )',
|
||||||
|
$not_matched_count
|
||||||
|
);
|
||||||
|
|
||||||
$response = [
|
$response = [
|
||||||
'not_in_tpa' => $not_in_tpa,
|
'not_in_tpa' => $not_in_tpa,
|
||||||
'not_in_nhance' => $not_in_nhance,
|
'not_in_nhance' => $not_in_nhance,
|
||||||
@ -4418,6 +4442,9 @@ class EmployeeController extends AdminController
|
|||||||
'not_in_nhance_inception_count' => $not_in_nhance_inception_count,
|
'not_in_nhance_inception_count' => $not_in_nhance_inception_count,
|
||||||
'not_in_nhance_deletion_count' => $not_in_nhance_deletion_count,
|
'not_in_nhance_deletion_count' => $not_in_nhance_deletion_count,
|
||||||
'not_in_nhance_proceed_button_text' => $not_in_nhance_proceed_button_text,
|
'not_in_nhance_proceed_button_text' => $not_in_nhance_proceed_button_text,
|
||||||
|
'not_matched_count' => $not_matched_count,
|
||||||
|
'not_matched_data' => $not_matched_data,
|
||||||
|
'need_to_review_proceed_button_text' => $need_to_review_proceed_button_text,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($type === 'view') {
|
if ($type === 'view') {
|
||||||
@ -4889,10 +4916,12 @@ class EmployeeController extends AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mismatchRows = [];
|
$mismatchRows = [];
|
||||||
|
$consumedTpaIdsByEmpCode = [];
|
||||||
|
|
||||||
foreach ($dbRows as $dbRow) {
|
foreach ($dbRows as $dbRow) {
|
||||||
|
$empCode = (string) ($dbRow['emp_code'] ?? '');
|
||||||
$tpaRows = $TpaApiDataModel->select('*')
|
$tpaRows = $TpaApiDataModel->select('*')
|
||||||
->where('emp_code', $dbRow['emp_code'])
|
->where('emp_code', $empCode)
|
||||||
->where('file_id', $batchFileId)
|
->where('file_id', $batchFileId)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->findAll();
|
->findAll();
|
||||||
@ -4901,7 +4930,14 @@ class EmployeeController extends AdminController
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$match = $this->reconcileDbWithTpa($dbRow, $tpaRows);
|
$excludeTpaIds = $consumedTpaIdsByEmpCode[$empCode] ?? [];
|
||||||
|
$match = $this->reconcileDbWithTpa($dbRow, $tpaRows, $excludeTpaIds);
|
||||||
|
if (($match['status'] ?? '') === 'matched') {
|
||||||
|
$matchedTpaId = (int) ($match['tpa_record']['id'] ?? 0);
|
||||||
|
if ($matchedTpaId > 0) {
|
||||||
|
$consumedTpaIdsByEmpCode[$empCode][] = $matchedTpaId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (($match['status'] ?? '') !== 'matched') {
|
if (($match['status'] ?? '') !== 'matched') {
|
||||||
continue;
|
continue;
|
||||||
@ -5190,9 +5226,12 @@ class EmployeeController extends AdminController
|
|||||||
$rowsSkippedNoDiff = 0;
|
$rowsSkippedNoDiff = 0;
|
||||||
$rowsSkippedNoEmployee = 0;
|
$rowsSkippedNoEmployee = 0;
|
||||||
|
|
||||||
|
$consumedTpaIdsByEmpCode = [];
|
||||||
|
|
||||||
foreach ($dbRows as $dbRow) {
|
foreach ($dbRows as $dbRow) {
|
||||||
|
$empCode = (string) ($dbRow['emp_code'] ?? '');
|
||||||
$tpaRows = $TpaApiDataModel->select('*')
|
$tpaRows = $TpaApiDataModel->select('*')
|
||||||
->where('emp_code', $dbRow['emp_code'] ?? '')
|
->where('emp_code', $empCode)
|
||||||
->where('file_id', $batchFileId)
|
->where('file_id', $batchFileId)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->findAll();
|
->findAll();
|
||||||
@ -5201,7 +5240,8 @@ class EmployeeController extends AdminController
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$match = $this->reconcileDbWithTpa($dbRow, $tpaRows);
|
$excludeTpaIds = $consumedTpaIdsByEmpCode[$empCode] ?? [];
|
||||||
|
$match = $this->reconcileDbWithTpa($dbRow, $tpaRows, $excludeTpaIds);
|
||||||
|
|
||||||
if (($match['status'] ?? '') !== 'matched') {
|
if (($match['status'] ?? '') !== 'matched') {
|
||||||
continue;
|
continue;
|
||||||
@ -5210,6 +5250,11 @@ class EmployeeController extends AdminController
|
|||||||
$tpaRecord = $match['tpa_record'] ?? [];
|
$tpaRecord = $match['tpa_record'] ?? [];
|
||||||
$notMatching = $match['not_matching'] ?? [];
|
$notMatching = $match['not_matching'] ?? [];
|
||||||
|
|
||||||
|
$matchedTpaId = (int) ($tpaRecord['id'] ?? 0);
|
||||||
|
if ($matchedTpaId > 0) {
|
||||||
|
$consumedTpaIdsByEmpCode[$empCode][] = $matchedTpaId;
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_array($notMatching) || $notMatching === []) {
|
if (!is_array($notMatching) || $notMatching === []) {
|
||||||
$rowsSkippedNoDiff++;
|
$rowsSkippedNoDiff++;
|
||||||
continue;
|
continue;
|
||||||
@ -5524,57 +5569,169 @@ class EmployeeController extends AdminController
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reconcileDbWithTpa(array $db, array $tpaRows): array
|
/**
|
||||||
|
* Convert a single variation-report date value to dd/mm/yyyy for display.
|
||||||
|
*/
|
||||||
|
private function formatVariationReportDateField($value): string
|
||||||
{
|
{
|
||||||
// Name normalization
|
if ($value === null || $value === '') {
|
||||||
$normalizeName = function ($name) {
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$formatted = change_date_format((string) $value, null, 'd/m/Y');
|
||||||
|
|
||||||
|
return ($formatted !== null && $formatted !== '') ? (string) $formatted : (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format known date columns on one variation-report row (TPA or DB).
|
||||||
|
*/
|
||||||
|
private function formatVariationReportRowDates(array $row, array $dateFields = ['dob', 'doj']): array
|
||||||
|
{
|
||||||
|
foreach ($dateFields as $field) {
|
||||||
|
if (!array_key_exists($field, $row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($row[$field] === null || $row[$field] === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$row[$field] = $this->formatVariationReportDateField($row[$field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply dd/mm/yyyy formatting to all variation-report payloads returned to the UI/export.
|
||||||
|
*/
|
||||||
|
private function formatVariationReportDataForDisplay(
|
||||||
|
array &$notInTpa,
|
||||||
|
array &$notInNhance,
|
||||||
|
array &$mismatchData
|
||||||
|
): void {
|
||||||
|
foreach ($notInTpa as $idx => $row) {
|
||||||
|
$notInTpa[$idx] = $this->formatVariationReportRowDates($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($notInNhance as $idx => $row) {
|
||||||
|
$notInNhance[$idx] = $this->formatVariationReportRowDates($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($mismatchData as $idx => $row) {
|
||||||
|
$row = $this->formatVariationReportRowDates($row);
|
||||||
|
if (isset($row['match']['tpa_record']) && is_array($row['match']['tpa_record'])) {
|
||||||
|
$row['match']['tpa_record'] = $this->formatVariationReportRowDates($row['match']['tpa_record']);
|
||||||
|
}
|
||||||
|
$mismatchData[$idx] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pair one Nhance policy row with a TPA API row for the same emp_code.
|
||||||
|
*
|
||||||
|
* When multiple dependents share a relation (e.g. two Sons), candidates are scored
|
||||||
|
* on name / DOB / gender and the best unique match wins. Already-paired TPA ids
|
||||||
|
* (same emp_code) can be passed via $excludeTpaIds so each TPA row maps once.
|
||||||
|
*/
|
||||||
|
public function reconcileDbWithTpa(array $db, array $tpaRows, array $excludeTpaIds = []): array
|
||||||
|
{
|
||||||
|
$normalizeName = static function ($name) {
|
||||||
return strtolower(
|
return strtolower(
|
||||||
preg_replace('/[.\s_]+/', '', trim($name))
|
preg_replace('/[.\s_]+/', '', trim((string) $name))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach ($tpaRows as $tpa) {
|
$normalizeRelation = static function ($relation) {
|
||||||
|
return strtolower(trim((string) $relation));
|
||||||
|
};
|
||||||
|
|
||||||
// 1️⃣ emp_code + relation must match
|
$dbRel = $normalizeRelation($db['relationship'] ?? '');
|
||||||
if (
|
|
||||||
// ($db['emp_code'] ?? '') !== ($tpa['emp_code'] ?? '') ||
|
$candidates = [];
|
||||||
strtolower($db['relationship']) !== strtolower($tpa['relation'])
|
foreach ($tpaRows as $tpa) {
|
||||||
) {
|
if (isset($tpa['match']['status']) && $tpa['match']['status'] === 'matched') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2️⃣ Field comparison
|
$tpaId = (int) ($tpa['id'] ?? 0);
|
||||||
|
if ($tpaId > 0 && in_array($tpaId, $excludeTpaIds, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($normalizeRelation($tpa['relation'] ?? '') !== $dbRel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$candidates[] = $tpa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($candidates === []) {
|
||||||
|
return ['status' => 'no_match'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$scoreCandidate = static function (array $tpa) use ($db, $normalizeName) {
|
||||||
|
$score = 0;
|
||||||
|
if ($normalizeName($db['name'] ?? '') === $normalizeName($tpa['name'] ?? '')) {
|
||||||
|
$score += 4;
|
||||||
|
}
|
||||||
|
if ((string) ($db['dob'] ?? '') === (string) ($tpa['dob'] ?? '')) {
|
||||||
|
$score += 2;
|
||||||
|
}
|
||||||
|
if (strtoupper(trim((string) ($db['gender'] ?? ''))) === strtoupper(trim((string) ($tpa['gender'] ?? '')))) {
|
||||||
|
$score += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $score;
|
||||||
|
};
|
||||||
|
|
||||||
|
$bestTpa = null;
|
||||||
|
$bestScore = -1;
|
||||||
|
foreach ($candidates as $tpa) {
|
||||||
|
$score = $scoreCandidate($tpa);
|
||||||
|
if ($score > $bestScore) {
|
||||||
|
$bestScore = $score;
|
||||||
|
$bestTpa = $tpa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bestTpa === null) {
|
||||||
|
return ['status' => 'no_match'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple Son/Daughter rows: require a unique tie-breaker (name or DOB).
|
||||||
|
if (count($candidates) > 1) {
|
||||||
|
$topCount = 0;
|
||||||
|
foreach ($candidates as $tpa) {
|
||||||
|
if ($scoreCandidate($tpa) === $bestScore) {
|
||||||
|
$topCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($topCount > 1 || $bestScore < 2) {
|
||||||
|
return ['status' => 'no_match'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$diff = [];
|
$diff = [];
|
||||||
|
|
||||||
if (
|
if ($normalizeName($db['name'] ?? '') !== $normalizeName($bestTpa['name'] ?? '')) {
|
||||||
($db['name'] ?? '') !==
|
|
||||||
($tpa['name'] ?? '')
|
|
||||||
) {
|
|
||||||
$diff[] = 'name';
|
$diff[] = 'name';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($db['dob'] ?? '') !== ($tpa['dob'] ?? '')) {
|
if ((string) ($db['dob'] ?? '') !== (string) ($bestTpa['dob'] ?? '')) {
|
||||||
$diff[] = 'dob';
|
$diff[] = 'dob';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
strtoupper($db['gender'] ?? '') !==
|
strtoupper(trim((string) ($db['gender'] ?? ''))) !==
|
||||||
strtoupper($tpa['gender'] ?? '')
|
strtoupper(trim((string) ($bestTpa['gender'] ?? '')))
|
||||||
) {
|
) {
|
||||||
$diff[] = 'gender';
|
$diff[] = 'gender';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3️⃣ Match found
|
|
||||||
return [
|
return [
|
||||||
'status' => 'matched',
|
'status' => 'matched',
|
||||||
'tpa_record' => $tpa,
|
'tpa_record' => $bestTpa,
|
||||||
'not_matching' => $diff // empty = perfect match
|
'not_matching' => $diff,
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4️⃣ No match found
|
|
||||||
return [
|
|
||||||
'status' => 'no_match'
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -554,30 +554,32 @@ class FhplApiController extends BaseController
|
|||||||
foreach ($employeePolicyData as $policy) {
|
foreach ($employeePolicyData as $policy) {
|
||||||
$hasMatchForThisPolicy = false;
|
$hasMatchForThisPolicy = false;
|
||||||
foreach ($allMembers as $m) {
|
foreach ($allMembers as $m) {
|
||||||
|
$apiRelation = map_relationship(trim($m['RELATION'] ?? ''));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
strtolower(trim($policy['name'])) === strtolower(trim($m['EMPLOYEE_NAME'] ?? '')) &&
|
strtolower(trim((string) ($policy['name'] ?? ''))) === strtolower(trim((string) ($m['BENEFICIARY_NAME'] ?? '')))
|
||||||
($policy['emp_code'] ?? '') == ($m['EMPLOYEE_ID'] ?? '') &&
|
&& trim((string) ($policy['emp_code'] ?? '')) === trim((string) ($m['EMPLOYEE_ID'] ?? ''))
|
||||||
strtolower($policy['relationship']) === strtolower($m['RELATION'] ?? '')
|
&& strtolower(trim((string) ($policy['relationship'] ?? ''))) === strtolower(trim((string) $apiRelation))
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$hasMatchForThisPolicy = true;
|
$hasMatchForThisPolicy = true;
|
||||||
|
|
||||||
|
$tpaId = $m['MEMBERSHIP_NO'] ?? $m['MEMBERSHIP_NO'] ?? null;
|
||||||
$sql = "UPDATE employee_polices SET tpa_id = ? WHERE id = ?";
|
$sql = 'UPDATE employee_polices SET tpa_id = ? WHERE id = ?';
|
||||||
$this->db->query($sql, [$m['TPA_TPADetailID'], $policy['emp_policy_id']]);
|
$this->db->query($sql, [$tpaId, $policy['emp_policy_id']]);
|
||||||
|
|
||||||
// for e-card send
|
// for e-card send
|
||||||
if(strtolower(trim($policy['relationship'])) == 'self'){
|
if (strtolower(trim((string) ($policy['relationship'] ?? ''))) === 'self') {
|
||||||
$employee_policy_ids[] = $policy['emp_policy_id'];
|
$employee_policy_ids[] = $policy['emp_policy_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->db->affectedRows() > 0) {
|
if ($this->db->affectedRows() > 0) {
|
||||||
$updated++;
|
$updated++;
|
||||||
log_message('error', "FHPL - TPA ID Pull Updated tpa_id={$m['TPA_TPADetailID']} for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}");
|
log_message('error', "FHPL - TPA ID Pull Updated tpa_id={$tpaId} for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}");
|
||||||
} else {
|
} else {
|
||||||
log_message('error', "FHPL - TPA ID Pull No update (already set or not matched) for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}");
|
log_message('error', "FHPL - TPA ID Pull No update (already set or not matched) for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1063,14 +1065,14 @@ class FhplApiController extends BaseController
|
|||||||
'file_id' => $file_id, // ← pass from controller
|
'file_id' => $file_id, // ← pass from controller
|
||||||
'emp_code' => trim($row['EMPLOYEE_ID'] ?? ''),
|
'emp_code' => trim($row['EMPLOYEE_ID'] ?? ''),
|
||||||
|
|
||||||
'name' => trim($row['EMPLOYEE_NAME'] ?? ''),
|
'name' => trim($row['BENEFICIARY_NAME'] ?? ''),
|
||||||
'dob' => !empty($row['DATE_OF_BIRTH'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $row['DATE_OF_BIRTH']))) : null,
|
'dob' => !empty($row['DATE_OF_BIRTH'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $row['DATE_OF_BIRTH']))) : null,
|
||||||
|
|
||||||
'relation' => trim(strtolower($row['RELATION'] ?? '')),
|
'relation' => trim(strtolower($row['RELATION'] ?? '')),
|
||||||
'gender' => format_gender_v2($row['GENDER'] ?? null),
|
'gender' => format_gender_v2($row['GENDER'] ?? null),
|
||||||
'self' => strtolower($row['RELATION'] ?? '') === 'self' ? 1 : 0,
|
'self' => strtolower($row['RELATION'] ?? '') === 'self' ? 1 : 0,
|
||||||
|
|
||||||
'tpa_id' => trim($row['TPA_TPADETAIL_ID'] ?? null),
|
'tpa_id' => trim($row['MEMBERSHIP_NO'] ?? null),
|
||||||
'age' => is_numeric($row['AGE'] ?? null) ? (int) $row['AGE'] : null,
|
'age' => is_numeric($row['AGE'] ?? null) ? (int) $row['AGE'] : null,
|
||||||
|
|
||||||
'is_active' => 1,
|
'is_active' => 1,
|
||||||
|
|||||||
@ -19,6 +19,7 @@ abstract class BaseTpaClaimImportService
|
|||||||
protected $claimDumpFileModel;
|
protected $claimDumpFileModel;
|
||||||
protected $clientPolicyModel;
|
protected $clientPolicyModel;
|
||||||
protected $policyNumberMapping;
|
protected $policyNumberMapping;
|
||||||
|
protected $tpaTableMapping;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -33,6 +34,14 @@ abstract class BaseTpaClaimImportService
|
|||||||
(int) env('R_CARE_PRIMARY_KEY_CONSTANT') => 'Policy Number',
|
(int) env('R_CARE_PRIMARY_KEY_CONSTANT') => 'Policy Number',
|
||||||
(int) env('ICICI_PRIMARY_KEY_CONSTANT') => 'POLICY_NO',
|
(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)) {
|
if (empty($rows)) {
|
||||||
$this->db->transRollback(); // ROLLBACK BEFORE RETURN
|
return $this->failTpaClaimDumpInsert($fileId, 'Excel file contains no data or wrong file upload');
|
||||||
return ['status' => false, 'message' => 'Excel file contains no data or wrong file upload'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->policyNumberMapping[$fileData['tpa_id']]) && !empty($this->policyNumberMapping[$fileData['tpa_id']])){
|
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] ?? '')){
|
if($client_policy_data['policy_no'] != ($rows[0][$policy_number_column] ?? '')){
|
||||||
$this->db->transRollback();
|
return $this->failTpaClaimDumpInsert($fileId, 'Policy number mismatch in the file and in the system');
|
||||||
return ['status' => false, 'message' => 'Policy number mismatch in the file and in the system'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$tpaInsertData = $this->mapTPAData($rows, $fileId);
|
$tpaInsertData = $this->mapTPAData($rows, $fileId);
|
||||||
|
|
||||||
if (empty($tpaInsertData)) {
|
if (empty($tpaInsertData)) {
|
||||||
$this->db->transRollback(); // ROLLBACK BEFORE RETURN
|
return $this->failTpaClaimDumpInsert($fileId, 'These records already exist in the system.');
|
||||||
return ['status' => false, 'message' => 'These records already exist in the system.'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$return_res = $this->bulkInsertTPATable($tpaInsertData);
|
$return_res = $this->bulkInsertTPATable($tpaInsertData);
|
||||||
|
|
||||||
if ($return_res !== true) {
|
if ($return_res !== true) {
|
||||||
$this->db->transRollback(); // ROLLBACK BEFORE RETURN
|
return $this->failTpaClaimDumpInsert($fileId, 'TPA Import bulk insert failed');
|
||||||
return ['status' => false, 'message' => 'TPA Import bulk insert failed'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Commit if everything is fine
|
// 2. Commit if everything is fine
|
||||||
@ -93,9 +98,7 @@ abstract class BaseTpaClaimImportService
|
|||||||
return ['status' => true, 'message' => 'File uploaded successfully', 'record_count' => count($tpaInsertData)];
|
return ['status' => true, 'message' => 'File uploaded successfully', 'record_count' => count($tpaInsertData)];
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// 3. Rollback on any crash/exception
|
return $this->failTpaClaimDumpInsert($fileId, 'System error : ' . $e->getMessage());
|
||||||
$this->db->transRollback();
|
|
||||||
return ['status' => false, 'message' => 'System error : ' . $e->getMessage()];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +116,7 @@ abstract class BaseTpaClaimImportService
|
|||||||
|
|
||||||
// Check if mapping failed
|
// Check if mapping failed
|
||||||
if (!$ticketMasterData['status']) {
|
if (!$ticketMasterData['status']) {
|
||||||
$this->db->transRollback(); // ALWAYS rollback before early return
|
$this->rollbackAndCleanupClaimDumpData($file_id);
|
||||||
return $ticketMasterData;
|
return $ticketMasterData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,13 +128,11 @@ abstract class BaseTpaClaimImportService
|
|||||||
if (!empty($ticketMasterData['mapped_array'])) {
|
if (!empty($ticketMasterData['mapped_array'])) {
|
||||||
$insert_res = $this->importClaimMaster($ticketMasterData['mapped_array']);
|
$insert_res = $this->importClaimMaster($ticketMasterData['mapped_array']);
|
||||||
if (!$insert_res) {
|
if (!$insert_res) {
|
||||||
$this->db->transRollback();
|
return $this->failTicketMasterInsert($file_id, 'Ticket Master Claim bulk insert failed');
|
||||||
return ['status' => false, 'message' => 'Ticket Master Claim bulk insert failed'];
|
|
||||||
}
|
}
|
||||||
// Map newly created ticket IDs back to the TPA staging table
|
// Map newly created ticket IDs back to the TPA staging table
|
||||||
if (!$this->updateTicketIdInTPATable($file_id)) {
|
if (!$this->updateTicketIdInTPATable($file_id)) {
|
||||||
$this->db->transRollback();
|
return $this->failTicketMasterInsert($file_id, 'Updating ticket_id in TPA table failed');
|
||||||
return ['status' => false, 'message' => 'Updating ticket_id in TPA table failed'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$message .= 'Ticket Master Claim bulk insert success. ';
|
$message .= 'Ticket Master Claim bulk insert success. ';
|
||||||
@ -144,8 +145,7 @@ abstract class BaseTpaClaimImportService
|
|||||||
if (!empty($ticketMasterData['rejected_reason_array'])) {
|
if (!empty($ticketMasterData['rejected_reason_array'])) {
|
||||||
$update_res = $this->updateTicketMasterRejectedReasonInTPATable($ticketMasterData['rejected_reason_array']);
|
$update_res = $this->updateTicketMasterRejectedReasonInTPATable($ticketMasterData['rejected_reason_array']);
|
||||||
if (!$update_res) {
|
if (!$update_res) {
|
||||||
$this->db->transRollback();
|
return $this->failTicketMasterInsert($file_id, 'Updating rejected reasons failed');
|
||||||
return ['status' => false, 'message' => 'Updating rejected reasons failed'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$message .= empty($ticketMasterData['mapped_array'])
|
$message .= empty($ticketMasterData['mapped_array'])
|
||||||
@ -156,24 +156,69 @@ abstract class BaseTpaClaimImportService
|
|||||||
|
|
||||||
// If nothing was processed but no error occurred
|
// If nothing was processed but no error occurred
|
||||||
if (!$hasExecutedTask) {
|
if (!$hasExecutedTask) {
|
||||||
$this->db->transRollback();
|
return $this->failTicketMasterInsert($file_id, 'No data found to process.');
|
||||||
return ['status' => false, 'message' => 'No data found to process.'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Commit the transaction
|
// 2. Commit the transaction
|
||||||
$this->db->transCommit();
|
$this->db->transCommit();
|
||||||
|
|
||||||
|
if (!$status) {
|
||||||
|
$this->cleanupClaimDumpData($file_id);
|
||||||
|
}
|
||||||
|
|
||||||
return ['status' => $status, 'message' => trim($message)];
|
return ['status' => $status, 'message' => trim($message)];
|
||||||
|
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
// 3. Rollback on crash
|
$fileId = (int) ($params['file_id'] ?? 0);
|
||||||
$this->db->transRollback();
|
return $this->failTicketMasterInsert(
|
||||||
return [
|
$fileId,
|
||||||
'status' => false,
|
'System error during Ticket Master Insert: ' . $th->getMessage()
|
||||||
'message' => '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)
|
* Read Excel and return associative rows (header based)
|
||||||
*/
|
*/
|
||||||
@ -325,6 +370,14 @@ abstract class BaseTpaClaimImportService
|
|||||||
*/
|
*/
|
||||||
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): 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();
|
$EmployeeModel = new EmployeeModel();
|
||||||
$employeeData = $EmployeeModel
|
$employeeData = $EmployeeModel
|
||||||
->select([
|
->select([
|
||||||
|
|||||||
@ -756,14 +756,17 @@ class EmployeePolicyModel extends Model
|
|||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
emp_code,
|
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 = '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 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,
|
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
|
MAX(CASE WHEN field_name = 'si_enhancement_date' THEN new_value END) AS date_of_coverage
|
||||||
FROM emp_endorsement
|
FROM emp_endorsement
|
||||||
$subquery_endorsement_condition
|
$subquery_endorsement_condition
|
||||||
GROUP BY emp_code
|
AND actions = 'si'
|
||||||
) AS sidata ON a.emp_code = sidata.emp_code
|
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}'
|
WHERE employee_polices.client_policy_id = '{$client_policy_id}'
|
||||||
AND employees.client_branch_id = '{$client_branch_id}'
|
AND employees.client_branch_id = '{$client_branch_id}'
|
||||||
$endorsement_condition
|
$endorsement_condition
|
||||||
@ -2252,7 +2255,7 @@ class EmployeePolicyModel extends Model
|
|||||||
}
|
}
|
||||||
else if(count($emp_codes) > 0 && $all == false)
|
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)
|
else if(count($emp_codes) == 0 && $all == true)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -112,18 +112,44 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
max-width: 95%;
|
max-width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make the modal content a flex column so only the body scrolls */
|
||||||
#tpa_variation_modal .modal-content {
|
#tpa_variation_modal .modal-content {
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Internal vertical scroll for large tables, keep header/footer fixed */
|
||||||
#tpa_variation_modal .modal-body {
|
#tpa_variation_modal .modal-body {
|
||||||
max-height: calc(85vh - 50px);
|
flex: 1 1 auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
direction: ltr;
|
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 {
|
#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 {
|
#tpa_variation_modal table {
|
||||||
@ -548,6 +574,10 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
let tpaNotInNhanceProceedEnabled = true;
|
let tpaNotInNhanceProceedEnabled = true;
|
||||||
/** From API `not_in_nhance_proceed_button_text` — label for Proceed on "Not in Nhance" tab. */
|
/** From API `not_in_nhance_proceed_button_text` — label for Proceed on "Not in Nhance" tab. */
|
||||||
let tpaNotInNhanceProceedButtonText = 'Proceed - Not in Nhance';
|
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 = {
|
const tpaVariationColumns = {
|
||||||
not_in_nhance: [
|
not_in_nhance: [
|
||||||
@ -694,6 +724,16 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
tpaNotInNhanceProceedButtonText = 'Proceed - Not in Nhance';
|
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');
|
showTPAVariationTabByLinkId('not-in-nhance-tab');
|
||||||
updateTPAProceedButton('not-in-nhance-tab');
|
updateTPAProceedButton('not-in-nhance-tab');
|
||||||
adjustVariationTableByTabId('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'));
|
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() {
|
function getVariationDataTableConfig() {
|
||||||
return {
|
return {
|
||||||
dom: "<'dt-top'lf>" +
|
dom: "<'dt-top'lf>" +
|
||||||
@ -722,7 +776,15 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
paging: true,
|
paging: true,
|
||||||
ordering: false,
|
ordering: false,
|
||||||
info: false,
|
info: false,
|
||||||
|
scrollX: true,
|
||||||
|
scrollY: false,
|
||||||
|
scrollCollapse: false,
|
||||||
autoWidth: false,
|
autoWidth: false,
|
||||||
|
initComplete: function () {
|
||||||
|
const $wrap = $(this.api().table().container());
|
||||||
|
$wrap.closest('.table-responsive').addClass('nh-dt-no-outer-scroll');
|
||||||
|
bindVariationScrollHeadSync($wrap);
|
||||||
|
},
|
||||||
language: {
|
language: {
|
||||||
search: `
|
search: `
|
||||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||||
@ -773,8 +835,17 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dt = $table.DataTable();
|
const dt = $table.DataTable();
|
||||||
|
const $wrap = $(dt.table().container());
|
||||||
|
bindVariationScrollHeadSync($wrap);
|
||||||
dt.columns.adjust().draw(false);
|
dt.columns.adjust().draw(false);
|
||||||
bindVariationSearchIcons($table);
|
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) {
|
function adjustVariationTableByTabId(tabId) {
|
||||||
@ -814,7 +885,10 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
$button.prop('disabled', true);
|
$button.prop('disabled', true);
|
||||||
}
|
}
|
||||||
} else if (activeId === 'need-to-review-tab') {
|
} 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') {
|
} else if (activeId === 'not-in-tpa-tab') {
|
||||||
$button.addClass('d-none');
|
$button.addClass('d-none');
|
||||||
} else {
|
} else {
|
||||||
@ -829,12 +903,32 @@ for ($i = 0; $i < $batch_col_count; $i++) {
|
|||||||
const activeId = $(e.target).attr('id');
|
const activeId = $(e.target).attr('id');
|
||||||
updateTPAProceedButton(activeId);
|
updateTPAProceedButton(activeId);
|
||||||
adjustVariationTableByTabId(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 () {
|
$('#tpa_variation_modal').on('shown.bs.modal', function () {
|
||||||
showTPAVariationTabByLinkId('not-in-nhance-tab');
|
showTPAVariationTabByLinkId('not-in-nhance-tab');
|
||||||
updateTPAProceedButton('not-in-nhance-tab');
|
updateTPAProceedButton('not-in-nhance-tab');
|
||||||
adjustVariationTableByTabId('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 () {
|
$('#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') {
|
} else if (activeId === 'not-in-tpa-tab') {
|
||||||
proceedNotInTPA();
|
proceedNotInTPA();
|
||||||
} else if (activeId === 'need-to-review-tab') {
|
} else if (activeId === 'need-to-review-tab') {
|
||||||
|
if (!tpaNeedToReviewProceedEnabled) {
|
||||||
|
toastr.warning('No mismatched records to proceed.', 'WARNING');
|
||||||
|
return;
|
||||||
|
}
|
||||||
proceedNeedToReview();
|
proceedNeedToReview();
|
||||||
} else {
|
} else {
|
||||||
toastr.warning('Unknown tab selected.', 'WARNING');
|
toastr.warning('Unknown tab selected.', 'WARNING');
|
||||||
|
|||||||
@ -393,7 +393,8 @@ function getClaimSourceBadgeHtml(claimCreatedBy) {
|
|||||||
if (!cls) {
|
if (!cls) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return '<span class="claim-source-badge ' + cls + '">' + escapeHtml(key) + '</span>';
|
var label = key === 'CRM' ? 'STAFF' : key;
|
||||||
|
return '<span class="claim-source-badge ' + cls + '">' + escapeHtml(label) + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPolicyTypeIconHtml(tpaClaimType) {
|
function getPolicyTypeIconHtml(tpaClaimType) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user