diff --git a/app/Config/Database.php b/app/Config/Database.php index f59b49f..d1632d6 100755 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -20,6 +20,7 @@ class Database extends Config * use if no other is specified. */ public string $defaultGroup = 'default'; + public string $enableSSL; /** * The default database connection. @@ -38,6 +39,7 @@ class Database extends Config 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', 'encrypt' => false, + // 'encrypt' => ['ssl_verify' => true,'ssl_ca' => ROOTPATH .'ca.pem'], 'compress' => false, 'strictOn' => false, 'failover' => [], @@ -95,5 +97,17 @@ class Database extends Config if (ENVIRONMENT === 'testing') { $this->defaultGroup = 'tests'; } + + $this->enableSSL = env('DB_SSL_ENABLE') ?? false; + + if ($this->enableSSL === true || $this->enableSSL === 'true' || $this->enableSSL === 1 || $this->enableSSL === '1') { + + + // Enable SSL authentication + $this->default['encrypt'] = [ + 'ssl_ca' => ROOTPATH . 'ca.pem', + 'ssl_verify' => true, + ]; + } } } diff --git a/app/Config/Feature.php b/app/Config/Feature.php index 0bc45c6..af76291 100755 --- a/app/Config/Feature.php +++ b/app/Config/Feature.php @@ -21,7 +21,7 @@ class Feature extends BaseConfig * - property $filtersInfo, instead of $filterInfo * - CodeIgniter\Router\RouteCollection::getFiltersForRoute(), instead of getFilterForRoute() */ - public bool $multipleFilters = false; + public bool $multipleFilters = true; /** * Use improved new auto routing instead of the default legacy version. diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 0855ec3..76924a2 100755 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -12,8 +12,10 @@ use CodeIgniter\Filters\SecureHeaders; use App\Filters\AuthMVC; use App\Filters\HttpRequestLog; use App\Filters\CloseDbConnection; +use App\Filters\VerifyAppSignature; use App\Filters\AuthJWT; +use App\Filters\Cors; class Filters extends BaseConfig { @@ -34,7 +36,10 @@ class Filters extends BaseConfig 'authMVC' => AuthMVC::class, 'HttpRequestLog' => HttpRequestLog::class, 'authJWT' => AuthJWT::class, - 'CloseDbConnection' => CloseDbConnection::class + 'CloseDbConnection' => CloseDbConnection::class, + 'Cors' => Cors::class, + 'appSignature' => VerifyAppSignature::class, + ]; /** @@ -47,11 +52,13 @@ class Filters extends BaseConfig public array $globals = [ 'before' => [ 'HttpRequestLog' => ['except' => 'cli/*'], + 'Cors', // 'csrf', // 'invalidchars', ], 'after' => [ - 'CloseDbConnection' + 'CloseDbConnection', + 'Cors', // 'secureheaders', ], ]; diff --git a/app/Config/Routes.php b/app/Config/Routes.php index d5cc961..eaf7741 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -3,6 +3,14 @@ use CodeIgniter\Router\RouteCollection; + +// Allow OPTIONS for all routes +$routes->options('(:any)', function() { + // This will never be called because the CORS filter returns early + // But having this route ensures OPTIONS isn't rejected as 404 +}); + + /** * @var RouteCollection $routes */ @@ -431,45 +439,21 @@ $routes->cli('cli/check_bounce_mail_cli', 'MasterController::testCheckBounceMail $routes->cli('cli/app_check_list', 'MasterController::appCheckList'); $routes->cli('cli/new_gdrive_token', 'GoogleDriveController::generateNewGoogleDriveAccessToken'); -//Employee login api's -$routes->post("/employeeRest/verifyEmployeeNumber", "RestAuthenticationController::verifyEmployeeWithMobileNumber"); -$routes->post("/employeeRest/getVerifiedUserData", "RestAuthenticationController::getVerifiedUserData"); -$routes->post("/employeeRest/verifyEmployeeEmailId", "RestAuthenticationController::verifyEmployeeWithEmailId"); -// $routes->post("/employeeRest/saveMpin", "RestAuthenticationController::saveMpin"); -//HR login api's -$routes->post("/employeeRest/verifyHrWithMobileNumber", "RestAuthenticationController::verifyHrWithMobileNumber"); -$routes->post("/employeeRest/verifyHrWithEmail", "RestAuthenticationController::verifyHrWithEmail"); -$routes->post("/employeeRest/getVerifiedHrData", "RestAuthenticationController::getVerifiedHrData"); - $routes->group("/api", ["filter" => "authJWT"], function ($routes) { $routes->post("logined", "RestAuthenticationController::logined"); $routes->post("getId", "RestAuthenticationController::getUserIdFromToken"); }); -// MPIN api's -$routes->post("employeeRest/saveMpin", "RestAuthenticationController::saveMpin"); -$routes->post("employeeRest/updateMpin", "RestAuthenticationController::updateMpin"); -$routes->post("/employeeRest/verifyMpin", "RestAuthenticationController::verifyMpin"); -$routes->post("/employeeRest/checkMpin", "RestAuthenticationController::checkMpin"); -$routes->post("employeeRest/forgotMPIN", "RestAuthenticationController::forgotMPIN"); -$routes->post("employeeRest/updateMobileNumber", "RestAuthenticationController::updateMobileNumber"); -// PASSWORD api's -$routes->post("employeeRest/savePassword", "RestAuthenticationController::savePassword"); -$routes->post("employeeRest/changePassword", "RestAuthenticationController::changePassword"); -$routes->post("employeeRest/verifyPassword", "RestAuthenticationController::verifyPassword"); -$routes->post("employeeRest/verifyOtp", "RestAuthenticationController::verifyOtp"); -$routes->post("employeeRest/checkPassword", "RestAuthenticationController::checkPassword"); -$routes->get("employeeRest/downloadSampleExcel", "EmployeeRestController::downloadSampleExcel"); // $routes->post("employeeRest/createOrUpdateEmployeePolicySiAmount", "EmployeeRestController::createOrUpdateEmployeePolicySiAmount"); // $routes->post("employeeRest/calculatePremium", "EmployeeRestController::calculatePremium"); -$routes->group("employeeRest", ["filter" => "authJWT"], function ($routes) { +$routes->group("employeeRest", ['filter' => ['appSignature' , 'authJWT'] ], function ($routes) { // $routes->post("getVerifiedUserData", "RestAuthenticationController::getVerifiedUserData"); @@ -515,6 +499,41 @@ $routes->group("employeeRest", ["filter" => "authJWT"], function ($routes) { $routes->get("hrFileDownload", "EmployeeRestController::hrFileDownload"); $routes->post("hrFileUpload", "EmployeeRestController::hrFileUpload"); + $routes->get("copyActiveEmployeeAndDependentDetails", "EmployeeRestController::copyActiveEmployeeAndDependentDetails"); + +}); + +$routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($routes) { + + //Employee login api's + $routes->post("verifyEmployeeNumber", "RestAuthenticationController::verifyEmployeeWithMobileNumber"); + $routes->post("getVerifiedUserData", "RestAuthenticationController::getVerifiedUserData"); + $routes->post("verifyEmployeeEmailId", "RestAuthenticationController::verifyEmployeeWithEmailId"); + // $routes->post("saveMpin", "RestAuthenticationController::saveMpin"); + + + //HR login api's + $routes->post("verifyHrWithMobileNumber", "RestAuthenticationController::verifyHrWithMobileNumber"); + $routes->post("verifyHrWithEmail", "RestAuthenticationController::verifyHrWithEmail"); + $routes->post("getVerifiedHrData", "RestAuthenticationController::getVerifiedHrData"); + + // MPIN api's + $routes->post("saveMpin", "RestAuthenticationController::saveMpin"); + $routes->post("updateMpin", "RestAuthenticationController::updateMpin"); + $routes->post("verifyMpin", "RestAuthenticationController::verifyMpin"); + $routes->post("checkMpin", "RestAuthenticationController::checkMpin"); + $routes->post("forgotMPIN", "RestAuthenticationController::forgotMPIN"); + $routes->post("updateMobileNumber", "RestAuthenticationController::updateMobileNumber"); + + // PASSWORD api's + $routes->post("savePassword", "RestAuthenticationController::savePassword"); + $routes->post("changePassword", "RestAuthenticationController::changePassword"); + $routes->post("verifyPassword", "RestAuthenticationController::verifyPassword"); + $routes->post("verifyOtp", "RestAuthenticationController::verifyOtp"); + $routes->post("checkPassword", "RestAuthenticationController::checkPassword"); + $routes->get("downloadSampleExcel", "EmployeeRestController::downloadSampleExcel"); + + }); $routes->get("getEmployeeActiveOrInactivePolicy", "EmployeeRestController::getEmployeeActiveOrInactivePolicy"); @@ -530,7 +549,7 @@ $routes->cli("cli/sendCroneRemainderMail", "DashboardController::sendCroneRemain $routes->get('enrollOpendAndClose', 'DashboardController::updatePolicyEnrollmentStatus'); $routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail"); -$routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicy"); +// $routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicy"); $routes->get("getAddOnPolicy", "EmployeeRestController::getAddOnPolicy"); $routes->post("addEmployeeAndDependence", "EmployeeRestController::addEmployeeAndDependence"); $routes->post("getPreEmployeePolicyCount", "EmployeeRestController::getPreEmployeePolicyCount"); diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index 0cd0fdd..348b42a 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -369,7 +369,7 @@ class EmployeeRestController extends AdminController $basic_cover_si = $policy_terms->sum_insured; } - $checkDataExist = $this->employeePolicyModel->where('employee_id',$employee_id)->where('client_policy_id',$client_policy_id)->first(); + $checkDataExist = $this->employeePolicyModel->where('employee_id',$employee_id)->where('client_policy_id',$client_policy_id)->where('is_active', 1)->first(); if($checkDataExist){ @@ -526,16 +526,30 @@ class EmployeeRestController extends AdminController try { if($this->request->getGet('id')) - { - $this->employeeModel->where('id', $this->request->getGet('id') ) - ->where('is_active', 1 ) - ->set(array('is_active'=> 0 )) - ->update(); + { + $is_from_copy = $this->request->getGet('is_from_copy') ?? null; + + if($is_from_copy == 'false' || $is_from_copy == false || $is_from_copy == 'null' || $is_from_copy == null){ + + $this->employeeModel->where('id', $this->request->getGet('id') ) + ->where('is_active', 1 ) + ->set(array('is_active'=> 0 )) + ->update(); + } + + $query = $this->employeePolicyModel + ->where('employee_id', $this->request->getGet('id')) + ->where('is_active', 1); + + // 👉 Add condition only if client_policy_id exists + $clientPolicyId = $this->request->getGet('client_policy_id') ?? null; + if (!empty($clientPolicyId)) { + $query->where('client_policy_id', $clientPolicyId); + } + + $query->set(['is_active' => 0])->update(); + - $this->employeePolicyModel->where('employee_id',$this->request->getGet('id') ) - ->where('is_active', 1 ) - ->set(array('is_active'=> 0 )) - ->update(); return $this->respond(['status' => 'success','code' => 200,'data' =>[] ], 200); }else{ @@ -803,12 +817,9 @@ class EmployeeRestController extends AdminController { if(isset($result['error_type'])){ $result = $empServiceController->getExcelErrorData($file_id); - return $this->respond(['status' => 'failed', 'code' => 404, 'message' => "file upload failed with errors",'data' => $result], 200); + return $this->respond(['status' => 'failed', 'code' => 404, 'message' => 'The data format is invalid. Click "Next" to view details.','data' => $result], 200); }else { - $message = "file upload failed with errors"; - if(isset($result['error_data'])){ - $message = $result['error_data']; - } + $message = "The import file is in an incorrect format. Please compare it with our template file to correct it."; return $this->respond(['status' => 'failed', 'code' => 404, 'message' => $message,'data' => $result], 200); } } @@ -1163,13 +1174,25 @@ class EmployeeRestController extends AdminController if ($empPolicy) { - + + $latest_gmc_policy_id = getLatestGMCPolicy((array)$empPolicy); $result = []; foreach ($empPolicy as $array) { - // Reset employee array - $empData = $employeeData; - + // Reset employee array + // $empData = $employeeData; + $empData = $this->employeeModel + ->select('employees.*') + ->join('employee_polices', 'employee_polices.employee_id = employees.id') + ->where('employees.emp_code',$emp_code) + ->where('employees.client_id',$client_id) + ->where('employees.client_branch_id',$client_branch_id) + ->where('employee_polices.client_policy_id',$array->ClientPolicyId) + ->where('employees.is_active', 1 ) + ->where('employee_polices.is_active', 1 ) + ->where('employees.is_addon_value',0) + ->findAll(); + // Removes specific keys from the decoded array and assigns the result to $refusingData $decodedArray = json_decode($array->Policy_Terms); $refusingData = (object) array_diff_key((array) $decodedArray, array_flip($keysToRemove)); @@ -1248,7 +1271,14 @@ class EmployeeRestController extends AdminController }else if($array->policy_type_id == 2 && $this->request->getGet('policy') == 'GMC' ) { + $array->to_be_added_relationship = []; + $array->existing_relationship = []; + if(!empty($latest_gmc_policy_id) && $latest_gmc_policy_id == $array->ClientPolicyId){ + $array->copy_dependence_data_enable = true; + }else{ + $array->copy_dependence_data_enable = false; + } // Map family floaters that already exist in the employee table $familyFloates = $array->Policy_Terms->family_floaters; @@ -1259,7 +1289,7 @@ class EmployeeRestController extends AdminController if($getSlabAndGridData['slab_rates'][0]['premium_type'] == 1 || $getSlabAndGridData['slab_rates'][0]['premium_type'] == 3) { $array->floter_text_heading = 'Floater Sum Insured'; - $array->floter_text_description = 'This is a floater sum insured. A floater is a type of sum insured that provides coverage to more than one member ot a family at the same time. Simply put, its a single insurance cover for the entire family.'; + $array->floter_text_description = 'This is a floater sum insured. A floater is a type of sum insured that provides coverage to more than one member of a family at the same time. Simply put, its a single insurance cover for the entire family.'; }else{ $array->floter_text_heading = 'Sum Insured'; $array->floter_text_description = ''; @@ -1302,6 +1332,8 @@ class EmployeeRestController extends AdminController $temp['data']['age_validation'] = $this->getAgeRange($decodedArray,$familyFloatesValue); + + array_push($array->existing_relationship,$temp['data']['relationship']); array_push($data,$temp); unset($empData[$key]); @@ -1350,6 +1382,7 @@ class EmployeeRestController extends AdminController $temp2['is_value_exist'] = false; $temp2['data']['family_floater_key'] = $familyFloatesValue; + $temp2['data']['relationship'] = ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); $temp2['data']['client_policy_id'] = $array->ClientPolicyId; $temp2['data']['button_name'] = 'Add '.ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); $temp2['data']['form_type'] = preg_replace('/\d/', '', $familyFloatesValue); @@ -1359,9 +1392,58 @@ class EmployeeRestController extends AdminController array_push($data,$temp2); + array_push($array->to_be_added_relationship,['relationship'=>$temp2['data']['relationship'],'age_validation'=>$temp2['data']['age_validation']]); + $array->to_be_added_relationship = array_values( + array_map('unserialize', + array_unique( + array_map('serialize', $array->to_be_added_relationship) + ) + ) + ); + } } + + $array->relationship = []; + foreach ($array->to_be_added_relationship as $key => $value) { + if($value['relationship'] == 'Spouse'){ + array_push($array->relationship,['relationship'=>'Spouse','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + }else if($value['relationship'] == 'Child'){ + array_push($array->relationship, ['relationship'=>'Son','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + array_push($array->relationship, ['relationship'=>'Daughter','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + }else if($value['relationship'] == 'Parent'){ + $Father = array_search('Father',$array->existing_relationship); + $Mother = array_search('Mother',$array->existing_relationship); + if ($Father === false && $Mother === false) { + array_push($array->relationship, ['relationship'=>'Father','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + array_push($array->relationship, ['relationship'=>'Mother','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + }else if ($Father !== false && $Mother === false) { + array_push($array->relationship, ['relationship'=>'Mother','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + } else if ($Mother !== false && $Father === false) { + array_push($array->relationship, ['relationship'=>'Father','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + } + }else if($value['relationship'] == 'Parent in law'){ + $FatherinLaw = array_search('Father in Law',$array->existing_relationship); + $MotherinLaw = array_search('Mother in Law',$array->existing_relationship); + if ($FatherinLaw === false && $MotherinLaw === false) { + array_push($array->relationship, ['relationship'=>'Father in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + array_push($array->relationship, ['relationship'=>'Mother in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + }else if ($FatherinLaw !== false && $MotherinLaw === false) { + array_push($array->relationship, ['relationship'=>'Mother in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + } else if ($MotherinLaw !== false && $FatherinLaw === false) { + array_push($array->relationship, ['relationship'=>'Father in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]); + } + } + } + + // echo '
';
+                        // print_r($array->existing_relationship);
+                        // print_r($array->to_be_added_relationship);
+                        // print_r($array->relationship);
+                        // die;
+
+
                         $array->mapped_family_floaters = $data;
                         $array->type = "GMC";
                         $array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ?  $dependent_and_si_value : 0;
@@ -1386,12 +1468,14 @@ class EmployeeRestController extends AdminController
                         if($checkGmcParentsPolicyExist)
                         {
                             $GmcParrentsData =  $this->getGmcParrentsPolicy($checkGmcParentsPolicyExist,$emp_code,$client_id,$client_branch_id);
-                            return $this->respond(['status' => 'success','code' => 200,'data' => [$array,$GmcParrentsData]], 200);
-                        
+                            // return $this->respond(['status' => 'success','code' => 200,'data' => [$array,$GmcParrentsData]], 200);
+                            $result[] = $array;
+                            $result[] = $GmcParrentsData;
                         }
 
-                        if($this->request->getGet('policy') == 'GMC'){
-                            return $this->respond(['status' => 'success','code' => 200,'data' => [$array]], 200);
+                        if($this->request->getGet('policy') == 'GMC' && !$checkGmcParentsPolicyExist){
+                            // return $this->respond(['status' => 'success','code' => 200,'data' => [$array]], 200);
+                            $result[] = $array;
                         }
 
                     }
@@ -1399,14 +1483,31 @@ class EmployeeRestController extends AdminController
                 // $result[] = $array;
                 }
 
-                return $this->respond(['status' => 'success','code' => 200,'data' => []], 200);
+                if(!empty($result) && $this->request->getGet('policy') == 'GMC'){
+                    return $this->respond(['status' => 'success','code' => 200,'data' => $result], 200);
+                }else{
+                    return $this->respond(['status' => 'success','code' => 200,'data' => []], 200);
+                }
                 
 
             }else{
                 return $this->respond(['status' => 'failed','code' => 404,'data' => []], 200);
             }
+            
         } catch (\Exception $e) {
-            return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getLine()], 500);
+
+            $errorData = [
+                'message' => $e->getMessage(),
+                'file'    => $e->getFile(),
+                'line'    => $e->getLine(),
+                'code'    => $e->getCode(),
+                'trace'   => $e->getTraceAsString(),
+                'trace_array' => $e->getTrace(), // full array version (optional)
+                'function' => $e->getTrace()[0]['function'] ?? null,
+                'class' => $e->getTrace()[0]['class'] ?? null,
+            ];
+            
+            return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getLine(), 'error_data' => $errorData], 500);
         }
     }
 
@@ -1414,19 +1515,31 @@ class EmployeeRestController extends AdminController
     public function getGmcParrentsPolicy($GmcParrentsPolicy,$emp_code,$client_id,$client_branch_id)
     {
 
-        $employeeData = $this->employeeModel->where('emp_code',$emp_code)
-                                            ->where('client_id',$client_id)
-                                            ->where('client_branch_id',$client_branch_id)
-                                            ->where('is_active', 1 )
-                                            ->where('is_addon_value',0)->findAll();
+        // $employeeData = $this->employeeModel->where('emp_code',$emp_code)
+        //                                     ->where('client_id',$client_id)
+        //                                     ->where('client_branch_id',$client_branch_id)
+        //                                     ->where('is_active', 1 )
+        //                                     ->where('is_addon_value',0)->findAll();
         // return  $employeeData;
         foreach ($GmcParrentsPolicy as $key => $array) {
 
+            $array->to_be_added_relationship = [];
+            $array->existing_relationship = [];
         
 
             // Reset employee array
-            $empData = $employeeData;
-
+            // $empData = $employeeData;
+            $empData = $this->employeeModel
+                            ->select('employees.*')
+                            ->join('employee_polices', 'employee_polices.employee_id = employees.id')
+                            ->where('employees.emp_code',$emp_code)
+                            ->where('employees.client_id',$client_id)
+                            ->where('employees.client_branch_id',$client_branch_id)
+                            ->where('employee_polices.client_policy_id',$array->ClientPolicyId)
+                            ->where('employees.is_active', 1 )
+                            ->where('employee_polices.is_active', 1 )
+                            ->where('employees.is_addon_value',0)
+                            ->findAll();
 
             $array->Policy_Terms = json_decode($array->Policy_Terms);
 
@@ -1440,6 +1553,7 @@ class EmployeeRestController extends AdminController
                                             ->join('employee_polices', 'employee_polices.employee_id = employees.id')
                                             ->where('employees.emp_code',$emp_code)
                                             ->where('employees.is_active',1)
+                                            ->where('employee_polices.is_active',1)
                                             ->where('employee_polices.client_policy_id',$array->ClientPolicyId)
                                             ->get()
                                             ->getResult();
@@ -1513,6 +1627,7 @@ class EmployeeRestController extends AdminController
                             $temp['data']['basic_cover_si'] = isset($employee_policy->basic_cover_si) ? $employee_policy->basic_cover_si : 0;
                             $temp['data']['age_validation'] = $this->getAgeRange($array->Policy_Terms,$familyFloatesValue);  
                         
+                            array_push($array->existing_relationship,$temp['data']['relationship']);
                                     
                             array_push($data,$temp);
                             unset($empData[$key]);
@@ -1555,16 +1670,59 @@ class EmployeeRestController extends AdminController
 
             // Add family floter buttons placement data for FE validation
             if(count($floters)){
-            foreach ($floters as $familyFloatesValue) {
+                foreach ($floters as $familyFloatesValue) {
 
-                $temp2['is_value_exist'] = false;
-                $temp2['data']['family_floater_key'] = $familyFloatesValue;
-                $temp2['data']['client_policy_id'] = $array->ClientPolicyId; 
-                $temp2['data']['button_name'] = 'Add '.ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); 
-                $temp2['data']['form_type'] = preg_replace('/\d/', '', $familyFloatesValue);
-                $temp2['data']['age_validation'] = $this->getAgeRange($array->Policy_Terms,$familyFloatesValue);     
-                array_push($data,$temp2);
+                    $temp2['is_value_exist'] = false;
+                    $temp2['data']['family_floater_key'] = $familyFloatesValue;
+                    $temp2['data']['relationship'] = ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); 
+                    $temp2['data']['client_policy_id'] = $array->ClientPolicyId; 
+                    $temp2['data']['button_name'] = 'Add '.ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); 
+                    $temp2['data']['form_type'] = preg_replace('/\d/', '', $familyFloatesValue);
+                    $temp2['data']['age_validation'] = $this->getAgeRange($array->Policy_Terms,$familyFloatesValue);     
+                    array_push($data,$temp2);
 
+
+                    array_push($array->to_be_added_relationship,['relationship'=>$temp2['data']['relationship'],'age_validation'=>$temp2['data']['age_validation']]);
+                    $array->to_be_added_relationship = array_values(
+                        array_map('unserialize',
+                            array_unique(
+                                array_map('serialize', $array->to_be_added_relationship)
+                            )
+                        )
+                    );
+
+                }
+            }
+
+        $array->relationship = [];
+        foreach ($array->to_be_added_relationship as $key => $value) {
+            if($value['relationship'] == 'Spouse'){
+                    array_push($array->relationship,['relationship'=>'Spouse','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+            }else if($value['relationship'] == 'Child'){
+                array_push($array->relationship, ['relationship'=>'Son','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                array_push($array->relationship, ['relationship'=>'Daughter','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+            }else if($value['relationship'] == 'Parent'){
+                $Father = array_search('Father',$array->existing_relationship);
+                $Mother = array_search('Mother',$array->existing_relationship);
+                if ($Father === false && $Mother === false) {
+                    array_push($array->relationship, ['relationship'=>'Father','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                    array_push($array->relationship, ['relationship'=>'Mother','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                }else if ($Father !== false && $Mother === false) {
+                    array_push($array->relationship, ['relationship'=>'Mother','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                } else if ($Mother !== false && $Father === false) {
+                    array_push($array->relationship, ['relationship'=>'Father','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                }
+            }else if($value['relationship'] == 'Parent in law'){
+                $FatherinLaw = array_search('Father in Law',$array->existing_relationship);
+                $MotherinLaw = array_search('Mother in Law',$array->existing_relationship);
+                if ($FatherinLaw === false && $MotherinLaw === false) {
+                    array_push($array->relationship, ['relationship'=>'Father in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                    array_push($array->relationship, ['relationship'=>'Mother in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                }else if ($FatherinLaw !== false && $MotherinLaw === false) {
+                    array_push($array->relationship, ['relationship'=>'Mother in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                } else if ($MotherinLaw !== false && $FatherinLaw === false) {
+                    array_push($array->relationship, ['relationship'=>'Father in Law','age_validation'=>$value['age_validation'] , 'client_policy_id' => $array->ClientPolicyId]);
+                }
             }
         }
 
@@ -2013,6 +2171,10 @@ class EmployeeRestController extends AdminController
                 $siArray = $this->getSiMappedArray($array['id'],$array['base_policy']);
                 $responce['SlabRates'] = is_array($siArray) ? $siArray : $responce['SlabRates'];
 
+
+                $array['to_be_added_relationship'] = [];
+                $array['existing_relationship'] = [];
+
     
                         // Map family floaters that already exist in the employee table
                         $familyFloates = $policy_terms->family_floaters;
@@ -2051,7 +2213,9 @@ class EmployeeRestController extends AdminController
                                         $temp['data']['form_type'] = $dependent;  
                                         $temp['data']['basic_cover_si'] = isset($employee_policy->basic_cover_si) ? $employee_policy->basic_cover_si : null;
                                         $temp['data']['premium'] = isset($employee_policy->premium) ? $employee_policy->premium : null;   
-                                        $temp['data']['age_validation'] = $this->getAgeRange($decodedArray,$familyFloatesValue);          
+                                        $temp['data']['age_validation'] = $this->getAgeRange($decodedArray,$familyFloatesValue); 
+                                        
+                                        array_push($array['existing_relationship'],$temp['data']['relationship']);
                                     
                                         array_push($data,$temp);
                                         unset($addOnEmployeeData[$key]);
@@ -2103,6 +2267,7 @@ class EmployeeRestController extends AdminController
 
                             $temp2['is_value_exist'] = false;
                             $temp2['data']['family_floater_key'] = $familyFloatesValue;
+                            $temp2['data']['relationship'] = ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); 
                             $temp2['data']['client_policy_id'] = $array['id']; 
                             $temp2['data']['button_name'] = 'Add '.ucfirst(str_replace('_', ' ', preg_replace('/\d/', '', $familyFloatesValue))); 
                             $temp2['data']['form_type'] = preg_replace('/\d/', '', $familyFloatesValue);
@@ -2111,8 +2276,51 @@ class EmployeeRestController extends AdminController
 
                             array_push($data,$temp2);
 
+
+                            array_push($array['to_be_added_relationship'],['relationship'=>$temp2['data']['relationship'],'age_validation'=>$temp2['data']['age_validation']]);
+                            $array['to_be_added_relationship'] = array_values(
+                                array_map('unserialize',
+                                    array_unique(
+                                        array_map('serialize', $array['to_be_added_relationship'])
+                                    )
+                                )
+                            );
+
                         }
                         }
+
+
+                        $responce['relationship'] = [];
+                        foreach ($array['to_be_added_relationship'] as $key => $value) {
+                            if($value['relationship'] == 'Spouse'){
+                                array_push($responce['relationship'],['relationship'   => 'Spouse','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                            }else if($value['relationship'] == 'Child'){
+                                array_push($responce['relationship'], ['relationship'   => 'Son','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                array_push($responce['relationship'], ['relationship'   => 'Daughter','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                            }else if($value['relationship'] == 'Parent'){
+                                $Father = array_search('Father',$array['existing_relationship']);
+                                $Mother = array_search('Mother',$array['existing_relationship']);
+                                if ($Father === false && $Mother === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Father','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                    array_push($responce['relationship'], ['relationship'   => 'Mother','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                }else if ($Father !== false && $Mother === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Mother','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                } else if ($Mother !== false && $Father === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Father','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                }
+                            }else if($value['relationship'] == 'Parent in law'){
+                                $FatherinLaw = array_search('Father in Law',$array['existing_relationship']);
+                                $MotherinLaw = array_search('Mother in Law',$array['existing_relationship']);
+                                if ($FatherinLaw === false && $MotherinLaw === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Father in Law','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                    array_push($responce['relationship'], ['relationship'   => 'Mother in Law','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                }else if ($FatherinLaw !== false && $MotherinLaw === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Mother in Law','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                } else if ($MotherinLaw !== false && $FatherinLaw === false) {
+                                    array_push($responce['relationship'], ['relationship'   => 'Father in Law','age_validation' => $value['age_validation'],'client_policy_id'=>$array['id']]);
+                                }
+                            }
+                        }
                    
                         $responce['family_floaters_of_dependent_and_si_array'] = $data;
                         $responce['family_floaters_of_dependent_and_si_value'] = $dependent_and_si_value > 0 ?  $dependent_and_si_value : 0;
@@ -2290,7 +2498,7 @@ class EmployeeRestController extends AdminController
         $empData = $this->employeeModel->where('emp_code', $emp_code )->where('client_id', $client_id ) ->where('is_active', 1 )->findAll();
         $employeeIds = array_column($empData, 'id');
         //for mail common parameter
-        $filteredEmpData = array_filter($empData, fn($item) => $item['relationship'] === 'Self');
+        $filteredEmpData = array_values(array_filter($empData, fn($item) => $item['relationship'] === 'Self'));
 
         if (!is_null($client_policy_id) && is_array($client_policy_id))
         {
@@ -3437,7 +3645,7 @@ class EmployeeRestController extends AdminController
     }
 
 
-    public function getPreEmployeePolicyCount()
+    public function getPreEmployeePolicyCountOld()
     {
         log_message('error', 'STEP 1: getPreEmployeePolicyCount API called');
 
@@ -3485,6 +3693,7 @@ class EmployeeRestController extends AdminController
                 ->join('client_policy cp', 'employee_polices.client_policy_id = cp.id')
                 ->where('employees.is_active', 1)
                 ->whereIn('employees.emp_status', ['draft', 'enrolled'])
+                ->where('employees.family_floater_key', 'self')
                 ->where('employee_polices.is_active', 1)
                 ->whereIn('employee_polices.status', ['draft', 'enrolled'])
                 ->where('cp.enrolment_visibility', 1)
@@ -3521,156 +3730,410 @@ class EmployeeRestController extends AdminController
         }
     }
 
+    public function getPreEmployeePolicyCount()
+    {
+        log_message('error', 'STEP 1: getPreEmployeePolicyCount API called');
+
+        $request            = $this->request->getJSON(true);
+        $mobile_no          = $request['mobile_number'] ?? null;
+        $email_id           = $request['email_id'] ?? null;
+        $client_short_name  = $request['client_short_name'] ?? null;
+
+        log_message('error', 'STEP 2: Received input - ' . json_encode($request));
+
+        /** ---------------------------------------------------------------
+         *   STEP 3: Resolve Client ID (If client_short_name passed)
+         * --------------------------------------------------------------- */
+        $clientId = null;
+        if (!empty($client_short_name)) {
+            log_message('error', 'STEP 3: Looking up client with short_name: ' . $client_short_name);
+
+            $client = $this->clientModel
+                        ->select('id')
+                        ->where('is_active', 1)
+                        ->where('short_name', $client_short_name)
+                        ->first();
+            
+            if (!empty($client)) {
+                $clientId = $client['id'];
+                log_message('error', 'STEP 4: Found client ID: ' . $clientId);
+            } else {
+                log_message('error', 'STEP 4: No client found for short_name: ' . $client_short_name);
+            }
+        } else {
+            log_message('error', 'STEP 3: client_short_name is empty.');
+        }
+
+        /** ---------------------------------------------------------------
+         *   STEP 4: Validate Request (either mobile or email is required)
+         * --------------------------------------------------------------- */
+        if (empty($mobile_no) && empty($email_id)) {
+            log_message('error', 'STEP 5: Mobile number and Email both empty. Returning 0.');
+            return $this->respond(['data' => 0, 'empNotEnrolledCount' => 0]);
+        }
+
+        try {
+            /** ---------------------------------------------------------------
+             *   STEP 6: Base Query Builder (extract common conditions)
+             * --------------------------------------------------------------- */
+            $baseQuery = $this->employeeModel
+                ->join('employee_polices', 'employees.id = employee_polices.employee_id')
+                ->join('client_policy cp', 'employee_polices.client_policy_id = cp.id')
+                ->where('employees.is_active', 1)
+                ->where('employee_polices.is_active', 1)
+                ->where('cp.enrolment_visibility', 1)
+                ->where('cp.open_for_enrollment', 1)
+                ->where('cp.policy_status', 1)
+                ->whereIn('cp.policy_type_id', [1, 2, 3, 6, 7])
+                ->orderBy('employees.created_at', 'desc')
+                ->groupBy('employee_polices.client_policy_id');
+
+            // 🔹 Apply Client Filter if Provided
+            if (!empty($clientId)) {
+                $baseQuery->where('employees.client_id', $clientId);
+                log_message('error', 'Applied client ID filter: ' . $clientId);
+            }
+
+            // 🔹 Apply Mobile Filter if Provided
+            if (!empty($mobile_no)) {
+                $baseQuery->where('employees.mobile', $mobile_no);
+                log_message('error', 'Applied mobile_no filter: ' . $mobile_no);
+            }
+
+            // 🔹 Apply Email Filter if Provided
+            if (!empty($email_id)) {
+                $baseQuery->where('employees.email_corporate', $email_id);
+                log_message('error', 'Applied email_id filter: ' . $email_id);
+            }
+
+            /** ---------------------------------------------------------------
+             *   STEP 7: Count Enrolled + Draft (eligible)
+             * --------------------------------------------------------------- */
+            $baseQuery->whereIn('employees.emp_status', ['draft', 'enrolled'])
+                        ->whereIn('employee_polices.status', ['draft', 'enrolled'])
+                        ->where('employees.family_floater_key', 'self');
+
+            $count = $baseQuery->get()->getNumRows();
+            log_message('error', 'Eligible policy count = ' . $count);
+
+            /** ---------------------------------------------------------------
+             *   STEP 8: Count Only Draft (Not Enrolled yet)
+             * --------------------------------------------------------------- */
+            $notEnrolledQuery = $this->employeeModel
+                ->select("
+                    (
+                        SELECT COUNT(e2.id)
+                        FROM employees AS e2
+                        WHERE e2.is_active = 1
+                        AND e2.emp_status = 'draft'
+                        AND e2.emp_code = employees.emp_code
+                    ) AS draft_count
+                ")
+                ->join('employee_polices', 'employees.id = employee_polices.employee_id')
+                ->join('client_policy cp', 'employee_polices.client_policy_id = cp.id')
+                ->where('employees.is_active', 1)
+                ->where('employee_polices.is_active', 1)
+                ->where('cp.enrolment_visibility', 1)
+                ->where('cp.open_for_enrollment', 1)
+                ->where('cp.policy_status', 1)
+                ->whereIn('cp.policy_type_id', [1, 2, 3, 6, 7])
+                ->orderBy('employees.created_at', 'desc');
+
+            // 🔹 Apply Client Filter if Provided
+            if (!empty($clientId)) {
+                $notEnrolledQuery->where('employees.client_id', $clientId);
+                log_message('error', 'Applied client ID filter: ' . $clientId);
+            }
+
+            // 🔹 Apply Mobile Filter if Provided
+            if (!empty($mobile_no)) {
+                $notEnrolledQuery->where('employees.mobile', $mobile_no);
+                log_message('error', 'Applied mobile_no filter: ' . $mobile_no);
+            }
+
+            // 🔹 Apply Email Filter if Provided
+            if (!empty($email_id)) {
+                $notEnrolledQuery->where('employees.email_corporate', $email_id);
+                log_message('error', 'Applied email_id filter: ' . $email_id);
+            }
+
+            $notEnrolledQuery->whereIn('employees.emp_status', ['draft'])
+                            ->whereIn('employee_polices.status', ['draft']);
+
+            $not_enrolled_count = $notEnrolledQuery->get()->getRowArray()['draft_count'] ?? 0;
+            log_message('error', 'Not enrolled policy count = ' . $not_enrolled_count);
+
+            /** ---------------------------------------------------------------
+             *   STEP 9: Final Response
+             * --------------------------------------------------------------- */
+            return $this->respond([
+                'pre_policy_count'        => $count,
+                'emp_not_enrolled_count'  => (int) $not_enrolled_count
+            ]);
+
+        } catch (\Throwable $e) {
+            log_message('error', 'Exception occurred: ' . $e->getMessage());
+            return $this->respond([
+                'pre_policy_count'        => 0,
+                'emp_not_enrolled_count'  => 0
+            ]);
+        }
+    }
 
 
 
-        //--------------------------------------------------------------------------------------------
-        public function hrFileUpload()
-        {
-            try {
-                // Check file
-                $file = $this->request->getFile('file_name');
-                if (!$file) {
-                    return $this->response->setJSON([
-                        'status' => false,
-                        'message' => "Invalid file or file not uploaded.",
-                        'data'    => "No Data"
-                    ]);
-                }
+
+    //--------------------------------------------------------------------------------------------
     
-                // Upload folder path
-                $uploadPath = WRITEPATH . 'uploads/hr_files/';
-    
-                // If directory not exists, create it
-                if (!is_dir($uploadPath)) {
-                    mkdir($uploadPath, 0777, true);
-                }
-    
-                // New file name with timestamp
-                $newFileName = time() . '_' . $file->getRandomName();
-    
-                // Move file
-                $file->move($uploadPath, $newFileName);
-    
-                // Prepare data
-                $data = [
-                    'client_id'        => $this->request->getPost('client_id'),
-                    'client_branch_id' => $this->request->getPost('client_branch_id'),
-                    'policy_no'        => $this->request->getPost('policy_no'),
-                    'file_name'        => $newFileName,
-                    'file_action'      => $this->request->getPost('file_action'),
-                    'status'           => $this->request->getPost('status'),
-                    'created_by'       => $this->request->getPost('created_by'),
-                    'updated_by'       => $this->request->getPost('created_by'),
-                ];
-    
-                // Save into DB
-                $this->hrFileUploadModel->insert($data);
-    
-                return $this->respondCreated([
-                    'status'  => true,
-                    'message' => 'File uploaded successfully',
-                    'data'    => $data
+    public function hrFileUpload()
+    {
+        try {
+            // Check file
+            $file = $this->request->getFile('file_name');
+            if (!$file) {
+                return $this->response->setJSON([
+                    'status' => false,
+                    'message' => "Invalid file or file not uploaded.",
+                    'data'    => "No Data"
                 ]);
-    
-            } catch (\Exception $e) {
-                return $this->failServerError($e->getMessage());
             }
-    
-        }
-    
-        public function hrFileDownload($id = null)
-        {
-            try {
-    
-                $file_id = $this->request->getGet('id') ?? $id;
-    
-                // Find record
-                $record = $this->hrFileUploadModel->find($file_id);
-    
-                if (!$record) {
-                    return $this->failNotFound("File record not found");
-                }
-    
-                $uploadPath = WRITEPATH . 'uploads/hr_files/';
-                $filePath   = $uploadPath . $record['file_name'];
-    
-                if (!file_exists($filePath)) {
-                    return $this->failNotFound("File not found on server");
-                }
-    
-                // Force file download
-                return $this->response->download($filePath, null)
-                                    ->setFileName($record['file_name']);
-            } catch (\Exception $e) {
-                return $this->failServerError($e->getMessage());
+
+            // Upload folder path
+            $uploadPath = WRITEPATH . 'uploads/hr_files/';
+
+            // If directory not exists, create it
+            if (!is_dir($uploadPath)) {
+                mkdir($uploadPath, 0777, true);
             }
+
+            // New file name with timestamp
+            $newFileName = time() . '_' . $file->getRandomName();
+
+            // Move file
+            $file->move($uploadPath, $newFileName);
+
+            // Prepare data
+            $data = [
+                'client_id'        => $this->request->getPost('client_id'),
+                'client_branch_id' => $this->request->getPost('client_branch_id'),
+                'policy_no'        => $this->request->getPost('policy_no'),
+                'file_name'        => $newFileName,
+                'file_action'      => $this->request->getPost('file_action'),
+                'status'           => $this->request->getPost('status'),
+                'created_by'       => $this->request->getPost('created_by'),
+                'updated_by'       => $this->request->getPost('created_by'),
+            ];
+
+            // Save into DB
+            $this->hrFileUploadModel->insert($data);
+
+            return $this->respondCreated([
+                'status'  => true,
+                'message' => 'File uploaded successfully',
+                'data'    => $data
+            ]);
+
+        } catch (\Exception $e) {
+            return $this->failServerError($e->getMessage());
         }
-    
-        public function hrFileList()
-        {
-            try {
-                $request = service('request');
-                $builder = $this->hrFileUploadModel;
-    
-                // Allowed filter keys
-                $filters = [
-                    'client_id',
-                    'client_branch_id',
-                    'policy_no',
-                    'file_action',
-                    'status',
-                    'created_by'
-                ];
-    
-                // Apply filters dynamically
-                foreach ($filters as $key) {
-                    $value = $request->getGetPost($key); // supports both GET and POST
-                    if (!empty($value)) {
-                        $builder->where($key, $value);
+
+    }
+
+    public function hrFileDownload($id = null)
+    {
+        try {
+
+            $file_id = $this->request->getGet('id') ?? $id;
+
+            // Find record
+            $record = $this->hrFileUploadModel->find($file_id);
+
+            if (!$record) {
+                return $this->failNotFound("File record not found");
+            }
+
+            $uploadPath = WRITEPATH . 'uploads/hr_files/';
+            $filePath   = $uploadPath . $record['file_name'];
+
+            if (!file_exists($filePath)) {
+                return $this->failNotFound("File not found on server");
+            }
+
+            // Force file download
+            return $this->response->download($filePath, null)
+                                ->setFileName($record['file_name']);
+        } catch (\Exception $e) {
+            return $this->failServerError($e->getMessage());
+        }
+    }
+
+    public function hrFileList()
+    {
+        try {
+            $request = service('request');
+            $builder = $this->hrFileUploadModel;
+
+            // Allowed filter keys
+            $filters = [
+                'client_id',
+                'client_branch_id',
+                'policy_no',
+                'file_action',
+                'status',
+                'created_by'
+            ];
+
+            // Apply filters dynamically
+            foreach ($filters as $key) {
+                $value = $request->getGetPost($key); // supports both GET and POST
+                if (!empty($value)) {
+                    $builder->where($key, $value);
+                }
+            }
+
+            // Fetch results
+            $data = $builder->findAll();
+
+            return $this->respond([
+                'status'  => true,
+                'message' => 'File list fetched successfully',
+                'data'    => $data
+            ]);
+        } catch (\Exception $e) {
+            return $this->failServerError($e->getMessage());
+        }
+    }
+
+    public function hrFileUploadMasters()
+    {
+        try {
+            //for inception upload
+            $data['actions'] = ['inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement'];
+
+            return $this->respond([
+                'status'  => true,
+                'message' => 'File inception upload masters',
+                'data'    => $data
+            ]);
+        } catch (\Exception $e) {
+            return $this->failServerError($e->getMessage());
+        }
+    }
+
+    public function downloadSampleExcel()
+    {
+        $employeeController = new EmployeeController();
+        $file_path = $employeeController->downloadSampleExcelFile('enrollment', 1);
+        if(empty($file_path)){
+            return $this->respond(['status' => "success", 'code' => 404, 'data' => "", "message" => "Sample file not avilable"], 200);
+        }else{
+            return $this->respond(['status' => "success", 'code' => 200, 'data' => $file_path], 200);
+        }
+
+    }
+
+
+    // copy the active enrolled employee data
+    public function copyActiveEmployeeAndDependentDetails()
+    {
+        $received_payload = $this->request->getGet();
+        $client_id = $received_payload['client_id'] ?? null;
+        $emp_code = $received_payload['emp_code'] ?? null;
+        $new_client_policy_id = $received_payload['new_client_policy_id'] ?? null;
+
+        if (empty($client_id)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "client_id is required"], 200);
+        }
+
+        if (empty($new_client_policy_id)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "client_policy_id is required"], 200);
+        }
+
+        if (empty($emp_code)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "emp_code is required"], 200);
+        }
+
+        $client_policy_data = $this->clientPolicyModel
+            ->where('is_active', 1)
+            ->where('client_id', $client_id)
+            ->where('id', $new_client_policy_id)
+            ->whereIn('policy_type_id', [2, 3, 4, 5])
+            ->first();
+
+        if (empty($client_policy_data)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "Policy data not found", 'data' => []], 200);
+        }
+
+        $policy_terms = $client_policy_data['policy_terms'] ?? null;
+
+        if (empty($policy_terms)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "Policy terms not found", 'data' => []], 200);
+        }
+
+        $policy_terms = json_decode($policy_terms, true);
+
+        $family_floaters = $policy_terms['family_floaters'];
+
+        $empData = $this->employeeModel
+            ->where('emp_code', $emp_code)
+            ->where('client_id', $client_id)
+            // ->where('emp_status', 'enrolled')
+            ->where('is_active', 1)
+            ->findAll();
+        
+        if (empty($empData)) {
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "employee data not found", 'data' => []], 200);
+        }
+
+        $selfData = array_column(
+            array_filter($empData, function($row) {
+                return isset($row['relationship']) && strtolower($row['relationship']) == 'self';
+            }),
+            'id'
+        );
+
+        $selfId = !empty($selfData) ? reset($selfData) : null;
+        $floters = $this->FloterConvertion($family_floaters);
+
+        $data = [];
+        foreach ($floters as $familyFloatesValue) {
+
+            $dependent =   preg_replace('/\d/', '', $familyFloatesValue);
+
+            if (count($empData)) {
+                foreach ($empData as $key => $value) {
+                    if ($value['family_floater_key'] === $dependent) {
+
+                        $employee_policy = $this->employeePolicyModel->where('employee_id', $selfId)->where('client_policy_id',$new_client_policy_id)->where('is_active', 1 )->get()->getRow();
+
+                        $temp['is_value_exist'] = true;
+                        $temp['data']['family_floater_key'] = $familyFloatesValue;
+                        $temp['data']['employee_id'] = $value['id'];
+                        $temp['data']['relationship'] = $value['relationship'];
+                        $temp['data']['name'] = $value['name'];
+                        $temp['data']['dob'] = $this->convertDateFormatDMY($value['dob']);
+                        $temp['data']['client_policy_id'] = $new_client_policy_id;
+                        $temp['data']['form_type'] = $dependent;
+                        $temp['data']['basic_cover_si'] = isset($employee_policy->basic_cover_si) ? $employee_policy->basic_cover_si : 0;
+
+
+                        $temp['data']['age_validation'] = $this->getAgeRange($policy_terms, $familyFloatesValue);
+
+
+                        array_push($data, $temp);
+                        unset($empData[$key]);
+                        $floters = array_diff($floters, [$familyFloatesValue]);
+                        break;
                     }
                 }
-    
-                // Fetch results
-                $data = $builder->findAll();
-    
-                return $this->respond([
-                    'status'  => true,
-                    'message' => 'File list fetched successfully',
-                    'data'    => $data
-                ]);
-            } catch (\Exception $e) {
-                return $this->failServerError($e->getMessage());
             }
         }
-    
-        public function hrFileUploadMasters()
-        {
-            try {
-                //for inception upload
-                $data['actions'] = ['inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement'];
-    
-                return $this->respond([
-                    'status'  => true,
-                    'message' => 'File inception upload masters',
-                    'data'    => $data
-                ]);
-            } catch (\Exception $e) {
-                return $this->failServerError($e->getMessage());
-            }
-        }
-    
-        public function downloadSampleExcel()
-        {
-            $employeeController = new EmployeeController();
-            $file_path = $employeeController->downloadSampleExcelFile('enrollment', 1);
-            if(empty($file_path)){
-                return $this->respond(['status' => "success", 'code' => 404, 'data' => "", "message" => "Sample file not avilable"], 200);
-            }else{
-                return $this->respond(['status' => "success", 'code' => 200, 'data' => $file_path], 200);
-            }
-
-        }
 
+        if(!empty($data)){
+            return $this->respond(['status' => "success", 'code' => 200, 'data' => $data], 200);
+        }else{
+            return $this->respond(['status' => "failed", 'code' => 404, 'message' => "No data found", 'data' => []], 200);
+        }
+    }
 
 }
\ No newline at end of file
diff --git a/app/Controllers/RestAuthenticationController.php b/app/Controllers/RestAuthenticationController.php
index 69d9e5e..22e834e 100755
--- a/app/Controllers/RestAuthenticationController.php
+++ b/app/Controllers/RestAuthenticationController.php
@@ -69,7 +69,10 @@ class RestAuthenticationController extends AdminController
     {
         $client = \Config\Services::curlrequest();
         $url = env('POST_ENROLLMENT_BASEURL').$endPoint; 
-        $response = $client->post( $url, ['json' => $postData, 'http_errors' => false  ] );
+        $headers = [
+                        'App-Signature' => getenv('APP_SIGNATURE'),
+                   ];
+        $response = $client->post( $url, ['json' => $postData, 'headers' => $headers , 'http_errors' => false   ] );
         // return json_decode($response->getBody(), true);
         return $response->getBody();
     }
@@ -84,15 +87,17 @@ class RestAuthenticationController extends AdminController
         $options = [
             'query'       => $queryParams,
             'http_errors' => false,
+            'headers'     => [
+                'App-Signature' => getenv('APP_SIGNATURE'),
+                'Accept'        => 'application/json'
+            ]
         ];
 
-        if (isset($params['token']) && !empty($params['token'])) {
-            $options['headers'] = [
-                'Authorization' => 'Bearer ' . $params['token'],
-                'Accept'        => 'application/json'
-            ];
+        if (!empty($params['token'])) {
+            $options['headers']['Authorization'] = 'Bearer ' . $params['token'];
         }
 
+
         $response = $client->get($url, $options);
 
         return $response->getBody();
@@ -261,26 +266,63 @@ class RestAuthenticationController extends AdminController
             $empdata = RestAuthHelper::getPreAndPostDataByEmailOrMobile(['email_id' => $email]); 
             // print_r($empdata); die;
 
+            $otp = random_int(100000, 999999);
+
             if(empty($empdata)){
                 $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: No employee data found both PRE & POST");
                 log_message('error', '  ');
                 log_message('error', '************************ PRE END ********************************');
+
+                $retailUserdata = RestAuthHelper::getRetailUserData(['email_id' => $email]);
+                // print_r($retailUserdata); die;
+
+                if (!empty($retailUserdata)) {
+
+                    $retailApiParams['client_id'] = $retailUserdata['id'];
+                    $retailApiParams['email_id'] = $email;
+                    $retailApiParams['otp'] = $otp;
+
+                    // update the otp in retail user
+                    $api_response = $this->callThirdPartyAPI($retailApiParams, 'updateRetailUserAuthDetails');
+                    $api_response = json_decode($api_response ?? '{}', true) ?? $api_response;
+                    // print_r($api_response); die;
+                    if (isset($api_response['status']) && $api_response['status'] == true) {
+
+                        //send Email
+                        $retail_common = [
+                            'client_id' => $retailUserdata['id'],
+                            'client_branch_id' => null,
+                            'client_policy_id' => null,
+                            'employee_policy_id' =>  null,
+                            'employee_id' => null,
+                            'mail_type' => 'retail_user_otp_mail',
+                        ];
+
+                        $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Sending Retail user OTP email to " . $email);
+                        $res = $this->sendEmailOtp($email, $otp, $retail_common);
+
+                        if (json_decode($res)->status == 'success') {
+                            $result = ['user_verification' => true, 'message' => "Verified Successfully"];
+                            return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200);
+                        } else {
+                            $result = ['user_verification' => false, 'message' => "Mail sending failed , try again"];
+                            return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200);
+                        }
+                    }
+                }
+
                 $result = ['user_verification' => false , 'message' => "User not found"];
                 return $this->respond(['status' => 'failed','code' => 404,'data' => $result],200);
             }
 
             $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: employee data both PRE & POST = " . json_encode($empdata));
 
-           
-
             $employeeData = [];
             if (isset($empdata['pre']) && !empty($empdata['pre'])) {
                 $employeeData = $empdata['pre'];
                 $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Using PRE data");
             } 
 
-            $otp = random_int(100000, 999999);
-
             if (isset($employeeData['employee_id'])) 
             {
 
@@ -318,7 +360,6 @@ class RestAuthenticationController extends AdminController
                         $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Calling callThirdPartyAPI for updateEmpOTP");
                     }
 
-
                     //send Email
                     $common = [
                         'client_id' => $employeeData['client_id'],
@@ -329,11 +370,8 @@ class RestAuthenticationController extends AdminController
                         'mail_type' => 'otp_mail',
                     ];
 
-                    $subject = 'Nhance user verification - OTP';
-                    $mail_content = $otp . ' is your verification code for Nhance.';
-
                     $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Sending OTP email to " . $employeeData['email_corporate']);
-                    $res = MailHelper::send_email(['mail' => $employeeData['email_corporate'], 'subject' => $subject, 'common' => $common, 'message' => $mail_content]);
+                    $res = $this->sendEmailOtp($employeeData['email_corporate'], $otp, $common);
                     $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Mail response = " . $res);
 
                     if (json_decode($res)->status == 'success') {
@@ -347,6 +385,7 @@ class RestAuthenticationController extends AdminController
                         $result = ['user_verification' => false, 'message' => "Mail sending failed , try again"];
                         return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200);
                     }
+
                 } else {
                     $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: OTP update failed in PRE DATABASE");
                     log_message('error', '  ');
@@ -390,14 +429,37 @@ class RestAuthenticationController extends AdminController
             $email_id = isset($this->request->getJSON()->email_id) ? $this->request->getJSON()->email_id : null;
             $otp = isset($this->request->getJSON()->otp) ? $this->request->getJSON()->otp : null;
 
+            if(empty($otp)){
+                return $this->respond(['status' => 'OTP is required','code'   => 400,'message' => 'OTP is required'], 200);
+            }
+
+            if (empty($mobile_number) && empty($email_id)) {
+                return $this->respond(['status' => 'failed','code'   => 400,'message' => 'Mobile number or Email ID is required'], 200);
+            }
+
             $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - getVerifiedUserData: Fetching empdata via RestAuthHelper");
             $empdata = RestAuthHelper::getPreAndPostDataByEmailOrMobile(['email_id' => $email_id, 'otp' => $otp, 'mobile_number' => $mobile_number ]); 
             
 
             if(empty($empdata)){
                 $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - getVerifiedUserData: empdata is empty");
-                log_message('error', '  ');
-                log_message('error', '************************ PRE END ********************************');
+
+                $retailUserdata = RestAuthHelper::getRetailUserData(['email_id' => $email_id, 'mobile_number' => $mobile_number, 'otp' => $otp]);
+                // print_r($retailUserdata); die;
+
+                if (!empty($retailUserdata)) {
+
+                    $retailApiParams['client_id'] = $retailUserdata['id'];
+                    $retailApiParams['email_id'] = $email_id;
+                    $retailApiParams['mobile_number'] = $mobile_number;
+                    $retailApiParams['otp'] = $otp;
+
+                    // Call the third-party API function
+                    $apiResponse = $this->callThirdPartyAPI($retailApiParams, 'getVerifiedRetailUserData');
+                    // print_r($apiResponse); die;
+                    return $this->respond(['status' => 'failed', 'code' => 200, 'data' => [], 'post_enrollment' => json_decode($apiResponse, true)], 200);
+                }
+
                 return $this->respond(['status' => 'Invalid OTP','code' => 404,'data' => "", 'message' => "Invalid OTP"],200);                
             }
 
@@ -473,6 +535,15 @@ class RestAuthenticationController extends AdminController
 
     // employee auth api's end
 
+    public function sendEmailOtp($email, $otp, $common)
+    {
+        $subject = 'Nhance user verification - OTP';
+        $mail_content = $otp . ' is your verification code for Nhance.';
+        $response = MailHelper::send_email(['mail' => $email, 'subject' => $subject, 'common' => $common, 'message' => $mail_content]);
+
+        return $response;
+    }
+
 
 
 
diff --git a/app/Filters/AuthJWT.php b/app/Filters/AuthJWT.php
index 484372a..262a5e5 100755
--- a/app/Filters/AuthJWT.php
+++ b/app/Filters/AuthJWT.php
@@ -20,7 +20,7 @@ class AuthJWT implements FilterInterface
 {
     public function before(RequestInterface $request, $arguments = null)
     {
-        $jwt = $request->getHeader('Authorization');
+        $jwt = $request->getHeaderLine('Authorization');
 
         if ($jwt) {
             if (JWTToken::validateJWT($jwt)) {
diff --git a/app/Filters/Cors.php b/app/Filters/Cors.php
new file mode 100644
index 0000000..c47f276
--- /dev/null
+++ b/app/Filters/Cors.php
@@ -0,0 +1,422 @@
+
+     */
+    protected array $allowedOrigins = [];
+
+    /**
+     * Whether to allow credentials (cookies, authorization headers) in CORS requests
+     * WARNING: Cannot be true if using wildcard (*) origin
+     * 
+     * @var bool
+     */
+    protected bool $allowCredentials = false;
+
+    /**
+     * HTTP methods allowed for CORS requests
+     * 
+     * @var string
+     */
+    protected string $allowedMethods = 'GET,POST,PUT,PATCH,DELETE,OPTIONS';
+
+    /**
+     * HTTP headers allowed in CORS requests
+     * 
+     * @var string
+     */
+    protected string $allowedHeaders = 'Content-Type,Authorization,X-Requested-With,Accept,Origin';
+
+    /**
+     * Headers exposed to the client (accessible via JavaScript)
+     * 
+     * @var string
+     */
+    protected string $exposeHeaders = '';
+
+    /**
+     * How long (in seconds) the preflight response can be cached
+     * Default: 24 hours (86400 seconds)
+     * 
+     * @var int
+     */
+    protected int $maxAge = 86400;
+
+    /**
+     * Whether to enable debug logging for CORS requests
+     * 
+     * @var bool
+     */
+    protected bool $debug = false;
+
+    /**
+     * Initialize CORS configuration from environment variables
+     * 
+     * @throws \RuntimeException If configuration is invalid
+     */
+
+    protected $myLogger;
+    public function __construct()
+    {
+
+         $this->myLogger = \Config\Services::mylogger();
+        // Parse allowed origins from environment variable
+        // Format: comma or semicolon separated list
+        // Examples: "https://example.com,https://app.example.com" or "*.example.com"
+        $raw = env('CORS_ALLOWED_ORIGINS', '*');
+        $parts = preg_split('/\s*[,;]\s*/', trim($raw));
+        $this->allowedOrigins = array_filter(array_map('trim', $parts));
+
+        // Load other configuration from environment
+        $this->allowCredentials = filter_var(
+            env('CORS_ALLOW_CREDENTIALS', false),
+            FILTER_VALIDATE_BOOLEAN
+        );
+        $this->allowedMethods = env('CORS_ALLOWED_METHODS', $this->allowedMethods);
+        $this->allowedHeaders = env('CORS_ALLOWED_HEADERS', $this->allowedHeaders);
+        $this->exposeHeaders = env('CORS_EXPOSE_HEADERS', $this->exposeHeaders);
+        $this->maxAge = (int) env('CORS_MAX_AGE', $this->maxAge);
+        $this->debug = filter_var(env('CORS_DEBUG', false), FILTER_VALIDATE_BOOLEAN);
+
+        // Security validation: wildcard origin cannot be used with credentials
+        // This is a browser security requirement, not just a best practice
+        if ($this->allowCredentials && in_array('*', $this->allowedOrigins, true)) {
+            throw new \RuntimeException(
+                'CORS configuration error: Cannot use wildcard (*) origin with credentials enabled. ' .
+                'This violates browser security policies. Either disable credentials or specify explicit origins.'
+            );
+        }
+
+        $this->log('CORS filter initialized', [
+            'allowed_origins' => $this->allowedOrigins,
+            'allow_credentials' => $this->allowCredentials,
+            'allowed_methods' => $this->allowedMethods,
+        ]);
+    }
+
+    /**
+     * Check if a given origin is allowed to access this API
+     * 
+     * Supports:
+     * - Exact matches: https://example.com
+     * - Wildcard origins: *.example.com
+     * - Scheme-less matching: example.com (matches http and https)
+     * - Universal wildcard: *
+     * 
+     * @param string|null $origin The Origin header from the request
+     * @return bool True if origin is allowed, false otherwise
+     */
+    protected function isOriginAllowed(?string $origin): bool
+    {
+        // Reject empty origins
+        if (empty($origin)) {
+            $this->log('Origin rejected: empty origin header');
+            return false;
+        }
+
+        // Validate origin format - must include scheme (http:// or https://)
+        // This prevents malformed origins from being accepted
+        if (!preg_match('#^https?://#i', $origin)) {
+            $this->log('Origin rejected: invalid format (missing scheme)', ['origin' => $origin]);
+            return false;
+        }
+
+        // If wildcard present in configuration, allow any origin
+        if (in_array('*', $this->allowedOrigins, true)) {
+            $this->log('Origin allowed: wildcard match', ['origin' => $origin]);
+            return true;
+        }
+
+        // Parse the host from the origin for wildcard matching
+        // Example: https://app.example.com:8080 → app.example.com
+        $originHost = parse_url($origin, PHP_URL_HOST) ?: $origin;
+
+        foreach ($this->allowedOrigins as $allowed) {
+            if ($allowed === '') {
+                continue;
+            }
+
+            // 1. Exact match (including scheme and port)
+            // Example: https://example.com matches https://example.com
+            if (strcasecmp($allowed, $origin) === 0) {
+                $this->log('Origin allowed: exact match', [
+                    'origin' => $origin,
+                    'matched_rule' => $allowed
+                ]);
+                return true;
+            }
+
+            // 2. Handle scheme-less and wildcard patterns
+            // If the allowed entry doesn't contain ://, it's either a host-only or wildcard pattern
+            if (strpos($allowed, '://') === false) {
+                
+                // 2a. Wildcard subdomain pattern: *.example.com
+                // Matches: app.example.com, api.example.com, dev.app.example.com
+                // Does NOT match: example.com (use explicit entry for root domain)
+                if (strpos($allowed, '*.') === 0) {
+                    $allowedRoot = substr($allowed, 2); // Remove *. prefix
+                    
+                    // Check if origin host ends with the allowed root domain
+                    if ($originHost === $allowedRoot || str_ends_with($originHost, '.' . $allowedRoot)) {
+                        $this->log('Origin allowed: wildcard subdomain match', [
+                            'origin' => $origin,
+                            'matched_rule' => $allowed,
+                            'origin_host' => $originHost
+                        ]);
+                        return true;
+                    }
+                }
+                // 2b. Direct host match (scheme-less)
+                // Allows both http and https for the same host
+                // Example: example.com matches both http://example.com and https://example.com
+                else {
+                    if (strcasecmp($allowed, $originHost) === 0) {
+                        $this->log('Origin allowed: host match (scheme-less)', [
+                            'origin' => $origin,
+                            'matched_rule' => $allowed,
+                            'origin_host' => $originHost
+                        ]);
+                        return true;
+                    }
+                }
+            }
+        }
+
+        // No match found - reject this origin
+        $this->log('Origin rejected: no matching rule', [
+            'origin' => $origin,
+            'checked_rules' => $this->allowedOrigins
+        ]);
+        return false;
+    }
+
+    /**
+     * Build the Access-Control-Allow-Origin header value
+     * 
+     * Returns either:
+     * - '*' if wildcard is configured and credentials are disabled
+     * - The actual origin value if credentials are enabled or specific origins configured
+     * 
+     * Note: When credentials are enabled, you MUST echo back the specific origin,
+     * browsers reject wildcard with credentials.
+     * 
+     * @param string $origin The validated origin
+     * @return string The value for Access-Control-Allow-Origin header
+     */
+    protected function buildAllowOriginHeader(string $origin): string
+    {
+        // If wildcard configured and credentials NOT required, can safely return '*'
+        // This allows any origin to access the resource
+        if (in_array('*', $this->allowedOrigins, true) && !$this->allowCredentials) {
+            return '*';
+        }
+
+        // Otherwise, must return the specific origin
+        // This is required when allow-credentials is true
+        return $origin;
+    }
+
+    /**
+     * Add all CORS headers to the response
+     * 
+     * This method is called for both preflight and actual requests
+     * to ensure consistent CORS headers across all responses.
+     * 
+     * @param ResponseInterface $response The response object to add headers to
+     * @param RequestInterface $request The original request
+     * @param string $origin The validated origin
+     * @param bool $isPreflight Whether this is a preflight OPTIONS request
+     * @return void
+     */
+    protected function addCorsHeaders(
+        ResponseInterface $response,
+        RequestInterface $request,
+        string $origin,
+        bool $isPreflight = false
+    ): void {
+        // CRITICAL: Vary header prevents caching issues
+        // Without this, a cached response for origin A might be served to origin B,
+        // causing CORS errors because the Access-Control-Allow-Origin won't match
+        $response->setHeader('Vary', 'Origin');
+
+        // Set the allowed origin
+        $allowOrigin = $this->buildAllowOriginHeader($origin);
+        $response->setHeader('Access-Control-Allow-Origin', $allowOrigin);
+
+        // If credentials are allowed, set the header
+        // This allows cookies, authorization headers, and TLS client certificates
+        if ($this->allowCredentials) {
+            $response->setHeader('Access-Control-Allow-Credentials', 'true');
+        }
+
+        // Allowed HTTP methods
+        $response->setHeader('Access-Control-Allow-Methods', $this->allowedMethods);
+
+        // Handle allowed headers
+        if ($isPreflight) {
+            // For preflight: respect what the browser is asking for
+            // The browser sends Access-Control-Request-Headers to ask permission
+            $requestedHeaders = $request->getHeaderLine('Access-Control-Request-Headers');
+            $response->setHeader(
+                'Access-Control-Allow-Headers',
+                $requestedHeaders ?: $this->allowedHeaders
+            );
+        } else {
+            // For actual requests: use configured headers
+            // Access-Control-Request-Headers is only for preflight
+            $response->setHeader('Access-Control-Allow-Headers', $this->allowedHeaders);
+        }
+
+        // Expose additional headers to the client (accessible via JavaScript)
+        // Without this, only simple headers are accessible: Cache-Control, Content-Language,
+        // Content-Type, Expires, Last-Modified, Pragma
+        if (!empty($this->exposeHeaders)) {
+            $response->setHeader('Access-Control-Expose-Headers', $this->exposeHeaders);
+        }
+
+        // Cache duration for preflight responses
+        // Reduces preflight requests by allowing browser to cache the permissions
+        if ($this->maxAge > 0) {
+            $response->setHeader('Access-Control-Max-Age', (string) $this->maxAge);
+        }
+    }
+
+    /**
+     * Execute before the controller
+     * 
+     * Handles preflight OPTIONS requests by returning early with appropriate headers.
+     * For other requests, allows them to proceed to the controller.
+     * 
+     * @param RequestInterface $request The request object
+     * @param mixed $arguments Optional arguments
+     * @return ResponseInterface|null Response for preflight, null for other requests
+     */
+    public function before(RequestInterface $request, $arguments = null)
+    {
+        $origin = $request->getHeaderLine('Origin') ?: '';
+        $method = strtoupper($request->getMethod());
+
+        // Handle preflight OPTIONS requests
+        // Preflight is sent by browsers before actual cross-origin requests
+        // to check if the actual request is safe to send
+        if ($method === 'OPTIONS') {
+            $this->log('Preflight request received', [
+                'origin' => $origin,
+                'method' => $method,
+                'uri' => (string) $request->getUri()
+            ]);
+
+            // Validate origin - reject if not allowed
+            if (empty($origin) || !$this->isOriginAllowed($origin)) {
+                $this->log('Preflight rejected: origin not allowed', ['origin' => $origin]);
+                
+                // Return 403 Forbidden for rejected origins
+                // Some prefer 200 with no CORS headers, but 403 is more explicit
+                return Services::response()
+                    ->setStatusCode(403)
+                    ->setJSON(['error' => 'Origin not allowed']);
+            }
+
+            // Origin is valid - build preflight response
+            $response = Services::response();
+            $this->addCorsHeaders($response, $request, $origin, true);
+
+            // 204 No Content is the standard response for successful preflight
+            // It indicates "permission granted, but no data to return"
+            $response->setStatusCode(204);
+            $response->setBody('');
+
+            $this->log('Preflight approved', [
+                'origin' => $origin,
+                'allowed_methods' => $this->allowedMethods
+            ]);
+
+            return $response;
+        }
+
+        // For non-OPTIONS requests, don't return a response
+        // Let the request proceed to the controller
+        // CORS headers will be added in after() method
+        return null;
+    }
+
+    /**
+     * Execute after the controller
+     * 
+     * Adds CORS headers to the response for actual (non-preflight) requests.
+     * This ensures all API responses include proper CORS headers.
+     * 
+     * @param RequestInterface $request The request object
+     * @param ResponseInterface $response The response object
+     * @param mixed $arguments Optional arguments
+     * @return void
+     */
+    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
+    {
+        $origin = $request->getHeaderLine('Origin') ?: '';
+
+        // Only add CORS headers if origin is present and allowed
+        // No origin header means it's a same-origin request (no CORS needed)
+        if (empty($origin)) {
+            return;
+        }
+
+        if (!$this->isOriginAllowed($origin)) {
+            $this->log('Response blocked: origin not allowed', [
+                'origin' => $origin,
+                'uri' => (string) $request->getUri()
+            ]);
+            return;
+        }
+
+        // Add CORS headers to the response
+        $this->addCorsHeaders($response, $request, $origin, false);
+
+        $this->log('CORS headers added to response', [
+            'origin' => $origin,
+            'status' => $response->getStatusCode()
+        ]);
+    }
+
+    /**
+     * Log debug information if debug mode is enabled
+     * 
+     * Logs to CodeIgniter's log system at 'info' level.
+     * Enable with CORS_DEBUG=true in .env file.
+     * 
+     * @param string $message The log message
+     * @param array $context Additional context data
+     * @return void
+     */
+    protected function log(string $message, array $context = []): void
+    {
+        if (!$this->debug) {
+            return;
+        }
+
+        // $logger = Services::logger();
+        $contextString = !empty($context) ? json_encode($context, JSON_UNESCAPED_SLASHES) : '';
+       $this->myLogger->logme('error','[CORS] ' . $message . ($contextString ? ' | ' . $contextString : ''));
+        // $logger->info('[CORS] ' . $message . ($contextString ? ' | ' . $contextString : ''));
+    }
+}
\ No newline at end of file
diff --git a/app/Filters/VerifyAppSignature.php b/app/Filters/VerifyAppSignature.php
new file mode 100644
index 0000000..a5257b2
--- /dev/null
+++ b/app/Filters/VerifyAppSignature.php
@@ -0,0 +1,36 @@
+getHeaderLine('App-Signature');
+
+        // Load the server's expected signature from the .env
+        $validSignature = getenv('APP_SIGNATURE');
+
+        // Check if signature is valid
+        if ($clientSignature !== $validSignature) {
+            return service('response')
+                ->setStatusCode(403)
+                ->setJSON([
+                    'status' => false,
+                    'message' => 'Forbidden: Invalid App Signature',
+                ]);
+        }
+
+        // allow request to proceed
+    }
+
+    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
+    {
+        // nothing to do after response
+    }
+}
diff --git a/app/Helpers/RestAuthHelper.php b/app/Helpers/RestAuthHelper.php
index 1308676..121c9cb 100644
--- a/app/Helpers/RestAuthHelper.php
+++ b/app/Helpers/RestAuthHelper.php
@@ -346,6 +346,9 @@ class RestAuthHelper
             $client = \Config\Services::curlrequest();
             $endPoint = 'getPostEmployeeDataForAuth';
             $url = env('POST_ENROLLMENT_BASEURL') . $endPoint;
+            $headers = [
+                'App-Signature' => getenv('APP_SIGNATURE'),
+            ];
 
             $postData = [];
 
@@ -368,7 +371,7 @@ class RestAuthHelper
             log_message('error', 'Sending POST to external API: ' . $url);
             log_message('error', 'POST payload: ' . json_encode($postData));
 
-            $response = $client->post($url, ['json' => $postData, 'http_errors' => false]);
+            $response = $client->post($url, ['json' => $postData, 'headers' => $headers , 'http_errors' => false]);
 
             $post_json = $response->getBody();
             // log_message('error', 'Response from API: ' . $post_json);
@@ -380,7 +383,7 @@ class RestAuthHelper
             return $data;
         }
 
-        log_message('warning', 'No mobile or email present in params for post fetch.');
+        log_message('error', 'No mobile or email present in params for post fetch.');
     }
 
     public static function updatePreMpin(array $params)
@@ -437,4 +440,60 @@ class RestAuthHelper
         log_message('warning', '[updatePostMpin] Missing required parameters: employee_id or mpin');
         return false;
     }
+
+    // ----------------------------------------------------------------------------------------------
+
+    public static function getRetailUserData(array $params)
+    {
+        log_message('error', 'Function getRetailUserData called with: ' . json_encode($params));
+
+        $mobile_number = $params['mobile_number'] ?? null;
+        $email_id      = $params['email_id'] ?? null;
+        $otp           = $params['otp'] ?? null;
+        $old_mpin      = $params['old_mpin'] ?? null;
+
+        if (!empty($mobile_number) || !empty($email_id)) {
+            
+            $client = \Config\Services::curlrequest();
+            $endPoint = 'getRetailUserData';
+            $url = env('POST_ENROLLMENT_BASEURL') . $endPoint;
+            $headers = [
+                'App-Signature' => getenv('APP_SIGNATURE'),
+            ];
+
+            $postData = [];
+
+            if (!empty($mobile_number)) {
+                $postData['mobile_number'] = $mobile_number;
+            }
+
+            if (!empty($email_id)) {
+                $postData['email_id'] = $email_id;
+            }
+
+            if (!empty($otp)) {
+                $postData['otp'] = $otp;
+            }
+
+            if (!empty($old_mpin)) {
+                $postData['old_mpin'] = $old_mpin;
+            }
+
+            log_message('error', 'Sending POST to external API: ' . $url);
+            log_message('error', 'POST payload: ' . json_encode($postData));
+
+            $response = $client->post($url, ['json' => $postData, 'headers' => $headers, 'http_errors' => false]);
+
+            $post_json = $response->getBody();
+            // print_r($post_json); 
+
+            $post_data = json_decode($post_json, true);
+
+            $data = $post_data['data'] ?? [];
+            log_message('error', 'Parsed post_data: ' . json_encode($data));
+            return $data;
+        }
+
+        return [];
+    }
 }
diff --git a/app/Helpers/utility_helper.php b/app/Helpers/utility_helper.php
index 3372200..765b8af 100755
--- a/app/Helpers/utility_helper.php
+++ b/app/Helpers/utility_helper.php
@@ -666,3 +666,35 @@ if (!function_exists('check_pay_by_employee_or_company')) {
     
 }
 
+if (!function_exists('getLatestGMCPolicy')) {
+
+    function getLatestGMCPolicy(array $empPolicy)
+    {
+        try{
+            $filtered = array_filter($empPolicy, function ($row) {
+                $type = is_object($row) ? $row->policy_type_id : $row['policy_type_id'];
+                return isset($type) && (int)$type === 2;
+            });
+
+            if (count($filtered) > 1) {
+
+                usort($filtered, function ($a, $b) {
+                    $dateA = is_object($a) ? $a->policy_end_date : $a['policy_end_date'];
+                    $dateB = is_object($b) ? $b->policy_end_date : $b['policy_end_date'];
+                    return strtotime($dateB) <=> strtotime($dateA);
+                });
+
+                $row = reset($filtered);
+                return is_object($row) ? ($row->ClientPolicyId ?? null) : ($row['ClientPolicyId'] ?? null);
+            }
+
+            return null;
+
+        }catch(\Exception $e){
+            log_message('error', 'Exception getLatestGMCPolicy :' . $e->getMessage());
+            return null;
+        }
+    }
+
+}
+
diff --git a/app/Models/EmployeeModel.php b/app/Models/EmployeeModel.php
index 2cfc852..c556bdb 100755
--- a/app/Models/EmployeeModel.php
+++ b/app/Models/EmployeeModel.php
@@ -166,7 +166,8 @@ class EmployeeModel extends Model
                             client_policy.disclaimer,
                             client_policy.policy_type_id , 
                             employee_polices.tpa_id as tpa_id , 
-                            employee_polices.rand_string as rand_string
+                            employee_polices.rand_string as rand_string,
+                            client_policy.policy_end_date
                         ', FALSE) // Select all columns from both tables
                         ->join('client_policy', 'client_policy.id = employee_polices.client_policy_id')
                         ->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
diff --git a/app/Views/client_policy.php b/app/Views/client_policy.php
index fe028ca..b2f4917 100755
--- a/app/Views/client_policy.php
+++ b/app/Views/client_policy.php
@@ -60,8 +60,71 @@
 #table-client-policy_filter{
     text-align: left;
 }
-
 
+
+
+
+
 
 
 
@@ -227,7 +290,7 @@ required>
-
+ +
+
@@ -261,15 +333,26 @@
- -
+ +
+ +
+ + -
+ + +
+ +
+
+ +
diff --git a/ca.pem b/ca.pem new file mode 100644 index 0000000..a303fe9 --- /dev/null +++ b/ca.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQB/57HSuaqUkLaasdjxUdPjANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUxOTE3NDAzNFoYDzIwNjEwNTE5MTg0MDM0WjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbkaoVsUS76o +TgLFmcnaB8cswBk1M3Bf4IVRcwWT3a1HeJSnaJUqWHCJ+u3ip/zGVOYl0gN1MgBb +MuQRIJiB95zGVcIa6HZtx00VezDTr3jgGWRHmRjNVCCHGmxOZWvJjsIE1xavT/1j +QYV/ph4EZEIZ/qPq7e3rHohJaHDe23Z7QM9kbyqp2hANG2JtU/iUhCxqgqUHNozV +Zd0l5K6KnltZQoBhhekKgyiHqdTrH8fWajYl5seD71bs0Axowb+Oh0rwmrws3Db2 +Dh+oc2PwREnjHeca9/1C6J2vhY+V0LGaJmnnIuOANrslx2+bgMlyhf9j0Bv8AwSi +dSWsobOhNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQb7vJT +VciLN72yJGhaRKLn6Krn2TAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAAxEj8N9GslReAQnNOBpGl8SLgCMTejQ6AW/bapQvzxrZrfVOZOYwp/5oV0f +9S1jcGysDM+DrmfUJNzWxq2Y586R94WtpH4UpJDGqZp+FuOVJL313te4609kopzO +lDdmd+8z61+0Au93wB1rMiEfnIMkOEyt7D2eTFJfJRKNmnPrd8RjimRDlFgcLWJA +3E8wca67Lz/G0eAeLhRHIXv429y8RRXDtKNNz0wA2RwURWIxyPjn1fHjA9SPDkeW +E1Bq7gZj+tBnrqz+ra3yjZ2blss6Ds3/uRY6NYqseFTZWmQWT7FolZEnT9vMUitW +I0VynUbShVpGf6946e0vgaaKw20= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQGKVv+5VuzEZEBzJ+bVfx2zAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTE5MTc1MDU5WhgPMjEyMTA1MTkxODUwNTlaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgYXAtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMqdLJ0tZF/DGFZTKZDrGRJZID8ivC2I +JRCYTWweZKCKSCAzoiuGGHzJhr5RlLHQf/QgmFcgXsdmO2n3CggzhA4tOD9Ip7Lk +P05eHd2UPInyPCHRgmGjGb0Z+RdQ6zkitKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUC1yhRgVqU5bR8cGzOUCIxRpl4EYwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2cAMGQCMG0c/zLGECRPzGKJvYCkpFTCUvdP4J74YP0v/dPvKojL +t/BrR1Tg4xlfhaib7hPc7wIwFvgqHes20CubQnZmswbTKLUrgSUW4/lcKFpouFd2 +t2/ewfi/0VhkeUW+IiHhOMdU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIRAKlQ+3JX9yHXyjP/Ja6kZhkwDQYJKoZIhvcNAQEMBQAw +gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo +QW1hem9uIFJEUyBhcC1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE +BwwHU2VhdHRsZTAgFw0yMTA1MTkxNzQ1MjBaGA8yMTIxMDUxOTE4NDUyMFowgZgx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h +em9uIFJEUyBhcC1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH +U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKtahBrpUjQ6 +H2mni05BAKU6Z5USPZeSKmBBJN3YgD17rJ93ikJxSgzJ+CupGy5rvYQ0xznJyiV0 +91QeQN4P+G2MjGQR0RGeUuZcfcZitJro7iAg3UBvw8WIGkcDUg+MGVpRv/B7ry88 +7E4OxKb8CPNoa+a9j6ABjOaaxaI22Bb7j3OJ+JyMICs6CU2bgkJaj3VUV9FCNUOc +h9PxD4jzT9yyGYm/sK9BAT1WOTPG8XQUkpcFqy/IerZDfiQkf1koiSd4s5VhBkUn +aQHOdri/stldT7a+HJFVyz2AXDGPDj+UBMOuLq0K6GAT6ThpkXCb2RIf4mdTy7ox +N5BaJ+ih+Ro3ZwPkok60egnt/RN98jgbm+WstgjJWuLqSNInnMUgkuqjyBWwePqX +Kib+wdpyx/LOzhKPEFpeMIvHQ3A0sjlulIjnh+j+itezD+dp0UNxMERlW4Bn/IlS +sYQVNfYutWkRPRLErXOZXtlxxkI98JWQtLjvGzQr+jywxTiw644FSLWdhKa6DtfU +2JWBHqQPJicMElfZpmfaHZjtXuCZNdZQXWg7onZYohe281ZrdFPOqC4rUq7gYamL +T+ZB+2P+YCPOLJ60bj/XSvcB7mesAdg8P0DNddPhHUFWx2dFqOs1HxIVB4FZVA9U +Ppbv4a484yxjTgG7zFZNqXHKTqze6rBBAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFCEAqjighncv/UnWzBjqu1Ka2Yb4MA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQwFAAOCAgEAYyvumblckIXlohzi3QiShkZhqFzZultbFIu9 +GhA5CDar1IFMhJ9vJpO9nUK/camKs1VQRs8ZsBbXa0GFUM2p8y2cgUfLwFULAiC/ +sWETyW5lcX/xc4Pyf6dONhqFJt/ovVBxNZtcmMEWv/1D6Tf0nLeEb0P2i/pnSRR4 +Oq99LVFjossXtyvtaq06OSiUUZ1zLPvV6AQINg8dWeBOWRcQYhYcEcC2wQ06KShZ +0ahuu7ar5Gym3vuLK6nH+eQrkUievVomN/LpASrYhK32joQ5ypIJej3sICIgJUEP +UoeswJ+Z16f3ECoL1OSnq4A0riiLj1ZGmVHNhM6m/gotKaHNMxsK9zsbqmuU6IT/ +P6cR0S+vdigQG8ZNFf5vEyVNXhl8KcaJn6lMD/gMB2rY0qpaeTg4gPfU5wcg8S4Y +C9V//tw3hv0f2n+8kGNmqZrylOQDQWSSo8j8M2SRSXiwOHDoTASd1fyBEIqBAwzn +LvXVg8wQd1WlmM3b0Vrsbzltyh6y4SuKSkmgufYYvC07NknQO5vqvZcNoYbLNea3 +76NkFaMHUekSbwVejZgG5HGwbaYBgNdJEdpbWlA3X4yGRVxknQSUyt4dZRnw/HrX +k8x6/wvtw7wht0/DOqz1li7baSsMazqxx+jDdSr1h9xML416Q4loFCLgqQhil8Jq +Em4Hy3A= +-----END CERTIFICATE-----