FEAT_TPA_VARIATION_REPORT

This commit is contained in:
velz 2025-12-22 18:28:04 +05:30
parent 6268a1e829
commit 052cf9df97
6 changed files with 457 additions and 1 deletions

View File

@ -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");
});

View File

@ -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;
}
}

View File

@ -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.");

View File

@ -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;
}
}

View File

@ -176,6 +176,12 @@
<a href="<?= base_url('/util/download_import_file/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download</a>
<?php } ?>
<?php if ($file['actions'] == "fetch" && $file['status'] == 'partially success') { ?>
<a href="<?= base_url('employee/getTPADataVariationReport/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download TPA Variation report</a>
<?php } ?>
</div>
</div>
</td>

View File

@ -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();