diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 4764e7e1..360c6b8c 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -213,6 +213,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->post('get_emp_history','EmployeeController::getEmpHistory'); $routes->get("retail-endorsement-list", "EmployeeController::retailendorsementlist"); $routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave"); + $routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1"); }); diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index dc72314e..32217c91 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -28,6 +28,7 @@ use App\Models\PolicyPremium2Model; use App\Models\AuditHistoryModel; use App\Models\UserModel; use App\Models\PartnerEndorsementRequestModel; +use App\Models\TpaApiDataModel; use App\Controllers\Jobs; @@ -42,6 +43,8 @@ use CodeIgniter\API\ResponseTrait; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Style\Fill; use Dompdf\Dompdf; use Dompdf\Options; @@ -3579,5 +3582,388 @@ class EmployeeController extends AdminController } } + public function getTPADataVariationReport($file_id) + { + $file_info = $this->batchFileModel->where('id', $file_id)->find(); + $client_id = $file_info[0]['client_id']; + $client_policy_id = $file_info[0]['client_policy_id']; + $TpaApiDataModel = new TpaApiDataModel(); + $emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id); + + + //loop emp data with TPA data for matches + foreach ($emp_data_wo_tpa_id as $db_key => $db_row) + { + //get TPA API data from table for current DB ep code + $tpa_temp_data = $TpaApiDataModel->select('*') + ->where('emp_code', $db_row['emp_code']) + ->where('file_id', $file_id) + ->where('is_active', 1) + ->findAll(); + $match= $this->reconcileDbWithTpa($db_row,$tpa_temp_data); + $emp_data_wo_tpa_id[$db_key]['match'] = $match; + } + + // d($emp_data_wo_tpa_id); + + // die(); + //not_in_tpa + $tpa_emp_codes = $TpaApiDataModel->select('emp_code') + ->where('file_id', $file_id) + ->where('is_active', 1) + ->groupBy('emp_code') + ->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); + // d($not_in_tpa);die(); + + // not_in_nhance + $master_emp_codes = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id,[],true); + $master_emp_codes = array_column($master_emp_codes, 'emp_code'); + + $not_in_nhance = $TpaApiDataModel->select('*') + ->where('is_active',1) + ->where('file_id',$file_id) + ->whereNotIn('emp_code',$master_emp_codes) + ->findAll(); + // d($not_in_nhance);die(); + + if( !empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id) ) + { + $this->exportVariationReportExcel($not_in_tpa,$not_in_nhance,$emp_data_wo_tpa_id); + } + else + { + return false; + } + // $this->exportVariationReportExcel([],[],[]); + + + } + + + // not in use once all functionality workes well in this funciton then remvoe this function + function compareDbWithTpa(array $db, array $tpaRows): array + { + $partialMatches = []; + + // Normalize helper + $normalizeName = function ($name) { + return strtolower( + preg_replace('/[.\s_]+/', '', trim($name)) + ); + }; + + foreach ($tpaRows as $tpa) { + + // 0️⃣ emp_code must match + if (($db['emp_code'] ?? '') !== ($tpa['emp_code'] ?? '')) { + continue; + } + + $relationMatch = strtolower($db['relation'] ?? '') === strtolower($tpa['relation'] ?? ''); + $genderMatch = strtoupper($db['gender'] ?? '') === strtoupper($tpa['gender'] ?? ''); + $dobMatch = ($db['dob'] ?? '') === ($tpa['dob'] ?? ''); + + $nameMatch = $normalizeName($db['name'] ?? '') === + $normalizeName($tpa['name'] ?? ''); + + // ✅ FULL MATCH + if ($nameMatch && $relationMatch && $genderMatch && $dobMatch) { + return [ + 'match' => 'full_match', + 'record'=> $tpa + ]; + } + + // ⚠️ PARTIAL MATCH + if ($relationMatch || $genderMatch || $dobMatch) { + $partialMatches[] = [ + 'record' => $tpa, + 'matched_on' => [ + 'relation' => $relationMatch, + 'gender' => $genderMatch, + 'dob' => $dobMatch + ] + ]; + } + } + + // If no full match but partial exists + if (!empty($partialMatches)) { + return [ + 'match' => 'partial_match', + 'candidates' => $partialMatches + ]; + } + + // Nothing matched + return [ + 'match' => 'no_match' + ]; + } + + + function reconcileDbWithTpa(array $db, array $tpaRows): array + { + // Name normalization + $normalizeName = function ($name) { + return strtolower( + preg_replace('/[.\s_]+/', '', trim($name)) + ); + }; + + foreach ($tpaRows as $tpa) { + + // 1️⃣ emp_code + relation must match + if ( + // ($db['emp_code'] ?? '') !== ($tpa['emp_code'] ?? '') || + strtolower($db['relationship']) !== strtolower($tpa['relation']) + ) { + continue; + } + + // 2️⃣ Field comparison + $diff = []; + + if ( + ($db['name'] ?? '') !== + ($tpa['name'] ?? '') + ) { + $diff[] = 'name'; + } + + if (($db['dob'] ?? '') !== ($tpa['dob'] ?? '')) { + $diff[] = 'dob'; + } + + if ( + strtoupper($db['gender'] ?? '') !== + strtoupper($tpa['gender'] ?? '') + ) { + $diff[] = 'gender'; + } + + // 3️⃣ Match found + return [ + 'status' => 'matched', + 'tpa_record' => $tpa, + 'not_matching' => $diff // empty = perfect match + ]; + } + + // 4️⃣ No match found + return [ + 'status' => 'no_match' + ]; + } + + + function exportVariationReportExcel( + array $notInTPA, + array $notInNhance, + array $reviewNeeded, + string $filename = 'employee_review.xlsx' +) { + + function setCell($sheet, int $col, int $row, $value) +{ + $cell = Coordinate::stringFromColumnIndex($col) . $row; + $sheet->setCellValue($cell, $value); +} + +$EXPORT_COLUMNS = [ + + // Sheet 1 — Not in TPA + 'not_in_tpa' => [ + 'emp_code' => 'Employee Code', + 'name' => 'Employee Name', + 'relationship' => 'Relation', + 'dob' => 'Date of Birth', + 'gender' => 'Gender', + 'mobile' => 'Mobile No', + 'email_corporate' => 'Corporate Email', + 'policy_no' => 'Policy No', + 'tpa_name' => 'TPA Name', + 'change_event' => 'Change Event', + ], + + // Sheet 2 — Not in Nhance + 'not_in_nhance' => [ + 'emp_code' => 'Employee Code', + 'name' => 'Employee Name', + 'relation' => 'Relation', + 'dob' => 'Date of Birth', + 'gender' => 'Gender', + 'age' => 'Age', + 'tpa_id' => 'TPA Member ID', + ], + + // Sheet 3 — Review Needed (DB side) + 'review_main' => [ + 'emp_code' => 'Employee Code', + 'name' => 'Employee Name', + 'relationship' => 'Relation', + 'dob' => 'Date of Birth', + 'gender' => 'Gender', + 'policy_no' => 'Policy No', + 'uhid' => 'UHID', + 'change_event' => 'Change event' + ], + + // Sheet 3 — Review Needed (TPA side) + 'review_tpa' => [ + 'emp_code' => 'TPA Employee Code', + 'name' => 'TPA Name', + 'relation' => 'TPA Relation', + 'dob' => 'TPA DOB', + 'gender' => 'TPA Gender', + 'tpa_id' => 'TPA Member ID', + 'age' => 'TPA Age', + ], +]; + + + $spreadsheet = new Spreadsheet(); + + /* ========================================================= + * SHEET 1 — NOT IN TPA + * ========================================================= */ + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet1->setTitle('Not in TPA'); + + $cols = $EXPORT_COLUMNS['not_in_tpa']; + + $colNo = 1; + foreach ($cols as $label) { + setCell($sheet1, $colNo++, 1, $label); + } + + $rowNo = 2; + foreach ($notInTPA as $row) { + $colNo = 1; + foreach ($cols as $key => $label) { + setCell($sheet1, $colNo++, $rowNo, $row[$key] ?? ''); + } + $rowNo++; + } + + foreach (range(1, count($cols)) as $c) { + $sheet1->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true); + } + + /* ========================================================= + * SHEET 2 — NOT IN NHANCE + * ========================================================= */ + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setTitle('Not in Nhance'); + + $cols = $EXPORT_COLUMNS['not_in_nhance']; + + $colNo = 1; + foreach ($cols as $label) { + setCell($sheet2, $colNo++, 1, $label); + } + + $rowNo = 2; + foreach ($notInNhance as $row) { + $colNo = 1; + foreach ($cols as $key => $label) { + setCell($sheet2, $colNo++, $rowNo, $row[$key] ?? ''); + } + $rowNo++; + } + + foreach (range(1, count($cols)) as $c) { + $sheet2->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true); + } + + /* ========================================================= + * SHEET 3 — REVIEW NEEDED + * ========================================================= */ + $sheet3 = $spreadsheet->createSheet(); + $sheet3->setTitle('Review Needed'); + + $mainCols = $EXPORT_COLUMNS['review_main']; + $tpaCols = $EXPORT_COLUMNS['review_tpa']; + + // headers + $colNo = 1; + foreach ($mainCols as $label) { + setCell($sheet3, $colNo++, 1, $label); + } + foreach ($tpaCols as $label) { + setCell($sheet3, $colNo++, 1, $label); + } + + // rows + $rowNo = 2; + foreach ($reviewNeeded as $row) { + + // main (DB) + $colNo = 1; + foreach ($mainCols as $key => $label) { + setCell($sheet3, $colNo++, $rowNo, $row[$key] ?? ''); + } + + $tpaData = []; + $notMatching = []; + + if (($row['match']['status'] ?? '') === 'matched') { + $tpaData = $row['match']['tpa_record'] ?? []; + $notMatching = $row['match']['not_matching'] ?? []; + } + + // TPA + foreach ($tpaCols as $key => $label) { + setCell($sheet3, $colNo++, $rowNo, $tpaData[$key] ?? ''); + } + + // highlight mismatches + foreach ($notMatching as $field) { + + if (isset($mainCols[$field])) { + $idx = array_keys($mainCols); + $pos = array_search($field, $idx); + $cell = Coordinate::stringFromColumnIndex($pos + 1) . $rowNo; + $sheet3->getStyle($cell)->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor()->setARGB('FFFFFF00'); + } + + if (isset($tpaCols[$field])) { + $idx = array_keys($tpaCols); + $pos = array_search($field, $idx); + $cell = Coordinate::stringFromColumnIndex(count($mainCols) + $pos + 1) . $rowNo; + $sheet3->getStyle($cell)->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor()->setARGB('FFFFFF00'); + } + } + + $rowNo++; + } + + foreach (range(1, count($mainCols) + count($tpaCols)) as $c) { + $sheet3->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true); + } + + /* ========================================================= + * OUTPUT + * ========================================================= */ + $writer = new Xlsx($spreadsheet); + + if (ob_get_length()) ob_end_clean(); + + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Cache-Control: max-age=0'); + + $writer->save('php://output'); + exit; +} + + } diff --git a/app/Controllers/MediAssistApiController.php b/app/Controllers/MediAssistApiController.php index ba21f694..476cf2f7 100644 --- a/app/Controllers/MediAssistApiController.php +++ b/app/Controllers/MediAssistApiController.php @@ -351,6 +351,7 @@ class MediAssistApiController extends BaseController // now update DB + $batch_file_success = 'success'; $updated = 0; $employee_policy_ids = []; foreach ($employeePolicyData as $policy_data) { @@ -401,6 +402,7 @@ class MediAssistApiController extends BaseController 'gender' => $policy_data['gender'] ?? null, 'dob' => $policy_data['dob'] ?? null, ]; + $batch_file_success = 'partially success'; log_message( 'error', @@ -421,7 +423,8 @@ class MediAssistApiController extends BaseController // update file table status after the tpa id successfully updated if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { $file_model = new BatchFileModel(); - $file_model->where('id', $requestData['file_id'])->set('status', 'success')->update(); + + $file_model->where('id', $requestData['file_id'])->set('status', $batch_file_success)->update(); log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); } else { log_message('error', "Failed to update file table status."); diff --git a/app/Models/EmployeePolicyModel.php b/app/Models/EmployeePolicyModel.php index 16d4a815..0e86d94a 100755 --- a/app/Models/EmployeePolicyModel.php +++ b/app/Models/EmployeePolicyModel.php @@ -2139,5 +2139,60 @@ class EmployeePolicyModel extends Model return $data; } + public function getTPADataVariationReport($client_id, $client_policy_id, $file_id,$emp_codes = [],$all = false) + { + $result = $this->select([ + 'employee_polices.*', + 'tpam.name as tpa_name', + 'tpam.short_name as tpa_short_name', + 'emp.relationship', + 'emp.relationship_code', + 'emp.change_event', + 'emp.emp_code', + 'emp.name', + 'emp.email_corporate', + 'emp.dob', + 'emp.gender', + 'emp.emp_status', + 'emp.is_active as emp_is_active', + 'emp.mobile as mobile', + 'emp.doj', + 'emp.basic_pay', + 'emp.band as grade', + 'cp.policy_no', + 'cp.policy_type_id', + + ]) + ->join('employees emp', 'employee_polices.employee_id = emp.id') + ->join('client_policy cp', 'employee_polices.client_policy_id = cp.id') + ->join('tpa tpam', 'cp.tpa_id = tpam.id', 'left') //tpam - tpa master + // ->orderBy('emp.emp_code', 'ASC') + // ->orderBy('employee_polices.employee_id', 'ASC'); + ->where('employee_polices.status', 'active') + ->where('employee_polices.is_active', 1) + ->where('emp.is_active', 1) + ->where('employee_polices.status !=', 'inactive') + ->where('cp.id', $client_policy_id) + ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('emp.client_id', $client_id); + + if(count($emp_codes) == 0 && $all == false) + { + $result->where('employee_polices.tpa_id IS NULL'); + } + else if(count($emp_codes) > 0 && $all == false) + { + $result->whereNotIn('emp.emp_code',$emp_codes); + } + else if(count($emp_codes) == 0 && $all == true) + { + $result->groupBy('emp.emp_code'); + } + + return $result->findAll(); + // print_r($result); + // return $result; + } + } \ No newline at end of file diff --git a/app/Views/batch_list.php b/app/Views/batch_list.php index 123950fb..aa2aa0da 100755 --- a/app/Views/batch_list.php +++ b/app/Views/batch_list.php @@ -176,6 +176,12 @@ + + + + + + diff --git a/app/Views/insurer_or_tpa_data.php b/app/Views/insurer_or_tpa_data.php index 60efbcb4..70296812 100755 --- a/app/Views/insurer_or_tpa_data.php +++ b/app/Views/insurer_or_tpa_data.php @@ -813,6 +813,11 @@ } function fetchTpaIdFromTpa(){ + let user_confirm = confirm('Are you sure you want to initiate the TPA fetch? This may take a while.'); + if(!user_confirm) + { + return false; + } let event = $('#event_type').val(); let client_id = $('#client').val();