diff --git a/.env.sample b/.env.sample index ebc9ab1d..ceb7085a 100755 --- a/.env.sample +++ b/.env.sample @@ -33,11 +33,11 @@ database.default.DBDriver = # session.driver = 'CodeIgniter\Session\Handlers\FileHandler' # session.cookieName = 'ci_session' -#session.expiration = 28800 +session.expiration = # session.savePath = null # session.matchIP = false -# session.timeToUpdate = 300 -# session.regenerateDestroy = false +session.timeToUpdate = 300 +session.regenerateDestroy = false #-------------------------------------------------------------------- # LOGGER @@ -81,16 +81,6 @@ cookie.secure = 'true';// if https set as true or if http set as false unlayer.projectID = email.enquiryMail = -database.postDB.hostname = -database.postDB.database = -database.postDB.username = -database.postDB.password = -database.postDB.DBDriver = -database.postDB.DBPrefix = -database.postDB.port = - -POST_ENROLLMENT_BASEURL = - #SMS SMS_API_KEY = SMS_SENDER_ID = @@ -108,3 +98,26 @@ CORS_MAX_AGE=7200 CORS_DEBUG=true APP_SIGNATURE = +TOKENTIMEOUT = +JWT_SECRET = + +ICICI_PRIMARY_KEY_CONSTANT = + +ABHI_PRIMARY_KEY_CONSTANT = + +R_CARE_PRIMARY_KEY_CONSTANT = + +FHPL_PRIMARY_KEY_CONSTANT = + +VIDAL_PRIMARY_KEY_CONSTANT = + +MEDI_ASSIST_PRIMARY_KEY_CONSTANT = + + +FHPL_TOKEN_URL = +FHPL_BASE_URL = +FHPL_USER_NAME = +FHPL_PASSWORD = +FHPL_GRANT_TYPE = + +METABASE_SECRET_KEY= \ No newline at end of file diff --git a/app/Config/Acl.php b/app/Config/Acl.php new file mode 100644 index 00000000..a1b44339 --- /dev/null +++ b/app/Config/Acl.php @@ -0,0 +1,170 @@ + ['public' => true], + '#^/downloadCdSplitUpFile#' => ['public' => true], + '#^/logout#' => ['public' => true], + '#^/auth#' => ['public' => true], + '#^/oauth2callback#' => ['public' => true], + '#^/loginPos#' => ['public' => true], + '#^/getVerifyPosMobileNo#' => ['public' => true], + '#^/getVerifiedPosUserData#' => ['public' => true], + '#^/swagger#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/fedeploy#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/visitOffBoardCheck#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/logs#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/util/log_list#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/util/view_log#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/util/download_log#' => ['roles' => [ADMIN_ROLE_ID]], + '#^/metaDashboardDemo#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]], + + // ===================== PUBLIC DOWNLOADS / FORMS ===================== + '#^/download-#' => ['public' => true], + '#^/claim-form-download#' => ['public' => true], + '#^/claims-feedback-form#' => ['public' => true], + '#^/autobookstackLogin#' => ['public' => true], + + // ===================== DASHBOARD ===================== + '#^/dashboard#' => [ + 'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [] + ], + + // ===================== VIEW INCEPTION PAGE ===================== + '#^/hrFileList#' => [ + 'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [] + ], + + // ===================== USER MANAGEMENT ===================== + '#^/user#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID], + 'teams' => [] + ], + + // ===================== CLIENT ===================== + '#^/client#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [] + ], + + // ===================== EMPLOYEE / ENROLLMENT ===================== + '#^/employee#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [ENROLLMENT_TEAM_ID] + ], + + // ===================== MASTERS ===================== + '#^/master#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [] + ], + '#^/util#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID], + 'teams' => [] + ], + + // ===================== POLICY TRANSACTION / BDS ===================== + '#^/policy_tranction#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID] + ], + '#^/bds_upload#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID] + ], + + // ===================== REPORTS ===================== + '#^/bdsReport#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID] + ], + + // ===================== PAYOUT / COMMISSION ===================== + '#^/payout#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID] + ], + '#^/commission#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID] + ], + + // ===================== CLAIMS / TICKETS ===================== + '#^/ticket#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [CLAIMS_TEAM_ID] + ], + '#^/claim_mis#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [CLAIMS_TEAM_ID] + ], + + // ===================== LEADS / SALES ===================== + '#^/leads#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID] + ], + '#^/rfq#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID], + 'teams' => [SALES_TEAM_ID] + ], + '#^/sales#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID], + 'teams' => [SALES_TEAM_ID] + ], + + // ===================== CMS / CONTENT ===================== + '#^/add_image_index#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID], + 'teams' => [] + ], + '#^/frontend_content#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID], + 'teams' => [] + ], + '#^/FAQ#' => [ + 'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID], + 'teams' => [] + ], + + // ===================== LOGS ===================== + '#^/logs#' => [ + 'roles' => [ADMIN_ROLE_ID], + 'teams' => [] + ], + + // ===================== INTERNAL TEST ===================== + '#^/test#' => [ + 'roles' => [ADMIN_ROLE_ID], + 'teams' => [] + ], + + // ===================== API (JWT / SIGNED) ===================== + '#^/api#' => ['public' => true], + '#^/employeeRest#' => ['public' => true], + '#^/clientApi#' => ['public' => true], + + // ===================== WEBHOOKS / 3RD PARTY ===================== + '#^/dispatchWebhookData#' => ['public' => true], + '#^/retrieveWebhookData#' => ['public' => true], + // '#^/ICICI#' => ['public' => true], + // '#^/Vidal#' => ['public' => true], + // '#^/MediAssist#' => ['public' => true], + + // ===================== CLI ===================== + '#^/cli/#' => ['public' => true], + + // ===================== DEFAULT DENY (ZERO TRUST) ===================== + '#^/#' => [ + 'roles' => [ADMIN_ROLE_ID], + 'teams' => [] + ], + ]; +} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 62b24b8d..58edbca1 100755 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -101,6 +101,6 @@ class Autoload extends AutoloadConfig * @phpstan-var list */ public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload', - 'excel_import_export', 'file', 'drive','ExcelSanitizeHelper', 'api_helper','exception','sms_helper' + 'excel_import_export', 'file', 'drive','ExcelSanitizeHelper', 'api_helper','exception','sms_helper','sanitizeInputArrayAdvanced' ]; } diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 2128f042..c9431b94 100755 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -16,6 +16,10 @@ use App\Filters\AuthClientApi; use App\Filters\CommissionApiFilter; use App\Filters\VerifyAppSignature; use App\Filters\Cors; +use App\Filters\SecurityInputFilter; +use App\Filters\GlobalPostFileUploadGuard; +use App\Filters\AclFilter; +use App\Filters\RateLimitFilter; use App\Filters\AuthJWT; @@ -43,6 +47,11 @@ class Filters extends BaseConfig 'CommissionApiFilter'=> CommissionApiFilter::class, 'appSignature' => VerifyAppSignature::class, 'Cors' => Cors::class, + 'SecurityInputFilter' => SecurityInputFilter::class, + 'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class, + 'AclFilter' => AclFilter::class, + 'ratelimit' => RateLimitFilter::class, + ]; /** @@ -56,6 +65,9 @@ class Filters extends BaseConfig 'before' => [ 'HttpRequestLog' => ['except' => 'cli/*'], 'Cors', + 'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission']], + 'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail'] ], + 'GlobalPostFileUploadGuard' // 'csrf', // 'invalidchars', ], @@ -86,5 +98,5 @@ class Filters extends BaseConfig * Example: * 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']] */ - public array $filters = []; + // public array $filters = [ 'Cors' => ['before' => ['employeeRest/*']]]; } diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 531d5836..02f194d1 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -21,6 +21,8 @@ $routes->get('/chatbot', 'ChatbotControllerNew::chatbot'); $routes->get('/swagger', 'SwaggerController::index', ['filter' => 'authMVC']); $routes->get('/fedeploy', 'DeployController::fedeploy_view', ['filter' => 'authMVC']); $routes->post('/fedeploy', 'DeployController::fedeploy', ['filter' => 'authMVC']); +$routes->get('/visitOffBoardCheck', 'EmployeeController::visitOffBoardCheck'); +$routes->get('/metaDashboardDemo', 'TestingController::metaDashboardDemo'); // Reminder Mail Notification @@ -35,7 +37,6 @@ $routes->get("view", "EmployeeController::viewECard/$1"); $routes->get("checkWellnessOnboardStatus/(:any)", "EmployeeController::checkWellnessOnboardStatus/$1"); $routes->get("initiateWellnessOnboard/(:any)", "EmployeeController::initiateWellnessOnboard/$1"); $routes->get("exportQCRandRFQ/(:any)", "LeadsController::exportQCRandRFQ/$1"); -$routes->get("smapletest", "ClientController::smapletest"); $routes->get("testMailAttachments", "ClientController::testMailAttachments"); $routes->get("updatePolicyTermsKey", "ClientController::updatePolicyTermsKey"); $routes->get("updateRemainderDate", "ClientController::updateRemainderDate"); @@ -215,6 +216,9 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave"); $routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1"); $routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3"); + $routes->get('clearCdSession', 'EmployeeController::clearCdSession'); + $routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus'); + }); @@ -427,6 +431,8 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->get('checkDuplicateCdAccount', 'MasterController::checkDuplicateCdAccount'); $routes->get('proceedExcelFileDataValidation', 'EmployeeController::proceedExcelFileDataValidation'); $routes->get('checkTpaApiEnable', 'EmployeeRestController::checkTpaApiEnable'); + $routes->get('generateDemographyDataTable', 'LeadsController::generateDemographyDataTable'); + $routes->post('insufficientCdBalanceHrMailSend', 'EmployeeController::insufficientCdBalanceHrMailSend'); }); $routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail"); @@ -525,7 +531,6 @@ $routes->cli('cli/check_env', 'MasterController::checkEnv'); $routes->cli('cli/enrollOpendAndClose', 'DashboardController::updatePolicyEnrollmentStatus'); $routes->cli("cli/sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail"); $routes->cli('cli/update-emp-policy-status', 'ClientController::updateEmpAndPolicyStatus'); -$routes->cli('cli/update-emp-policy-status', 'ClientController::updateEmpAndPolicyStatus'); $routes->cli('cli/insurerRFQRemainder', 'LeadsController::remainderForQcr'); $routes->cli('cli/sendMailWithAutoQuery','TicketController::sendMailWithAutoQuery'); $routes->cli('cli/MediAssit-ClaimStatusUpdate','MediAssistApiController::ClaimStatusUpdate'); @@ -544,7 +549,7 @@ $routes->get('get_ticket_data',"EmployeeRestController::get_ticket_data"); //api -$routes->group("/api", ["filter" => "authJWT"], function ($routes) { +$routes->group("/api", ["filter" => [ 'ratelimit' , 'authJWT']], function ($routes) { $routes->get('get_ticket_type',"EmployeeRestController::get_ticket_type"); $routes->post("logined", "RestAuthenticationController::logined"); $routes->post("getId", "RestAuthenticationController::getUserIdFromToken"); @@ -554,8 +559,12 @@ $routes->group("/api", ["filter" => "authJWT"], function ($routes) { $routes->get("getSSORedirectUrl", "ApiServiceController::getSSORedirectUrl"); $routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicy"); +$routes->get("downloadCdSplitUpFile", "EmployeeRestController::downloadCdSplitUpFile"); +$routes->get("downloadFileTableFile/(:any)", "EmployeeController::downloadFileList/$1"); -$routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($routes) { + + +$routes->group("employeeRest", ['filter' => ['ratelimit' , 'appSignature'] ], function ($routes) { //Employee login api's $routes->post("verifyEmployeeNumber", "RestAuthenticationController::verifyEmployeeWithMobileNumber"); @@ -598,7 +607,9 @@ $routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($route }); -$routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], function ($routes) { +$routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratelimit' , 'appSignature', 'authJWT']], function ($routes) { + + $routes->post('logout', 'RestAuthenticationController::logout'); $routes->post("ecardRequest", "ApiServiceController::ecardRequest"); $routes->get("getWellnessURL", "ApiServiceController::getWellnessURL"); @@ -648,6 +659,7 @@ $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], funct $routes->get("hrFileDownload", "EmployeeRestController::hrFileDownload"); $routes->post("hrFileUpload", "EmployeeRestController::hrFileUpload"); $routes->post("updateHrFileUploadData", "EmployeeRestController::updateHrFileUploadData"); + $routes->post("getHrDashboad", "EmployeeRestController::getHrDashboad"); //thz_master's @@ -659,7 +671,7 @@ $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], funct $routes->match(['get','post','put'], 'ticketType', 'ThzController::ticketType'); //this route for nhance website contact form (sales/service) - $routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($routes) { + $routes->group("employeeRest", ['filter' => ['ratelimit' , 'appSignature'] ], function ($routes) { $routes->post("ticketSave", "ThzController::ticketSave"); }); @@ -678,7 +690,10 @@ $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], funct // get insurer and policy type $routes->get("getPolicyTypeAndInsurer", "EmployeeRestController::getPolicyTypeAndInsurer"); - + $routes->get("getExcelFileErrors/(:any)", "EmployeeController::getExcelFileErrors/$1"); + $routes->get("getPolicyAndEndorsementFiles", "EmployeeRestController::getPolicyAndEndorsementFiles"); + $routes->get("downloadPolicyFiles", "EmployeeRestController::downloadPolicyFiles"); + $routes->post("bulkEcardDownloadAsZip", "EmployeeRestController::bulkEcardDownloadAsZip"); }); $routes->get("hrFileDownload", "EmployeeRestController::hrFileDownload"); @@ -736,6 +751,12 @@ $routes->group("/ticket", ["filter" => "authMVC"], function ($routes) { $routes->get('getTpaClaimStatus',"ApiServiceController::getClaimStatus"); }); +$routes->group("/claim_mis", ["filter" => "authMVC"], function ($routes) { + $routes->get('list','TicketController::claimMisFileList'); + $routes->get('download','TicketController::downloadClaimMisFile'); + $routes->post('upload','TicketController::uploadClaimMisFile'); +}); + $routes->group("clientApi",["filter" => "AuthClientApi"], function ($routes){ $routes->post("getPolicyMaster","ClientAPIController::sendPolicyMaster"); @@ -753,7 +774,6 @@ $routes->post("retrieveWebhookDataClaim","ClientWebHooksController::pullData_cla //Third party ICICILombard Api Call - $routes->get('generateAuthToken','ICICILombardController::generateAuthToken'); $routes->get('createEnrollmentBatch','ICICILombardController::createEnrollmentBatch'); $routes->get('getEnrollmentBatchStatus','ICICILombardController::getEnrollmentBatchStatus'); @@ -762,16 +782,17 @@ $routes->get('fetchUHIDDetails','ICICILombardController::fetchUHIDDetails'); //Third party - testing route - -$routes->get('EcardRequest','VidalApiController::EcardRequest'); +$routes->get('FhplGetBenefDetails','FhplApiController::FhplGetBenefDetails'); +$routes->get('EcardRequest','FhplApiController::EcardRequest'); $routes->get('HospitalNetwork','MediAssistApiController::HospitalNetwork'); $routes->get('VidalGetBenefDetails','VidalApiController::VidalGetBenefDetails'); -$routes->get('ClaimDetail','VidalApiController::ClaimDetail'); -$routes->get('SubmitClaim','MediAssistApiController::SubmitClaim'); +$routes->get('ClaimDetail','FhplApiController::ClaimDetail'); +$routes->get('SubmitClaim','HealthIndiaApiController::SubmitClaim'); $routes->get('IntimateClaim','MediAssistApiController::IntimateClaim'); $routes->get('IRSubmission','MediAssistApiController::IRSubmission'); $routes->get('ClaimStatusUpdate','MediAssistApiController::ClaimStatusUpdate'); $routes->get('syncTpaClaimToNhance','MediAssistApiController::syncTpaClaimToNhance'); +$routes->get('HealthIndiaGetBenefDetails','HealthIndiaApiController::HealthIndiaGetBenefDetails'); // API service (Don't delete) diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php index f59de0f0..6f2e8ae2 100644 --- a/app/Controllers/ApiServiceController.php +++ b/app/Controllers/ApiServiceController.php @@ -12,6 +12,7 @@ use App\Controllers\BaseController; use App\Controllers\VidalApiController; use App\Controllers\ICICILombardController; use App\Controllers\MediAssistApiController; +use App\Controllers\FhplApiController; use App\Models\BatchFileModel; use App\Models\FileModel; use App\Helpers\TPADataCompareHelper; @@ -26,6 +27,8 @@ class ApiServiceController extends BaseController protected $medi_assist_primary_key; protected $vidal_primary_key; protected $icici_primary_key; + protected $fhpl_primary_key; + protected $health_india_primary_key; public function __construct() { @@ -34,6 +37,8 @@ class ApiServiceController extends BaseController $this->medi_assist_primary_key = getenv('MEDI_ASSIST_PRIMARY_KEY_CONSTANT'); $this->vidal_primary_key = getenv('VIDAL_PRIMARY_KEY_CONSTANT'); $this->icici_primary_key = getenv('ICICI_PRIMARY_KEY_CONSTANT'); + $this->fhpl_primary_key = getenv('FHPL_PRIMARY_KEY_CONSTANT'); + $this->health_india_primary_key = getenv('HEALTH_INDIA_PRIMARY_KEY_CONSTANT'); } // Push Claims @@ -55,6 +60,10 @@ class ApiServiceController extends BaseController { // Vidal $vidalApiController = new VidalApiController; return $vidalApiController->SubmitClaim($claimId); + }else if ($tpaID == $this->fhpl_primary_key) + { // fhpl + $fhplApiController = new FhplApiController; + return $fhplApiController->SubmitClaim($claimId); }else{ log_message('error', "This ticket id ( {$claimId} ) TPA has no API service enabled. TPA ID : {$tpaID}"); } @@ -106,10 +115,11 @@ class ApiServiceController extends BaseController ->join('client_policy', 'employee_polices.client_policy_id = client_policy.id', 'left') ->where('employees.emp_code', $emp_code) ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('employees.id', $id) ->where('employee_polices.is_active', 1) ->where('employees.is_active', 1) - ->where('employee_polices.status', 'active') - ->where('employees.emp_status', 'active') + ->whereIn('employee_polices.status', ['active', 'expired']) + ->whereIn('employees.emp_status', ['active', 'expired']) ->findAll(); @@ -129,6 +139,12 @@ class ApiServiceController extends BaseController $vidalApiController = new VidalApiController; $data['eCardDownload'] = $vidalApiController->EcardRequest( $emp_code, $policy_no , $employee_policy[0]['tpa_id'] ); + }else if($employee_policy[0]['tpa_primary_id'] == $this->fhpl_primary_key)// Fhpl + { + + $fhplApiController = new FhplApiController; + $data['eCardDownload'] = $fhplApiController->EcardRequest( $emp_code, $policy_no , $employee_policy[0]['tpa_id'] ); + }else{ if($type == "download"){ @@ -265,6 +281,17 @@ class ApiServiceController extends BaseController return $this->respond(['status' => true, 'code' => 200, 'message' => 'Action Triggered','data' => [] ]); + }else if ($tpa_id == $this->fhpl_primary_key) // Fhpl + { + $file_id = $fileModel->insert($data); + log_message('error', "Files table inserted successfully, File id : {$file_id}"); + // $fhplApiController = new FhplApiController; + // $fhplApiController->FhplGetBenefDetails( [ 'polict_no' => $policy_no, 'file_id' =>$file_id ] ); + $r = Jobs::addJob(['job_name' => 'FhplGetBenefDetails', 'payload' => ['policy_no' => $policy_no, 'file_id' => $file_id, 'client_policy_id' => $policy_id, 'return_type' => 'job']]); + log_message('error', "FhplGetBenefDetails job pushed successfully."); + + return $this->respond(['status' => true, 'code' => 200, 'message' => 'Action Triggered','data' => [] ]); + }else{ return $this->respond(['status' => false, 'code' => 200,'message' => 'TPA not found ','data' => [] ]); } @@ -286,12 +313,20 @@ class ApiServiceController extends BaseController if ($tpaID == $this->medi_assist_primary_key) { // MediAssist $mediAssistController = new MediAssistApiController(); - return $mediAssistController->ClaimDetail($claimId); + $res = $mediAssistController->ClaimDetail($claimId); + return $this->response->setJSON(['status' => $res['status'],'message' => $res['message'] ]); }else if ($tpaID == $this->vidal_primary_key) { // vidal $vidalApiController = new VidalApiController; - return $vidalApiController->ClaimDetail($claimId); + $res = $vidalApiController->ClaimDetail($claimId); + return $this->response->setJSON(['status' => $res['status'],'message' => $res['message'] ]); + + }else if ($tpaID == $this->fhpl_primary_key) { // Fhpl + + $fhplApiController = new FhplApiController; + $res = $fhplApiController->ClaimDetail($claimId); + return $this->response->setJSON(['status' => $res['status'],'message' => $res['message'] ]); }else{ log_message('error', "This ticket id ( {$claimId} ) TPA has no API service enabled. TPA ID : {$tpaID}"); diff --git a/app/Controllers/AppContentManagementController.php b/app/Controllers/AppContentManagementController.php index 33d592e9..a7032a4c 100755 --- a/app/Controllers/AppContentManagementController.php +++ b/app/Controllers/AppContentManagementController.php @@ -59,8 +59,48 @@ class AppContentManagementController extends AdminController // add and edit public function add_advertise_image() { try { + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = ((int) $sanitized_post_data['add_image_id']) ?? null; + + $rules = [ + 'client_id' => [ + 'rules' => 'required|integer', + 'errors' => [ + 'required' => 'Client is required', + 'integer' => 'Invalid client selected' + ] + ], + ]; + $rules['advertise_image'] = [ + 'rules' => ($id === 0 ? 'uploaded[advertise_image]|' : '') // required only for ADD + . 'is_image[advertise_image]' + . '|mime_in[advertise_image,image/jpg,image/jpeg,image/png]' + . '|max_size[advertise_image,200]' + . '|min_dims[advertise_image,1640,664]' + . '|max_dims[advertise_image,1640,664]', + 'errors' => [ + 'uploaded' => 'Image is required', + 'is_image' => 'File must be an image', + 'mime_in' => 'Only JPG, JPEG, PNG allowed', + 'max_size' => 'Image size must not exceed 200 KB', + 'min_dims' => 'Image dimensions must be exactly 1640x664 pixels', + 'max_dims' => 'Image dimensions must be exactly 1640x664 pixels', + ] + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $file = $this->request->getFile('advertise_image'); - $client_id = $this->request->getPost('client_id'); + $client_id = $sanitized_post_data['client_id'] ?? null; //1) original file name for vaildations $fileName = $file->getClientName(); //original file name for vaildations $existing = $this->addImgModel->where('name', $fileName)->where('client_id', $fileName)->where('is_active', 1)->first(); @@ -80,13 +120,13 @@ class AppContentManagementController extends AdminController $file->move($uploadPath, $fileName); - $id = $this->request->getPost('add_image_id'); - $data = ['name' => $fileName,'client_id'=>$client_id]; + $id = $sanitized_post_data['add_image_id'] ?? null; + $details = ['name' => $fileName,'client_id'=>$client_id]; if ($id == 0) { - $this->addImgModel->insert($data); + $this->addImgModel->insert($details); } else { - $this->addImgModel->update($id, $data); + $this->addImgModel->update($id, $details); } return $this->respond(['status' => true, 'message' => 'Image saved successfully.']); @@ -143,8 +183,56 @@ class AppContentManagementController extends AdminController if ($this->request->getMethod() === 'post') { - $id = $this->request->getPost('fe_id'); - $data = $this->request->getPost(); + $rules = [ + 'type' => [ + 'rules' => 'required|max_length[255]', + 'errors' => [ + 'required' => 'Type is required', + 'max_length' => 'Type cannot exceed 255 characters' + ] + ], + 'content_section' => [ + 'rules' => 'required|max_length[255]', + 'errors' => [ + 'required' => 'Content Section is required', + 'max_length' => 'Content Section cannot exceed 255 characters' + ] + ], + 'heading' => [ + 'rules' => 'required|max_length[255]', + 'errors' => [ + 'required' => 'Heading is required', + 'max_length' => 'Heading cannot exceed 255 characters' + ] + ], + 'content' => [ + 'rules' => 'required|max_length[5000]', + 'errors' => [ + 'required' => 'Content is required', + 'max_length' => 'Content cannot exceed 5000 characters' + ] + ], + 'notes' => [ + 'rules' => 'required|max_length[1500]', + 'errors' => [ + 'required' => 'Notes are required', + 'max_length' => 'Notes cannot exceed 1500 characters' + ] + ] + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $request_post_data = $this->request->getPost(); + $data = sanitizeInputArrayAdvanced($request_post_data); + $id = $data['fe_id']; + unset($data['fe_id']); @@ -231,8 +319,30 @@ class AppContentManagementController extends AdminController try { // --- 1. POST: CREATE OR UPDATE --- if ($method === 'post') { - $id = $this->request->getPost('faq_id'); - $data = array_filter($this->request->getPost(), fn($v) => $v !== '' && $v !== null); + $rules = [ + 'category' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Category is required' + ] + ], + 'question' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Question is required' + ] + ], + 'answer' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Answer is required' + ] + ] + ]; + $request_post_data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data); + $data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null); + $id = $data['faq_id']; if (empty($id)) { $status = $this->faqModel->insert($data); @@ -264,9 +374,9 @@ class AppContentManagementController extends AdminController if (!empty($id)) { if($returnType === 'web'){ - $row = $this->faqModel->find($id); + $row = $this->faqModel->find((int)$id); }else{ - $row = $this->faqModel->where('is_active', 1)->find($id); + $row = $this->faqModel->where('is_active', 1)->find((int)$id); } $data['faq_list'] = $row ? [$row] : []; diff --git a/app/Controllers/BDSReportController.php b/app/Controllers/BDSReportController.php index abc1ac14..f9d68b1e 100644 --- a/app/Controllers/BDSReportController.php +++ b/app/Controllers/BDSReportController.php @@ -101,10 +101,10 @@ class BDSReportController extends AdminController SELECT ins.id, ins.name AS insurers, - COALESCE(SUM(CASE WHEN pt.action_type = 'inception' AND pt.insurer_id = ins.id AND ptp.policy_category = $insurerCategory THEN 1 ELSE 0 END), 0) AS Policies, - COALESCE(SUM(CASE WHEN ptp.policy_category = $insurerCategory AND pt_co.insurer_id = ins.id THEN (pt_co.cop_amt + pt_co.cotp_amt + pt_co.cotep_amt + pt_co.non_comm_per_amt) ELSE 0 END), 0) AS Premium, - COALESCE(SUM(CASE WHEN stmt.co_share_id = pt_co.id AND ptp.policy_category = $insurerCategory AND pt_co.insurer_id = ins.id THEN stmt.reward + stmt.actual_bp_brokerage_amt + stmt.actual_tep_brokerage_amt + stmt.actual_tp_brokerage_amt ELSE 0 END), 0) AS Revenue, - COALESCE(SUM(CASE WHEN ptp.policy_category = $insurerCategory AND pt_co.insurer_id = ins.id THEN pt_co.exp_amt ELSE 0 END), 0) AS Expected_amt + COALESCE(SUM(CASE WHEN pt.action_type = 'inception' AND pt.insurer_id = ins.id AND ptp.policy_category = :insurerCategory: THEN 1 ELSE 0 END), 0) AS Policies, + COALESCE(SUM(CASE WHEN ptp.policy_category = :insurerCategory: AND pt_co.insurer_id = ins.id THEN (pt_co.cop_amt + pt_co.cotp_amt + pt_co.cotep_amt + pt_co.non_comm_per_amt) ELSE 0 END), 0) AS Premium, + COALESCE(SUM(CASE WHEN stmt.co_share_id = pt_co.id AND ptp.policy_category = :insurerCategory: AND pt_co.insurer_id = ins.id THEN stmt.reward + stmt.actual_bp_brokerage_amt + stmt.actual_tep_brokerage_amt + stmt.actual_tp_brokerage_amt ELSE 0 END), 0) AS Revenue, + COALESCE(SUM(CASE WHEN ptp.policy_category = :insurerCategory: AND pt_co.insurer_id = ins.id THEN pt_co.exp_amt ELSE 0 END), 0) AS Expected_amt FROM insurers ins LEFT JOIN @@ -112,16 +112,17 @@ class BDSReportController extends AdminController LEFT JOIN co_share_stmt_details stmt ON pt_co.id = stmt.co_share_id AND stmt.is_active = 1 LEFT JOIN - policy_transaction pt ON pt_co.pt_id = pt.id AND pt.is_active = 1 AND pt.policy_issue_date >= '$fromDate' AND pt.policy_issue_date <= '$toDate' + policy_transaction pt ON pt_co.pt_id = pt.id AND pt.is_active = 1 AND pt.policy_issue_date >= :fromDate: AND pt.policy_issue_date <= :toDate: LEFT JOIN - policy_type ptp ON pt.policy_type_id = ptp.id AND ptp.policy_category = $insurerCategory AND ptp.is_active = 1 + policy_type ptp ON pt.policy_type_id = ptp.id AND ptp.policy_category = :insurerCategory: AND ptp.is_active = 1 WHERE - ins.is_active = 1 AND (ptp.policy_category = $insurerCategory OR ptp.policy_category IS NULL) + ins.is_active = 1 AND (ptp.policy_category = :insurerCategory: OR ptp.policy_category IS NULL) GROUP BY ins.id; "; - $data['insurer_wise_data'] = $this->insurerModel->query($sql)->getResultArray(); + $binds = ['insurerCategory'=>$insurerCategory,'fromDate'=>$fromDate,'toDate'=>$toDate]; + $data['insurer_wise_data'] = $this->insurerModel->query($sql,$binds)->getResultArray(); // log_message('error',$this->insurerModel->getLastQuery()); // log_message('error',json_encode($data)); } catch (\Exception $e) { @@ -146,15 +147,16 @@ class BDSReportController extends AdminController COALESCE(SUM(co.reward + co.actual_bp_brokerage_amt + co.actual_tep_brokerage_amt + co.actual_tp_brokerage_amt), 0) AS Revenue, COALESCE(SUM(pt_co.exp_amt), 0) AS Expected_amt FROM policy_type AS pot - LEFT JOIN policy_transaction AS pt ON pot.id = pt.policy_type_id AND pt.is_active = 1 AND pt.action_type = 'inception' and pt.policy_issue_date >= '$fromDate' and pt.policy_issue_date <= '$toDate' + LEFT JOIN policy_transaction AS pt ON pot.id = pt.policy_type_id AND pt.is_active = 1 AND pt.action_type = 'inception' and pt.policy_issue_date >= :fromDate: and pt.policy_issue_date <= :toDate: LEFT JOIN pt_co_share_details AS pt_co ON pt.id = pt_co.pt_id AND pt_co.is_active = 1 LEFT JOIN co_share_stmt_details AS co ON pt_co.id = co.co_share_id AND co.is_active = 1 WHERE pot.is_active = 1 - AND pot.policy_category = $insurerCategory + AND pot.policy_category = :insurerCategory: GROUP BY pot.bap; "; - $data['bap_wise_data'] = $this->policyTypeModel->query($sql)->getResultArray(); + $binds = ['insurerCategory'=>$insurerCategory,'fromDate'=>$fromDate,'toDate'=>$toDate]; + $data['bap_wise_data'] = $this->policyTypeModel->query($sql,$binds)->getResultArray(); } catch (\Exception $e) { $data['message'] = 'No Data Found'; $this->myLogger->logme('error', $e->getMessage()); @@ -189,10 +191,13 @@ class BDSReportController extends AdminController return $this->loadLayout('irba_report', $data); } else { - $fromDate = $this->request->getPost('fromDate'); - $toDate = $this->request->getPost('toDate'); - $category = $this->request->getPost('category'); - $report_type = $this->request->getPost('report_type'); + $request_post_data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data); + + $fromDate = $sanitized_post_data['fromDate'] ?? null; + $toDate = $sanitized_post_data['toDate'] ?? null; + $category = $sanitized_post_data['category'] ?? null; + $report_type = $sanitized_post_data['report_type'] ?? null; // log_message('error',json_encode($_POST));die(); if ($report_type == 'insurer') { $life = $category == 'life' ? 1 : 0; @@ -265,6 +270,7 @@ class BDSReportController extends AdminController //Function for get BAP INSURE wise data for premium , policy count , revenue only public function getBAPInsurerData($category = 0, $start_date = null, $end_date = null) { + $category = (int)$category; try { $this->myLogger->logme('error', 'getBAPInsurerData function called'); $this->myLogger->logme('error', 'category : {data}', ['data' => $category]); @@ -424,7 +430,7 @@ class BDSReportController extends AdminController //Function for get BAP CLIENT wise data for premium , policy count , revenue only public function getBAPClientData($category = 0, $start_date = null, $end_date = null) { - + $category = (int)$category; try { $this->myLogger->logme('error', 'getBAPClientData function called'); $this->myLogger->logme('error', 'category : {data}', ['data' => $category]); @@ -525,7 +531,7 @@ class BDSReportController extends AdminController //Function for get CLIENT wise data for premium and policy count only public function getClientData($category = 0, $start_date = null, $end_date = null) { - + $category = (int)$category; try { $this->myLogger->logme('error', 'getClientData function called'); @@ -935,11 +941,13 @@ class BDSReportController extends AdminController return $this->loadLayout('renewal_search', $data); } else { - $fromDate = $this->request->getPost('fromDate'); - $toDate = $this->request->getPost('toDate'); - $client_id = $this->request->getPost('client_id'); - $client_type = $this->request->getPost('client_type'); - $issuer_branch = $this->request->getPost('issuer_branch'); + $request_post_data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data); + $fromDate = $sanitized_post_data['fromDate'] ?? null; + $toDate = $sanitized_post_data['toDate'] ?? null; + $client_id = $sanitized_post_data['client_id'] ?? null; + $client_type = $sanitized_post_data['client_type'] ?? null; + $issuer_branch = $sanitized_post_data['issuer_branch'] ?? null; $data['issuer_branch'] = $this->nhanceBranchModel->where('is_active', 1)->findAll(); $data['client_type'] = [1 => 'Group', 2 => 'Individual']; diff --git a/app/Controllers/ClaimsUploadController.php b/app/Controllers/ClaimsUploadController.php new file mode 100644 index 00000000..02c9a187 --- /dev/null +++ b/app/Controllers/ClaimsUploadController.php @@ -0,0 +1,48 @@ +db = \Config\Database::connect(); + } + + public function uploadDump() + { + $file = $this->request->getFile('file'); + + if (!$file || !$file->isValid()) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid file' + ], 400); + } + + $data = [ + 'client_id' => $this->request->getPost('client_id'), + 'tpa_id' => $this->request->getPost('tpa_id'), + 'client_policy_id' => $this->request->getPost('client_policy_id'), + 'from_date' => $this->request->getPost('from_date'), + 'to_date' => $this->request->getPost('to_date'), + 'upload_file' => $file->getRandomName(), + // 'uploaded_by' => user_id() + ]; + + $file->move(WRITEPATH . 'uploads/claims_dump', $data['upload_file']); + + $this->db->table('claims_dump_uploads')->insert($data); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Claims dump uploaded' + ]); + } +} diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index 4853fea1..2874c2cc 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -141,6 +141,7 @@ class ClientController extends AdminController $isDuplicate = $this->clientModel->isDuplicateByClientBranch($value, $field, $clientId, $branchId); return $this->response->setJSON(['isDuplicate' => $isDuplicate]); } + public function checkDuplicateTableFieldValue() { $table = $this->request->getPost('table'); @@ -162,35 +163,6 @@ class ClientController extends AdminController return $this->response->setJSON(['isDuplicate' => $isDuplicate]); } - public function smapletest() - { - $headers = [ - 'S.No', - 'NAME OF EMP/DEP', - 'EMP ID', - // 'EMP/DEP TYPE', - // 'RELATIONSHIP CODE', - 'DOB', - 'GENDER', - 'PRE EXISTING AILMENTS', - 'BASIC COVER SI', - 'DATE OF COVERAGE', - 'AGE', - 'RELATIONSHIP', - 'REMARKS', - 'POLICY END DATE', - 'NO OF DAYS', - 'TPA ID', - 'UHID', - // 'PREMIUM', - 'PRO RATA PREMIUM', - 'GST', - 'TOTAL AMOUNT' - ]; - $filePath = generateExcelWithHeader($headers); - echo "Excel file created at: $filePath"; - } - public function testMailAttachments($email = 'venkateshraman786@gmail.com') { $message = '

Request for Quotation (RFQ)

Dear {{RECIPIENT_NAME}},

We are reaching out to request a quotation for the following insurance coverage. Please review the details below and provide your quote at your earliest convenience.

RFQ Details

Client name{{CLIENT_NAME}}
Coverage Type{{POLICY_LONG_NAME}}
Policy Start Date{{POLICY_START_DATE}}
Policy Duration{{DURATION}}
Please note: Additional terms and details are included in the attachment for your reference.

Please feel free to reach out if you require any further information to prepare the quote. We look forward to receiving your proposal.

Best regards,

Nhance India Pvt Ltd

'; @@ -478,13 +450,32 @@ class ClientController extends AdminController public function index() { $this->myLogger->logme('error', 'Client list function called'); + // echo ""; + $headerData['tab_name'] = 'Clients'; $headerData['page_name'] = 'Clients'; // Both Browser Tab name And Page name are same. - $data['clientList'] = $this->clientModel->getCreatedByUserName(1); // passing client_type + // $data['clientList'] = $this->clientModel->getCreatedByUserName(1); // passing client_type $data['client_rm'] = $this->clientRMModel->getAllClientRM(); $data['lead_data'] = $this->leadsModel->getLeadForInsertClientList(); + + $rawList = $this->clientModel->getCreatedByUserName(1); + $clientRM = $data['client_rm']; + $rmMap = []; + foreach ($clientRM as $rm) { + $rmMap[$rm->client_id][] = $rm->account_manager; + } + $data['clientList'] = (!empty($rawList) && is_array($rawList)) ? array_map(function($item) use ($rmMap) { + $managers = isset($rmMap[$item->id]) ? implode(", ", $rmMap[$item->id]) : "N/A"; + return (object) [ + 'id' => $item->id, + 'client_name' => $item->client_name, + 'short_name' => $item->short_name, + 'account_managers' => $managers + ]; + }, $rawList) : []; // dd($data); + // dd($data['clientList']); echo view('layout/header', $headerData); echo view('client_list', $data); @@ -493,11 +484,25 @@ class ClientController extends AdminController // $this->loadLayout('client_onboarding', $data); } - public function typeList($id = null) { try { - $data = $this->clientModel->getCreatedByUserName($id); // passing client_type + $rawList = $this->clientModel->getCreatedByUserName($id); // passing client_type + $clientRM = $this->clientRMModel->getAllClientRM();; + $rmMap = []; + foreach ($clientRM as $rm) { + $rmMap[$rm->client_id][] = $rm->account_manager; + } + $data['clientList'] = (!empty($rawList) && is_array($rawList)) ? array_map(function($item) use ($rmMap) { + $managers = isset($rmMap[$item->id]) ? implode(", ", $rmMap[$item->id]) : "N/A"; + return (object) [ + 'id' => $item->id, + 'client_name' => $item->client_name, + 'short_name' => $item->short_name, + 'account_managers' => $managers + ]; + }, $rawList) : []; + if (empty($data)) { return $this->response ->setJSON(['status' => 'error', 'message' => 'No Records found']) @@ -515,10 +520,10 @@ class ClientController extends AdminController } } - public function updateEmpAndPolicyStatus() { $return = $this->clientPolicyModel->updateStatus(); + print_rr($return); $this->myLogger->logme('error', 'Client Policy Status Update Count: {data}', ['data' => $return['client']]); $this->myLogger->logme('error', 'Employee Policy Status Update Count: {data}', ['data' => $return['emp']]); } @@ -645,7 +650,7 @@ class ClientController extends AdminController // echo "
";
         // print_r($data); die;
-        $data['placeHolders'] = ['member_name', 'nhance_logo', 'tpa_id', 'ecard_download_link', 'client_logo', 'policy_no', 'member_summary', 'app_link', 'post_enrollment_app_link', 'client_name'];
+        $data['placeHolders'] = ['hr_name', 'member_name', 'nhance_logo', 'tpa_id', 'ecard_download_link', 'client_logo', 'policy_no', 'member_summary', 'app_link', 'post_enrollment_app_link', 'client_name'];
 
         // dd($data['placeHolders']);
 
@@ -659,18 +664,25 @@ class ClientController extends AdminController
     }
 
     // In your controller
-    public function deposit($id = null, $requestFrom = null, $policyId = null)
+    public function deposit($ClientId = null, $requestFrom = null, $policyId = null)
     {
         $headerData['tab_name'] = 'Client Deposit';
         $headerData['page_name'] = 'Clients';
 
-        $data['clientName'] = $this->clientModel->where('id', $id)->find();
-        $data['clientData'] = $this->clientPolicyModel->getinsurerswithclientid($id, $policyId);
+        if (is_string($ClientId) && preg_match('/^[a-f0-9]{32}$/i', $ClientId)) 
+        {
+           $data['clientName'] = $this->clientModel->where('md5(id)', $ClientId)->find();
+        }else  {
+           $data['clientName'] = $this->clientModel->where('id', $ClientId)->find();
+        }
 
-        $data['depositsummary'] = $this->clientPolicyModel->getDepositlistsummary($id);
+        
+        $data['clientData'] = $this->clientPolicyModel->getinsurerswithclientid($ClientId, $policyId);
+
+        $data['depositsummary'] = $this->clientPolicyModel->getDepositlistsummary($ClientId);
 
         // Fetch associated insurer names and balances
-        $balances = $this->clientPolicyModel->getBalances($id);
+        $balances = $this->clientPolicyModel->getBalances($ClientId);
         $data['balances'] = $balances;
 
         // dd($data);
@@ -735,14 +747,102 @@ class ClientController extends AdminController
 
     public function saveDeposit()
     {
+
+       $rules = [
+
+            'amount' => [
+                'rules' => 'required|numeric|greater_than_equal_to[0]',
+                'errors' => [
+                    'required' => 'Amount is required',
+                    'numeric'  => 'Amount must be a valid number',
+                    'greater_than_equal_to' => 'Amount cannot be negative',
+                ]
+            ],
+
+            'client_id' => [
+                'rules' => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required' => 'Client ID is required',
+                    'is_natural_no_zero' => 'Client ID must be a positive integer',
+                ]
+            ],
+
+            'insurer_id' => [
+                'rules' => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required' => 'Insurer ID is required',
+                    'is_natural_no_zero' => 'Insurer ID must be a positive integer',
+                ]
+            ],
+
+            'cd_ac_pk' => [
+                'rules' => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required' => 'Account PK is required',
+                    'is_natural_no_zero' => 'Account PK must be a positive integer',
+                ]
+            ],
+
+            'cd_ac_no' => [
+                'rules' => 'required|regex_match[/^[a-zA-Z0-9\/_\\-]+$/]',
+                'errors' => [
+                    'required' => 'Account number is required',
+                    'regex_match' => 'Account number can only contain letters, numbers, slashes (/), underscores (_), and hyphens (-)',
+                ]
+            ],
+
+            'sub_type_id' => [
+                'rules' => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required' => 'Sub type ID is required',
+                    'is_natural_no_zero' => 'Sub type ID must be a positive integer',
+                ]
+            ],
+
+            'description' => [
+                'rules' => 'required|string|min_length[3]|max_length[255]',
+                'errors' => [
+                    'required' => 'Description is required',
+                    'string' => 'Description must be text',
+                    'min_length' => 'Description must be at least 3 characters',
+                    'max_length' => 'Description must not exceed 255 characters',
+                ]
+            ],
+
+            'transaction_type' => [
+                'rules' => 'required|in_list[Credit,Debit]',
+                'errors' => [
+                    'required' => 'Transaction type is required',
+                    'in_list' => 'Transaction type must be either credit or debit',
+                ]
+            ],
+
+        ];
+
+
+        if (! $this->validate($rules)) {
+            return $this->response
+                ->setStatusCode(400)
+                ->setJSON([
+                    'status' => 'error',
+                    'message' => 'Input validation failed',
+                    'errors' => $this->validator->getErrors()
+                ]);
+        }
+
+        //sanitize the post params
+        $post_data = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($post_data);
         // Retrieve form data from POST request
         $loggedInUserID = get_session_userid();
+        // print_rr($sanitized_post_data);die();
+        $client_id = $sanitized_post_data['client_id'] ?? null;
+        $insurer_id = $sanitized_post_data['insurer_id'] ?? null;
+        $record_date = $sanitized_post_data['record_date'] ?? null;
+        $cd_ac_pk = $sanitized_post_data['cd_ac_pk'] ?? null;
+        $cd_ac_no = $sanitized_post_data['cd_ac_no'] ?? null;
 
-        $client_id = $this->request->getPost('client_id');
-        $insurer_id = $this->request->getPost('insurer_id');
-        $record_date = $this->request->getPost('record_date');
-        $cd_ac_pk = $this->request->getPost('cd_ac_pk');
-        $cd_ac_no = $this->request->getPost('cd_ac_no');
+         
 
         // $CD_Account_Number = $this->CDMasterModel
         //     ->where('client_id', $client_id)
@@ -760,16 +860,16 @@ class ClientController extends AdminController
         }
 
         $data = [
-            'amount' => $this->request->getPost('amount'),
-            'sub_type_id' => $this->request->getPost('sub_type_id'),
-            'client_id' => $this->request->getPost('client_id'),
+            'amount' => $sanitized_post_data['amount'] ?? null,
+            'sub_type_id' => $sanitized_post_data['sub_type_id'] ?? null,
+            'client_id' => $sanitized_post_data['client_id'] ?? null,
             'client_policy_id' => null,
             'cd_ac_no' => $cd_ac_no ?? null,
             'cd_ac_pk' => $cd_ac_pk ?? null,
             'endorsement_no' => null,
-            'insurer_id' => $this->request->getPost('insurer_id'),
-            'description' => $this->request->getPost('description'),
-            'transaction_type' => $this->request->getPost('transaction_type') ?: 'Credit',
+            'insurer_id' => $sanitized_post_data['insurer_id'] ?? null,
+            'description' => $sanitized_post_data['description'] ?? null,
+            'transaction_type' => $sanitized_post_data['transaction_type'] ?: 'Credit',
             'updated_by' => 1,
             'record_date' => $record_date
         ];
@@ -829,9 +929,32 @@ class ClientController extends AdminController
         }
 
         $editData['client_policy']    = $clientPoliceData;
+        //Notes : $editData['client_policy'] this on called in client_policy page 
+        $rawList = $editData['client_policy'];
+
+        $editData['client_policy'] = (!empty($rawList) && is_array($rawList))
+            ? array_values(array_map(function ($item) {
+                    return (object) [
+                        'id'                   => $item->id,
+                        'policy_no'            => $item->policy_no,
+                        'policy_type_name'     => $item->policy_type_name,
+                        'policy_start_date'    => $item->policy_start_date,
+                        'policy_end_date'      => $item->policy_end_date,
+                        'insurer_short'        => $item->insurer_short,
+                        'insurer_branch_name'  => $item->insurer_branch_name,
+                        'branch_name'          => $item->branch_name ?? ' - ',
+                        'tpa_short'            => $item->tpa_short,
+                        'policy_type_name'     => $item->policy_type_name,
+                        'policy_type_id'       => $item->policy_type_id,
+                        'lead_cd_amount'       => $item->lead_cd_amount,
+                        'tpa_branch_code'      => $item->tpa_branch_code,
+                        'branch_name'          => $item->branch_name ?? ' - ',
+                        'policy_type_id'       => $item->policy_type_id,
+                    ];
+                }, $rawList)) : [];
         $editData['client_policy']['role'] = get_role_id();
         $editData['notification'] = $this->notificationModel->select('template_name,enabled')->where('client_id', $id)->findAll();
-        $editData['placeHolders'] = ['member_name', 'member_mobile', 'nhance_logo', 'tpa_id', 'ecard_download_link', 'client_logo', 'policy_no', 'member_summary', 'app_link', 'post_enrollment_app_link', 'client_name'];
+        $editData['placeHolders'] = ['hr_name', 'member_name', 'member_mobile', 'nhance_logo', 'tpa_id', 'ecard_download_link', 'client_logo', 'policy_no', 'member_summary', 'app_link', 'post_enrollment_app_link', 'client_name'];
         $editData['api_data'] = $this->clientApi->where("client_id", $id)->where("is_active", 1)->first();
         // dd($editData);
 
@@ -847,40 +970,109 @@ class ClientController extends AdminController
 
     public function createClientGeneralInfo()
     {
-
         $this->myLogger->logme('error', 'Client general info function called');
+
+        $rules = [
+            'entity_type_id' => [
+                'rules' => 'required|numeric',
+                'errors' => [
+                    'required' => 'Entity Type is required',
+                    'numeric'  => 'Invalid Entity Type selected'
+                ]
+            ],
+            'client_name' => [
+                'rules' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[2]||max_length[45]',
+                'errors' => [
+                    'required'    => 'Client Name is required',
+                    'regex_match' => 'Client Name can only contain letters, numbers, spaces, hyphens(-), and underscores(_).',
+                    'min_length'  => 'Client Name must be at least 2 characters.',
+                    'max_length'  => 'Client Name must not exceed 45 characters.',
+                ]
+            ],
+            'short_name' => [
+                'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]|min_length[2]||max_length[15]',
+                'errors' => [
+                    'required'    => 'Client Short Name is required',
+                    'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens(-), and underscores(_) with no spaces.',
+                    'min_length'  => 'Client Short Name must be at least 2 characters.',
+                    'max_length'  => 'Client Short Name must not exceed 15 characters.',
+                ]
+            ],
+            'pan' => [
+                'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
+                'errors' => [
+                    'required'    => 'PAN Number is required.',
+                    'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
+                ]
+            ],
+            'hr_file_processed_by' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required'    => 'HR File Processed By is required.',
+                ]
+            ],
+            'client_logo' => [
+                    'rules' => [
+                        'is_image[client_logo]',
+                        'max_size[client_logo,200]', // 200 KB
+                        'ext_in[client_logo,jpg,jpeg,png]',
+                        'max_dims[client_logo,100,100]',
+                    ],
+                    'errors' => [
+                        'is_image' => 'The uploaded file must be an image',
+                        'max_size' => 'File size should not exceed 200 KB',
+                        'ext_in'   => 'Allowed file types: jpg, jpeg, png',
+                        'max_dims' => 'Image dimensions must be 100 x 100 pixels',
+                    ]
+            ],
+
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        
+
         $uploadFilePath = ROOTPATH . 'public/uploads/logo/';
         $file_name = file_Upload($this->request->getFile('client_logo'), $uploadFilePath);
         $data   = $this->request->getPost();
-        $data['created_by'] = get_session_userid();
-        $data['client_logo'] = $file_name;
-        $data['client_code'] = generate_client_code();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($data);
+        $sanitized_post_data['created_by'] = get_session_userid();
+        $sanitized_post_data['client_logo'] = $file_name;
+        $sanitized_post_data['client_code'] = generate_client_code();
 
 
-        if (!isset($data['is_download_btn'])) {
-            $data['is_download_btn'] = 0;
-        } elseif ($data['is_download_btn']) {
-            $data['is_download_btn'] = 1;
+        if (!isset($sanitized_post_data['is_download_btn'])) {
+            $sanitized_post_data['is_download_btn'] = 0;
+        } elseif ($sanitized_post_data['is_download_btn']) {
+            $sanitized_post_data['is_download_btn'] = 1;
         }
 
-        if (empty($data['parent_client_id'])) {
-            $data['parent_client_id'] = null;
+        if (empty($sanitized_post_data['parent_client_id'])) {
+            $sanitized_post_data['parent_client_id'] = null;
         }
 
-        $insert = $this->clientModel->insert($data);
+        $insertID = $this->clientModel->insert($sanitized_post_data);
 
-        if ($insert) {
-            $client_data = $this->clientModel->where(['id' => $insert, 'is_active' => 1])->first();
-
-            $client_id = $insert;
-            $default_template_creation = $this->createDefaultMailTemplate($client_id , $client_data);
-            if($default_template_creation == false){
-                $this->myLogger->logme('error', 'Default Mail Template Creation Failed for Client ID: {data}', ['data' => $client_id]);
+        if ($insertID) {
+            
+            $client_info = $this->clientModel->where(['id' => $insertID, 'is_active' => 1])->first();
+            
+            // Template Creation
+            if (!$this->createDefaultMailTemplate($insertID, $client_info)) {
+                $this->myLogger->logme('error', 'Default Mail Template Creation Failed for Client ID: ' . $insertID);
             }
-            return $this->respond(['status' => true, 'code' => 200, 'data' => $client_data], 200);
-        } else {
-            return $this->respond(['status' => false, 'code' => 404, 'message' => 'no data found'], 200);
+
+            return $this->respond(['status' => true, 'code' => 200, 'data' => $client_info], 200);
         }
+
+        return $this->respond(['status' => false, 'code' => 500, 'message' => 'Failed to create client'], 500);
     }
 
     public function editClientGeneralInfo()
@@ -920,7 +1112,7 @@ class ClientController extends AdminController
 
 
 
-    public function createClientKYCInfo()
+    public function createClientKYCInf()
     {
         $this->myLogger->logme('error', 'create Client kyc function called');
         $data = $this->request->getPost();
@@ -953,33 +1145,124 @@ class ClientController extends AdminController
         }
     }
 
+    /** Create Client KYC Documents V1 */
+    public function createClientKYCInfo()
+    {
+        $this->myLogger->logme('error', 'Create Client KYC function called');
+        
+        $rules = [
+            'other_docs_name' => [
+                'label'  => 'Documents Name',
+                'rules'  => 'permit_empty|regex_match[/^[a-zA-Z0-9_\- ]+$/]',
+                'errors' => [
+                    'regex_match' => 'Document Name can only contain letters, numbers, hyphens, and underscores'
+                ]
+            ],
+
+            'file_name' => [
+                'rules'  => 'uploaded[file_name]|max_size[file_name,5120]|ext_in[file_name,pdf,jpg,jpeg,png]',
+                'errors' => [
+                    'uploaded' => 'KYC document file is required',
+                    'max_size' => 'File size should not exceed 5MB',
+                    'ext_in'   => 'Allowed file types: pdf, jpg, jpeg, png',
+                ]
+            ],
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $data = $this->request->getPost();
+        $sanitized_data = sanitizeInputArrayAdvanced($data);
+        $form_type = $sanitized_data['form_type'] ?? null;
+        $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
+
+        unset($data['file_name']);
+        
+        $file = $this->request->getFile('file_name');
+
+        $fileName = file_Upload($file, $uploadFilePath);
+
+        if (!empty($fileName)) {
+            $sanitized_data['file_name'] = $fileName;
+        }
+
+        $sanitized_data['created_by'] = get_session_userid();
+
+        $insertID = $this->clientKYCDocsModel->insert($sanitized_data);
+        if ($insertID) {
+            if ($form_type === 'others') {
+                $kycDocs = $this->generateKycOthersTable($sanitized_data['client_id']);
+            } else {
+                $kycDocs = $this->generateKycPrimaryTable($sanitized_data['client_id']);
+            }
+            return $this->respond(['status' => true, 'code' => 200, 'data' => $kycDocs, 'file_name' => $file
+            ], 200);
+        } else {
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to upload KYC document'], 200);
+        }
+    }
+
+    /** Edit Client KYC Documents V1 */
     public function editClientKYCInfo()
     {
+        $this->myLogger->logme('error', 'Edit Client KYC function called');
+        
+        $rules = [
+            'file_name' => [
+                'rules'  => 'uploaded[file_name]|max_size[file_name,5120]|ext_in[file_name,pdf,jpg,jpeg,png]',
+                'errors' => [
+                    'uploaded' => 'KYC document file is required',
+                    'max_size' => 'File size should not exceed 5MB',
+                    'ext_in'   => 'Allowed file types: pdf, jpg, jpeg, png',
+                ]
+            ],
+        ];
 
-        $this->myLogger->logme('error', 'edit client kyc function called');
-        $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
-        $File = file_Upload($this->request->getFile('file_name'), $uploadFilePath);
-        $form_type = $this->request->getPost('form_type') ?? null;
-
-        if (!empty($File)) {
-            $data['file_name'] = $File;
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
         }
-        $id                 = $this->request->getPost('PrimaryKey');
-        $data['client_id']  = $id;
-        $data['kyc_doc_type_id'] = $this->request->getPost('kyc_doc_id');
-        $data['updated_by'] = get_session_userid();
-        $insert = $this->clientKYCDocsModel->insert($data);
 
-        if ($insert) {
-            // $kycDocs = $this->clientKYCDocsModel->getKycDocsName($id);
-            if ($form_type == "others") {
-                $kycDocs = $this->generateKycOthersTable($this->request->getPost('client_id'));
+        $data = $this->request->getPost();
+        $sanitized_data = sanitizeInputArrayAdvanced($data);
+        $form_type = $sanitized_data['form_type'] ?? null;
+        $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
+
+        unset($sanitized_data['file_name']);
+        
+        $file = $this->request->getFile('file_name');
+
+        $fileName = file_Upload($file, $uploadFilePath);
+
+        if (!empty($fileName)) {
+            $sanitized_data['file_name'] = $fileName;
+        }
+        $id                 = $sanitized_data['PrimaryKey'];
+        $sanitized_data['client_id']  = $id;
+        $sanitized_data['kyc_doc_type_id'] = $this->request->getPost('kyc_doc_id');
+        $sanitized_data['updated_by'] = get_session_userid();
+
+        $insertID = $this->clientKYCDocsModel->insert($sanitized_data);
+        if ($insertID) {
+            if ($form_type === 'others') {
+                $kycDocs = $this->generateKycOthersTable($sanitized_data['client_id']);
             } else {
-                $kycDocs = $this->generateKycPrimaryTable($this->request->getPost('client_id'));
+                $kycDocs = $this->generateKycPrimaryTable($sanitized_data['client_id']);
             }
-            return $this->respond(['status' => true, 'code' => 200, 'data' => $kycDocs], 200);
+            return $this->respond(['status' => true, 'code' => 200, 'data' => $kycDocs, 'file_name' => $file], 200);
         } else {
-            return $this->respond(['status' => false, 'code' => 404, 'message' => 'no data found'], 200);
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to upload KYC document'], 200);
         }
     }
 
@@ -1006,12 +1289,33 @@ class ClientController extends AdminController
     }
 
 
-
+    /** Client KYC Documents V2 */
     public function createClientKYCInfo_2()
     {
-        $this->myLogger->logme('error', 'create Client kyc function called');
-        $data = $this->request->getPost();
 
+        $this->myLogger->logme('error', 'Create Client KYC V2 function called');
+        
+        $rules = [
+            'file_name' => [
+                'rules'  => 'uploaded[file_name]|max_size[file_name,5120]|ext_in[file_name,pdf,jpg,jpeg,png]',
+                'errors' => [
+                    'uploaded' => 'KYC document file is required',
+                    'max_size' => 'File size should not exceed 5MB',
+                    'ext_in'   => 'Allowed file types: pdf, jpg, jpeg, png',
+                ]
+            ],
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $data = $this->request->getPost();
         $uploadedFile = $this->request->getFile('file_name');
 
         if ($uploadedFile && $uploadedFile->isValid() && !$uploadedFile->hasMoved()) {
@@ -1019,34 +1323,78 @@ class ClientController extends AdminController
         } else {
             $this->myLogger->logme('error', 'File failed validation or was not uploaded.');
         }
+        unset($data['file_name']);
+        $sanitized_data = sanitizeInputArrayAdvanced($data);
+        $form_type = $sanitized_data['form_type'] ?? null;
+        
+        $file = $this->request->getFile('file_name');
 
         $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
+        $fileName = file_Upload($file, $uploadFilePath);
+
+        if (!empty($fileName)) {
+            $sanitized_data['file_name'] = $fileName;
+        }
+
+        $sanitized_data['created_by'] = get_session_userid();
         
-        $File = file_Upload($uploadedFile, $uploadFilePath);
-        $this->myLogger->logme('info', 'Result of file_Upload: ' . $File);
+        
+        
+        $this->myLogger->logme('info', 'Result of file_Upload: ' . $fileName);
         unset($data['file_name']);
 
-        if (!empty($File)) { $data['file_name'] = $File; }
+        $insertID = $this->clientKYCDocsModel->insert($sanitized_data);
 
-        $data['created_by'] = get_session_userid();
-        $insert = $this->clientKYCDocsModel->insert($data);
-
-        if ($insert) {
-            $html = $this->generateKycSingleTable($data['client_id']);
-            $dropdown = $this->fetch_dropdown($data['client_id']);
-            return $this->respond(['status' => true, 'code' => 200, 'file_name' => $File, 'html' => $html,'dropdown'=>$dropdown], 200);
+        if ($insertID) {
+            $html = $this->generateKycSingleTable($sanitized_data['client_id']);
+            $dropdown = $this->fetch_dropdown($sanitized_data['client_id']);
+            return $this->respond(['status' => true, 'code' => 200, 'file_name' => $fileName, 'html' => $html,'dropdown'=>$dropdown], 200);
         } else {
             $this->myLogger->logme('error', 'Database insert failed.');
             return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to add document'], 200);
         }
     }
 
+    /** Edit Client KYC Documents V2 */
     public function editClientKYCInfo_2()
     {
         
-        $kyc_id = $this->request->getPost('id'); 
-        $client_id = $this->request->getPost('client_id');
-        $old_file_name = $this->request->getPost('old_file_name');
+        $this->myLogger->logme('error', 'Edit Client KYC V2 function called');
+        
+        $rules = [
+            'file_name' => [
+                'rules'  => 'uploaded[file_name]|max_size[file_name,5120]|ext_in[file_name,pdf,jpg,jpeg,png]',
+                'errors' => [
+                    'uploaded' => 'KYC document file is required',
+                    'max_size' => 'File size should not exceed 5MB',
+                    'ext_in'   => 'Allowed file types: pdf, jpg, jpeg, png',
+                ]
+            ],
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+        
+    
+        $data = $this->request->getPost();
+        unset($data['file_name']);    
+        $sanitized_data = sanitizeInputArrayAdvanced($data);
+        $form_type = $sanitized_data['form_type'] ?? null;
+        $kyc_id        = $sanitized_data['id'] ?? null;
+        $client_id     = $sanitized_data['client_id'] ?? null;
+        $old_file_name = $sanitized_data['old_file_name'] ?? null;
+
+        if (empty($kyc_id)) {
+            return $this->respond(['status' => false, 'message' => 'Missing KYC ID'], 400);
+        }
+
+        $updateData = [];
         $uploadedFile = $this->request->getFile('file_name');
         $new_file_name = null;
         $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
@@ -1054,12 +1402,11 @@ class ClientController extends AdminController
         
         if ($uploadedFile && $uploadedFile->isValid() && !$uploadedFile->hasMoved()) {
             
-            
-            $new_file_name = file_Upload($uploadedFile, $uploadFilePath); 
-
-            if (!empty($new_file_name)) {
-                
-                $updateData['file_name'] = $new_file_name;
+            $new_file_name = file_Upload($uploadedFile, $uploadFilePath);
+            if (!$new_file_name) {
+                return $this->respond(['status' => false, 'code' => 500, 'message' => 'New file upload failed on server.'], 200);
+            }
+            $updateData['file_name'] = $new_file_name;
 
                 // Delete the old file from the storage if it exists
                 // if (!empty($old_file_name)) {
@@ -1069,56 +1416,64 @@ class ClientController extends AdminController
                 //         // Optionally delete from G-Drive here if applicable
                 //     }
                 // }
-            } else {
-                // New file upload failed
-                return $this->respond(['status' => false, 'code' => 500, 'message' => 'New file upload failed on server.'], 200);
-            }
+            
         } 
 
-        // 2. Perform the database update
-        if (!empty($updateData)) {
-            
-            $updateData['updated_by'] = get_session_userid();
-            $update = $this->clientKYCDocsModel->update($kyc_id, $updateData);
-            $html = $this->generateKycSingleTable($client_id);
-            $dropdown = $this->fetch_dropdown($client_id);
-
-            if ($update) {
-                return $this->respond(['status' => true, 'code' => 200, 'message' => 'Document updated successfully.','html' => $html,'dropdown' => $dropdown], 200);
-            }
-        } else {
-            
+         if (empty($updateData)) {
             return $this->respond(['status' => true, 'code' => 200, 'message' => 'No changes detected. Document remains the same.'], 200);
         }
 
-        
+        $updateData['updated_by'] = get_session_userid();
+
+        $update = $this->clientKYCDocsModel->update($kyc_id, $updateData);
+
+        if ($update) {
+            return $this->respond([
+                'status' => true,
+                'message' => 'Document updated successfully',
+                'html' => $this->generateKycSingleTable($client_id),
+                'dropdown' => $this->fetch_dropdown($client_id)
+            ], 200);
+        }
+
         return $this->respond(['status' => false, 'code' => 404, 'message' => 'Database update failed or record not found.'], 200);
     }
 
-
-    public function deleteClientKycDocs_2() 
+    /** Delete Client KYC Documents V2 */
+    public function deleteClientKycDocs_2()
     {
-        
-        $kyc_id = $this->request->getPost('id'); 
-        $client_id = $this->request->getPost('client_id');
-        // echo "KID".$kyc_id;
-        // echo "CID".$client_id;
+        $this->myLogger->logme('error', 'Delete Client KYC V2 function called');
+        $data = $this->request->getPost();
+        $sanitized_data = sanitizeInputArrayAdvanced($data);
+        $kyc_id    = $sanitized_data['id'] ?? null;
+        $client_id = $sanitized_data['client_id'] ?? null;
+        $is_active = $sanitized_data['is_active'] ?? null;
 
         if (empty($kyc_id)) {
             return $this->respond(['status' => false, 'code' => 400, 'message' => 'Missing document ID.'], 200);
         }
-        $updateData['updated_by'] = get_session_userid();
-        $updateData['is_active'] = $this->request->getPost('is_active'); 
+
+    
+        $updateData = [
+            'is_active'  => (int) $is_active,
+            'updated_by' => get_session_userid()
+        ];
+
 
         $delete = $this->clientKYCDocsModel->update($kyc_id, $updateData);
-        // $delete = 1;
+
         if ($delete) {
-            $html = $this->generateKycSingleTable($client_id);
-            $dropdown = $this->fetch_dropdown($client_id);
-            return $this->respond(['status' => true, 'code' => 200, 'id' => $kyc_id, 'message' => 'Document successfully deactivated.','html' => $html,'dropdown' => $dropdown], 200);
-        } else {
-            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to update record (ID not found or DB error).'], 200);
+            return $this->respond([
+                'status' => true,
+                'code' => 200,
+                'id' => $kyc_id,
+                'message' => 'Document successfully deactivated.',
+                'html' => $this->generateKycSingleTable($client_id),
+                'dropdown' => $this->fetch_dropdown($client_id)
+            ], 200);
         }
+
+        return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to update record (ID not found or DB error).'], 200);
     }
 
     public function fetch_dropdown($client_id)
@@ -1167,6 +1522,52 @@ class ClientController extends AdminController
 
         $this->myLogger->logme('error', 'Client relation function called');
 
+        $rules = [
+            // --- Hidden Fields ---
+            'PrimaryKey' => [
+                'rules'  => 'permit_empty|numeric',
+            ],
+            'client_id' => [
+                'rules'  => 'required|numeric',
+                'errors' => ['required' => 'Client ID is missing.']
+            ],
+
+            // --- Account Manager (Array Category) ---
+            // Note the .* which validates every item inside the multiple select array
+            'account_manager' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Please select at least one Account Manager (L1).',
+                ]
+            ],
+
+            // --- Manager (L2) ---
+            'manager' => [
+                'rules'  => 'required|numeric',
+                'errors' => [
+                    'required' => 'Please select a Manager (L2).',
+                    'numeric'  => 'Invalid Manager selection.'
+                ]
+            ],
+
+            // --- Head ---
+            'head' => [
+                'rules'  => 'required|numeric',
+                'errors' => [
+                    'required' => 'Please select a Head.',
+                    'numeric'  => 'Invalid Head selection.'
+                ]
+            ],
+        ];
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
         $data['client_id'] = $this->request->getPost('client_id');
         $data['created_by'] = get_session_userid();
         $inserted = false;
@@ -1201,6 +1602,52 @@ class ClientController extends AdminController
     public function editClientRelation()
     {
         $this->myLogger->logme('error', 'Client relation function called');
+        $rules = [
+            // --- Hidden Fields ---
+            'PrimaryKey' => [
+                'rules'  => 'permit_empty|numeric',
+            ],
+
+            'client_id' => [
+                'rules'  => 'required|numeric',
+                'errors' => ['required' => 'Client ID is missing.']
+            ],
+
+            // --- Account Manager (Array Category) ---
+            // Note the .* which validates every item inside the multiple select array
+            'account_manager' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Please select at least one Account Manager (L1).',
+                ]
+            ],
+            
+            // --- Manager (L2) ---
+            'manager' => [
+                'rules'  => 'required|numeric',
+                'errors' => [
+                    'required' => 'Please select a Manager (L2).',
+                    'numeric'  => 'Invalid Manager selection.'
+                ]
+            ],
+
+            // --- Head ---
+            'head' => [
+                'rules'  => 'required|numeric',
+                'errors' => [
+                    'required' => 'Please select a Head.',
+                    'numeric'  => 'Invalid Head selection.'
+                ]
+            ],
+        ];
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
 
         $id                 = $this->request->getPost('PrimaryKey');
         $data['client_id']  = $this->request->getPost('client_id');
@@ -1272,11 +1719,498 @@ class ClientController extends AdminController
     }
 
 
+    public function createClientBranchNew()
+    {
+
+        $this->myLogger->logme('error', 'Client branch CREATE function called');
+
+        $rules = [
+                    'branch_name' => [
+                        'rules' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[3]',
+                        'errors' => [
+                            'required'    => 'Branch Name is required',
+                            'regex_match' => 'Branch Name can only contain letters, numbers, spaces, hyphens(-), and underscores(_).',
+                        ]
+                    ],
+                    'branch_code' => [
+                        'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
+                        'errors' => [
+                            'required'    => 'Branch Code is required',
+                            'regex_match' => 'Branch Code can only contain letters, numbers, hyphens(-), and underscores(_).',
+                        ]
+                    ],
+                    'address1' => [
+                        'rules' => 'required',
+                        'errors' => ['required' => 'Address Line 1 is required']
+                    ],
+                    'address2' => [
+                        'rules' => 'permit_empty|string',
+                    ],
+                    'state' => [
+                        'rules' => 'required|numeric',
+                        'errors' => ['required' => 'State is required']
+                    ],
+                    'district' => [
+                        'rules' => 'required',
+                        'errors' => ['required' => 'District is required.']
+                    ],
+                    'city' => [
+                        'rules' => 'required',
+                        'errors' => ['required' => 'City is required.']
+                    ],
+                    'pincode' => [
+                        'rules' => 'required|numeric|exact_length[6]',
+                        'errors' => [
+                            'required' => 'Pincode is required.',
+                            'numeric'  => 'Pincode must be numeric',
+                            'exact_length' => 'Pincode must be exactly 6 digits'
+                        ]
+                    ],
+                    'gst' => [
+                        'rules'  => 'required|regex_match[/^\d{2}[A-Z]{5}\d{4}[A-Z]{1}[A-Z\d]{1}Z[A-Z\d]{1}$/]',
+                        'errors' => [
+                            'required'    => 'GST number is required',
+                            'regex_match' => 'Invalid GST Number format.'
+                        ]
+                    ],
+                    'name.*' => [
+                        'rules'  => 'required|alpha_space',
+                        'errors' => [
+                            'required'    => 'Contact name is required',
+                            'alpha_space' => 'Contact name may contain only letters and spaces'
+                        ]
+                    ],
+                    'designation.*' => [
+                        'rules'  => 'required|alpha_space',
+                        'errors' => [
+                            'required'    => 'Designation is required',
+                            'alpha_space' => 'Designation may contain only letters and spaces'
+                        ]
+                    ],
+                    'email.*' => [
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                    ],
+                    'mobile.*' => [
+                        'rules'  => 'required|numeric|exact_length[10]',
+                        'errors' => [
+                            'required'      => 'Mobile number is required',
+                            'numeric'       => 'Mobile number must contain digits only',
+                            'exact_length'  => 'Mobile number must be exactly 10 digits'
+                        ]
+                    ],
+                    'units.*' => [
+                        'rules' => 'permit_empty|numeric' // For the multiple select
+                    ],
+                    'sez' => [
+                        'rules' => 'permit_empty' // For the checkbox
+                    ]
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $data   = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($data);
+
+        if (!isset($sanitized_post_data['sez'])) {
+            $sanitized_post_data['sez'] = 0;
+        } elseif ($sanitized_post_data['sez']) {
+            $sanitized_post_data['sez'] = 1;
+        }
+
+        $units = json_decode($sanitized_post_data['units'], true) ?? [];
+
+        if (!is_array($units) || empty($units)) {
+            $client_data = $this->clientModel->where('id', $sanitized_post_data['client_id'])->first();
+            $default_unit = trim(($client_data['short_name'] ?? '') . '-' . ($sanitized_post_data['branch_code'] ?? ''), '-');
+            $sanitized_post_data['units'] = json_encode([$default_unit]);
+        }
+
+        $sanitized_post_data['created_by'] = get_session_userid();
+
+        
+        // before updating check if pre_branch_id is already existing in the current db
+
+        if(isset($sanitized_post_data['pre_branch_id']) && !empty($sanitized_post_data['pre_branch_id']))
+        {
+            $existing_pre_branch = $this->clientBranchModel
+                                    ->where('pre_branch_id',$sanitized_post_data['pre_branch_id'])
+                                    //->where('id !=',$post_branch_id)
+                                    ->first();
+
+            if($existing_pre_branch)
+            {
+                return $this->respond([
+                    'status' => false,
+                    'code' => 409,
+                    'message' => 'The  branch  is already mapped with another branch. Please check.',
+                ], 409);
+            }
+        }
+
+
+
+        $insert = $this->clientBranchModel->insert($sanitized_post_data);
+
+        $post_branch_id = $insert;
+
+        if ($insert) {
+            
+            $level_contact_data_raw = $this->request->getPost('level_contect_data');
+            $level_contact_data = [];
+
+            if (!empty($level_contact_data_raw)) {
+                $level_contact_data_decoded = json_decode($level_contact_data_raw, true);
+
+                if (json_last_error() === JSON_ERROR_NONE && is_array($level_contact_data_decoded)) {
+                    $level_contact_data = sanitizeInputArrayAdvanced($level_contact_data_decoded);
+                }
+            }
+
+            if (!empty($level_contact_data)) {
+                $this->saveLevelContacts($level_contact_data, $insert);
+            }
+
+        }
+
+        if($post_branch_id && isset($sanitized_post_data['pre_branch_id']) && !empty($sanitized_post_data['pre_branch_id']))
+        {
+            // need to update the client_branch in the pre
+            $result = $this->updatePreClientBranch($sanitized_post_data['pre_branch_id'],$post_branch_id , "create");
+
+            log_message('error','Pre client_branch update result for pre_branch_id '.$sanitized_post_data['pre_branch_id'].' and post_branch_id '.$post_branch_id.' is '.json_encode($result));
+        }
+
+        if ($insert) {
+            $branchData = $this->clientBranchModel->where('client_id', $sanitized_post_data['client_id'])->findAll();
+            $branchData['role'] = get_role_id();
+            return $this->respond([
+                'status' => true,
+                'code' => 200,
+                'data' => $branchData,
+                'message' => 'Client branch created successfully',
+            ], 200);
+        } else {
+            return $this->respond([
+                'status' => false,
+                'code' => 404,
+                'message' => 'Failed to create client branch ',
+            ], 200);
+        }
+    }
+
+    public function editClientBranchNew()
+    {
+
+        $this->myLogger->logme('error', 'Client branch EDIT function called');
+        $data = $this->request->getPost();
+    
+        $sanitized_post_data = sanitizeInputArrayAdvanced($data);
+        $id     = $sanitized_post_data['branch_id_primarykey'] ?? null;
+        $client_id     = $sanitized_post_data['client_id'] ?? null;
+        $pre_branch_id = $sanitized_post_data['pre_branch_id'] ?? '';
+
+        $data['pre_branch_id']  = $pre_branch_id;
+
+        $raw_units = $this->request->getPost('units');
+
+        $emp_unit_count = 0;
+        $rr_unit_count  = 0;
+        $rr_unit_count2 = 0;
+        $total_count    = 0;
+
+        $list_of_branch_units = $this->clientBranchModel->find((int)$id);
+        $units = !empty($list_of_branch_units['units']) ? json_decode($list_of_branch_units['units'], true) : [];
+
+        if (!is_array($units) || empty($units)) {
+            $client_data = $this->clientModel->where('id', $sanitized_post_data['client_id'])->first();
+            $default_unit = trim(($client_data['short_name'] ?? '') . '-' . ($sanitized_post_data['branch_code'] ?? ''), '-');
+            $sanitized_post_data['units'] = json_encode([$default_unit]);
+            $units = [$default_unit]; // Update local variable for counting
+        }
+
+        if (!empty($units)) {
+            foreach ($units as $unit) {
+                $emp_unit_count += $this->employeeModel->where('unit', $unit)->countAllResults();
+                $rr_unit_count += $this->policyPremium2Model->where('unit', $unit)->countAllResults();
+                $rr_unit_count2 += $this->policyPremium1Model->where('unit', $unit)->countAllResults();
+            }
+
+            $total_count = $emp_unit_count + $rr_unit_count + $rr_unit_count2;
+        }
+
+        $uncommonValues = [];
+
+
+        if ($total_count > 0) {
+            
+            $post_units_raw = $sanitized_post_data['units'] ?? '[]'; 
+
+            $branch_units = json_decode($list_of_branch_units['units'] ?? '[]', true);
+            
+            // Ensure we handle both string-json and array types
+            $incoming_units = is_array($post_units_raw) ? $post_units_raw : json_decode($post_units_raw, true);
+            
+            if (is_array($branch_units) && is_array($incoming_units)) {
+                $uncommonValues = array_diff($branch_units, $incoming_units);
+            }
+
+            if (count($uncommonValues) > 0) {
+                $branchData = $this->clientBranchModel->where('client_id', $client_id)->findAll();
+
+                return $this->respond([
+                    'status' => false,
+                    'code' => 404,
+                    'message' => "Deleted unit(s) in use. couldn't complete this operation.",
+                    'uncommonValues' => $uncommonValues,
+                    'data' => $branchData,
+                ], 200);
+            }
+        }
+        
+        if (!isset($sanitized_post_data['sez'])) {
+            $sanitized_post_data['sez'] = 0;
+        } elseif ($sanitized_post_data['sez']) {
+            $sanitized_post_data['sez'] = 1;
+        }
+
+        $sanitized_post_data['updated_by'] = get_session_userid();
+
+        $post_branch_id = $id;
+
+        // before updating check if pre_branch_id is already existing in the current db
+
+        if(isset($sanitized_post_data['pre_branch_id']) && !empty($sanitized_post_data['pre_branch_id']))
+        {
+            $existing_pre_branch = $this->clientBranchModel
+                                    ->where('pre_branch_id',$sanitized_post_data['pre_branch_id'])
+                                    ->where('id !=',$post_branch_id)
+                                    ->first();
+
+            if($existing_pre_branch)
+            {
+                return $this->respond([
+                    'status' => false,
+                    'code' => 409,
+                    'message' => 'The  branch  is already mapped with another branch. Please check.',
+                ], 409);
+            }
+        }
+
+        $insert = $this->clientBranchModel->update($id, $sanitized_post_data);
+
+
+
+        if($post_branch_id && isset($sanitized_post_data['pre_branch_id']) && !empty($sanitized_post_data['pre_branch_id']))
+        {
+            // need to update the client_branch in the pre
+            $result = $this->updatePreClientBranch($sanitized_post_data['pre_branch_id'],$post_branch_id , "update");
+
+            log_message('error','Pre client_branch update result for pre_branch_id '.$sanitized_post_data['pre_branch_id'].' and post_branch_id '.$post_branch_id.' is '.json_encode($result));
+        }
+
+
+        $this->myLogger->logme('error', 'Client branch EDITED by {data}', ['data' => get_session_userid()]);
+
+
+        if ($insert) {
+            
+            $level_contact_data_raw = $this->request->getPost('level_contect_data');
+            $level_contact_data = [];
+
+            if (!empty($level_contact_data_raw)) {
+                $level_contact_data_decoded = json_decode($level_contact_data_raw ?? '{}', true);
+
+                    $level_contact_data = sanitizeInputArrayAdvanced($level_contact_data_decoded);
+            }
+
+            if (!empty($level_contact_data)) {
+                $this->saveLevelContacts($level_contact_data, $insert);
+            }
+
+        }
+
+        if ($insert) {
+            $branchData = $this->clientBranchModel->where('client_id', $client_id)->findAll();
+            return $this->respond([
+                'status' => true,
+                'code' => 200,
+                'data' => $branchData,
+                'emp_unit_count' => $emp_unit_count,
+                'rr_unit_count' => $rr_unit_count,
+                'list_of_branch_units' => $list_of_branch_units,
+                'total_count' => $total_count,
+                'uncommonValues' => $uncommonValues,
+                'message' => 'Client branch updated successfully',
+                'response_data' => $sanitized_post_data
+
+            ], 200);
+        } else {
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to update client branch'], 200);
+        }
+    }
+
+    public function saveLevelContactsNew($level_contact_data, $branch_id)
+    {
+        if (!empty($level_contact_data) && is_array($level_contact_data)) {
+            foreach ($level_contact_data as $value) {
+                if (!empty($value['id'])) {
+                    $id = $value['id'];
+                    unset($value['id']);
+                    $this->levelContactModel->update($id, $value);
+                } else {
+                    unset($value['id']);
+                    $value['contact_type'] = "client";
+                    $value['ref_id'] = $branch_id ?? null;
+                    $this->levelContactModel->insert($value);
+                }
+            }
+        }
+    }
+
+    public function removeLevelContactsNew()
+    {
+        $id = $this->request->getGet('id');
+        try {
+            if (empty($id)) {
+                return $this->respond([
+                    'status'  => false,
+                    'message' => 'Invalid ID provided. ID is empty',
+                    'data' => $id
+                ], 400);
+            }
+
+            $updated = $this->levelContactModel->update($id, ['is_active' => 0]);
+
+            if ($updated === false) {
+                return $this->respond([
+                    'status'  => false,
+                    'message' => 'Failed to remove contact'
+                ], 500);
+            }
+
+            return $this->respond([
+                'status'  => true,
+                'message' => 'Contact removed successfully'
+            ]);
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status'  => false,
+                'message' => 'An error occurred: ' . $e->getMessage()
+            ], 500);
+        }
+    }
+
+
+    
     public function createClientBranch()
     {
 
         $this->myLogger->logme('error', 'Client branch CREATE function called');
-        $data   = $this->request->getPost();
+
+        $rules = [
+            'branch_name' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required' => 'Branch Name is required',
+                ]
+            ],
+            'branch_code' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required' => 'Branch Code is required',
+                ]
+            ],
+            'address1' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required' => 'Address Line 1 is required',
+                ]
+            ],
+            'state' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required' => 'State is required',
+                ]
+            ],
+            'district' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required'    => 'District is required.',
+                ]
+            ],
+            'city' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required'    => 'City is required.',
+                ]
+            ],
+            'pincode' => [
+                'rules' => 'required',
+                'errors' => [
+                    'required'    => 'Pincode is required.',
+                ]
+            ],
+            'gst' => [
+                'rules'  => 'required|regex_match[/^\d{2}[A-Z]{5}\d{4}[A-Z]{1}[A-Z\d]{1}Z[A-Z\d]{1}$/]',
+                'errors' => [
+                    'required'    => 'GST number is required',
+                    'regex_match' => 'Invalid GST Number. Example: 12ABCDE1234F5Z6'
+                ]
+            ],
+            'name.*' => [
+                'rules'  => 'required|alpha_space',
+                'errors' => [
+                    'required'    => 'Contact name is required',
+                    'alpha_space' => 'Contact name may contain only letters and spaces'
+                ]
+            ],
+            'designation.*' => [
+                'rules'  => 'required|alpha_space',
+                'errors' => [
+                    'required'    => 'Designation is required',
+                    'alpha_space' => 'Designation may contain only letters and spaces'
+                ]
+            ],
+            'email.*' => [
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+            ],
+            'mobile.*' => [
+                'rules'  => 'required|numeric|exact_length[10]',
+                'errors' => [
+                    'required'      => 'Mobile number is required',
+                    'numeric'       => 'Mobile number must contain digits only',
+                    'exact_length'  => 'Mobile number must be exactly 10 digits'
+                ]
+            ],
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $data = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($data);
+
 
         if (!isset($data['sez'])) {
             $data['sez'] = 0;
@@ -1361,6 +2295,7 @@ class ClientController extends AdminController
         $pre_branch_id = $this->request->getPost('pre_branch_id') ?? '';
 
         $data   = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($data);
         $data['pre_branch_id']  = $pre_branch_id;
 
         $units   = $this->request->getPost('units');
@@ -1370,7 +2305,7 @@ class ClientController extends AdminController
         $rr_unit_count2 = 0;
         $total_count    = 0;
 
-        $list_of_branch_units = $this->clientBranchModel->find($id);
+        $list_of_branch_units = $this->clientBranchModel->find((int)$id);
         $units = json_decode($list_of_branch_units['units']);
 
         if (!is_array($units) || empty($units)) {
@@ -1394,7 +2329,7 @@ class ClientController extends AdminController
         if ($total_count > 0) {
             $units = (string) $this->request->getPost('units'); // Assuming 'units' is an array
 
-            $list_of_branch_units = $this->clientBranchModel->find($id);
+            $list_of_branch_units = $this->clientBranchModel->find((int)$id);
             $branch_units = json_decode($list_of_branch_units['units'], true);
             $units = json_decode($units);
 
@@ -1538,8 +2473,7 @@ class ClientController extends AdminController
     }
 
 
-
-
+    /** here ci4 rules not implemented, because UI screen Fields are hide/show implemented thats why */
     public function createClientPolicy()
     {
 
@@ -1548,20 +2482,109 @@ class ClientController extends AdminController
 
         $this->myLogger->logme('error', 'Client policy CREATE function called');
 
-        $policy_type_id   = $this->request->getPost('policy_type_id');
-        $client_branch_id = $this->request->getPost('client_branch_id');
-        $client_id        = $this->request->getPost('client_id');
-        $base_policy      = $this->request->getPost('base_policy');
+        $request_post_data = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
+
+        $rules = [
+            // --- BASIC SELECTS (Required) ---
+            'client_branch_id' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select a Client Branch.']
+            ],
+            'policy_type_id' => [
+                'rules'  => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required'            => 'Policy Type is required.',
+                    'is_natural_no_zero'  => 'Please select a valid Policy Type.'
+                ]
+            ],
+            'insurer' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select an Insurer.']
+            ],
+            'tpa' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select a TPA.']
+            ],
+
+            // --- TEXT FIELDS (Required & Specific Format) ---
+            'policy_no' => [
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9\s_\-\/\\\]+$/]',
+                'errors' => [
+                    'required'    => 'This field is required.',
+                    'regex_match' => 'Only letters, numbers, spaces, _, -, /, and \ are allowed.'
+                ]
+            ],
+            'gst' => [
+                'rules'  => 'required|numeric|greater_than_equal_to[0]|less_than_equal_to[100]',
+                'errors' => [
+                    'required'              => 'GST percentage is required.',
+                    'numeric'               => 'GST must be a valid number.',
+                    'greater_than_equal_to' => 'GST cannot be negative.',
+                    'less_than_equal_to'    => 'GST percentage cannot exceed 100%.'
+                ]
+            ],
+
+            // --- DATE FIELDS (Required) ---
+            'policy_start_date' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required'   => 'Start date is required.',
+                ]
+            ],
+            'policy_end_date' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required'   => 'End date is required.',
+                ]
+            ],
+
+            // --- PERMIT EMPTY FIELDS (Optional) ---
+            'base_policy' => [
+                'rules' => 'permit_empty'
+            ],
+            'wellness_plan_id' => [
+                'rules'  => 'permit_empty|alpha_numeric',
+                'errors' => [
+                    'alpha_numeric' => 'Wellness Plan ID can only contain letters and numbers (no spaces or special characters).'
+                ]
+            ],
+            'wellness_vendor_id' => [
+                'rules' => 'permit_empty'
+            ],
+            'disclaimer' => [
+                'rules'  => 'permit_empty|string|min_length[5]',
+                'errors' => ['min_length' => 'Disclaimer should be at least 5 characters long if provided.']
+            ],
+
+            // --- CHECKBOXES (Permit Empty) ---
+            'enrolment_visibility' => ['rules' => 'permit_empty'],
+            'is_lgbtq'             => ['rules' => 'permit_empty']
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $policy_type_id   = $sanitized_post_data['policy_type_id'] ?? null;
+        $client_branch_id = $sanitized_post_data['client_branch_id'] ?? null;
+        $client_id        = $sanitized_post_data['client_id'] ?? null;
+        $base_policy      = $sanitized_post_data['base_policy'] ?? null;
 
 
-        $insurerValue  = (string) $this->request->getPost('insurer');
+        $insurerValue  = (string) $sanitized_post_data['insurer'] ?? null;
         list($insurerBranchId, $insurerId) = explode('-', $insurerValue);
 
 
-        $data['insurer_branch_id'] = $insurerBranchId;
-        $data['insurer_id']        = $insurerId;
+        $sanitized_post_data['insurer_branch_id'] = $insurerBranchId;
+        $sanitized_post_data['insurer_id']        = $insurerId;
 
-        $tpaValue = (string) $this->request->getPost('tpa');
+        $tpaValue = (string) $sanitized_post_data['tpa'] ?? null;
 
         if ($tpaValue === null || $tpaValue === '') {
             $tpaBranchId = null;
@@ -1571,98 +2594,185 @@ class ClientController extends AdminController
         }
 
 
-        $data['client_id']         = $client_id;
-        $data['tpa_branch_id']     = $tpaBranchId;
-        $data['tpa_id']            = $tpaId;
-        $data['policy_type_id']    = $this->request->getPost('policy_type_id');
+        $sanitized_post_data['client_id']         = $client_id;
+        $sanitized_post_data['tpa_branch_id']     = $tpaBranchId;
+        $sanitized_post_data['tpa_id']            = $tpaId;
+        $sanitized_post_data['policy_type_id']    = $sanitized_post_data['policy_type_id'] ?? null;
 
-        $data['no_of_lives']                        = $this->request->getPost('no_of_lives');
-        $data['policy_status']                      = $this->request->getPost('policy_status');
-        $data['no_of_employees']                    = $this->request->getPost('no_of_employees');
-        $data['earned_premium_date']                = change_date_format($this->request->getPost('earned_premium_date'), 'd-m-Y', 'Y-m-d')  ?? null;
-        $data['claims_incurred_date']               = change_date_format($this->request->getPost('claims_incurred_date'), 'd-m-Y', 'Y-m-d') ?? null;
-        $data['incurred_claims_ratio']              = $this->request->getPost('incurred_claims_ratio');
-        $data['no_lives_at_inception']              = $this->request->getPost('no_lives_at_inception');
-        $data['premium_paid_at_inception']          = $this->request->getPost('premium_paid_at_inception');
-        $data['claims_experience_for_last_3_years'] = $this->request->getPost('claims_experience_for_last_3_years');
-        $data['earned_premium_amount']              = $this->request->getPost('earned_premium_amount');
-        $data['claims_incurred_amount']             = $this->request->getPost('claims_incurred_amount');
-        $data['base_policy']                        = ($this->request->getPost('base_policy') === '' || $this->request->getPost('base_policy') == 0) ? null : $this->request->getPost('base_policy');
-        $data['policy_status']                      = 1;
-        $data['inception_type']                     = $this->request->getPost('inception_type') ? 2 : 1;
-        $data['client_branch_id']                   = $this->request->getPost('client_branch_id');
-        $data['cd_ac_pk']                           = $this->request->getPost('cd_ac_no');
-        $data['gst']                                = $this->request->getPost('gst');
-        $data['disclaimer']                         = $this->request->getPost('disclaimer');
-        $data['is_member_modify_allowed']           = $this->request->getPost('is_member_modify_allowed') ? 1 : 0;
-        $data['enrolment_visibility']               = $this->request->getPost('enrolment_visibility') ? 1 : 0;
-        $data['is_lgbtq']                           = $this->request->getPost('is_lgbtq') ? 1 : 0;
-        $data['wellness_plan_id']                   = $this->request->getPost('wellness_plan_id');
-        $data['wellness_vendor_id']                 = $this->request->getPost('wellness_vendor_id');
-        $data['wellness_vendor_id']                 = !empty($data['wellness_vendor_id']) ? $data['wellness_vendor_id'] : null;
+        
+        $sanitized_post_data['earned_premium_date']                = change_date_format($sanitized_post_data['earned_premium_date']  ?? null, 'd-m-Y', 'Y-m-d')  ?? null;
+        $sanitized_post_data['claims_incurred_date']               = change_date_format($sanitized_post_data['claims_incurred_date']  ?? null, 'd-m-Y', 'Y-m-d') ?? null;
+        
+        $sanitized_post_data['base_policy']                        = ($sanitized_post_data['base_policy'] === '' || $sanitized_post_data['base_policy'] == 0) ? null : $sanitized_post_data['base_policy'];
+        $sanitized_post_data['policy_status']                       = 1;
+        $sanitized_post_data['inception_type']                       = (isset($sanitized_post_data['inception_type']) && $sanitized_post_data['inception_type'] !== "")
+                                                                        ? $sanitized_post_data['inception_type']
+                                                                        : 1;
+
+        
+        
+        
+        $sanitized_post_data['is_member_modify_allowed']           = $sanitized_post_data['is_member_modify_allowed'] ?? null ? 1 : 0;
+        $sanitized_post_data['enrolment_visibility']               = $sanitized_post_data['enrolment_visibility'] ?? null ? 1 : 0;
+        $sanitized_post_data['is_lgbtq']                           = $sanitized_post_data['is_lgbtq'] ?? null ? 1 : 0;
+        $sanitized_post_data['wellness_vendor_id']                 = !empty($sanitized_post_data['wellness_vendor_id']) ? $sanitized_post_data['wellness_vendor_id'] : null;
+        $sanitized_post_data['cd_ac_pk']                           = $sanitized_post_data['cd_ac_no']  ?? null;
 
 
         if ($policy_type_id == 1 || $policy_type_id == 2 || $policy_type_id == 6 || $policy_type_id == 7) {
 
-            $data['is_addon'] = 1; // Base Policy
+            $sanitized_post_data['is_addon'] = 1; // Base Policy
 
         } else if ($policy_type_id == 4 || $policy_type_id == 5) {
 
-            $data['is_addon'] = 2; // SI TOPUP
+            $sanitized_post_data['is_addon'] = 2; // SI TOPUP
 
         } else if ($policy_type_id == 3) {
 
             if ($base_policy) {
-                $data['is_addon'] = 3; // Dependent Addon
+                $sanitized_post_data['is_addon'] = 3; // Dependent Addon
             } else {
-                $data['is_addon'] = 1;
+                $sanitized_post_data['is_addon'] = 1;
             }
         }
 
 
-        $data['policy_start_date'] = change_date_format($this->request->getPost('policy_start_date'), 'd-m-Y', 'Y-m-d');
-        $data['policy_end_date']   = change_date_format($this->request->getPost('policy_end_date'), 'd-m-Y', 'Y-m-d');
-        $data['policy_no'] = $this->request->getPost('policy_no');
-        if ($data['inception_type'] == 2) {
-            $data['open_date'] = change_date_format($this->request->getPost('open_date'), 'd-m-Y', 'Y-m-d');
-            $data['close_date']   = change_date_format($this->request->getPost('close_date'), 'd-m-Y', 'Y-m-d');
+        $sanitized_post_data['policy_start_date'] = change_date_format($sanitized_post_data['policy_start_date'] ?? null, 'd-m-Y', 'Y-m-d');
+        $sanitized_post_data['policy_end_date']   = change_date_format($sanitized_post_data['policy_end_date'] ?? null, 'd-m-Y', 'Y-m-d');
+        $sanitized_post_data['policy_no'] = $sanitized_post_data['policy_no'] ?? null;
+        if (isset($sanitized_post_data['inception_type']) && $sanitized_post_data['inception_type'] == 2) {
+            $sanitized_post_data['open_date'] = change_date_format($sanitized_post_data['open_date'] ?? null, 'd-m-Y', 'Y-m-d');
+            $sanitized_post_data['close_date']   = change_date_format($sanitized_post_data['close_date'] ?? null, 'd-m-Y', 'Y-m-d');
             // $data['reminder_date']   = change_date_format($this->request->getPost('reminder_date'), 'd-m-Y', 'Y-m-d');
-            $data['reminder_date']   = $this->request->getPost('reminder_date');
+            $sanitized_post_data['reminder_date']   = $sanitized_post_data['reminder_date'] ?? null;
         } else {
-            $data['open_date'] = null;
-            $data['closedate'] = null;
-            $data['reminder_date'] = null;
+            $sanitized_post_data['open_date'] = null;
+            $sanitized_post_data['closedate'] = null;
+            $sanitized_post_data['reminder_date'] = null;
         }
 
-        $data['created_by']  = get_session_userid();
+        $sanitized_post_data['created_by']  = get_session_userid();
 
-        $insert = $this->clientPolicyModel->insert($data);
+        $insert = $this->clientPolicyModel->insert($sanitized_post_data);
         if ($insert) {
-            $clientPoliceData =  $this->clientPolicyModel->getClientPolicyByClientId($this->request->getPost('client_id'));
+            // this function to create a policy transaction entry at the time of client policy create
+            $this->createPolicyTransactionInceptionEntry($insert, $sanitized_post_data);
+            $clientPoliceData =  $this->clientPolicyModel->getClientPolicyByClientId($sanitized_post_data['client_id']);
             $clientPoliceData['role'] = get_role_id();
-            return $this->respond(['status' => true, 'code' => 200, 'data' => $clientPoliceData, 'method' => 'CERATE', 'post_data' => $data], 200);
+            return $this->respond(['status' => true, 'code' => 200, 'data' => $clientPoliceData, 'client_id' => $client_id, 'method' => 'CERATE', 'post_data' => $insert], 200);
         } else {
             return $this->respond(['status' => false, 'code' => 404, 'message' => 'no data found'], 200);
         }
     }
 
+    /** here ci4 rules not implemented, because UI screen Fields are hide/show implemented thats why */
     public function editClientPolicy()
     {
+        $rules = [
+            // --- BASIC SELECTS (Required) ---
+            'client_branch_id' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select a Client Branch.']
+            ],
+            'policy_type_id' => [
+                'rules'  => 'required|is_natural_no_zero',
+                'errors' => [
+                    'required'            => 'Policy Type is required.',
+                    'is_natural_no_zero'  => 'Please select a valid Policy Type.'
+                ]
+            ],
+            'insurer' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select an Insurer.']
+            ],
+            'tpa' => [
+                'rules'  => 'required',
+                'errors' => ['required' => 'Please select a TPA.']
+            ],
+
+            // --- TEXT FIELDS (Required & Specific Format) ---
+            'policy_no' => [
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9\s_\-\/\\\]+$/]',
+                'errors' => [
+                    'required'    => 'This field is required.',
+                    'regex_match' => 'Only letters, numbers, spaces, _, -, /, and \ are allowed.'
+                ]
+            ],
+            'gst' => [
+                'rules'  => 'required|numeric|greater_than_equal_to[0]|less_than_equal_to[100]',
+                'errors' => [
+                    'required'              => 'GST percentage is required.',
+                    'numeric'               => 'GST must be a valid number.',
+                    'greater_than_equal_to' => 'GST cannot be negative.',
+                    'less_than_equal_to'    => 'GST percentage cannot exceed 100%.'
+                ]
+            ],
+
+            // --- DATE FIELDS (Required) ---
+            'policy_start_date' => [
+                'rules'  => 'required|valid_date',
+                'errors' => [
+                    'required'   => 'Start date is required.',
+                    'valid_date' => 'Enter a valid date format (YYYY-MM-DD).'
+                ]
+            ],
+            'policy_end_date' => [
+                'rules'  => 'required|valid_date',
+                'errors' => [
+                    'required'   => 'End date is required.',
+                    'valid_date' => 'Enter a valid date format (YYYY-MM-DD).'
+                ]
+            ],
+
+            // --- PERMIT EMPTY FIELDS (Optional) ---
+            'base_policy' => [
+                'rules' => 'permit_empty'
+            ],
+            'wellness_plan_id' => [
+                'rules'  => 'permit_empty|alpha_numeric',
+                'errors' => [
+                    'alpha_numeric' => 'Wellness Plan ID can only contain letters and numbers (no spaces or special characters).'
+                ]
+            ],
+            'wellness_vendor_id' => [
+                'rules' => 'permit_empty'
+            ],
+            'disclaimer' => [
+                'rules'  => 'permit_empty|string|min_length[5]',
+                'errors' => ['min_length' => 'Disclaimer should be at least 5 characters long if provided.']
+            ],
+
+            // --- CHECKBOXES (Permit Empty) ---
+            'enrolment_visibility' => ['rules' => 'permit_empty'],
+            'is_lgbtq'             => ['rules' => 'permit_empty']
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
         $this->myLogger->logme('error', 'Client policy function called');
+        $data = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($data);
 
-        $id             = $this->request->getPost('PrimaryKey');
-        $client_id      = $this->request->getPost('client_id');
-        $policy_type_id = $this->request->getPost('policy_type_id');
-        $base_policy    = $this->request->getPost('base_policy');
+        $id             = $sanitized_post_data['PrimaryKey'] ?? null;
+        $client_id      = $sanitized_post_data['client_id'] ?? null;
+        $policy_type_id = $sanitized_post_data['policy_type_id'] ?? null;
+        $base_policy    = $sanitized_post_data['base_policy'] ?? null;
 
 
-        $insurerValue                      = (string) $this->request->getPost('insurer');
+        $insurerValue                      = (string) $sanitized_post_data['insurer'];
         list($insurerBranchId, $insurerId) = explode('-', $insurerValue);
 
-        $data['insurer_branch_id'] = $insurerBranchId;
-        $data['insurer_id']        = $insurerId;
+        $sanitized_post_data['insurer_branch_id'] = $insurerBranchId ?? null;
+        $sanitized_post_data['insurer_id']        = $insurerId ?? null;
 
-        $tpaValue                  = (string) $this->request->getPost('tpa');
+        $tpaValue                  = (string) $sanitized_post_data['tpa'] ?? null;
         if (!empty($tpaValue) || $tpaValue !== '') {
             list($tpaBranchId, $tpaId) = explode('-', $tpaValue);
         } else {
@@ -1670,75 +2780,75 @@ class ClientController extends AdminController
             $tpaId = null;
         }
 
-        $data['tpa_branch_id']     = $tpaBranchId;
-        $data['client_id']         = $client_id;
-        $data['tpa_id']            = $tpaId;
-        $data['policy_type_id']    = $this->request->getPost('policy_type_id');
-        $data['policy_no']         = $this->request->getPost('policy_no');
+        $sanitized_post_data['tpa_branch_id']     = $tpaBranchId ?? null;
+        $sanitized_post_data['client_id']         = $client_id ?? null;
+        $sanitized_post_data['tpa_id']            = $tpaId ?? null;
+        $sanitized_post_data['policy_type_id']    = $sanitized_post_data['policy_type_id'] ?? null;
+        $sanitized_post_data['policy_no']         = $sanitized_post_data['policy_no'] ?? null;
 
-        $data['insured']                            = $this->request->getPost('insured');
-        $data['no_of_lives']                        = $this->request->getPost('no_of_lives');
-        $data['policy_status']                      = $this->request->getPost('policy_status');
-        $data['no_of_employees']                    = $this->request->getPost('no_of_employees');
-        $data['earned_premium_date']                = change_date_format($this->request->getPost('earned_premium_date'), 'd-m-Y', 'Y-m-d') ?? null;
-        $data['claims_incurred_date']               = change_date_format($this->request->getPost('claims_incurred_date'), 'd-m-Y', 'Y-m-d') ?? null;
-        $data['incurred_claims_ratio']              = $this->request->getPost('incurred_claims_ratio');
-        $data['no_lives_at_inception']              = $this->request->getPost('no_lives_at_inception');
-        $data['premium_paid_at_inception']          = $this->request->getPost('premium_paid_at_inception');
-        $data['claims_experience_for_last_3_years'] = $this->request->getPost('claims_experience_for_last_3_years');
-        $data['earned_premium_amount']              = $this->request->getPost('earned_premium_amount');
-        $data['claims_incurred_amount']             = $this->request->getPost('claims_incurred_amount');
-        $data['base_policy']                        = ($this->request->getPost('base_policy') === '' || $this->request->getPost('base_policy') == 0) ? null : $this->request->getPost('base_policy');
-        $data['policy_status']                      = 1;
-        $data['inception_type']                     = $this->request->getPost('inception_type') ? 2 : 1;
-        $data['enrolment_visibility']               = $this->request->getPost('enrolment_visibility') ? 1 : 0;
-        $data['client_branch_id']                   = $this->request->getPost('client_branch_id');
-        $data['cd_ac_pk']                           = $this->request->getPost('cd_ac_no');
-        $data['gst']                                = $this->request->getPost('gst');
-        $data['disclaimer']                         = $this->request->getPost('disclaimer');
-        $data['policy_start_date']                  = change_date_format($this->request->getPost('policy_start_date'), 'd-m-Y', 'Y-m-d');
-        $data['policy_end_date']                    = change_date_format($this->request->getPost('policy_end_date'), 'd-m-Y', 'Y-m-d');
-        $data['is_member_modify_allowed']           = $this->request->getPost('is_member_modify_allowed') ? 1 : 0;
-        $data['is_lgbtq']                           = $this->request->getPost('is_lgbtq') ? 1 : 0;
-        $data['wellness_plan_id']                   = $this->request->getPost('wellness_plan_id');
-        $data['wellness_vendor_id']                 = $this->request->getPost('wellness_vendor_id');
-        $data['wellness_vendor_id']                 = !empty($data['wellness_vendor_id']) ? $data['wellness_vendor_id'] : null;
+        // $sanitized_post_data['insured']                            = $sanitized_post_data['insured'];
+        $sanitized_post_data['no_of_lives']                        = $sanitized_post_data['no_of_lives']  ?? null;
+        $sanitized_post_data['policy_status']                      = $sanitized_post_data['policy_status']  ?? null;
+        $sanitized_post_data['no_of_employees']                    = $sanitized_post_data['no_of_employees']  ?? null;
+        $sanitized_post_data['earned_premium_date']                = change_date_format($sanitized_post_data['earned_premium_date'] ?? null, 'd-m-Y', 'Y-m-d') ?? null;
+        $sanitized_post_data['claims_incurred_date']               = change_date_format($sanitized_post_data['claims_incurred_date']  ?? null, 'd-m-Y', 'Y-m-d') ?? null;
+        $sanitized_post_data['incurred_claims_ratio']              = $sanitized_post_data['incurred_claims_ratio']  ?? null;
+        $sanitized_post_data['no_lives_at_inception']              = $sanitized_post_data['no_lives_at_inception']  ?? null;
+        $sanitized_post_data['premium_paid_at_inception']          = $sanitized_post_data['premium_paid_at_inception']  ?? null;
+        $sanitized_post_data['claims_experience_for_last_3_years'] = $sanitized_post_data['claims_experience_for_last_3_years']  ?? null;
+        $sanitized_post_data['earned_premium_amount']              = $sanitized_post_data['earned_premium_amount']  ?? null;
+        $sanitized_post_data['claims_incurred_amount']             = $sanitized_post_data['claims_incurred_amount']  ?? null;
+        $sanitized_post_data['base_policy']                        = ($sanitized_post_data['base_policy'] === '' || $sanitized_post_data['base_policy'] == 0) ? null : $sanitized_post_data['base_policy'];
+        $sanitized_post_data['policy_status']                      = 1;
+        $sanitized_post_data['inception_type']                     = $sanitized_post_data['inception_type'] ?? null ? 2 : 1;
+        $sanitized_post_data['enrolment_visibility']               = $sanitized_post_data['enrolment_visibility'] ?? null ? 1 : 0;
+        $sanitized_post_data['client_branch_id']                   = $sanitized_post_data['client_branch_id']  ?? null;
+        $sanitized_post_data['cd_ac_pk']                           = $sanitized_post_data['cd_ac_no']  ?? null;
+        $sanitized_post_data['gst']                                = $sanitized_post_data['gst']  ?? null;
+        $sanitized_post_data['disclaimer']                         = $sanitized_post_data['disclaimer']  ?? null;
+        $sanitized_post_data['policy_start_date']                  = change_date_format($sanitized_post_data['policy_start_date']  ?? null, 'd-m-Y', 'Y-m-d');
+        $sanitized_post_data['policy_end_date']                    = change_date_format($sanitized_post_data['policy_end_date']  ?? null, 'd-m-Y', 'Y-m-d');
+        $sanitized_post_data['is_member_modify_allowed']           = $sanitized_post_data['is_member_modify_allowed'] ?? null ? 1 : 0;
+        $sanitized_post_data['is_lgbtq']                           = $sanitized_post_data['is_lgbtq'] ?? null ? 1 : 0;
+        $sanitized_post_data['wellness_plan_id']                   = $sanitized_post_data['wellness_plan_id']  ?? null;
+        $sanitized_post_data['wellness_vendor_id']                 = $sanitized_post_data['wellness_vendor_id']  ?? null;
+        $sanitized_post_data['wellness_vendor_id']                 = !empty($sanitized_post_data['wellness_vendor_id']) ? $sanitized_post_data['wellness_vendor_id'] : null;
 
-        if ($data['inception_type'] == 2) {
-            $data['open_date'] = change_date_format($this->request->getPost('open_date'), 'd-m-Y', 'Y-m-d');
-            $data['close_date']   = change_date_format($this->request->getPost('close_date'), 'd-m-Y', 'Y-m-d');
-            // $data['reminder_date']   = change_date_format($this->request->getPost('reminder_date'), 'd-m-Y', 'Y-m-d');
-            $data['reminder_date']   = $this->request->getPost('reminder_date');
+        if ($sanitized_post_data['inception_type'] == 2) {
+            $sanitized_post_data['open_date'] = change_date_format($sanitized_post_data['open_date'] ?? null, 'd-m-Y', 'Y-m-d');
+            $sanitized_post_data['close_date']   = change_date_format($sanitized_post_data['close_date'] ?? null, 'd-m-Y', 'Y-m-d');
+            // $sanitized_post_data['reminder_date']   = change_date_format($sanitized_post_data['reminder_date'), 'd-m-Y', 'Y-m-d');
+            $sanitized_post_data['reminder_date']   = $sanitized_post_data['reminder_date']  ?? null;
         } else {
-            $data['open_date'] = null;
-            $data['close_date'] = null;
-            $data['reminder_date'] = null;
+            $sanitized_post_data['open_date'] = null;
+            $sanitized_post_data['close_date'] = null;
+            $sanitized_post_data['reminder_date'] = null;
         }
 
         if ($policy_type_id == 1 || $policy_type_id == 2 || $policy_type_id == 6 || $policy_type_id == 7) {
 
-            $data['is_addon'] = 1; // Base Policy
+            $sanitized_post_data['is_addon'] = 1; // Base Policy
 
         } else if ($policy_type_id == 4 || $policy_type_id == 5) {
 
-            $data['is_addon'] = 2; // SI TOPUP
+            $sanitized_post_data['is_addon'] = 2; // SI TOPUP
 
         } else if ($policy_type_id == 3) {
 
             if ($base_policy) {
-                $data['is_addon'] = 3; // Dependent Addon
+                $sanitized_post_data['is_addon'] = 3; // Dependent Addon
             } else {
-                $data['is_addon'] = 1;
+                $sanitized_post_data['is_addon'] = 1;
             }
         }
 
-        $policy_terms =  $this->clientPolicyModel->where('id', $this->request->getPost('base_policy'))->first();
+        $policy_terms =  $this->clientPolicyModel->where('id', $sanitized_post_data['base_policy'])->first();
 
         $old_client_policy_data = $this->clientPolicyModel->where('is_active', 1)->where('id', $id)->first();
-        $data['updated_by'] = get_session_userid();
-        $insert = $this->clientPolicyModel->update($id, $data);
+        $sanitized_post_data['updated_by'] = get_session_userid();
+        $update = $this->clientPolicyModel->update($id, $sanitized_post_data);
 
-        if ($insert) {
+        if ($update) {
 
             $new_client_policy_data = $this->clientPolicyModel->where('is_active', 1)->where('id', $id)->first();
             $policy_transaction_data = $this->policyTransactionModel->where('is_active', 1)->where('client_policy_id', $id)->countAllResults();
@@ -1752,7 +2862,7 @@ class ClientController extends AdminController
                 ]]);
             }
 
-            return $this->respond(['status' => true, 'code' => 200, 'data' => $clientPoliceData, 'method' => 'EDIT'], 200);
+            return $this->respond(['status' => true, 'code' => 200, 'data' => $clientPoliceData, 'client_id' => $client_id, 'method' => 'EDIT'], 200);
         } else {
             return $this->respond(['status' => false, 'code' => 404, 'message' => 'no data found'], 200);
         }
@@ -1819,436 +2929,177 @@ class ClientController extends AdminController
         }
     }
 
+    public function createPolicyTransactionInceptionEntry($client_policy_id, $data)
+    {   
+        $data['action_type'] = 'inception';
+        $data['client_policy_id'] = $client_policy_id;
+        $data['entry_from'] = 3;
+        $data['issuer_branch'] = 1;
+        $data['issuer'] = 2;
+        $data['status'] = 'completed';
+        $data['renewal_date'] = $data['policy_end_date'];
+        $data['bro_payable_by'] = 1;
+        $insert = $this->policyTransactionModel->insert($data);
 
+        if($insert){
+
+            $pcsd_data['pt_id'] = $insert;
+            $pcsd_data['insurer_id'] = $data['insurer_id'] ?? null;
+            $pcsd_data['insurer_branch_id'] = $data['insurer_branch_id'] ?? null;
+
+            $this->PTCOShareDetailsModel->insert($pcsd_data);
+        }
+        return $insert;
+    }
 
     // Save Rack Rate function
     public function createClientPolicyPremium()
     {
-
-        // echo json_encode(['key' => $this->request->getPost()]); die;
-
         try {
-            $client_id = $this->request->getPost('client_id');
-            $client_policy_id = $this->request->getPost('client_policy_id');
-            $record    = $this->clientPolicyModel->where('client_policy.id', $client_policy_id)->first();
-            $premium_type = $this->request->getPost('premium_type');
-            if (!empty($client_id) && $client_id != null) {
-                $client_policy_data = $this->clientPolicyModel->where('id', $client_policy_id)->first();
-                $client_id =  $client_policy_data['client_id'];
+            // 1. Fetch and Sanitize all input at once
+            $rawPost = $this->request->getPost();
+            $data = sanitizeInputArrayAdvanced($rawPost);
+
+            // Extract base variables from the sanitized array
+            $client_id        = $data['client_id'] ?? null;
+            $client_policy_id = $data['client_policy_id'] ?? null;
+            $premium_type     = $data['premium_type'] ?? null;
+            $policy_grid_id   = $data['policy_grid_id'] ?? null;
+            $rack_rate_name   = $data['rack_rate_name'] ?? null;
+            $si_or_bp         = $data['si_or_bp'] ?? null;
+
+            $record = $this->clientPolicyModel->where('id', $client_policy_id)->first();
+            if (!$record) {
+                return $this->respond(['status' => false, 'message' => 'Policy record not found'], 404);
             }
 
-            $branch_units = $this->getBranchUnitsByBranchId($record['client_branch_id']);
-            $branch_units = json_decode($branch_units);
+            if (empty($client_id)) {
+                $client_id = $record['client_id'];
+            }
 
-            $policy_grid_id = $this->request->getPost('policy_grid_id');
-            $rack_rate_name = $this->request->getPost('rack_rate_name');
+            $branch_units_raw = $this->getBranchUnitsByBranchId($record['client_branch_id']);
+            $branch_units = json_decode($branch_units_raw, true);
+            $default_unit = !empty($branch_units) ? $branch_units[0] : null;
 
+            // Relationship Logic
             $relation_data = [
-                'self'          => $this->request->getPost('self') ?? 'NA',
-                'spouse'        => $this->request->getPost('spouse') ?? 'NA',
-                'childrens'     => $this->request->getPost('childrens') ?? 'NA',
-                'parents'       => $this->request->getPost('parents') ?? 'NA',
-                'parents-in-law' => $this->request->getPost('parents-in-law') ?? 'NA',
+                'self'           => $data['self'] ?? 'NA',
+                'spouse'         => $data['spouse'] ?? 'NA',
+                'childrens'      => $data['childrens'] ?? 'NA',
+                'parents'        => $data['parents'] ?? 'NA',
+                'parents-in-law' => $data['parents-in-law'] ?? 'NA',
             ];
 
-            $relation_data_for_form_submit_check = [
-                $rack_rate_name => [
-                    'self'          => $this->request->getPost('self') ?? 'NA',
-                    'spouse'        => $this->request->getPost('spouse') ?? 'NA',
-                    'childrens'     => $this->request->getPost('childrens') ?? 'NA',
-                    'parents'       => $this->request->getPost('parents') ?? 'NA',
-                    'parents-in-law' => $this->request->getPost('parents-in-law') ?? 'NA',
-                ]
-            ];
-
-            if ($policy_grid_id == 1 || $policy_grid_id == 2) {
-                $relation_data = [
-                    'self'          => 1,
-                    'spouse'        => 'NA',
-                    'childrens'     => 'NA',
-                    'parents'       => 'NA',
-                    'parents-in-law' => 'NA',
-                ];
+            if (in_array($policy_grid_id, ['1', '2'])) {
+                $relation_data = ['self' => 1, 'spouse' => 'NA', 'childrens' => 'NA', 'parents' => 'NA', 'parents-in-law' => 'NA'];
             }
 
-            // Convert to JSON
             $jsonDataForRelation = json_encode($relation_data);
-            $json_data_relation_data_for_form_submit_check = json_encode($relation_data_for_form_submit_check);
+            $submit_check_json = json_encode([$rack_rate_name => $relation_data]);
 
-            $si_or_bp = $this->request->getPost('si_or_bp');
-            $basic_multiplier = str_replace(',', '', $this->request->getPost('basic_multiplier'));
-            $premium_multiplier = str_replace(',', '', $this->request->getPost('premium_multiplier'));
-            $multiplier = str_replace(',', '', $this->request->getPost('multiplier'));
-            $basic_pay = str_replace(',', '', $this->request->getPost('basic_pay'));
-
-            $data = [];
-            $data['client_id'] = $client_id;
-            $data['client_policy_id'] = $client_policy_id;
-            $data['policy_grid_id'] = $policy_grid_id;
-            $data['premium_type'] = $premium_type;
-            $data['rack_rate_name'] = $rack_rate_name;
-            $data['additional_relationship'] = $jsonDataForRelation;
-
-            $premium = [];
-
-            if ($policy_grid_id == '1' || $policy_grid_id == '2') {
-                $this->policyPremium1Model->where('client_id', $client_id)->where('client_policy_id', $client_policy_id)->set('is_active', 0)->update();
+            // Deactivate old records
+            if (in_array($policy_grid_id, ['1', '2'])) {
+                $this->policyPremium1Model->where(['client_id' => $client_id, 'client_policy_id' => $client_policy_id])->set(['is_active' => 0])->update();
             } else {
-                $this->policyPremium2Model->where('client_id', $client_id)->where('client_policy_id', $client_policy_id)->where('rack_rate_name', $rack_rate_name)->set('is_active', 0)->update();
+                $this->policyPremium2Model->where(['client_id' => $client_id, 'client_policy_id' => $client_policy_id, 'rack_rate_name' => $rack_rate_name])->set(['is_active' => 0])->update();
             }
 
+            // Helper closures for cleaning and unit selection
+            $cleanNum = fn($val) => str_replace(',', '', (string)($val ?? '0'));
+            $getUnit  = fn($unitArr, $index) => (empty($unitArr[$index]) || $unitArr[$index] === 'undefined') ? $default_unit : $unitArr[$index];
+
+            $insert = false;
+            $baseInsertData = [
+                'client_id' => $client_id,
+                'client_policy_id' => $client_policy_id,
+                'policy_grid_id' => $policy_grid_id,
+                'premium_type' => $premium_type,
+                'rack_rate_name' => $rack_rate_name,
+                'additional_relationship' => $jsonDataForRelation
+            ];
+
+            // --- Logic Switch based on Grid ID ---
+
             if ($policy_grid_id == '1') {
-
                 if ($si_or_bp == '1') {
-
-                    $premium = str_replace(',', '', $this->request->getPost('gpa_sum_premium[]'));
-                    $sum_insure = str_replace(',', '', $this->request->getPost('gpa_sum_si[]'));
-                    $multiplier = $this->request->getPost('gpa_sum_multiplier');
-                    $unit = $this->request->getPost('gpa_unit_1[]');
-
-                    for ($i = 0; $i < count($premium); $i++) {
-                        $data['si'] = $sum_insure[$i];
-                        $data['premium'] = $premium[$i];
-                        $data['multiplier'] = $multiplier;
-                        $data['si_or_bp'] = $this->request->getPost('si_or_bp');
-                        if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                            $data['unit'] = $branch_units[0];
-                        } else {
-                            $data['unit'] = $unit[$i];
-                        }
-
-                        $policyPremium = $this->policyPremium1Model->insert($data);
+                    $premiums = $data['gpa_sum_premium'] ?? [];
+                    foreach ($premiums as $i => $p) {
+                        $this->policyPremium1Model->insert(array_merge($baseInsertData, [
+                            'si' => $cleanNum($data['gpa_sum_si'][$i] ?? 0),
+                            'premium' => $cleanNum($p),
+                            'multiplier' => $data['gpa_sum_multiplier'] ?? null,
+                            'si_or_bp' => $si_or_bp,
+                            'unit' => $getUnit($data['gpa_unit_1'] ?? [], $i)
+                        ]));
+                        $insert = true;
                     }
                 } else if ($si_or_bp == '3') {
-
-                    $premium = str_replace(',', '', $this->request->getPost('gpa_sum_premium2[]'));
-                    $sum_insure = str_replace(',', '', $this->request->getPost('gpa_sum_si2[]'));
-                    $multiplier = $this->request->getPost('gpa_sum_multiplier2');
-                    $grade = $this->request->getPost('gpa_band[]');
-                    $unit = $this->request->getPost('gpa_unit_3[]');
-
-
-                    for ($i = 0; $i < count($premium); $i++) {
-                        $data['si'] = $sum_insure[$i];
-                        $data['premium'] = $premium[$i];
-                        $data['grade'] = $grade[$i];
-                        $data['multiplier'] = $multiplier;
-                        $data['si_or_bp'] = $this->request->getPost('si_or_bp');
-                        if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                            $data['unit'] = $branch_units[0];
-                        } else {
-                            $data['unit'] = $unit[$i];
-                        }
-
-                        $policyPremium = $this->policyPremium1Model->insert($data);
+                    $premiums = $data['gpa_sum_premium2'] ?? [];
+                    foreach ($premiums as $i => $p) {
+                        $this->policyPremium1Model->insert(array_merge($baseInsertData, [
+                            'si' => $cleanNum($data['gpa_sum_si2'][$i] ?? 0),
+                            'premium' => $cleanNum($p),
+                            'grade' => $data['gpa_band'][$i] ?? null,
+                            'multiplier' => $data['gpa_sum_multiplier2'] ?? null,
+                            'si_or_bp' => $si_or_bp,
+                            'unit' => $getUnit($data['gpa_unit_3'] ?? [], $i)
+                        ]));
+                        $insert = true;
                     }
                 } else if ($si_or_bp == '2') {
-
-                    $premium = str_replace(',', '', $this->request->getPost('gpa_basic_premium[]'));
-                    $sum_insure = str_replace(',', '', $this->request->getPost('gpa_basic_si[]'));
-                    $basic_pay = str_replace(',', '', $this->request->getPost('basic_pay[]'));
-                    $unit = $this->request->getPost('gpa_unit[]');
-
-
-                    for ($i = 0; $i < count($premium); $i++) {
-
-                        $data['si_or_bp'] = $this->request->getPost('si_or_bp');
-                        $data['basic_multiplier'] = str_replace(',', '', $this->request->getPost('basic_multiplier'));
-                        $data['multiplier'] = $this->request->getPost('premium_multiplier');
-                        $data['basic_pay'] = $basic_pay[$i];
-                        $data['si'] = $sum_insure[$i];
-                        $data['premium'] = $premium[$i];
-                        if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                            $data['unit'] = $branch_units[0];
-                        } else {
-                            $data['unit'] = $unit[$i];
-                        }
-
-                        $policyPremium = $this->policyPremium1Model->insert($data);
+                    $premiums = $data['gpa_basic_premium'] ?? [];
+                    foreach ($premiums as $i => $p) {
+                        $this->policyPremium1Model->insert(array_merge($baseInsertData, [
+                            'si_or_bp' => $si_or_bp,
+                            'basic_multiplier' => $cleanNum($data['basic_multiplier'] ?? 0),
+                            'multiplier' => $cleanNum($data['premium_multiplier'] ?? 0),
+                            'basic_pay' => $cleanNum($data['basic_pay'][$i] ?? 0),
+                            'si' => $cleanNum($data['gpa_basic_si'][$i] ?? 0),
+                            'premium' => $cleanNum($p),
+                            'unit' => $getUnit($data['gpa_unit'] ?? [], $i)
+                        ]));
+                        $insert = true;
                     }
-                } else {
-
-                    $data['premium'] = str_replace(',', '', $this->request->getPost('gpa_basic_premium'));
-                    $data['si'] = str_replace(',', '', $this->request->getPost('gpa_basic_si'));
-                    $data['basic_multiplier'] = str_replace(',', '', $this->request->getPost('basic_multiplier'));
-                    $data['multiplier'] = $this->request->getPost('premium_multiplier');
-                    $data['basic_pay'] = str_replace(',', '', $this->request->getPost('basic_pay'));
-                    $data['si_or_bp'] = $this->request->getPost('si_or_bp');
-
-                    $policyPremium = $this->policyPremium1Model->insert($data);
                 }
-
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '2') {
-
-                $premium = $this->request->getPost('gpa_premium29[]');
-                $sum_insure = $this->request->getPost('gpa_si29[]');
-                $unit = $this->request->getPost('gpa_unit29[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-                    $dataa = $this->policyPremium1Model->insert($data);
+            } else if (in_array($policy_grid_id, ['2', '9'])) {
+                $premiums = $data['gpa_premium29'] ?? [];
+                $model = ($policy_grid_id == '2') ? $this->policyPremium1Model : $this->policyPremium2Model;
+                foreach ($premiums as $i => $p) {
+                    $model->insert(array_merge($baseInsertData, [
+                        'premium' => $cleanNum($p),
+                        'si' => $cleanNum($data['gpa_si29'][$i] ?? 0),
+                        'unit' => $getUnit($data['gpa_unit29'] ?? [], $i)
+                    ]));
+                    $insert = true;
                 }
+            } else {
+                // Handles grids 3, 4, 5, 6, 7, 8, 10, 11, 12, 13
+                $pfx = $policy_grid_id . "_";
+                $premiums = $data[$pfx . 'premium'] ?? [];
+                $is_si_array = is_array($data[$pfx . 'si'] ?? null);
 
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '3') {
-
-                $premium = $this->request->getPost('3_premium[]');
-                $sum_insure = $this->request->getPost('3_si[]');
-                $unit = $this->request->getPost('3_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-                    $dataa = $this->policyPremium2Model->insert($data);
+                foreach ($premiums as $i => $p) {
+                    $this->policyPremium2Model->insert(array_merge($baseInsertData, [
+                        'premium'      => $cleanNum($p),
+                        'si'           => $cleanNum($is_si_array ? ($data[$pfx . 'si'][$i] ?? 0) : ($data[$pfx . 'si'] ?? 0)),
+                        'unit'         => $getUnit($data[$pfx . 'unit'] ?? [], $i),
+                        'age_from'     => $data[$pfx . 'age_from'][$i] ?? null,
+                        'age_to'       => $data[$pfx . 'age_to'][$i] ?? null,
+                        'grade'        => $data[$pfx . 'grade'][$i] ?? null,
+                        'relationship' => $data[$pfx . 'relationship'][$i] ?? null,
+                        'max_si'       => $cleanNum($data[$pfx . 'max_si'][$i] ?? 0)
+                    ]));
+                    $insert = true;
                 }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '4') {
-
-                $premium = $this->request->getPost('4_premium[]');
-                $sum_insure = $this->request->getPost('4_si');
-                $age_from = $this->request->getPost('4_age_from[]');
-                $age_to = $this->request->getPost('4_age_to[]');
-                $unit = $this->request->getPost('4_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '5') {
-
-                $premium = $this->request->getPost('5_premium[]');
-                $sum_insure = $this->request->getPost('5_si[]');
-                $age_from = $this->request->getPost('5_age_from[]');
-                $age_to = $this->request->getPost('5_age_to[]');
-                $unit = $this->request->getPost('5_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i] || $unit[$i] == 'undefined')) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '6') {
-
-                $premium = $this->request->getPost('6_premium[]');
-                $sum_insure = $this->request->getPost('6_si');
-                $age_from = $this->request->getPost('6_age_from[]');
-                $age_to = $this->request->getPost('6_age_to[]');
-                $unit = $this->request->getPost('6_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '7') {
-                $premium = $this->request->getPost('7_premium[]');
-                $sum_insure = $this->request->getPost('7_si[]');
-                $age_from = $this->request->getPost('7_age_from[]');
-                $age_to = $this->request->getPost('7_age_to[]');
-                $unit = $this->request->getPost('7_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '8') {
-                $premium = $this->request->getPost('8_premium[]');
-                $sum_insure = $this->request->getPost('8_si[]');
-                $grade = $this->request->getPost('8_grade[]');
-                $unit = $this->request->getPost('8_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['grade'] = $grade[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '9') {
-
-                $premium = $this->request->getPost('gpa_premium29[]');
-                $sum_insure = $this->request->getPost('gpa_si29[]');
-                $unit = $this->request->getPost('gpa_unit29[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '10') {
-                $premium = $this->request->getPost('10_premium[]');
-                $sum_insure = $this->request->getPost('10_si[]');
-                $age_from = $this->request->getPost('10_age_from[]');
-                $age_to = $this->request->getPost('10_age_to[]');
-                $unit = $this->request->getPost('10_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-
-                    $policyPremium = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '11') {
-
-                $premium = $this->request->getPost('11_premium[]');
-                $sum_insure = $this->request->getPost('11_si[]');
-                $grade = $this->request->getPost('11_grade[]');
-                $max_sum_insure = $this->request->getPost('11_max_si[]');
-                $unit = $this->request->getPost('11_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['grade'] = $grade[$i];
-                    $data['max_si'] = str_replace(',', '', $max_sum_insure[$i]);
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '12') {
-
-                $premium = $this->request->getPost('12_premium[]');
-                $sum_insure = $this->request->getPost('12_si[]');
-                $relationship = $this->request->getPost('12_relationship[]');
-                $unit = $this->request->getPost('12_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['relationship'] = $relationship[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
-            } else if ($policy_grid_id == '13') {
-
-                $premium = $this->request->getPost('13_premium[]');
-                $sum_insure = $this->request->getPost('13_si[]');
-                $age_from = $this->request->getPost('13_age_from[]');
-                $age_to = $this->request->getPost('13_age_to[]');
-                $relationship = $this->request->getPost('13_relationship[]');
-                $unit = $this->request->getPost('13_unit[]');
-
-                for ($i = 0; $i < count($premium); $i++) {
-                    $data['premium'] = str_replace(',', '', $premium[$i]);
-                    $data['si'] = str_replace(',', '', $sum_insure[$i]);
-                    $data['age_from'] = $age_from[$i];
-                    $data['age_to'] = $age_to[$i];
-                    $data['relationship'] = $relationship[$i];
-                    if (empty($unit) || !isset($unit[$i]) || empty($unit[$i])) {
-                        $data['unit'] = $branch_units[0];
-                    } else {
-                        $data['unit'] = $unit[$i];
-                    }
-
-                    $dataa = $this->policyPremium2Model->insert($data);
-                }
-                $data = $this->request->getPost();
-                $insert = true;
             }
 
             if ($insert) {
-                return $this->respond(['status' => true, 'code' => 200, 'data' => $data, 'rack_rate_json' => $json_data_relation_data_for_form_submit_check,], 200);
-            } else {
-                return $this->respond(['status' => false, 'code' => 404, 'data' => $data, 'message' => 'no data found'], 200);
+                return $this->respond(['status' => true, 'code' => 200, 'data' => $data, 'rack_rate_json' => $submit_check_json], 200);
             }
+
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'No data found'], 200);
         } catch (\Exception $e) {
-            echo 'Error: ' . $e->getMessage() . ' at line no ' . $e->getLine();
+            return $this->respond(['status' => false, 'message' => $e->getMessage() . ' line ' . $e->getLine()], 500);
         }
     }
 
@@ -2349,7 +3200,6 @@ class ClientController extends AdminController
         }
     }
 
-
     private function updatePTCoShareTableEntry($policy_transaction_data, $old_client_policy_data, $data)
     {
         $updated_ids = [];
@@ -2395,9 +3245,6 @@ class ClientController extends AdminController
         ];
     }
 
-
-
-
     public function uploadVehicleFile($params = null)
     {
         $this->myLogger->logme('error', 'uploadVehicleFile function called');
@@ -2409,10 +3256,11 @@ class ClientController extends AdminController
 
         if(empty($params) && $this->request){
             $data = $this->request->getPost();
+            $sanitized_post_data = sanitizeInputArrayAdvanced($data);
             $files = $this->request->getFiles();
-            $client_id = $data['client_id'] ?? null;
-            $vehicle_id = $data['vehicle_id'] ?? null;
-            $docs_name = $data['other_docs_name'] ?? null;
+            $client_id = $sanitized_post_data['client_id'] ?? null;
+            $vehicle_id = $sanitized_post_data['vehicle_id'] ?? null;
+            $docs_name = $sanitized_post_data['other_docs_name'] ?? null;
         }else {
             $client_id = $params['client_id'] ?? null;
             $vehicle_id = $params['vehicle_id'] ?? null;
@@ -2463,8 +3311,8 @@ class ClientController extends AdminController
             if (!empty($insertedDocs)) {
                 // Fetch all documents for the client
                 $vehicleDocs = $this->clientKYCDocsModel
-                    ->where('client_id', $data['client_id'])
-                    ->where('vehicle_id', $data['vehicle_id'])
+                    ->where('client_id', $sanitized_post_data['client_id'])
+                    ->where('vehicle_id', $sanitized_post_data['vehicle_id'])
                     ->findAll();
                 return $this->respond(['status' => true, 'code' => 200, 'vehicle_docs' => $vehicleDocs, 'inserted_docs' => $insertedDocs, 'message' => 'File uploaded successfully'], 200);
             } else {
@@ -3116,194 +3964,6 @@ class ClientController extends AdminController
         }
     }
 
-    // public function enrollmentGMCDisplayValueTransform()
-    // {
-    //     $data = [];
-
-    //     if ($this->request->getPost("waiverofpreexistingdiseases_display")) {
-    //         $data['Waiver of Pre-existing Diseases'] = $this->request->getPost("waiverofpreexistingdiseases");
-    //     }
-
-    //     if ($this->request->getPost("waiverof1234thyearexclusions_display")) {
-    //         $data['Waiver of 1, 2, 3 & 4th year Exclusions'] = $this->request->getPost("waiverof1,2,3&4thyearexclusions");
-    //     }
-
-    //     if ($this->request->getPost("waiverof30dayswaitingperiod_display")) {
-    //         $data['Waiver of 30 days waiting period'] = $this->request->getPost("waiverof30dayswaitingperiod");
-    //     }
-
-    //     if ($this->request->getPost("suminsuredenhancement_display")) {
-    //         $data['Sum Insured Enhancement'] = $this->request->getPost("suminsuredenhancement");
-    //     }
-
-    //     if ($this->request->getPost("waiver_of_90_days_waiting_period_display")) {
-    //         $data['Waiver of 90 Days Waiting Period'] = $this->request->getPost("waiver_of_90_days_waiting_period");
-    //     }
-
-    //     if ($this->request->getPost("waiver_of_other_waiting_periods_display")) {
-    //         $data['Waiver of Other Waiting Periods'] = $this->request->getPost("waiver_of_other_waiting_periods");
-    //     }
-
-    //     if ($this->request->getPost("maternity_benefit_display")) {
-    //         $data['Maternity Benefit'] = $this->request->getPost("maternity_benefit");
-    //     }
-
-    //     if ($this->request->getPost("9monthwaitingperiodwaived_display")) {
-    //         $data['9-month waiting Period - waived'] = $this->request->getPost("9monthwaitingperiodwaived");
-    //     }
-
-    //     if ($this->request->getPost("maternitycoverage_display")) {
-    //         $data['Maternity Coverage'] = $this->request->getPost("maternitycoverage");
-    //     }
-
-    //     if ($this->request->getPost("twindelivery_display")) {
-    //         $data['Twin Delivery'] = $this->request->getPost("twindelivery");
-    //     }
-
-    //     if ($this->request->getPost("well_baby_well_mother_expenses_display")) {
-    //         $data['Well baby / Well Mother Expenses'] = $this->request->getPost("well_baby_well_mother_expenses");
-    //     }
-
-    //     if ($this->request->getPost("preandpostnatal_display")) {
-    //         $data['Pre and Post natal'] = $this->request->getPost("preandpostnatal");
-    //     }
-
-    //     if ($this->request->getPost("infertility_treatment_coverage_display")) {
-    //         $data['Infertility Treatment Coverage'] = $this->request->getPost("infertility_treatment_coverage");
-    //     }
-
-    //     if ($this->request->getPost("babyday1cover_display")) {
-    //         $data['Baby Day 1 Cover'] = $this->request->getPost("babyday1cover");
-    //     }
-
-    //     if ($this->request->getPost("coverfromthedateofjoining_display")) {
-    //         $data['Cover from the date of Joining'] = $this->request->getPost("coverfromthedateofjoining");
-    //     }
-
-    //     if ($this->request->getPost("mid_term_addition_of_new_born_newly_wedded_spouse_display")) {
-    //         $data['Mid Term Addition of New Born / Newly Wedded Spouse'] = $this->request->getPost("mid_term_addition_of_new_born_newly_wedded_spouse");
-    //     }
-
-    //     if ($this->request->getPost("prehospitalizationcover_display")) {
-    //         $data['Pre Hospitalization Cover'] = $this->request->getPost("prehospitalizationcover");
-    //     }
-
-    //     if ($this->request->getPost("posthospitalizationcover_display")) {
-    //         $data['Post Hospitalization Cover'] = $this->request->getPost("posthospitalizationcover");
-    //     }
-
-    //     if ($this->request->getPost("congenitaldiseasesinternal_display")) {
-    //         $data['Congenital Diseases - Internal'] = $this->request->getPost("congenitaldiseasesinternal");
-    //     }
-
-    //     if ($this->request->getPost("congenitaldiseasesexternal_display")) {
-    //         $data['Congenital Diseases - External'] = $this->request->getPost("congenitaldiseasesexternal");
-    //     }
-
-    //     if ($this->request->getPost("copayzonewisecopay_display")) {
-    //         $data['Co-Pay/Zone wise Co Pay'] = $this->request->getPost("copayzonewisecopay");
-    //     }
-
-    //     if ($this->request->getPost("roomrentlimit_display")) {
-    //         $data['Room Rent Limit'] = $this->request->getPost("roomrentlimit");
-    //     }
-
-    //     if ($this->request->getPost("icu_limit_display")) {
-    //         $data['ICU Limit'] = $this->request->getPost("icu_limit");
-    //     }
-
-    //     if ($this->request->getPost("proportionatedeductionclause_display")) {
-    //         $data['Proportionate Deduction Clause'] = $this->request->getPost("proportionatedeductionclause");
-    //     }
-
-    //     if ($this->request->getPost("ailmentcapping_display")) {
-    //         $data['Ailment capping'] = $this->request->getPost("ailmentcapping");
-    //     }
-
-    //     if ($this->request->getPost("ailment_capping_details_display")) {
-    //         $data['Ailment capping Details'] = $this->request->getPost("ailment_capping_details");
-    //     }
-
-    //     if ($this->request->getPost("corporatebuffer_display")) {
-    //         $data['Corporate Buffer'] = $this->request->getPost("corporatebuffer");
-    //     }
-
-    //     if ($this->request->getPost("non_admissible_contingency_corporate_buffer_display")) {
-    //         $data['Non-Admissible / Contingency Corporate Buffer'] = $this->request->getPost("non_admissible_contingency_corporate_buffer");
-    //     }
-
-    //     if ($this->request->getPost("ambulancecharges_display")) {
-    //         $data['Ambulance Charges'] = $this->request->getPost("ambulancecharges");
-    //     }
-
-    //     if ($this->request->getPost("airambulance_display")) {
-    //         $data['Air Ambulance'] = $this->request->getPost("airambulance");
-    //     }
-
-    //     if ($this->request->getPost("reasonableandcustomarycharges_display")) {
-    //         $data['Reasonable and Customary Charges'] = $this->request->getPost("reasonableandcustomarycharges");
-    //     }
-
-    //     if ($this->request->getPost("daycaretreatment_display")) {
-    //         $data['Day Care Treatment'] = $this->request->getPost("daycaretreatment");
-    //     }
-
-    //     if ($this->request->getPost("lasiksurgery_display")) {
-    //         $data['Lasik Surgery'] = $this->request->getPost("lasiksurgery");
-    //     }
-
-    //     if ($this->request->getPost("ayushtreatmentcover_display")) {
-    //         $data['AYUSH treatment cover'] = $this->request->getPost("ayudhtreatmentcover");
-    //     }
-
-    //     if ($this->request->getPost("moderntreatmentsasperirdai_display")) {
-    //         $data['Modern treatments as per IRDAI'] = $this->request->getPost("moderntreatmentsasperirdai");
-    //     }
-
-    //     if ($this->request->getPost("opd_treatment_display")) {
-    //         $data['OPD Treatment'] = $this->request->getPost("opd_treatment");
-    //     }
-
-    //     if ($this->request->getPost("days_of_discharge_display")) {
-    //         $data['Claim Intimation Clause'] = $this->request->getPost("days_of_discharge");
-    //     }
-
-    //     if ($this->request->getPost("days_from_dod_display")) {
-    //         $data['Claim Submission'] = $this->request->getPost("days_from_dod");
-    //     }
-
-    //     if ($this->request->getPost("terrorism_display")) {
-    //         $data['Terrorism'] = $this->request->getPost("terrorism");
-    //     }
-
-    //     if ($this->request->getPost("widower_cover_display")) {
-    //         $data['Widower Cover'] = $this->request->getPost("widower_cover");
-    //     }
-
-    //     if ($this->request->getPost("breavement_cover_display")) {
-    //         $data['Breavement Cover'] = $this->request->getPost("breavement_cover");
-    //     }
-
-    //     if ($this->request->getPost("special_condition_display_value")) {
-
-    //         $specialConditionKeyValue = $this->request->getPost("special_condition_display_value") ?? [];
-    //         $specialConditionKeyValue = json_decode($specialConditionKeyValue, true);
-
-    //         if (is_array($specialConditionKeyValue) && count($specialConditionKeyValue) > 0) {
-    //             $mergedArray = array_merge(...array_map(fn($item) => (array) $item, $specialConditionKeyValue));
-    //             $data = array_merge($data, $mergedArray);
-    //         }
-    //     }
-
-    //     if(isset($data) && !empty($data)){
-    //         return $data;
-    //     }else{
-    //         $data = [];
-    //         return $data;
-    //     }
-
-    // }
-
     public function enrollmentGMCDisplayValueTransform()
     {
         $data = [];
@@ -3378,7 +4038,6 @@ class ClientController extends AdminController
         return $data;
     }
 
-
     public function getterms()
     {
         $client_policy_id = $this->request->getVar('client_policy_id');
@@ -3539,94 +4198,6 @@ class ClientController extends AdminController
         }
     }
 
-    // public function enrollmentGPADisplayValueTransform()
-    // {   
-    //     $data = [];
-
-    //     if ($this->request->getPost("accidentalDeathBenefit_display")) {
-    //         $data['Accidental Death Benefit'] = $this->request->getPost("accidentalDeathBenefit");
-    //     }
-    //     if ($this->request->getPost("permanentTotalDisablement_display")) {
-    //         $data['Permanent Total Disablement'] = $this->request->getPost("permanentTotalDisablement");
-    //     }
-    //     if ($this->request->getPost("permanentPartialDisablement_display")) {
-    //         $data['Permanent Partial Disablement'] = $this->request->getPost("permanentPartialDisablement");
-    //     }
-    //     if ($this->request->getPost("temporaryTotalDisablementBenefit_display")) {
-    //         $data['Temporary Total Disablement Benefit'] = $this->request->getPost("temporaryTotalDisablementBenefit");
-    //     }
-    //     if ($this->request->getPost("medical_expenses_medical_extension_display")) {
-    //         $data['Medical Expenses / Medical Extension (IPD)'] = $this->request->getPost("medical_expenses_medical_extension");
-    //     }
-    //     if ($this->request->getPost("opd_treatment_cover_display")) {
-    //         $data['OPD Treatment Cover'] = $this->request->getPost("opd_treatment_cover");
-    //     }
-    //     if ($this->request->getPost("ambulanceCharges_display")) {
-    //         $data['Ambulance Charges'] = $this->request->getPost("ambulanceCharges");
-    //     }
-    //     if ($this->request->getPost("repatriation_of_mortal_remains_display")) {
-    //         $data['Repatriation of Mortal Remains'] = $this->request->getPost("repatriation_of_mortal_remains");
-    //     }
-    //     if ($this->request->getPost("childrenEducationWelfareFund_display")) {
-    //         $data['Children Education Welfare Fund'] = $this->request->getPost("childrenEducationWelfareFund");
-    //     }
-    //     if ($this->request->getPost("terrorism_display")) {
-    //         $data['Terrorism'] = $this->request->getPost("terrorism");
-    //     }
-    //     if ($this->request->getPost("worldwideCover_display")) {
-    //         $data['Worldwide Cover'] = $this->request->getPost("worldwideCover");
-    //     }
-    //     if ($this->request->getPost("family_transportation_benefits_display")) {
-    //         $data['Family Transportation Benefits'] = $this->request->getPost("family_transportation_benefits");
-    //     }
-    //     if ($this->request->getPost("fractures_dislocation_burns_display")) {
-    //         $data['Fractures / Dislocation / Burns'] = $this->request->getPost("fractures_dislocation_burns");
-    //     }
-    //     if ($this->request->getPost("coma_display")) {
-    //         $data['Coma'] = $this->request->getPost("coma");
-    //     }
-    //     if ($this->request->getPost("carriageofDeadBody_display")) {
-    //         $data['Carriage of Dead Body'] = $this->request->getPost("carriageOfDeadBody");
-    //     }
-    //     if ($this->request->getPost("compassionateVisitExpenses_display")) {
-    //         $data['Compassionate Visit Expenses'] = $this->request->getPost("compassionateVisitExpenses");
-    //     }
-    //     if ($this->request->getPost("travel_expenses_for_medical_treatment_display")) {
-    //         $data['Travel expenses for Medical Treatment'] = $this->request->getPost("travel_expenses_for_medical_treatment");
-    //     }
-    //     if ($this->request->getPost("daily_cash_allowance_display")) {
-    //         $data['Daily cash Allowance'] = $this->request->getPost("daily_cash_allowance");
-    //     }
-    //     if ($this->request->getPost("artifical_limb_and_prosthesis_display")) {
-    //         $data['Artifical Limb and Prosthesis'] = $this->request->getPost("artifical_limb_and_prosthesis");
-    //     }
-    //     if ($this->request->getPost("animalSnakeInsectBite_display")) {
-    //         $data['Animal/Snake/Insect Bite'] = $this->request->getPost("animalSnakeInsectBite");
-    //     }
-    //     if ($this->request->getPost("air_ambulance_display")) {
-    //         $data['Air Ambulance'] = $this->request->getPost("air_ambulance");
-    //     }
-
-    //     if ($this->request->getPost("gpa_special_condition_display_value")) {
-
-    //         $specialConditionKeyValue = $this->request->getPost("gpa_special_condition_display_value") ?? [];
-    //         $specialConditionKeyValue = json_decode($specialConditionKeyValue, true);
-
-    //         if (is_array($specialConditionKeyValue) && count($specialConditionKeyValue) > 0) {
-    //             $mergedArray = array_merge(...array_map(fn($item) => (array) $item, $specialConditionKeyValue));
-    //             $data = array_merge($data, $mergedArray);
-    //         }
-    //     }
-    //     // print_r($data); die;
-
-    //     if (isset($data) && !empty($data)) {
-    //         return $data;
-    //     } else {
-    //         $data = [];
-    //         return $data;
-    //     }
-    // }
-
     public function enrollmentGPADisplayValueTransform()
     {
         $data = [];
@@ -3761,9 +4332,9 @@ class ClientController extends AdminController
         // $policy_status = $this->clientPolicyModel->where('id', $client_policy_id )->set('policy_status', 1)->update();
 
         $json_data = '';
-        $open_for_enrollment = $this->clientPolicyModel->where('id', $client_policy_id)->set('open_for_enrollment', $open_for_enrollment_update_value)->update();
+        $open_for_enrollment = $this->clientPolicyModel->where('id', (int)$client_policy_id)->set('open_for_enrollment', $open_for_enrollment_update_value)->update();
         if ($open_for_enrollment) {
-            $open_for_enrollment_1 = $this->clientPolicyModel->where('id', $client_policy_id)->find();
+            $open_for_enrollment_1 = $this->clientPolicyModel->where('id', (int)$client_policy_id)->find();
             $open_for_enrollment_value = $open_for_enrollment_1[0]['open_for_enrollment'];
             $client_policy_id_value = $client_policy_id;
 
@@ -5103,12 +5674,65 @@ class ClientController extends AdminController
 
     public function createVehicleWithMinimalData()
     {
+        $rules = [
+            'Owner_type' => [
+                'rules'  => 'required|in_list[1,2]',
+                'errors' => [
+                    'required' => 'Owner type is required',
+                    'in_list'  => 'Invalid owner type selected'
+                ]
+            ],
+            'vehicle_no' => [
+                'rules'  => 'required|regex_match[/^[A-Z]{2}[0-9]{2}[A-Z]{1,2}[0-9]{4}$/]',
+                'errors' => [
+                    'required'    => 'Vehicle number is required',
+                    'regex_match' => 'Invalid Vehicle Number. Example: TN22AB1234'
+                ]
+            ],
+            'rc' => [
+                'rules'  => 'required|trim',
+                'errors' => [
+                    'required' => 'RC Book number is required'
+                ]
+            ],
+
+            'type' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Vehicle type is required'
+                ]
+            ],
+
+            'description' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Vehicle description is required'
+                ]
+            ],
+            'owner' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Owner is required'
+                ]
+            ],
+
+        ];
+         if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
         $data = $this->request->getPost();
-        $id = $this->request->getPost('vehicle_primary_key');
+        $sanitized_post_data = sanitizeInputArrayAdvanced($data);
+        $id = $sanitized_post_data['vehicle_primary_key'] ?? null;
+
 
         if ($id) {
 
-            $vehicle_update = $this->vehicleModel->where('id', $id)->set($data)->update();
+            $vehicle_update = $this->vehicleModel->where('id', $id)->set($sanitized_post_data)->update();
 
             if ($vehicle_update) {
                 return $this->respond(['status' => true, 'message' => 'Vehicle details updated successfully'], 200);
@@ -5117,7 +5741,7 @@ class ClientController extends AdminController
             }
         } else {
 
-            $vehicle_insert = $this->vehicleModel->insert($data);
+            $vehicle_insert = $this->vehicleModel->insert($sanitized_post_data);
 
             if ($vehicle_insert) {
 
@@ -5130,9 +5754,9 @@ class ClientController extends AdminController
                 return $this->respond([
                     'status' => true,
                     'vehicle_id' => $vehicle_insert,
-                    'owner_id' => $data['owner'],
-                    'owner_branch_id' => $data['branch_id'] ?? null,
-                    'owner_type' => $data['Owner_type'],
+                    'owner_id' => $sanitized_post_data['owner'] ?? null,
+                    'owner_branch_id' => $sanitized_post_data['branch_id'] ?? null,
+                    'owner_type' => $sanitized_post_data['Owner_type']?? null,
                     'vehicles' => $vehicles,
                     'message' => 'Vehicle created successfully
                     '
@@ -6065,7 +6689,23 @@ class ClientController extends AdminController
     }
 
     public function sendextraparam()
-    {              
+    {      
+        // $zipService = new \App\Libraries\ZipService();
+        // $source = WRITEPATH . 'uploads/hr_files';
+        // $destination = WRITEPATH . 'tmp/archive_' . date('Ymd') . '.zip';
+
+        // $result = $zipService->createLocalZip($source, $destination);
+
+        // if ($result['status']) {
+        //     echo "File is ready at: " . $result['path'];
+        // } else {
+        //     echo "Error creating zip: " . $result['message'];
+        // }
+
+        // die;
+
+        // $cd = $this->view_Deposit(2, 'rest', ['client_id' => 58, 'cd_ac_pk' => 94]);
+        // dd($cd);
         // $return = db_connect()->table('jobs')->where('id', 1677)->get()->getRowArray();
         // $return = $this->updatePolicyTransactionDataWhileClinetPolicyUpdate(json_decode($data['payload'], true));
         // dd($return);
@@ -6078,7 +6718,10 @@ class ClientController extends AdminController
         // $res = $medi_assist->MediAssistGetBenefDetails(['policy_no' => '97000034240400000030', 'file_id' => 389, 'return_type' => 'job', 'client_policy_id' => 6192 ]); 
         // dd($res);
 
-        // $employeeController = new EmployeeController();
+        $employeeController = new EmployeeController();
+        // $response = $employeeController->getEmployeeEcardFromTmpFolderAndZipToS3(json_decode('{"batch_no":2,"last_emp_policy_id":"13218","folder_name":"bulk_ecards_IOCL-77448855996699885555_2026-02-05_09-32-22","processed_in_this_batch_data_count":7,"pdf_count":0,"hr_id":"1"}', true));
+        // $response = $employeeController->bulkEcardDownloadAsZipFromS3(json_decode('{"client_policy_id":"6066","hr_id":"1"}', true));
+        // dd($response);
         // $employeeController->truncateFileData('633');
         // $res = $this->getHrAccessData(4075); dd($res);
 
@@ -6090,6 +6733,16 @@ class ClientController extends AdminController
         // $response = $ticketServiceController->getClaimExcelErrorData(["file_id" => 41]);
         // $response = $ticketServiceController->claimDumpOnBoardProcess(["file_id" => 17]);
         // $response = $ticketServiceController->extractExcelData("claims_dump_form_client.xlsx");
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 51]); //abhi
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 50]); //fhpl
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 53]); //icici
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 54]); //mediassist
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 52]); //reliance
+        // $response = $ticketServiceController->tpaClaimDumpImporter(["file_id" => 48]); //vidal
+        // $response = $ticketServiceController->tpaClaimDumpToTicketMasterImporters(["file_id" => 48]); //vidal
+        // $response = $ticketServiceController->tpaClaimDumpToTicketMasterImporters(["file_id" => 54]); //mediassist
+
+        // $response = $ticketServiceController->tpaClaimDumpToTicketMasterImporters(["file_id" => 4]); //abhi
         // dd($response);
 
         // ---------- TICKET CONTROLLER --------------------------------------------------------------------------------
@@ -6102,9 +6755,10 @@ class ClientController extends AdminController
         // ---------- EMP SERVICE CONTROLLER --------------------------------------------------------------------------------
 
         $empServiceController = new EmployeeServiceController();
-        // $res = $empServiceController->excelFileFormatValidation(['file_id' => '1248']);
-        // $res = $empServiceController->excelFileDataValidation(['file_id' => '1252']);
-        // $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1183]);
+        // $res = $empServiceController->excelFileFormatValidation(['file_id' => '1264']);
+        // $res = $empServiceController->excelFileDataValidation(['file_id' => '1264']);
+        // $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1264]);
+        // $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1262]);
         // $res = $empServiceController->employeesOnboardPreprocess(['file_id' => 1169]);
         // $res = $empServiceController->employeesOnboardProcess(['file_id' => 835]);
         // $res = $empServiceController->employeesEnrollmentInsert(['file_id' => 836]);
@@ -6131,31 +6785,32 @@ class ClientController extends AdminController
         $policyTransactionController = new PolicyTransactionController();
         // $res = $policyTransactionController->validateInsurerStatement(['file_id' => '281']);
         // $res = $policyTransactionController->updateInsurerStatement(['file_id' => '62']);
-        // dd('-----', $res);
+        // $res = $policyTransactionController->bdsDumpExcelFileFormatValidation(['file_id' => '73']);
+        // dd($res);
 
         // ----------EMP DATA SERVICE CONTROLLER--------------------------------------------------------------------------------
 
-        $batch_data = [
-            'client_id' => 51,
-            'client_branch_id' => 40,
-            'client_policy_id' => 63,
-            'insurer_or_tpa' => "insurer",
-            'event_type' => "correction",
-            'actions' => "export",
-            'file_name' => "deletion_enhancement_test_file.xlsx",
-        ];
-
         // $batch_data = [
-        //     'client_id' => 20,
-        //     'client_policy_id' => 77,
-        //     'client_branch_id' => 72,
+        //     'client_id' => 51,
+        //     'client_branch_id' => 40,
+        //     'client_policy_id' => 63,
         //     'insurer_or_tpa' => "insurer",
-        //     // 'insurer_or_tpa' => "tpa",
-        //     'event_type' => "si_enhancement",
-        //     'file_name' => "si_enhancement_test_file.xlsx",
+        //     'event_type' => "correction",
         //     'actions' => "export",
+        //     'file_name' => "deletion_enhancement_test_file.xlsx",
         // ];
 
+        $batch_data = [
+            'client_id' => 12,
+            'client_policy_id' => 8063,
+            'client_branch_id' => 1,
+            'insurer_or_tpa' => "insurer",
+            // 'insurer_or_tpa' => "tpa",
+            'event_type' => "inception",
+            'file_name' => "si_enhancement_test_file.xlsx",
+            'actions' => "export",
+        ];
+
         // $batch_data['insurer_or_tpa'] = 'insurer';
         // $batch_data['insurer_or_tpa'] = 'tpa';
 
@@ -6180,7 +6835,7 @@ class ClientController extends AdminController
         // $res = $EmpDataServiceController->importInceptionUpdateTPAandUHID(['file_id' => 310]); dd($res);//for live
         // $res = $EmpDataServiceController->getInceptionBasePremium(["13332","13333","13334","13335","13336"]); dd($res);//for live
         // $res = $EmpDataServiceController->getDeletionBasePremium(["13248","13250","13249","13247"], json_decode('{"employeeIds":["13248","13250","13249","13247"],"client_id":"3927","client_policy_id":"6183","client_branch_id":"1874","cd_ac_no":"CD9751909505","endorsement_no":"END1238","count":4,"event_name":"deletion","policy_name":"GMC","user_id":"48"}', true)); dd($res);//for live
-        // $res = $EmpDataServiceController->makeEntryForBDSPolicyTransaction(json_decode('{"client_policy_id":"6187","endorsement_no":null,"emp_count":5,"action_type":"inception","no_of_insured":3,"no_of_dependent":0}', true)); dd($res);//for live
+        // $res = $EmpDataServiceController->makeEntryForBDSPolicyTransaction(json_decode('{"client_policy_id":"6187","endorsement_no":"000000001","emp_count":5,"action_type":"addition","no_of_insured":3,"no_of_dependent":0, "base_premium" : "1000", "gst" : "180", "policy_issue_date" : "2025-12-31"}', true)); dd($res);//for live
         // $res = $EmpDataServiceController->sendMailForDownloadingECard(['ids' => $ids, 'client_policy_id' => $client_policy_id]);
         // $res = $this->employeePolicyModel->getDeletionEmployeeDataForExportExcel($batch_data, 1); dd($result);
         // $res = $this->employeePolicyModel->getSIEnhancementEmployeesDataForExportExcel($batch_data, 1);
@@ -6210,6 +6865,21 @@ class ClientController extends AdminController
         // $EmpDataServiceController->cashDepositCalculationForDeletion($array);
 
         // $result = $this->employeePolicyModel->getInceptionEmployeeDataForExportExcel($batch_data);
+        // $totals = 0;
+        // foreach ($result as $item) {
+        //     $totals =  $totals + $item->total;
+        // }
+
+        // clear_cd_balance_session();
+
+        // $data = [
+        //     'cd_balance' => session()->get('cd_balance'),
+        //     'hr_data' => session()->get('hr_data'),
+        //     'cd_balance_info' => session()->get('cd_balance_info'),
+        //     get_cd_balance()
+        // ];
+
+        // dd($data);
         // $result = $this->employeePolicyModel->getCorrectionEmployeesDataForExportExcel($batch_data);
         // dd(db_connect()->getLastQuery());
 
@@ -6230,8 +6900,8 @@ class ClientController extends AdminController
         // $res = $LeadsController->convertQCRJsonToPolicyTerms($jsonArray, $policy_type, $proposel_name, $insurer_name);
         // $res = $LeadsController->handleMemberDataGPATotalSumInsurerFromExcel(['lead_id' => 169]);
         // $res = $LeadsController->reorderProposalsByInsurerTotal($jsonArray);
-        // $res = $LeadsController->calculateMembersDemography(['lead_id' => 326]);
-        // $res = $LeadsController->generateDemographyDataTable(['lead_id' => 326]);
+        // $res = $LeadsController->calculateMembersDemography(['lead_id' => 287], "internal");
+        // $res = $LeadsController->generateDemographyDataTable(['lead_id' => 287]);
         // echo $res;
         // dd($res);
 
@@ -7453,180 +8123,6 @@ class ClientController extends AdminController
         $combinedHrAccessData = $this->constructHrAccessData($hrAccessData, $client_id ,$pre_client_data['id']);
         return $combinedHrAccessData;
     }
-
-    // do not remove this commented items
-    // public function constructHrAccessData($data, $client_id , $pre_client_id)
-    // {
-    //     // print_rr($data);die;
-
-    //     $preHrs = $data['pre_hr_data'];
-    //     $postHrs = $data['post_hr_data'];
-    //     $hrAccessTableData = $data['hr_access_table_data'];
-    //     $merged = [];
-
-    //     // Merge based on mobile and email
-    //     foreach ($postHrs as $post) {
-    //         $found = false;
-    //         foreach ($preHrs as $index => $pre) {
-
-    //             if ( trim($post['hr_mobile']) == trim($pre['hr_mobile'])  &&  trim($post['hr_mail']) == trim($pre['hr_mail']) ) {
-
-    //                 $merged[] = [
-    //                     'pre_hr_id' => $pre['pre_hr_id'],
-    //                     'post_hr_id' => $post['post_hr_id'],
-    //                     'hr_name' => $post['hr_name'],
-    //                     'hr_mobile' => $post['hr_mobile'],
-    //                     'hr_mail' => $post['hr_mail'],
-    //                     'pre_branch_id' => $pre['pre_branch_id'] ?? null,
-    //                     'post_branch_id'   => $post['post_branch_id'] ?? null,
-    //                     'post_branch_name' => $post['post_branch_name'] ?? null 
-    //                 ];
-    //                 unset($preHrs[$index]); // remove matched pre_hr
-    //                 $found = true;
-    //                 break;
-    //             }
-    //         }
-
-    //         if (!$found) {
-    //             $merged[] = [
-    //                 'pre_hr_id' => null,
-    //                 'post_hr_id' => $post['post_hr_id'],
-    //                 'hr_name' => $post['hr_name'],
-    //                 'hr_mobile' => $post['hr_mobile'],
-    //                 'hr_mail' => $post['hr_mail'],
-    //                 'pre_branch_id' => $pre['pre_branch_id'] ?? null,
-    //                 'post_branch_id' => $post['post_branch_id'] ?? null ,
-    //                 'post_branch_name' => $post['post_branch_name'] ?? null
-    //             ];
-    //         }
-    //     }
-
-    //     // Remaining preHrs (not matched)
-    //     foreach ($preHrs as $pre) {
-
-    //         foreach ($merged as $value) {
-    //             if ( trim($pre['hr_mobile']) == trim($value['hr_mobile'])  &&  trim($pre['hr_mail']) == trim($value['hr_mail']) ) {
-    //                 continue 2; // Skip adding this pre_hr as it's already matched
-    //             }
-    //         }
-            
-    //         $merged[] = [
-    //             'pre_hr_id' => $pre['pre_hr_id'],
-    //             'post_hr_id' => null,
-    //             'hr_name' => $pre['hr_name'],
-    //             'hr_mobile' => $pre['hr_mobile'],
-    //             'hr_mail' => $pre['hr_mail'],
-    //             'pre_branch_id' => $pre['pre_branch_id'] ?? null,
-    //             'post_branch_id' => $post['post_branch_id'] ?? null ,
-    //             'post_branch_name' => $post['post_branch_name'] ?? null
-    //         ];
-    //     }
-
-    //     // dd($merged);
-
-    //     $result = [];
-
-    //     if (empty($hrAccessTableData)) {
-
-    //         // No access data — fill result with hr data and other fields as null
-    //         foreach ($merged as $hr) {
-    //             $result[] = [
-    //                 'hr_access_table_pk' => null,
-    //                 'post_client_id' => $client_id ?? null,
-    //                 'pre_hr_id' => $hr['pre_hr_id'] ?? null,
-    //                 'post_hr_id' => $hr['post_hr_id'] ?? null,
-    //                 'allowed_pre_modules' => [],
-    //                 'allowed_post_modules' => [],
-    //                 'allowed_pre_policies' => [],
-    //                 'allowed_active_policies' => [],
-    //                 'allowed_cd' => [],
-    //                 'hr_name' => $hr['hr_name'] ?? null,
-    //                 'hr_mobile' => $hr['hr_mobile'] ?? null,
-    //                 'hr_mail' => $hr['hr_mail'] ?? null,
-    //                 'pre_branch_id' => $hr['pre_branch_id'] ?? null,
-    //                 'post_branch_id' => $hr['post_branch_id'] ?? null ,
-    //                 'post_branch_name' => $hr['post_branch_name'] ?? null ,
-    //                 'pre_client_id'    => $pre_client_id ?? null
-    //             ];
-    //         }
-    //     } else {
-    //         // First create a map of HR data by post_hr_id for quick lookup
-    //         $hrMap = [];
-    //         foreach ($merged as $hr) {
-    //             $hrMap[$hr['post_hr_id']] = $hr;
-    //         }
-
-    //         // Process access data first
-    //         foreach ($hrAccessTableData as $access) {
-    //             $post_hr_id = $access['post_hr_id'];
-
-    //             // Check if this HR exists in our merged data
-    //             if (isset($hrMap[$post_hr_id])) {
-    //                 $hr = $hrMap[$post_hr_id];
-    //                 $temp_arr_key = $hr['hr_mobile'] . $hr['hr_mail'];
-
-    //                 // Parse allowed modules
-    //                 $allowed_modules = json_decode($access['allowed_modules'], true) ?? null;
-    //                 $allowed_pre_modules = $allowed_modules['pre'] ?? [];
-    //                 $allowed_post_modules = $allowed_modules['post'] ?? [];
-
-    //                 $result[$temp_arr_key] = [
-    //                     'hr_access_table_pk' => $access['id'] ?? null,
-    //                     'post_client_id' => $access['post_client_id'] ?? null,
-    //                     'pre_hr_id' => $hr['pre_hr_id'] ?? null,
-    //                     'post_hr_id' => $hr['post_hr_id'] ?? null,
-    //                     'allowed_pre_modules' => $allowed_pre_modules,
-    //                     'allowed_post_modules' => $allowed_post_modules,
-    //                     'allowed_pre_policies' => json_decode($access['allowed_pre_policies'], true) ?? [],
-    //                     'allowed_active_policies' => json_decode($access['allowed_active_policies'], true) ?? [],
-    //                     'allowed_cd' => json_decode($access['allowed_cd'], true) ?? [],
-    //                     'hr_name' => $hr['hr_name'],
-    //                     'hr_mobile' => $hr['hr_mobile'],
-    //                     'hr_mail' => $hr['hr_mail'],
-    //                     'pre_branch_id' => $hr['pre_branch_id'] ?? null,
-    //                     'post_branch_id' => $hr['post_branch_id'] ?? null ,
-    //                     'post_branch_name' => $hr['post_branch_name'] ?? null ,
-    //                     'pre_client_id'    => $pre_client_id ?? null
-    //                 ];
-
-    //                 // Remove from map so we know it's been processed
-    //                 unset($hrMap[$post_hr_id]);
-    //             }
-    //         }
-
-    //         // Now process any remaining HRs that didn't have access records
-    //         foreach ($hrMap as $hr) {
-    //             $temp_arr_key = $hr['hr_mobile'] . $hr['hr_mail'];
-
-    //             $result[$temp_arr_key] = [
-    //                 'hr_access_table_pk' => null,
-    //                 'post_client_id' => $client_id ?? null,
-    //                 'pre_hr_id' => $hr['pre_hr_id'] ?? null,
-    //                 'post_hr_id' => $hr['post_hr_id'] ?? null,
-    //                 'allowed_pre_modules' => [],
-    //                 'allowed_post_modules' => [],
-    //                 'allowed_pre_policies' => [],
-    //                 'allowed_active_policies' => [],
-    //                 'allowed_cd' => [],
-    //                 'hr_name' => $hr['hr_name'] ?? null,
-    //                 'hr_mobile' => $hr['hr_mobile'] ?? null,
-    //                 'hr_mail' => $hr['hr_mail'] ?? null,
-    //                 'pre_branch_id' => $hr['pre_branch_id'] ?? null,
-    //                 'post_branch_id' => $hr['post_branch_id'] ?? null ,
-    //                 'post_branch_name' => $hr['post_branch_name'] ?? null ,
-    //                 'pre_client_id'    => $pre_client_id ?? null
-    //             ];
-    //         }
-    //     }
-
-
-    //     $resultData['hr_access_data']   = array_values($result);
-    //     $resultData['pre_policy_data']  = $data['pre_policy_data'];
-    //     $resultData['post_policy_data'] = $data['post_policy_data'];
-    //     $resultData['post_cd_data']     = $data['post_cd_data'];
-
-    //     return $resultData;
-    // }    
     
     public function constructHrAccessData($data, $client_id , $pre_client_id)
     {   
@@ -7882,7 +8378,180 @@ class ClientController extends AdminController
             ];
             return ['status' => 'failed', 'code' => 500, 'message' => $th->getMessage(), 'error_data' => $errorData];
         }
-    }       
+    }   
+
+    public function constructHrAccessData_OLD($data, $client_id , $pre_client_id)
+    {
+        // print_rr($data);die;
+
+        $preHrs = $data['pre_hr_data'];
+        $postHrs = $data['post_hr_data'];
+        $hrAccessTableData = $data['hr_access_table_data'];
+        $merged = [];
+
+        // Merge based on mobile and email
+        foreach ($postHrs as $post) {
+            $found = false;
+            foreach ($preHrs as $index => $pre) {
+
+                if ( trim($post['hr_mobile']) == trim($pre['hr_mobile'])  &&  trim($post['hr_mail']) == trim($pre['hr_mail']) ) {
+
+                    $merged[] = [
+                        'pre_hr_id' => $pre['pre_hr_id'],
+                        'post_hr_id' => $post['post_hr_id'],
+                        'hr_name' => $post['hr_name'],
+                        'hr_mobile' => $post['hr_mobile'],
+                        'hr_mail' => $post['hr_mail'],
+                        'pre_branch_id' => $pre['pre_branch_id'] ?? null,
+                        'post_branch_id'   => $post['post_branch_id'] ?? null,
+                        'post_branch_name' => $post['post_branch_name'] ?? null 
+                    ];
+                    unset($preHrs[$index]); // remove matched pre_hr
+                    $found = true;
+                    break;
+                }
+            }
+
+            if (!$found) {
+                $merged[] = [
+                    'pre_hr_id' => null,
+                    'post_hr_id' => $post['post_hr_id'],
+                    'hr_name' => $post['hr_name'],
+                    'hr_mobile' => $post['hr_mobile'],
+                    'hr_mail' => $post['hr_mail'],
+                    'pre_branch_id' => $pre['pre_branch_id'] ?? null,
+                    'post_branch_id' => $post['post_branch_id'] ?? null ,
+                    'post_branch_name' => $post['post_branch_name'] ?? null
+                ];
+            }
+        }
+
+        // Remaining preHrs (not matched)
+        foreach ($preHrs as $pre) {
+
+            foreach ($merged as $value) {
+                if ( trim($pre['hr_mobile']) == trim($value['hr_mobile'])  &&  trim($pre['hr_mail']) == trim($value['hr_mail']) ) {
+                    continue 2; // Skip adding this pre_hr as it's already matched
+                }
+            }
+            
+            $merged[] = [
+                'pre_hr_id' => $pre['pre_hr_id'],
+                'post_hr_id' => null,
+                'hr_name' => $pre['hr_name'],
+                'hr_mobile' => $pre['hr_mobile'],
+                'hr_mail' => $pre['hr_mail'],
+                'pre_branch_id' => $pre['pre_branch_id'] ?? null,
+                'post_branch_id' => $post['post_branch_id'] ?? null ,
+                'post_branch_name' => $post['post_branch_name'] ?? null
+            ];
+        }
+
+        // dd($merged);
+
+        $result = [];
+
+        if (empty($hrAccessTableData)) {
+
+            // No access data — fill result with hr data and other fields as null
+            foreach ($merged as $hr) {
+                $result[] = [
+                    'hr_access_table_pk' => null,
+                    'post_client_id' => $client_id ?? null,
+                    'pre_hr_id' => $hr['pre_hr_id'] ?? null,
+                    'post_hr_id' => $hr['post_hr_id'] ?? null,
+                    'allowed_pre_modules' => [],
+                    'allowed_post_modules' => [],
+                    'allowed_pre_policies' => [],
+                    'allowed_active_policies' => [],
+                    'allowed_cd' => [],
+                    'hr_name' => $hr['hr_name'] ?? null,
+                    'hr_mobile' => $hr['hr_mobile'] ?? null,
+                    'hr_mail' => $hr['hr_mail'] ?? null,
+                    'pre_branch_id' => $hr['pre_branch_id'] ?? null,
+                    'post_branch_id' => $hr['post_branch_id'] ?? null ,
+                    'post_branch_name' => $hr['post_branch_name'] ?? null ,
+                    'pre_client_id'    => $pre_client_id ?? null
+                ];
+            }
+        } else {
+            // First create a map of HR data by post_hr_id for quick lookup
+            $hrMap = [];
+            foreach ($merged as $hr) {
+                $hrMap[$hr['post_hr_id']] = $hr;
+            }
+
+            // Process access data first
+            foreach ($hrAccessTableData as $access) {
+                $post_hr_id = $access['post_hr_id'];
+
+                // Check if this HR exists in our merged data
+                if (isset($hrMap[$post_hr_id])) {
+                    $hr = $hrMap[$post_hr_id];
+                    $temp_arr_key = $hr['hr_mobile'] . $hr['hr_mail'];
+
+                    // Parse allowed modules
+                    $allowed_modules = json_decode($access['allowed_modules'], true) ?? null;
+                    $allowed_pre_modules = $allowed_modules['pre'] ?? [];
+                    $allowed_post_modules = $allowed_modules['post'] ?? [];
+
+                    $result[$temp_arr_key] = [
+                        'hr_access_table_pk' => $access['id'] ?? null,
+                        'post_client_id' => $access['post_client_id'] ?? null,
+                        'pre_hr_id' => $hr['pre_hr_id'] ?? null,
+                        'post_hr_id' => $hr['post_hr_id'] ?? null,
+                        'allowed_pre_modules' => $allowed_pre_modules,
+                        'allowed_post_modules' => $allowed_post_modules,
+                        'allowed_pre_policies' => json_decode($access['allowed_pre_policies'], true) ?? [],
+                        'allowed_active_policies' => json_decode($access['allowed_active_policies'], true) ?? [],
+                        'allowed_cd' => json_decode($access['allowed_cd'], true) ?? [],
+                        'hr_name' => $hr['hr_name'],
+                        'hr_mobile' => $hr['hr_mobile'],
+                        'hr_mail' => $hr['hr_mail'],
+                        'pre_branch_id' => $hr['pre_branch_id'] ?? null,
+                        'post_branch_id' => $hr['post_branch_id'] ?? null ,
+                        'post_branch_name' => $hr['post_branch_name'] ?? null ,
+                        'pre_client_id'    => $pre_client_id ?? null
+                    ];
+
+                    // Remove from map so we know it's been processed
+                    unset($hrMap[$post_hr_id]);
+                }
+            }
+
+            // Now process any remaining HRs that didn't have access records
+            foreach ($hrMap as $hr) {
+                $temp_arr_key = $hr['hr_mobile'] . $hr['hr_mail'];
+
+                $result[$temp_arr_key] = [
+                    'hr_access_table_pk' => null,
+                    'post_client_id' => $client_id ?? null,
+                    'pre_hr_id' => $hr['pre_hr_id'] ?? null,
+                    'post_hr_id' => $hr['post_hr_id'] ?? null,
+                    'allowed_pre_modules' => [],
+                    'allowed_post_modules' => [],
+                    'allowed_pre_policies' => [],
+                    'allowed_active_policies' => [],
+                    'allowed_cd' => [],
+                    'hr_name' => $hr['hr_name'] ?? null,
+                    'hr_mobile' => $hr['hr_mobile'] ?? null,
+                    'hr_mail' => $hr['hr_mail'] ?? null,
+                    'pre_branch_id' => $hr['pre_branch_id'] ?? null,
+                    'post_branch_id' => $hr['post_branch_id'] ?? null ,
+                    'post_branch_name' => $hr['post_branch_name'] ?? null ,
+                    'pre_client_id'    => $pre_client_id ?? null
+                ];
+            }
+        }
+
+
+        $resultData['hr_access_data']   = array_values($result);
+        $resultData['pre_policy_data']  = $data['pre_policy_data'];
+        $resultData['post_policy_data'] = $data['post_policy_data'];
+        $resultData['post_cd_data']     = $data['post_cd_data'];
+
+        return $resultData;
+    }   
 
     public function saveHrAccessData()
     {
@@ -8439,6 +9108,8 @@ class ClientController extends AdminController
         $member_ecard_mail_template              = $this->notificationModel->where('client_id', NULL)->where('template_name', 'member_ecard_mail')->get()->getResultArray()[0] ?? [];
 
         $member_common_mail_template             = $this->notificationModel->where('client_id', NULL)->where('template_name', 'member_common_mail')->get()->getResultArray()[0] ?? [];
+        
+        $hr_cd_insufficient_balance_mail_template  = $this->notificationModel->where('client_id', NULL)->where('template_name', 'hr_cd_insufficient_balance_mail')->get()->getResultArray()[0] ?? [];
 
         $default_member_welcome_mail_template = [
             'client_id'        => $client_id,
@@ -8510,6 +9181,20 @@ class ClientController extends AdminController
             'mail_content_copy' => null
         ];
 
+        $hr_cd_insufficient_balance_mail_template = [
+            'client_id'        => $client_id,
+            'template_name'    => 'hr_cd_insufficient_balance_mail',
+            'subject'          => $hr_cd_insufficient_balance_mail_template['subject'] ?? '',
+            'mail_content'     => $hr_cd_insufficient_balance_mail_template['mail_content'] ?? '',
+            'mail_content_json' => $hr_cd_insufficient_balance_mail_template['mail_content_json'] ?? '',
+
+            'created_by'       => get_session_userid(),
+            'created_at'       => date('Y-m-d H:i:s'),
+            'updated_by'       => null,
+            'updated_at'       => date('Y-m-d H:i:s'),
+            'mail_content_copy' => null
+        ];
+
         $this->myLogger->logme('error', 'Default Mail Template Data ' . json_encode([
             $default_member_welcome_mail_template,
             $default_member_remainder_mail_template,
@@ -8523,24 +9208,14 @@ class ClientController extends AdminController
             $default_member_remainder_mail_template,
             $default_member_review_and_summary_mail_template,
             $default_member_ecard_mail_template,
-            $default_member_common_mail_template
+            $default_member_common_mail_template,
+            $hr_cd_insufficient_balance_mail_template,
         ]);
 
 
         return $default_templates_inserted ? true : false;
     }
 
-    // public function downloadClientKycDocs_2() 
-    // {
-        
-    //     $file = WRITEPATH . 'uploads/client_kyc_documents/' . $file_name; // Example file path
-
-    //     if (file_exists($file)) {
-    //         return $this->response->download($file, null)->setFileName($file_name);
-    //     } else {
-    //         return "File not found.";
-    //     }
-    // }
     public function downloadClientKycDocs_2($file_name = null) 
     {
         if (!$file_name) {
diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php
index 50a9e4c1..1dab325b 100755
--- a/app/Controllers/DashboardController.php
+++ b/app/Controllers/DashboardController.php
@@ -443,7 +443,7 @@ class DashboardController extends AdminController
         foreach ($client_policy_data as $client_policy) {
 
             // Fetch client data
-            $client_data = $this->clientModel->find($client_policy['client_id']);
+            $client_data = $this->clientModel->find((int)$client_policy['client_id']);
 
             // Fetch notification settings
             $notification_data = $this->notificationModel
diff --git a/app/Controllers/EmpDataServiceController.php b/app/Controllers/EmpDataServiceController.php
index 452f414b..4a4e9faf 100755
--- a/app/Controllers/EmpDataServiceController.php
+++ b/app/Controllers/EmpDataServiceController.php
@@ -33,6 +33,8 @@ use App\Models\LeadsModel;
 use App\Models\LeadInstallmentPaymentDetails;
 use App\Models\PolicyTransactionModel;
 use App\Models\PTCOShareDetailsModel;
+use App\Models\LevelContactModel;
+
 
 use App\Controllers\Jobs;
 use App\Controllers\JobWorker;
@@ -71,6 +73,8 @@ class EmpDataServiceController extends BaseController
     protected $leadInstallmentPaymentDetailesModel;
     protected $policyTransactionModel;
     protected $PTCOShareDetailsModel;
+    protected $LevelContactModel;
+
 
 
     public function __construct()
@@ -99,6 +103,7 @@ class EmpDataServiceController extends BaseController
         $this->leadInstallmentPaymentDetailesModel  = new LeadInstallmentPaymentDetails();
         $this->policyTransactionModel  = new PolicyTransactionModel();
         $this->PTCOShareDetailsModel  = new PTCOShareDetailsModel();
+        $this->LevelContactModel  = new LevelContactModel();
 
     }
 
@@ -199,24 +204,28 @@ class EmpDataServiceController extends BaseController
             $totals =  $totals + $item->total;
         }
         
-        $totals = round($totals, 2);
+        $totals = round($totals);
 
         //check CD amt insufficient only insurer, not tpa // DO NOT REMOVE THIS
         if($export_data['insurer_or_tpa'] == 'insurer')
         {
-            if (!empty($cash_balance)) {
-                if ((int) $cash_balance['balance']  < (int) $totals) {
-                    $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.');
-                    $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.  CASH BALANCE : {balance}  and  TOTAL AMOUNT : {total}', ['balance' => $cash_balance['balance'], 'total' => $totals]);
-                     session()->set('cd_balance', false);
-                     session()->set('cd_amount', $cash_balance['balance']);
-                     session()->set('excel_file_amt', $totals);
-                }else{
-                     session()->set('cd_balance', true);
-                     session()->set('cd_amount', $cash_balance['balance']);
-                     session()->set('excel_file_amt', $totals);
-
-                }
+            if ((int) $cash_balance['balance'] < (int) $totals) {
+                clear_cd_balance_session(); // Clear old junk first
+                
+                $cd_session_data = json_encode([
+                    'cd_balance' => false, 
+                    'cd_amount' => $cash_balance['balance'], 
+                    'excel_file_amt' => $totals
+                ]);
+                
+                session()->set('cd_balance_info', $cd_session_data);
+                session()->set('cd_balance', 'insufficient'); // Use a string status for clarity
+                
+                $hr_data = $this->getHrdataForInsufficientMailSend($export_data['client_branch_id']);
+                session()->set('hr_data', json_encode(['client_id' => $export_data['client_id'], 'hr_data' => $hr_data]));
+            }else{
+                session()->set('cd_balance', 'sufficient'); // Use a string status for clarity
+                session()->set('cd_balance_info', null);
             }
         }
 
@@ -487,13 +496,18 @@ class EmpDataServiceController extends BaseController
                         WHEN `client_policy`.`policy_type_id` IN (2, 3, 4, 5) THEN 2
                         ELSE `client_policy`.`policy_type_id`
                     END
-                WHERE `client_policy`.`id` = '".$export_data['client_policy_id']."'
-                AND `insurer_excel_export_template`.`event_name` = '".$export_data['event_type']."'
+                WHERE `client_policy`.`id` = :client_policy_id:
+                AND `insurer_excel_export_template`.`event_name` = :event_name:
                 AND `insurer_excel_export_template`.`is_active` = 1
-                AND `insurer_excel_export_template`.`type_name` = '".$export_data['actions']."'
+                AND `insurer_excel_export_template`.`type_name` =  :type_name:
                 LIMIT 1";
-            
-            $query = db_connect()->query($sql);
+
+                $binds = [
+                    'client_policy_id' => (int)$export_data['client_policy_id'],
+                    'event_name'       => $export_data['event_type'],
+                    'type_name'        => $export_data['actions'],
+                ];
+            $query = db_connect()->query($sql,$binds);
             $template_json = $query->getRowArray();
         
             // dd(db_connect()->getLastQuery());
@@ -720,17 +734,23 @@ class EmpDataServiceController extends BaseController
         if($export_data['insurer_or_tpa'] == 'insurer')
         {
             if (!empty($cash_balance)) {
-                if ((int) $cash_balance['balance']  < (int) $rounded_totals) {
-                    $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.');
-                    $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.  CASH BALANCE : {balance}  and  TOTAL AMOUNT : {total}', ['balance' => $cash_balance['balance'], 'total' => $totals]);
-                    session()->set('cd_balance', false);
-                    session()->set('cd_amount', $cash_balance['balance']);
-                    session()->set('excel_file_amt', $rounded_totals);
+                if ((int) $cash_balance['balance'] < (int) $totals) {
+                    clear_cd_balance_session(); // Clear old junk first
+                    
+                    $cd_session_data = json_encode([
+                        'cd_balance' => false, 
+                        'cd_amount' => $cash_balance['balance'], 
+                        'excel_file_amt' => $totals
+                    ]);
+                    
+                    session()->set('cd_balance_info', $cd_session_data);
+                    session()->set('cd_balance', 'insufficient'); // Use a string status for clarity
+                    
+                    $hr_data = $this->getHrdataForInsufficientMailSend($export_data['client_branch_id']);
+                    session()->set('hr_data', json_encode(['client_id' => $export_data['client_id'], 'hr_data' => $hr_data]));
                 }else{
-                    session()->set('cd_balance', true);
-                    session()->set('cd_amount', $cash_balance['balance']);
-                    session()->set('excel_file_amt', $rounded_totals);
-
+                    session()->set('cd_balance', 'sufficient'); // Use a string status for clarity
+                    session()->set('cd_balance_info', null);
                 }
             }
         }
@@ -1051,17 +1071,23 @@ class EmpDataServiceController extends BaseController
             if($export_data['insurer_or_tpa'] != 'tpa')// check overal emp premium amt with cd balance only for insurer export, not tpa export
             { 
                 if (!empty($cash_balance)) {
-                    if ((int) $cash_balance['balance']  < (int) $totals) {
-                        $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.');
-                        $this->myLogger->logme('error', 'Inception export failed due to insufficient deposit amount.  CASH BALANCE : {balance}  and  TOTAL AMOUNT : {total}', ['balance' => $cash_balance['balance'], 'total' => $totals]);
-                        session()->set('cd_balance', false);
-                        session()->set('cd_amount', $cash_balance['balance']);
-                        session()->set('excel_file_amt', $totals);
+                    if ((int) $cash_balance['balance'] < (int) $totals) {
+                        clear_cd_balance_session(); // Clear old junk first
+                        
+                        $cd_session_data = json_encode([
+                            'cd_balance' => false, 
+                            'cd_amount' => $cash_balance['balance'], 
+                            'excel_file_amt' => $totals
+                        ]);
+                        
+                        session()->set('cd_balance_info', $cd_session_data);
+                        session()->set('cd_balance', 'insufficient'); // Use a string status for clarity
+                        
+                        $hr_data = $this->getHrdataForInsufficientMailSend($export_data['client_branch_id']);
+                        session()->set('hr_data', json_encode(['client_id' => $export_data['client_id'], 'hr_data' => $hr_data]));
                     }else{
-                        session()->set('cd_balance', true);
-                        session()->set('cd_amount', $cash_balance['balance']);
-                        session()->set('excel_file_amt', $totals);
-
+                        session()->set('cd_balance', 'sufficient'); // Use a string status for clarity
+                        session()->set('cd_balance_info', null);
                     }
                 }
             }
@@ -1095,13 +1121,15 @@ class EmpDataServiceController extends BaseController
                     WHEN `client_policy`.`policy_type_id` IN (2, 3, 4, 5) THEN 2
                     ELSE `client_policy`.`policy_type_id`
                 END
-            WHERE `client_policy`.`id` = '".$export_data['client_policy_id']."'
+            WHERE `client_policy`.`id` = :client_policy_id:
             AND `insurer_excel_export_template`.`event_name` = 'all'
             AND `insurer_excel_export_template`.`is_active` = 1
-            AND `insurer_excel_export_template`.`type_name` = '".$export_data['actions']."'
+            AND `insurer_excel_export_template`.`type_name` = :type_name:
             LIMIT 1";
         
-        $query = db_connect()->query($sql);
+            $binds = ['client_policy_id' => (int)$export_data['client_policy_id'],
+                             'type_name' => $export_data['actions']];
+        $query = db_connect()->query($sql,$binds);
         $template_json = $query->getRowArray();
 
 
@@ -1931,7 +1959,7 @@ class EmpDataServiceController extends BaseController
         $this->myLogger->logme('error', 'Inception Update TPA and UHID --  Function called');
 
         $file_id = $params['file_id'];
-        $file = $this->batchFileModel->find($file_id);
+        $file = $this->batchFileModel->find((int)$file_id);
         // dd($file);
 
         if (!$file) {
@@ -1940,10 +1968,10 @@ class EmpDataServiceController extends BaseController
                 'status' => 'failed-4',
             ];
 
-            $this->batchFileModel->where('id', $file_id)->set($data)->update();
+            $this->batchFileModel->where('id', (int)$file_id)->set($data)->update();
             $this->myLogger->logme('error', 'Inception Update TPA and UHID --   The Physical file not found --   File id : {data}', ['data' => $file_id]);
 
-            $file_data = $this->getDataByFileId($file_id, 'failure');
+            $file_data = $this->getDataByFileId((int)$file_id, 'failure');
             $this->setPullNotification($file_data);
 
             return ['status' => 'error', 'message' => 'Inception Update TPA and UHID --   The Physical file not found']; // Return error code if file not found
@@ -1958,17 +1986,17 @@ class EmpDataServiceController extends BaseController
         $user_id = $file['created_by'];
         $policy_issue_date = $file['policy_issue_date'];
 
-        $insurer_id = $this->clientPolicyModel->where('id', $client_policy_id)->first();
+        $insurer_id = $this->clientPolicyModel->where('id', (int)$client_policy_id)->first();
 
         $CD_Account_Number = $this->CDMasterModel
-                    ->where('client_id', $client_id)
-                    ->where('insurer_id', $insurer_id['insurer_id'])
+                    ->where('client_id', (int)$client_id)
+                    ->where('insurer_id', (int)$insurer_id['insurer_id'])
                     ->first();
 
         $get_policy_type = $this->clientPolicyModel
             ->select('client_policy.*, policy_type.policy_type')
             ->join('policy_type', 'policy_type.id = client_policy.policy_type_id', 'left')
-            ->where('client_policy.id', $client_policy_id)
+            ->where('client_policy.id', (int)$client_policy_id)
             ->first();
 
         // dd($get_policy_type);
@@ -2178,6 +2206,7 @@ class EmpDataServiceController extends BaseController
                 'event_name' => $file['event_type'],
                 'policy_name' => $policy_name['policy_name'],
                 'user_id' => $user_id,
+                'file_id' => $file_id,
             ]]);
 
             $r = Jobs::addJob(['job_name' => 'makeEntryForBDSPolicyTransaction', 'payload' => [
@@ -2201,7 +2230,7 @@ class EmpDataServiceController extends BaseController
         //send ecard mail if the event is tpa only
         if ($file['insurer_or_tpa'] == 'tpa') {
 
-            if ($get_policy_type['policy_type_id'] != 1) {
+            if (in_array($get_policy_type['policy_type_id'], [2, 3])) {
 
                 // generate e-card and store S3
                 $r = Jobs::addJob(['job_name' => 'bulkGenerateEcardAndStoreinS3', 'payload' => ['client_policy_id' => $client_policy_id]]);
@@ -2500,7 +2529,7 @@ class EmpDataServiceController extends BaseController
 
         $this->myLogger->logme('error', 'importCorrectionUpdateEndorsementID called');
 
-        $file_id = $params['file_id'];
+        $file_id = (int)$params['file_id'];
         $file = $this->batchFileModel->find($file_id);
         if (!$file) {
 
@@ -2617,7 +2646,7 @@ class EmpDataServiceController extends BaseController
             'status' =>  $status_val,
         ]);
 
-        if($client_policy_data['policy_type_id'] != 1){
+        if(in_array($client_policy_data['policy_type_id'], [2, 3])){
             // re-generate e-card and store S3
             $r = Jobs::addJob(['job_name' => 'bulkGenerateEcardAndStoreinS3', 'payload' => ['client_policy_id' => $client_policy_id]]);
         }
@@ -3135,7 +3164,7 @@ class EmpDataServiceController extends BaseController
 
         $this->myLogger->logme('error', 'SI Enhancement Update Endorsement ID --   Function called');
 
-        $file_id = $params['file_id'];
+        $file_id = (int)$params['file_id'];
         $file = $this->batchFileModel->find($file_id);
         if (!$file) {
 
@@ -3361,6 +3390,7 @@ class EmpDataServiceController extends BaseController
                 'event_name' => $file['event_type'],
                 'policy_name' => $policy_name['policy_name'],
                 'user_id' => $user_id,
+                'file_id' => $file_id,
             ]]);
 
             $r = Jobs::addJob(['job_name' => 'makeEntryForBDSPolicyTransaction', 'payload' => [
@@ -3709,7 +3739,7 @@ class EmpDataServiceController extends BaseController
 
         $this->myLogger->logme('error', 'Deletion Update Endorsement ID --   Function called');
 
-        $file_id = $params['file_id'];
+        $file_id = (int)$params['file_id'];
         $file = $this->batchFileModel->find($file_id);
         if (!$file) {
 
@@ -3872,6 +3902,7 @@ class EmpDataServiceController extends BaseController
                 'event_name' => $file['event_type'],
                 'policy_name' => $policy_name['policy_name'],
                 'user_id' => $user_id,
+                'file_id' => $file_id,
             ]]);
 
             $r = Jobs::addJob(['job_name' => 'makeEntryForBDSPolicyTransaction', 'payload' => [
@@ -4193,7 +4224,7 @@ class EmpDataServiceController extends BaseController
 
         $this->myLogger->logme('error', 'importCorrectionUpdateEndorsementID called');
 
-        $file_id = $params['file_id'];
+        $file_id = (int)$params['file_id'];
         $file = $this->batchFileModel->find($file_id);
         if (!$file) {
 
@@ -4356,7 +4387,8 @@ class EmpDataServiceController extends BaseController
                         'updated_by' => $arrayData['user_id'],
                         'event_name' => $arrayData['event_name'],
                         'is_active' => 1,
-                        'cd_ac_pk'  => $cd_ac_pk['cd_ac_pk']
+                        'cd_ac_pk'  => $cd_ac_pk['cd_ac_pk'],
+                        'file_id' => $arrayData['file_id'],
                     ];
 
                     $response = DepositHelper::saveDeposit($data,  $arrayData['user_id']);
@@ -4454,11 +4486,12 @@ class EmpDataServiceController extends BaseController
                         SELECT SUM(rata_premimum + gst) AS total_sum
                         FROM employee_polices
                         JOIN employees ON employees.id = employee_polices.employee_id
-                        WHERE employees.unit = '{$unit}'
-                        AND employee_polices.id = '{$pk}'
+                        WHERE employees.unit = :unit:
+                        AND employee_polices.id = :pk:
                     ";
 
-                    $amount = $this->employeePolicyModel->query($query)->getRow();
+                    $binds = ['unit' => $unit,'pk' => $pk];
+                    $amount = $this->employeePolicyModel->query($query,$binds)->getRow();
 
                     if ($amount && $amount->total_sum > 0) {
                         if ($si_adjustment == 2) {
@@ -4492,7 +4525,8 @@ class EmpDataServiceController extends BaseController
                         'updated_by' => $arrayData['user_id'],
                         'event_name' => $arrayData['event_name'],
                         'is_active' => 1,
-                        'cd_ac_pk'  => $policy_data['cd_ac_pk']
+                        'cd_ac_pk'  => $policy_data['cd_ac_pk'],
+                        'file_id' => $arrayData['file_id'],
                     ];
 
                     DepositHelper::saveDeposit($data, $arrayData['user_id']);
@@ -4522,7 +4556,8 @@ class EmpDataServiceController extends BaseController
                         'updated_by' => $arrayData['user_id'],
                         'event_name' => $arrayData['event_name'],
                         'is_active' => 1,
-                        'cd_ac_pk'  => $policy_data['cd_ac_pk']
+                        'cd_ac_pk'  => $policy_data['cd_ac_pk'],
+                        'file_id' => $arrayData['file_id'],
                     ];
 
                     DepositHelper::saveDeposit($data, $arrayData['user_id']);
@@ -4634,7 +4669,8 @@ class EmpDataServiceController extends BaseController
                         'updated_by' => $arrayData['user_id'],
                         'event_name' => $arrayData['event_name'],
                         'is_active' => 1,
-                        'cd_ac_pk'  => $insurer_id['cd_ac_pk']
+                        'cd_ac_pk'  => $insurer_id['cd_ac_pk'],
+                        'file_id' => $arrayData['file_id'],
                     ];
 
                     $response = DepositHelper::saveDeposit($data, $arrayData['user_id']);
@@ -5400,7 +5436,7 @@ class EmpDataServiceController extends BaseController
             $this->myLogger->logme("error", "Policy transaction inserted successfully: " . json_encode(['insert_id' => $insert_id]));
 
             //do not remove this commented item
-            $coShareDetails = $this->ConstructPTShareData($policy_data, $insert_id, $params, $lead_data);
+            $coShareDetails = $this->ConstructPTShareData($policy_data, $insert_id, $params, $lead_data, $params['action_type']);
             $pt_co_share_id = $this->PTCOShareDetailsModel->insert($coShareDetails);
             $this->myLogger->logme("error", "PT Co share data inserted successfully: " . json_encode(['pt_co_share_id' => $pt_co_share_id]));
 
@@ -5532,7 +5568,46 @@ class EmpDataServiceController extends BaseController
         return $policyTransactionData;
     }
 
-    private function ConstructPTShareData($policy_data, $pt_id, $params, $lead_data)
+    private function ConstructPTShareData($policy_data, $pt_id, $params, $lead_data, $action_type)
+    {   
+        $policyTypeModel = new PolicyTypeModel();
+        $policy_type_data = $policyTypeModel->where('is_active', 1)->where('id', $policy_data['policy_type_id'])->first();
+
+        // Determine if we should negate the values
+        $multiplier = (strtolower($action_type) === 'deletion') ? -1 : 1;
+
+        $coShareData = [
+            'pt_id' => $pt_id,
+            'insurer_id' => $policy_data['insurer_id'],
+            'insurer_branch_id' => $policy_data['insurer_branch_id'],
+            'bp_amt' => ($params['base_premium'] ?? 0) * $multiplier,
+            'cop_amt' => ($params['base_premium'] ?? 0) * $multiplier,
+            'bp_gst_amt' => ($params['gst'] ?? 0) * $multiplier,
+            'bp_sgst' => 9, // Usually tax percentages remain positive, but multiply if this is an amount
+            'bp_cgst' => 9,
+            'co_share_type' => 1,
+            'co_share_per' => 100,
+            'standerd_bp_per' => $policy_type_data['ebp'],
+            'pt_policy_issue_date' => $params['policy_issue_date'] ?? null,
+            'amount' => (($params['base_premium'] ?? 0) + ($params['gst'] ?? 0)) * $multiplier,
+        ];
+
+        if(isset($policy_data['gst']) && $policy_data['gst'] != null){
+            $coShareData['bp_igst'] = $policy_data['gst'] ?? 0;
+            $coShareData['bp_sgst'] = 0;
+            $coShareData['bp_cgst'] = 0;
+        }
+
+        // Calculate exp_amt logic
+        $agreed_per = $lead_data['agreed_percentage'] ?? $policy_type_data['ebp'] ?? 0;
+        $coShareData['exp_amt'] = ((($params['base_premium'] ?? 0) * $agreed_per) / 100) * $multiplier;
+
+        $this->myLogger->logme("error", "Constructed PT co-share data: " . json_encode($coShareData));
+
+        return $coShareData;
+    }
+
+    private function ConstructPTShareDataOld($policy_data, $pt_id, $params, $lead_data, $action_type = null)
     {   
         $policyTypeModel = new PolicyTypeModel();
         $policy_type_data = $policyTypeModel->where('is_active', 1)->where('id', $policy_data['policy_type_id'])->first();
@@ -5850,4 +5925,16 @@ class EmpDataServiceController extends BaseController
             'gst' => round($amount['gst'] ?? 0, 2)
         ];
     }
+
+    public function getHrdataForInsufficientMailSend($ref_id)
+    {
+        $data = $this->LevelContactModel
+            ->select('id, name, email')
+            ->where('ref_id', $ref_id)
+            ->where('is_active', 1)
+            ->where('contact_type', 'client')
+            ->findAll();
+            
+        return $data;
+    }
 }
diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php
index 118b5330..4a019293 100755
--- a/app/Controllers/EmployeeController.php
+++ b/app/Controllers/EmployeeController.php
@@ -8,6 +8,7 @@ use CodeIgniter\HTTP\ResponseInterface;
 use Psr\Log\LoggerInterface;
 
 use App\Helpers\DepositHelper;
+use App\Helpers\MailHelper;
 
 
 use App\Models\EmployeeModel;
@@ -29,6 +30,8 @@ use App\Models\AuditHistoryModel;
 use App\Models\UserModel;
 use App\Models\PartnerEndorsementRequestModel;
 use App\Models\TpaApiDataModel;
+use App\Models\LevelContactModel;
+use App\Models\NotificationModel;
 
 
 use App\Controllers\Jobs;
@@ -74,6 +77,7 @@ class EmployeeController extends AdminController
     protected $auditHistory;
     protected $userModel;
     protected $partnerEndorsementRequestModel;
+    protected $LevelContactModel;
 
     public function __construct()
     {
@@ -97,6 +101,7 @@ class EmployeeController extends AdminController
         $this->auditHistory = new AuditHistoryModel();
         $this->userModel = new userModel();
         $this->partnerEndorsementRequestModel  = new PartnerEndorsementRequestModel();
+        $this->LevelContactModel  = new LevelContactModel();
     }
 
     public function list()
@@ -177,7 +182,7 @@ class EmployeeController extends AdminController
         // die();
         // $file_id = $this->request->getGet();
         // echo $file_id;die();
-        $file = $this->fileModel->find($file_id);
+        $file = $this->fileModel->find((int)$file_id);
         // print_r($result);die();
         if (!isset($file)) {
             return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'no data found'], 200);
@@ -188,7 +193,7 @@ class EmployeeController extends AdminController
     }
 
     //handles employee & dependent  bulk upload with events like inception,addition,deletion, correction and SI enhancements
-    public function employeesUplodWithEvents()
+    public function employeesUplodWithEvents($post_data = null)
     {
 
         // $empDataServiceController = new EmpDataServiceController();
@@ -242,64 +247,85 @@ class EmployeeController extends AdminController
         // $this->truncateFileData(747, 5) ;
         // print_rr($this->cloneWorksheet());
         // die();
-        if ($this->request->getMethod() == 'post') {
+
+        if (!empty($post_data) || $this->request->is('post') == 'post') {
 
             //validate uploaded file
             $filename = '';
             $fileSize = '';
-            $validated = $this->validate([
-                'emplist' => [
-                    'uploaded[emplist]',
-                    'mime_in[emplist,application/vnd.ms-excel,application/vnd,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
-                    'max_size[emplist,16384]',
-                ],
-            ]);
-            
-            if ($validated) 
-            {
-                    $avatar = $this->request->getFile('emplist');
-                    if (!$avatar) {
-                        $this->myLogger->logme("error", 'File not found');
+
+            if (empty($post_data)) {
+                $validated = $this->validate([
+                    'emplist' => [
+                        'uploaded[emplist]',
+                        'mime_in[emplist,application/vnd.ms-excel,application/vnd,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
+                        'max_size[emplist,16384]',
+                    ],
+                ]);
+            } else {
+                $validated = validateExcelFile($post_data['file_name']);
+            }
+
+            if ($validated) {
+
+                $avatar = isset($post_data['file_name']) ? $post_data['file_name'] : $this->request->getFile('emplist');
+                if (!$avatar) {
+                    $this->myLogger->logme("error", 'File not found');
+                    if (!empty($post_data)) {
+                        return ['status' => false, 'message' => 'File not found'];
+                    } else {
                         return $this->respond(['dataStatus' => false, 'code' => 400, 'message' => 'File not found'], 400);
                     }
+                }
 
-                    $is_moved = $avatar->move(WRITEPATH . 'uploads/excel/');
-                    if ($is_moved) {
-                        $filename = $avatar->getName();
-                        $fileSize = $avatar->getSize(); // File size in bytes
-                        $fileSize = $fileSize / (1024 * 1024); // Convert to MB
-                        // Handle successful upload, e.g., log success or further processing
-                        $this->myLogger->logme("error", 'File move successful');
-                        
+                $is_moved = $avatar->move(WRITEPATH . 'uploads/excel/');
+                if ($is_moved) {
+                    $filename = $avatar->getName();
+                    $fileSize = $avatar->getSize(); // File size in bytes
+                    $fileSize = $fileSize / (1024 * 1024); // Convert to MB
+                    // Handle successful upload, e.g., log success or further processing
+                    $this->myLogger->logme("error", 'File move successful');
+                } else {
+                    $this->myLogger->logme("error", 'File move failed');
+                    if (!empty($post_data)) {
+                        return ['status' => false, 'message' => 'File move failed'];
                     } else {
-                        $this->myLogger->logme("error", 'File move failed');
                         return $this->respond(['dataStatus' => false, 'code' => 500, 'message' => 'File move failed'], 500);
                     }
+                }
             } else {
                 $this->myLogger->logme("error", 'Upload failed Invalid file');
-                return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
+                if (!empty($post_data)) {
+                    return ['status' => false, 'message' => 'Invalid file'];
+                } else {
+                    return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
+                }
             }
 
             //process post variable entry in file table
-            $loggedInUserID = get_session_userid();
-            // dd($loggedInUserID);
-            // $loggedInUserID = 8;
+            $loggedInUserID = $post_data['created_by'] ?? get_session_userid();
 
-            $client_id = $this->request->getPost('client_id');
-            $policy_id = $this->request->getPost('policy_id');
-            $branch_id = $this->request->getPost('branch_id');
-            $hr_file_id = $this->request->getPost('hr_file_id') ?? null;
-            $action = $this->request->getPost('upload-action-type');
-            $status = 'inprogress';
+            $client_id  = isset($post_data['client_id']) ? $post_data['client_id'] : $this->request->getPost('client_id');
+            $policy_id  = isset($post_data['policy_id']) ? $post_data['policy_id'] : $this->request->getPost('policy_id');
+            $branch_id  = isset($post_data['client_branch_id']) ? $post_data['client_branch_id'] : $this->request->getPost('branch_id');
+            $action     = isset($post_data['file_action']) ? $post_data['file_action'] : $this->request->getPost('upload-action-type');
+            
+            if(empty($post_data)){
+                $hr_file_id = $this->request->getPost('hr_file_id') ?? null;
+            }else{
+                $hr_file_id = $post_data['hr_file_id'] ?? null ;
+            }
 
-            $file_id = $this->fileModel->insert(['file_name' => $filename, 'client_id' => $client_id, 'policy_id' => $policy_id, 'created_by' => $loggedInUserID, 'status' => $status, 'action' => $action, 'client_branch_id' => $branch_id,'uploaded_by' => 1, 'hr_file_id' => $hr_file_id]); //here field policy_id have client_policy_id and not policy id from policy master
+            $hr_id      = $post_data['created_by'] ?? null;
+            $status     = 'inprogress';
+
+            $file_id = $this->fileModel->insert(['file_name' => $filename, 'client_id' => $client_id, 'policy_id' => $policy_id, 'created_by' => $loggedInUserID, 'status' => $status, 'action' => $action, 'client_branch_id' => $branch_id, 'uploaded_by' => 1, 'hr_file_id' => $hr_file_id, 'hr_id' => $hr_id]); //here field policy_id have client_policy_id and not policy id from policy master
             $this->myLogger->logme("error", '{file_id} uploaded success', ['file_id' => $file_id]);
 
             if ($action == "all") {
 
                 $r = Jobs::addJob(['job_name' => 'excelMultieventFileFormateValidation', 'payload' => ['file_id' => $file_id]]);
                 $this->myLogger->logme("error", '{file_id} is greather than 1MB, validating with job queue', ['file_id' => $file_id]);
-
             } else {
                 //start validation process
                 if ($fileSize < 1) // if file size less than 1
@@ -309,7 +335,11 @@ class EmployeeController extends AdminController
                     $this->myLogger->logme("error", '{file_id} is less than 1MB, validating on the fly', ['file_id' => $file_id]);
                     //endof validation process
                     if (isset($result['error_summary']) && count($result['error_summary'])) {
-                        return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'file rejected with errors'], 200);
+                        if(!empty($post_data)){
+                            return ['status' => false, 'message' => 'file rejected with errors', 'file_id' => $file_id];
+                        }else{
+                            return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'file rejected with errors'], 200);
+                        }
                     }
                 } else //if file size greater than 1 add the file as job
                 {
@@ -319,7 +349,11 @@ class EmployeeController extends AdminController
                 }
             }
 
-            return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => 'file upload success'], 200);
+            if (!empty($post_data)) {
+                return ['status' => true, 'message' => 'File upload successs, Data validation is in-progress', 'file_id' => $file_id];
+            } else {
+                return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => 'file upload success'], 200);
+            }
         }
 
         $data['tab_name'] = 'View Inception';
@@ -350,7 +384,7 @@ class EmployeeController extends AdminController
                             ->select([
                                 'files.id','files.file_name','files.created_by','files.created_at','files.is_active','files.status','files.client_id','files.policy_id','files.client_branch_id','files.action','files.uploaded_by',
                                 'up.emp_code',
-                                'up.first_name',
+                                // 'up.first_name',
                                 'c.short_name',
                                 'cb.branch_name',
                                 'cp.id as client_policy_id',
@@ -358,8 +392,22 @@ class EmployeeController extends AdminController
                                 '"0" as total',
                                  'policy_type.policy_type' ,
                                  'cp.policy_no' ,
+                                "CASE
+                                    WHEN files.hr_id IS NOT NULL THEN
+                                        CONCAT(
+                                            (
+                                                SELECT lc.name
+                                                FROM level_contacts lc
+                                                WHERE lc.id = files.hr_id
+                                                LIMIT 1
+                                            ),
+                                            ' (HR)'
+                                        )
+                                    ELSE up.first_name
+                                END AS first_name
+                                "
                             ])
-                            ->join('user_profiles up', 'files.created_by = up.id')
+                            ->join('user_profiles up', 'files.created_by = up.id', 'left')
                             ->join('client_policy cp', 'files.policy_id = cp.id', 'left')
                             ->join('client_branch cb', 'files.client_branch_id = cb.id', 'left')
                             ->join('policy_type', 'policy_type.id = cp.policy_type_id')
@@ -369,7 +417,7 @@ class EmployeeController extends AdminController
                             if (in_array($role_id, [2, 3])) {
             
                                 // If the user is a Account Manager or Manager, filter by user_id
-                                $query->where('cr.user_id', $user_id);
+                                $query->where('cr.user_id', (int)$user_id);
                             }
                             // ->where('files.created_by', get_session_userid())
                             $data['fileList'] = $query->groupBy("files.id")->orderBy('files.created_at', 'desc')
@@ -398,6 +446,7 @@ class EmployeeController extends AdminController
                          batch_files.amount, 
                          batch_files.status,
                          batch_files.client_branch_id,
+                         DATE_FORMAT(batch_files.policy_issue_date, '%d/%m/%Y') AS policy_issue_date,
 
                         CASE 
                             WHEN batch_files.status IN ('partially success', 'in-progress-partially', 'failed-7') 
@@ -420,7 +469,7 @@ class EmployeeController extends AdminController
                             if (in_array($role_id, [2, 3])) {
             
                                 // If the user is a Account Manager or Manager, filter by user_id
-                                $query2->where('cr.user_id', $user_id);
+                                $query2->where('cr.user_id', (int)$user_id);
                             }
                             // ->where('files.created_by', get_session_userid())
                             $data['batch_list'] = $query2->groupBy("batch_files.id")->orderBy('batch_files.id', 'desc')
@@ -435,14 +484,50 @@ class EmployeeController extends AdminController
         }
     }
 
-    public function getExcelFileErrors()
-    {
-        $file_id = $this->request->uri->getSegment(3);
+    public function getExcelFileErrors($file_id, $retun_type = null)
+    {   
+
+        $file_data = $this->fileModel->where('id', $file_id)->first();
+        $error = json_decode($file_data['reason'] ?? '{}', true);
+        if(!empty($error) && $retun_type == 'api'){
+            $send = isset($error['error_summary'][5]) || isset($error['error_summary'][6]) ? true : false;
+            if($send){
+
+
+                $string = $error['error_data'] ?? 'System error';
+
+                $errorMap = [
+                    "Column order conflict" => "Invalid file format. Please use the sample file.",
+                ];
+
+                $message = $string; // Default to the original error
+
+                foreach ($errorMap as $keyword => $friendlyMessage) {
+                    if (strpos($string, $keyword) !== false) {
+                        $message = $friendlyMessage;
+                        break; // Stop looking once we find a match
+                    }
+                }
+
+
+                return $this->respond(['status' => false, 'code' => 404,  'message' => $message, 'data' => []], 200);
+            }
+        }
+
+        // $file_id = $this->request->uri->getSegment(3);
         $empServiceController = new EmployeeServiceController();
 
         // Render views and capture output
         $result = $empServiceController->getExcelErrorData($file_id);
 
+        if($retun_type == 'api'){
+            if(!empty($result)){
+                return $this->respond(['status' => true, 'code' => 200, 'message' => 'Error data feteched successfully', 'data' => $result], 200);
+            }else{
+                return $this->respond(['status' => false, 'code' => 404,  'message' => 'Failed to fetch error data', 'data' => []], 200);
+            }
+        }
+
         if ($result != 0) {
 
             $result['file_id'] = $file_id;
@@ -496,6 +581,8 @@ class EmployeeController extends AdminController
              $filePath = ROOTPATH . 'public/sample_excel/sample_multievent_file.xlsx';
         }else if ($actionType == 'bds_upload') {
              $filePath = ROOTPATH . 'public/sample_excel/sample_bds_bulk_upload_excel.xlsx';
+        }else if ($actionType == 'claim_dump_upload') {
+             $filePath = ROOTPATH . 'public/sample_excel/sample_claim_dump_upload_excel.xlsx';
         }
 
         // Check if the file exists
@@ -790,6 +877,7 @@ class EmployeeController extends AdminController
     {
 
         $data = [];
+        
         $data['status'] = ['pending' => 'Pending', 'inprogress' => 'In-Progress', 'complete' => 'Complete'];
         if (count($this->request->getGet())) {
             $filterData = $this->request->getGet();
@@ -799,9 +887,9 @@ class EmployeeController extends AdminController
                 status: $filterData['status'],
                 branch_id: $filterData['branch_id']
             );
-                        $data['getData'] = $filterData;
+            $data['getData'] = $filterData;
             // echo "
";
-            // print_r($data); die;
+            
         }
 
         $this->myLogger->logme('error', 'list called');
@@ -1057,7 +1145,39 @@ class EmployeeController extends AdminController
 
         try {
 
-            $filePath = WRITEPATH . '/uploads/excel/' . $file_name['file_name'];
+            if (!$file_name) {
+                return $this->respond([
+                    'dataStatus' => false,
+                    'code' => 200,
+                    'data' => '
No Data Found
', + 'file_data' => null + ], 200); + } + + $filePath = WRITEPATH . 'uploads/excel/' . $file_name['file_name']; + + // ✅ File not exists on disk + if (!file_exists($filePath)) { + return $this->respond([ + 'dataStatus' => false, + 'code' => 200, + 'data' => '
No Data Found
', + 'file_data' => $file_name + ], 200); + } + + + $excel_data = $empDataServiceController->readExcelFileToArray($filePath); + + // ✅ Excel empty or header only + if (empty($excel_data) || count($excel_data) <= 1) { + return $this->respond([ + 'dataStatus' => false, + 'code' => 200, + 'data' => '
No Data Found
', + 'file_data' => $file_name + ], 200); + } if (file_exists($filePath)) { @@ -1093,8 +1213,7 @@ class EmployeeController extends AdminController $errorMessage = 'Error occurred:' . PHP_EOL . json_encode($errorData, JSON_PRETTY_PRINT); $this->myLogger->logme('error', $errorMessage); - $html = '
No Data Found
'; - return $this->respond(['dataStatus' => false, 'code' => 500, 'data' => $html, 'file_data' => $file_name], 500); + return $this->respond(['dataStatus' => false, 'code' => 500, 'data' => '
Something went wrong
', 'file_data' => $file_name ?? null], 500); } } @@ -1116,7 +1235,7 @@ class EmployeeController extends AdminController public function downloadFullExcelErrorFile($file_id, $rowIndex = 1, $colIndex = 1) { // Get file data from the database - $file_data = $this->fileModel->find($file_id); + $file_data = $this->fileModel->find((int)$file_id); $error = json_decode($file_data['reason']); // echo '
';
@@ -1466,10 +1585,10 @@ class EmployeeController extends AdminController
                 ->join('client_policy', 'client_policy.id = employee_polices.client_policy_id')
                 ->join('employees', 'employees.id = employee_polices.employee_id')
                 ->join('tpa', 'tpa.id = client_policy.tpa_id')
-                ->where('employees.emp_status', 'active')
                 ->where('employees.is_active', '1')
                 ->where("employee_polices.tpa_id IS NOT NULL AND employee_polices.tpa_id <> ''")
-                ->where('employee_polices.status', 'active')
+                ->whereIn('employee_polices.status', ['active', 'expired'])
+                ->whereIn('employees.emp_status', ['active', 'expired'])
                 ->where('employee_polices.is_active', '1')
                 ->where('employee_polices.rand_string', $rand_string)
                 ->first();
@@ -1492,6 +1611,7 @@ class EmployeeController extends AdminController
             
             //new step check in S3 if yes then fetch from S3 bucket 
             $s3_key = 'ecard_'.$get_emp_code_and_client_policy_id['name'].'('.$get_emp_code_and_client_policy_id['emp_code'].')'.'_'.$get_emp_code_and_client_policy_id['tpa_id'].'.pdf';
+            $s3_key = $this->sanitizeFilePart($s3_key);
             // echo $s3_key;die();
             $s3 = \Config\Services::getS3Service();            
             if($s3->exists($s3_key) && $mode != 2) //2 => for bulk generate so skip s3 check and generate PDF
@@ -1727,7 +1847,7 @@ class EmployeeController extends AdminController
         $this->myLogger->logme('error', 'File id for truncate : -- FILE ID : {data} --', ['data' => $file_id]);
 
         //get the files data
-        $file = $this->fileModel->find($file_id);
+        $file = $this->fileModel->find((int)$file_id);
 
         $client_id = $file['client_id'];
         $client_policy_id = $file['policy_id'];
@@ -1785,9 +1905,11 @@ class EmployeeController extends AdminController
                         $query = "
                             UPDATE employee_polices 
                             SET employee_polices.status = 'truncated', employee_polices.is_active = 0 
-                            WHERE employee_polices.file_id = $file_id
+                            WHERE employee_polices.file_id = :file_id:  
                         ";
-                        $db->query($query);
+
+                        $binds = ["file_id" => $file_id];
+                        $db->query($query,$binds);
                         $affectedRows = $db->affectedRows();
                         $this->myLogger->logme('error', '---- employee_polices table update query : {data} ----', ['data' => $query]);
                         $this->myLogger->logme('error', '---- employee_polices table updated - Affected Rows : {data} ----', ['data' => $affectedRows]);
@@ -1879,12 +2001,13 @@ class EmployeeController extends AdminController
                     $dependent_policies = '(' . implode(',', $dependent_policies) . ')';
                     $query = "
                                 UPDATE employee_polices 
-                                JOIN employees ON employees.id = employee_polices.employee_id and employees.emp_code in $emp_codes 
+                                JOIN employees ON employees.id = employee_polices.employee_id and employees.emp_code in :emp_codes:
                                 SET employee_polices.status = 'truncated', employee_polices.is_active = 0 
-                                WHERE employee_polices.client_policy_id in $dependent_policies
+                                WHERE employee_polices.client_policy_id in :dependent_policies:
                             ";
                     // print_r($query); die;
-                    $db->query($query);
+                    $binds = ["emp_codes" => $emp_codes , "dependent_policies" => $dependent_policies];
+                    $db->query($query,$binds);
                     $affectedRows = $db->affectedRows();
 
                     // dd($affectedRows);
@@ -2008,7 +2131,7 @@ class EmployeeController extends AdminController
     {
         $file_id = $this->request->uri->getSegment(3);
         // $file_id = 747;
-        $file = $this->fileModel->find($file_id);
+        $file = $this->fileModel->find((int)$file_id);
         $client_id = $file['client_id'];
         $client_policy_id = $file['policy_id'];
         $loggedInUserID = get_session_userid();
@@ -2072,10 +2195,11 @@ class EmployeeController extends AdminController
                     $query = "
                                 UPDATE employee_polices 
                                 SET employee_polices.status = 'truncated', employee_polices.is_active = 0 
-                                WHERE employee_polices.file_id = $file_id
+                                WHERE employee_polices.file_id = :file_id:
                             ";
                 
-                    $db->query($query);
+                    $binds = ["file_id" => $file_id];
+                    $db->query($query,$binds);
                     $affectedRows = $db->affectedRows();
                     $this->myLogger->logme('error', 'employee_polices table update query : {data}', ['data' => $query]);
                     $this->myLogger->logme('error', 'employee_polices table updated - Affected Rows : {data}', ['data' => $affectedRows]);
@@ -2137,11 +2261,12 @@ class EmployeeController extends AdminController
                     $dependent_policies = '(' . implode(',', $dependent_policies) . ')';
                     $query = "
                                 UPDATE employee_polices 
-                                JOIN employees ON employees.id = employee_polices.employee_id and employees.emp_code in $emp_codes 
+                                JOIN employees ON employees.id = employee_polices.employee_id and employees.emp_code in :emp_codes:
                                 SET employee_polices.status = 'truncated', employee_polices.is_active = 0 
-                                WHERE employee_polices.client_policy_id in $dependent_policies
+                                WHERE employee_polices.client_policy_id in :dependent_policies:
                             ";
-                    $db->query($query);
+                    $binds = ["emp_codes" => $emp_codes , "dependent_policies" => $dependent_policies];
+                    $db->query($query,$binds);
                     $affectedRows = $db->affectedRows();
 
                     // dd($affectedRows);
@@ -2370,7 +2495,7 @@ class EmployeeController extends AdminController
     public function hasPolicyConfigCompleted()
     {
         $client_policy_id = $this->request->uri->getSegment(3);
-        $policy_details = $this->clientPolicyModel->find($client_policy_id);
+        $policy_details = $this->clientPolicyModel->find((int)$client_policy_id);
         $insurer_details = $this->insurerModel->where('id', $policy_details['insurer_id'])->first();
         $policy_terms = isset($policy_details['policy_terms']) ? true : false;
 
@@ -2619,12 +2744,71 @@ class EmployeeController extends AdminController
     //UPDATE EMPLOYEE
     public function update_emp_data()
     {
-        $data = $this->request->getPost();
+    
+        $rules = [
+            // 'emp_code' => [
+            //     'rules'  => 'required',
+            //     'errors' => [
+            //         'required' => 'Employee Code is missing'
+            //     ]
+            // ],
+            // 'name' => [
+            //     'rules'  => 'required|min_length[2]|max_length[100]',
+            //     'errors' => [
+            //         'required'   => 'Employee name is required',
+            //         'min_length' => 'Name must be at least 2 characters',
+            //         'max_length' => 'Name cannot exceed 100 characters'
+            //     ]
+            // ],
+            // 'gender' => [
+            //     'rules'  => 'permit_empty|in_list[M,F]',
+            //     'errors' => [
+            //         'in_list' => 'Invalid gender selected'
+            //     ]
+            // ],
+            'email_corporate' => [
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                'errors' => [
+                    'required'    => 'Email address is required.',
+                    'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                ]
+            ],
+            'mobile' => [
+                'rules'  => 'required|numeric|exact_length[10]',
+                'errors' => [
+                    'required'      => 'Mobile number is required',
+                    'numeric'       => 'Mobile number must contain digits only',
+                    'exact_length'  => 'Mobile number must be exactly 10 digits'
+                ]
+            ],
+        ];
+        $request_post_data   = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($request_post_data);
+        if (isset($data['relationship'])) { 
+                $rules['relationship'] = [
+                    'rules'  => 'required|in_list[Self,Spouse,Child,Father,Mother,Father-in-law,Mother-in-law]',
+                    'errors' => [
+                        'required' => 'Relationship is required',
+                        'in_list'  => 'The selected relationship is invalid.'
+                    ]
+                ];
+        }
+        
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        
+        
         // print_rr($data);die();
         // $data['dob'] = date('Y-m-d', strtotime($data['dob']));
-        if(isset( $data['dob'])){
-            $data['dob'] = change_date_format($data['dob'], null, 'Y-m-d');
-        }
+        $data['dob'] = (!empty($data['dob'])) ? change_date_format($data['dob'], null, 'Y-m-d') : null;
         // print_rr($data); die;
 
         // Fetch current employee data
@@ -2914,12 +3098,15 @@ class EmployeeController extends AdminController
     }
 
     public function mapEmployees(){
-        $client_id = $this->request->getPost('client_id');
-        $branch_id = $this->request->getPost('branch_id');
-        $policy_id = $this->request->getPost('client_policy_id');
-        $selected_employees = (array)$this->request->getPost('selected');
-        $si_amt = $this->request->getPost('si_amt');
-        $policy_start_date_unformatted = $this->request->getPost('policy_start_date');
+        $request_post_data   = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
+
+        $client_id = $sanitized_post_data['client_id'] ?? null;
+        $branch_id = $sanitized_post_data['branch_id'] ?? null;
+        $policy_id = $sanitized_post_data['client_policy_id'] ?? null;
+        $selected_employees = (array)$sanitized_post_data['selected'] ?? [];
+        $si_amt = $sanitized_post_data['si_amt'] ?? null;
+        $policy_start_date_unformatted = $sanitized_post_data['policy_start_date'] ?? null;
 
         $policy_start_date = change_date_format($policy_start_date_unformatted, 'd/M/Y', 'Y-m-d');
 
@@ -2956,7 +3143,9 @@ class EmployeeController extends AdminController
     }
 
     public function unmapEmployees($actionType){
-        $selected_employees = (array)$this->request->getPost('selected');
+        $request_post_data   = $this->request->getPost();
+        $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
+        $selected_employees = (array)$sanitized_post_data['selected'] ?? [];
         if($actionType == 0){
             for($i = 0;$iemployeeModel->set(['client_id' => null, 'client_branch_id' => null])->where('id',$selected_employees[$i])->update();log_message('error',$result1);
@@ -3216,7 +3405,32 @@ class EmployeeController extends AdminController
     public function retailendorsementsave()
     {
         try {
-            $data = $this->request->getPost();
+            $rules = [
+            'endorsement_no' => [
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9\/_\-]+$/]',
+                'errors' => [
+                    'required'    => 'Endorsement Number is missing.',
+                    'regex_match' => 'Endorsement Number can only contain letters, numbers, slashes (/), underscores (_), and hyphens (-).'
+                ]
+            ],
+            'status' => [
+                'rules'  => 'required',
+                'errors' => [
+                    'required'   => 'Status is required',
+                ]
+            ]
+        ];
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+        $request_post_data   = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($request_post_data);
+        
             if (!empty($data['id'])) {
                 $text   = "update";
                 $updateID = $data['id'];
@@ -3346,7 +3560,8 @@ class EmployeeController extends AdminController
 
     public function initiateWellnessOnboard($client_policy_id)
     {
-        $r = Jobs::addJob(['job_name' => 'initiateWellnessOnboardJob','payload' => ['client_policy_id' => $client_policy_id]]);
+         $r = Jobs::addJob(['job_name' => 'initiateWellnessOnboardJob','payload' => ['client_policy_id' => $client_policy_id]]);
+        //$this->initiateWellnessOnboardJob(['client_policy_id' => $client_policy_id]);
         return $this->respond(['status' => true, 'code' => 200, 'message' => 'Process started'], 200);
     }
 
@@ -3356,7 +3571,7 @@ class EmployeeController extends AdminController
         $client_policy_id = $arr['client_policy_id'];
         // $this->updateWellnessOnboardResponseToDB();die();
         // echo $client_policy_id;die();
-         $data = $this->employeePolicyModel->select('employee_polices.*,emp.name,emp.relationship,emp.emp_code,emp.name,emp.email_corporate,emp.mobile,emp.dob,cp.policy_no,cp.wellness_plan_id,cp.wellness_vendor_id,cp.policy_start_date as cp_policy_start_date,cp.policy_end_date,cls.short_name')
+         $data = $this->employeePolicyModel->select('employee_polices.*,emp.name,emp.relationship,emp.emp_code,emp.name,emp.gender,emp.email_corporate,emp.mobile,emp.dob,cp.policy_no,cp.wellness_plan_id,cp.wellness_vendor_id,cp.policy_start_date as cp_policy_start_date,cp.policy_end_date,cls.short_name')
             ->join('client_policy cp', 'cp.id = employee_polices.client_policy_id')
             ->join('clients cls', "cp.client_id = cls.id")
             ->join('employees emp', "emp.id = employee_polices.employee_id")
@@ -3377,7 +3592,7 @@ class EmployeeController extends AdminController
                 // echo '==============================';die();
         // $data = '[{"id":12847,"employee_id":"TEST_EMP_001","client_policy_id":null,"tpa_id":null,"uhid":null,"batch_code":null,"status":"active","pre_existing_alignments":null,"age_band":null,"basic_cover_si":"0","date_coverage":"2025-01-01","policy_end_date":"2025-12-31","days":"0","premium":"0","rata_premimum":"0","gst":"0","si_enhancement_date":null,"date_of_exit":null,"reason_for_exit":null,"claim_status":"0","created_by":null,"created_at":null,"updated_by":null,"updated_at":null,"is_active":"1","rand_string":null,"ecard_sent_status":"0","payable_employee":"0","file_id":null,"wellness_onboard":"0","name":"test name","relationship":"SELF","emp_code":"TEST_EMP_001","email_corporate":"test@gmail.com","mobile":"9797976565","dob":"1975-08-09"},{"id":12846,"employee_id":"TEST_EMP_001","client_policy_id":null,"tpa_id":null,"uhid":null,"batch_code":null,"status":"active","pre_existing_alignments":null,"age_band":null,"basic_cover_si":"0","date_coverage":"2025-01-01","policy_end_date":"2025-12-31","days":"0","premium":"0","rata_premimum":"0","gst":"0","si_enhancement_date":null,"date_of_exit":null,"reason_for_exit":null,"claim_status":"0","created_by":null,"created_at":null,"updated_by":null,"updated_at":null,"is_active":"1","rand_string":null,"ecard_sent_status":"0","payable_employee":"0","file_id":null,"wellness_onboard":"0","name":"dependent 1","relationship":"SON","emp_code":"TEST_EMP_001","email_corporate":"dependent1@gmail.com","mobile":"9898989898","dob":"2001-08-09"}]';
         // $data = (array)json_decode($data,true);
-        // print_r($data);
+        // print_r(count($data));
         // echo '==============================';die();
         if(is_array($data) && count($data))
         {
@@ -3406,7 +3621,7 @@ class EmployeeController extends AdminController
                 $familiesPayload[$empCode] = $this->buildFamilyPayload($empCode, $members);
             }
 
-            // print_r($familiesPayload);die();
+            // print_rr($familiesPayload);die();
             $apiResponse = $this->sendFamiliesToWellnessApi($familiesPayload);
             // print_r($apiResponse);
             $updatedData = $this->updateWellnessOnboardResponseToDB($apiResponse);
@@ -3439,7 +3654,7 @@ class EmployeeController extends AdminController
     {
         // Use the first member as primary reference for policy level data
         $primary = $members[0];
-
+        // print_rr($primary);die();
         // Map DB fields to your required "policyDetails" structure
         $policyStartDate = $primary['cp_policy_start_date'] ?? null;
         // $policyStartDate = '2025-01-01';
@@ -3468,7 +3683,7 @@ class EmployeeController extends AdminController
         foreach ($members as $index => $row) {
 
             // You don't have gender in data, so put null or default
-            $gender = null; // or "MALE" / "FEMALE" if you infer from somewhere
+            
 
             $payload["memberDetails"][] = [
                 "memberId"         => $row["id"],                       // or custom ID (e.g. employee_id.'-'.$index)
@@ -3476,7 +3691,7 @@ class EmployeeController extends AdminController
                 "phone"            => $row["mobile"],
                 "email"            => $row["email_corporate"],
                 "relationshipName" => strtoupper($row["relationship"] ?? ''),
-                "gender"           => $gender,
+                "gender"           => $row["gender"],
                 "dob"              => $row["dob"]
             ];
         }
@@ -3495,6 +3710,7 @@ class EmployeeController extends AdminController
     public function sendFamiliesToWellnessApi(array $familiesPayload): array
     {
         // CI4 HTTP client
+        // print_rr($familiesPayload);die();
         $client =  \Config\Services::curlrequest();//die();
         $endpointUrl = getenv('WELLNESS_ONBOARD_ENDPOINT_URL');
         // Custom headers
@@ -3504,7 +3720,7 @@ class EmployeeController extends AdminController
         ];
 
         foreach ($familiesPayload as $empCode => &$family) {
-
+             // print_rr($family);die();
             try {
                 $response = $client->post($endpointUrl, [
                     'headers' => $headers,
@@ -3522,6 +3738,8 @@ class EmployeeController extends AdminController
                     'rawBody'    => $body,
                     'data'       => $decoded,
                 ];
+
+
             } catch (\Throwable $e) {
                 // In case of exception, store error info
                 $family['apiResponse'] = [
@@ -3531,6 +3749,8 @@ class EmployeeController extends AdminController
                     'error'      => $e->getMessage(),
                 ];
             }
+
+              // print_rr($body );die();
         }
 
         unset($family); // break reference
@@ -4245,7 +4465,7 @@ class EmployeeController extends AdminController
          * 4. SEQUENTIAL RE-QUEUE (SAFE)
          * --------------------------------------------------------- */
         if ($execution_mode === 'sequential' && $rowCount === $batch_size) {
-            $this->myLogger->logme('info', "$log_search_context"."Re-queueing next batch - " .  json_encode([
+            $this->myLogger->logme('error', "$log_search_context"."Re-queueing next batch - " .  json_encode([
                 'next_batch' => $batch_no + 1,
                 'last_emp_id' => $new_last_emp_id
             ]));
@@ -4268,6 +4488,352 @@ class EmployeeController extends AdminController
         $this->myLogger->logme('error', "$log_search_context". "END Batch {$batch_no}");
     }
 
+    public function visitOffBoardCheck()
+    {
+         $params =            [
+                                "memberIds" =>  ["adi_1034292323", "EMPENHANCE-M1", "EMPENHANCE-M3"],
+                                // "policyNumber" => "570000/48/2026/290",
+                                "policyNumber" => "09823428509239",
+                                "source" => "NHANCE"
+                              ];
+        print_rr($this->visitOffBoard($params));
+    }
 
+    /**
+     * Executes the Delete Policy API call.
+     *
+     * @param array $params Contains 'memberIds', 'policyNumber', and 'source'.
+     * @return array
+     */
+    public function visitOffBoard(array $params)
+    {
+        // 1. Load credentials from .env
+        $apiUrl = env('WELLNESS_ONBOARD_ENDPOINT_URL').'delete-policy-with-dependents';
+        $apiToken = env('WELLNESS_ONBOARD_AUTHORIZATION');
+        // echo $apiUrl;die();
+        // 2. Initialize the CI4 CURL service
+        $client =  \Config\Services::curlrequest([
+            'base_uri' => $apiUrl,
+            'timeout'  => 30,
+        ]);
+        
+        // $client = Services::curlrequest([
+        //     'base_uri' => $apiUrl,
+        //     'timeout'  => 30,
+        // ]);
+        $params['source'] = 'NHANCE';
+        try {
+            // Log the start of the request for traceability
+             $this->myLogger->logme('error', 'VISIT_OFFBOARD: ' . ($params['policyNumber'] ?? 'N/A'));
+
+            // 3. Perform the POST request
+            $response = $client->request('POST', '', [
+                'headers' => [
+                    'Authorization' => 'JWT ' . $apiToken,
+                    'Content-Type'  => 'application/json',
+                    'Accept'        => 'application/json',
+                ],
+                'json' => [
+                    'memberIds'    => $params['memberIds'] ?? [],
+                    'policyNumber' => $params['policyNumber'] ?? '',
+                    'source'       => $params['source'] ?? '',
+                ],
+                'http_errors' => false, // Prevents throwing exceptions on 4xx/5xx responses
+            ]);
+
+            $statusCode = $response->getStatusCode();
+            $rawBody    = $response->getBody();
+            $result     = json_decode($rawBody, true);
+
+            // 4. Handle based on HTTP Status Code
+            if ($statusCode >= 200 && $statusCode < 300) {
+                $this->myLogger->logme('error', "VISIT_OFFBOARD API Success (Code $statusCode): Policy deleted.");
+                return [
+                    'status' => true,
+                    'data'   => $result
+                ];
+            }
+
+            // Log API-level errors (4xx or 5xx)
+            $this->myLogger->logme('error', "VISIT_OFFBOARD API Failure (Code $statusCode): " . json_encode($rawBody));
+            return [
+                'status'  => false,
+                'message' => 'The API returned an error response.',
+                'code'    => $statusCode,
+                'details' => $result
+            ];
+
+        } catch (\Exception $e) {
+            // 5. Catch network or system exceptions
+            $this->myLogger->logme('error', 'VISIT_OFFBOARD API Exception: ' . $e->getMessage());
+            return [
+                'status'  => false,
+                'message' => 'A critical error occurred while contacting the API.',
+                'error'   => $e->getMessage()
+            ];
+        }
+    }
+
+    public function insufficientCdBalanceHrMailSend()
+    {
+        $post_data = $this->request->getJson(true);
+
+        if(empty($post_data) || (!isset($post_data['mails']) && !empty($post_data['mails'])) || (!isset($post_data['client_id']) && !empty($post_data['client_id']))){
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'No data send mail'], 200);
+        }
+
+        $client_data = $this->clientModel->where('is_active', 1)->where('id', $post_data['client_id'])->first();
+
+        if(empty($client_data)){
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'No client found'], 200);
+        }
+
+        $notificationModal = new NotificationModel();
+        $notification_data = $notificationModal
+                ->where('client_id', $post_data['client_id'])
+                ->where('template_name', 'hr_cd_insufficient_balance_mail')
+                ->first();
+
+        if(empty($notification_data)){
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'No template found'], 200);
+        }
+
+        if(empty($notification_data['subject']) || empty($notification_data['mail_content'])){
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Template subject or mail content is empty'], 200);
+        }
+    
+        if(empty($notification_data['enabled']) || $notification_data['enabled'] != 1){
+            return $this->respond(['status' => false, 'code' => 404, 'message' => 'Template is disabled'], 200);
+
+        }
+
+        $common['mail_type'] = "hr_cd_insufficient_balance_mail";
+        $common['client_id'] = $post_data['client_id'];
+        $subject = $notification_data['subject'];
+        $mail_content = $notification_data['mail_content'];
+
+        foreach ($post_data['mails'] as $hr_id => $hr_data) {
+
+            $mail_content = str_ireplace('{{hr_name}}', $hr_data['name'], $mail_content);
+            $mail_content = str_ireplace('{{client_name}}', $client_data['client_name'], $mail_content);
+            $res = MailHelper::send_email(['mail' => $hr_data['mail'], 'subject' => $subject, 'message' => $mail_content, 'common' => $common]);
+        }
+
+        return $this->respond(['status' => true, 'code' => 200, 'message' => 'Mail send successfully'], 200);
+
+    }
+
+    public function clearCdSession() 
+    {
+        clear_cd_balance_session();
+        return $this->response->setJSON(['status' => 'cleared']);
+    }
+
+    public function checkSessionStatus()
+    {
+        // Uses your existing get_cd_balance() helper
+        $data = get_cd_balance();
+
+        // Return as JSON so JavaScript can read it
+        return $this->response->setJSON($data);
+    }
+
+    public function bulkEcardDownloadAsZipFromS3($params)
+    {
+        try {
+            $limit = $params['limit'] ?? 100;
+            $batch_no = $params['batch_no'] ?? 1;
+            $last_id = $params['last_emp_policy_id'] ?? 0;
+
+            // Fetch batch data
+            $employee_data = $this->employeePolicyModel->getEmployeeDataWithPolicyUsingClientPolicyIdOrEmployeePolicyIds($params, $limit, $last_id);
+
+            if (empty($employee_data)) {
+
+                if(!empty($last_id)){
+                    $this->myLogger->logme('error', "bulkEcardDownloadAsZipFromS3 - No more records after last_emp_policy_id: {$last_id}");
+                    $this->myLogger->logme('error', "getEmployeeEcardFromTmpFolderAndZipToS3 - Queuing ZIP creation for folder: " . ($params['folder_name'] ?? 'N/A'));
+                    $r = Jobs::addJob(['job_name' => 'getEmployeeEcardFromTmpFolderAndZipToS3', 'payload'  => $params]);
+                }
+
+                return ['status' => true, 'message' => 'Proceeding to Zip'];
+            }
+
+            // Use a consistent folder name across batches (passed in params)
+            $folderName = $params['folder_name'] ?? 'bulk_ecards_' . $employee_data[0]['policy_no'] . '_' . date('Y-m-d_H-i-s');
+            $tempPath = FCPATH . 'tmp/' . $folderName . '/';
+
+            if (!is_dir($tempPath)) {
+                mkdir($tempPath, 0777, true);
+            }
+
+            $s3 = \Config\Services::getS3Service();
+            $pdf_count = 0;
+            $current_last_id = end($employee_data)['emp_policy_id'];
+
+            foreach ($employee_data as $emp_value) {
+                // Track the last ID in this batch
+                $current_last_id = $emp_value['emp_policy_id'];
+
+                $s3_key = 'ecard_' . $emp_value['name'] . '(' . $emp_value['emp_code'] . ')' . '_' . $emp_value['tpa_id'] . '.pdf';
+                $s3_key = $this->sanitizeFilePart($s3_key);
+
+                if ($s3->exists($s3_key)) {
+                    $s3_url = $s3->getPresignedUrl($s3_key);
+                    $pdf_content = file_get_contents($s3_url['url']);
+
+                    if ($pdf_content !== false) {
+                        file_put_contents($tempPath . $s3_key, $pdf_content);
+                        $pdf_count++;
+                    }
+                }
+            }
+
+            $hasMore = count($employee_data) == $limit;
+
+            $payload = [
+                'batch_no' => $batch_no + 1,
+                'last_emp_policy_id' => $current_last_id,
+                'folder_name' => $folderName,
+                'processed_in_this_batch_data_count' => count($employee_data),
+                'pdf_count' => $pdf_count,
+                'hr_id' => $params['hr_id'] ?? null
+            ];
+
+            if ($hasMore) {
+                $this->myLogger->logme('error', "bulkEcardDownloadAsZipFromS3 - Queuing next batch: " . json_encode($payload));
+                $r = Jobs::addJob(['job_name' => 'bulkEcardDownloadAsZipFromS3', 'payload'  => $payload]);
+                $message = "Queuing next batch";
+            } else {
+                $this->myLogger->logme('error', "bulkEcardDownloadAsZipFromS3 - No more records after this batch. Next proceeding with getEmployeeEcardFromTmpFolderAndZipToS3");
+                $r = Jobs::addJob(['job_name' => 'getEmployeeEcardFromTmpFolderAndZipToS3', 'payload'  => $payload]);
+                $message = "All batch completed. Next proceeding with getEmployeeEcardFromTmpFolderAndZipToS3";
+            }
+
+            return ['status' => true, 'message' => $message];
+
+        } catch (\Throwable $e) {
+
+            $mail_response = $this->sendMailToHrWithZipAttachments($params);
+
+            $context = [
+                'error_message' => $e->getMessage(),
+                'exception_class' => get_class($e),
+                'file' => $e->getFile(),
+                'line' => $e->getLine(),
+                'trace' => $e->getTraceAsString(),
+                'mail_response' => $mail_response,
+                'params' => $params,
+            ];
+
+            // Log detailed context for debugging
+            $this->myLogger->logme('error', 'bulkEcardDownloadAsZipFromS3 - Exception' . json_encode($context, JSON_PRETTY_PRINT));
+
+            // Return a detailed, structured error response
+            return [
+                'status'  => false,
+                'message' => 'Exception occurred during bulk e-card download and ZIP creation.',
+                'error'   => $context,
+            ];
+        }
+    }
+
+    public function getEmployeeEcardFromTmpFolderAndZipToS3($params)
+    {
+        try {
+
+            $zipService = new \App\Libraries\ZipService();
+
+            $source = FCPATH . 'tmp/' . $params['folder_name'];
+            $destination = '/';
+            $zipName = $params['folder_name'] . '.zip' ?? '';
+
+            $result = $zipService->zipAndUploadS3($source, $destination, $zipName);
+
+            if($result['status'] === false){
+                $this->myLogger->logme('error', "getEmployeeEcardFromTmpFolderAndZipToS3 - ZIP creation/upload failed: " . json_encode($result));
+                $params['url'] = null; // Indicate failure
+            }else{
+                $params['url'] = $result['presigned_url']['url'] ?? null;
+            }
+
+
+
+
+            $mail_response = $this->sendMailToHrWithZipAttachments($params);
+
+            return ['zip_responce' => $result, 'mail_response' => $mail_response];
+
+        } catch (\Throwable $e) {
+
+            $mail_response = $this->sendMailToHrWithZipAttachments($params);
+
+            $context = [
+                'error_message' => $e->getMessage(),
+                'exception_class' => get_class($e),
+                'file' => $e->getFile(),
+                'line' => $e->getLine(),
+                'trace' => $e->getTraceAsString(),
+                'mail_response' => $mail_response,
+                'params' => $params,
+            ];
+
+            // Log detailed context for debugging
+            $this->myLogger->logme('error', 'getEmployeeEcardFromTmpFolderAndZipToS3 - Exception' . json_encode($context, JSON_PRETTY_PRINT));
+
+            // Return a detailed, structured error response
+            return [
+                'status'  => false,
+                'message' => 'Exception occurred during bulk e-card download and ZIP creation.',
+                'error'   => $context,
+            ];
+
+        }
+    }
+
+    public function sendMailToHrWithZipAttachments($params)
+    {
+        $hr_id = $params['hr_id'] ?? null;
+        $url = $params['url'] ?? null;
+
+        if (empty($hr_id)) {
+            $this->myLogger->logme('error', "sendMailToHrWithZipAttachments - No HR ID provided.");
+            return ['status' => false, 'message' => 'No HR ID provided.'];
+        }
+
+        $hr_data = $this->LevelContactModel->where('id', $hr_id)->where('is_active', 1)->first();
+
+        if (empty($hr_data) || empty($hr_data['email'])) {
+            $this->myLogger->logme('error', "sendMailToHrWithZipAttachments - No valid HR data found for ID: {$hr_id}");
+            return ['status' => false, 'message' => 'No valid HR data found for ID: ' . $hr_id];
+        }
+
+        if(empty($url)){
+            $subject = "Employee Bulk E-Card Download Failed";
+            $message = "Dear {$hr_data['name']},

We were unable to generate the bulk e-card ZIP file. Please re-initialize the process or contact support to retry."; + }else{ + $subject = "Employee Bulk E-Cards Download"; + $message = "Dear {$hr_data['name']},

Please find the employee e-cards attached below.

Download Link: Download E-Cards

Note : This link valid for 2 days only."; + } + + $bbc = 'venkateshraman786@gmail.com'; + + $mail_response = MailHelper::send_email([ + 'mail' => $hr_data['email'], + 'subject' => $subject, + 'message' => $message, + 'bcc' => $bbc, + 'common' => [ + 'mail_type' => 'employee_bulk_ecard_download_by_hr', + ] + ]); + + $this->myLogger->logme('error', "sendMailToHrWithZipAttachments - Email sent to HR ID: {$hr_id}, Email Response: " . json_encode($mail_response)); + return $mail_response; + } + + public function downloadZip(){ + + } } diff --git a/app/Controllers/EmployeeMultiEventServiceController.php b/app/Controllers/EmployeeMultiEventServiceController.php index 3c8846e9..634af029 100644 --- a/app/Controllers/EmployeeMultiEventServiceController.php +++ b/app/Controllers/EmployeeMultiEventServiceController.php @@ -1018,7 +1018,7 @@ class EmployeeMultiEventServiceController extends BaseController $this->myLogger->logme('error', 'Excel Multievent File Formate Validation function START: ' . json_encode(['params' => $params])); helper('excel_util_helper'); $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); if (!isset($file)) { //file not found in DB @@ -1182,7 +1182,7 @@ class EmployeeMultiEventServiceController extends BaseController helper('excel_util_helper'); $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); if (!isset($file)) { //file not found in DB @@ -1291,7 +1291,7 @@ class EmployeeMultiEventServiceController extends BaseController helper('excel_util_helper'); $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); if (!isset($file)) { //file not found in DB @@ -1387,7 +1387,7 @@ class EmployeeMultiEventServiceController extends BaseController // dd($params); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; // dd($file); @@ -1604,7 +1604,7 @@ class EmployeeMultiEventServiceController extends BaseController //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; // Kint::dump($file); @@ -1805,7 +1805,7 @@ class EmployeeMultiEventServiceController extends BaseController { //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; // dd($file); $return = []; @@ -1910,7 +1910,7 @@ class EmployeeMultiEventServiceController extends BaseController // $this->setPullNotification($this->getFileMetaDataByFileId($file_id, 'success')); } else if (isset($params['client_policy_id'])) //handle data from enrollment to inception { - $client_policy_id = $params['client_policy_id']; + $client_policy_id = (int)$params['client_policy_id']; //get client id $client_id = ($this->clientPolicyModel->select('client_id')->find($client_policy_id))['client_id']; // dd($client_id); @@ -1956,7 +1956,7 @@ class EmployeeMultiEventServiceController extends BaseController //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; // dd($file); @@ -1975,7 +1975,7 @@ class EmployeeMultiEventServiceController extends BaseController $endorsement_data = []; $policy_terms = $this->clientPolicyModel->getPolicyDetails($file['client_id'], $file['policy_id']); $insurer = new InsurerModel(); - $insurer = ($insurer->find($policy_terms[0]->insurer_id)); + $insurer = ($insurer->find((int)$policy_terms[0]->insurer_id)); // kint::dump($insurer); //make closure funciton which is going to use only by this method @@ -2095,7 +2095,7 @@ class EmployeeMultiEventServiceController extends BaseController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; @@ -2173,7 +2173,7 @@ class EmployeeMultiEventServiceController extends BaseController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $file['action'] = $params['action']; // $file_name_with_path = WRITEPATH . "/uploads/excel/" . $file['file_name']; @@ -2470,7 +2470,7 @@ class EmployeeMultiEventServiceController extends BaseController { try { - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // $error_data = json_decode($file['reason'], true); $error_data = $res; // dd($error_data); @@ -2632,7 +2632,7 @@ class EmployeeMultiEventServiceController extends BaseController // dd($this->request); helper('excel_util_helper'); //get file name - $file_id = $params['file_id']; + $file_id = (int)$params['file_id']; $file = $this->fileModel->find($file_id); // dd($file); $return = []; diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index 8f141d28..a187e819 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -40,6 +40,9 @@ use App\Models\HRAccessControlModel; use App\Models\InsurerModel; use App\Models\HrFileUploadModel; use App\Models\EmployeeRetailPolicy; +use App\Models\BatchFileModel; +use App\Models\ClientDepositModel; +use App\Models\PTFileModel; @@ -66,6 +69,8 @@ use Kreait\Firebase\Messaging\CloudMessage; use Kreait\Firebase\Messaging\Notification; use Kreait\Firebase\Exception\MessagingException; +use Firebase\JWT\JWT; + class EmployeeRestController extends AdminController { @@ -100,6 +105,7 @@ class EmployeeRestController extends AdminController protected $hrFileUploadModel; protected $ticketMailTemplateModel; protected $employeeRetailPolicy; + protected $batchFileModel; public function __construct() @@ -134,6 +140,7 @@ class EmployeeRestController extends AdminController $this->insurerModel = new InsurerModel(); $this->hrFileUploadModel = new HrFileUploadModel(); $this->ticketMailTemplateModel = new TicketMailTemplateModel(); + $this->batchFileModel = new BatchFileModel(); } @@ -159,9 +166,9 @@ class EmployeeRestController extends AdminController ->where('employees.client_branch_id', $client_branch_id) ->where('employees.is_active', 1) ->where('employees.relationship', $relationship) - ->where('employee_polices.status', ['active']) + ->whereIn('employee_polices.status', ['active', 'expired']) ->where('employees.is_active', 1) - ->where('employees.emp_status', ['active']) + ->whereIn('employees.emp_status', ['active', 'expired']) ->first(); if (null !== $this->request->getGet('client_policy_id')) { @@ -178,7 +185,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result, 'AccountManagerDetails' => isset($AccountManagerDetails[0]) ? $AccountManagerDetails[0] : null], 200); } else { $result = "No Match's"; - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200); } } catch (\Throwable $th) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $th], 500); @@ -199,7 +206,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200); } else { $result = "No Match's"; - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200); } } } catch (\Throwable $th) { @@ -225,7 +232,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $employee], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } } catch (\Throwable $th) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $th], 500); @@ -254,7 +261,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200); } else { $result = "No Matches"; - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200); } } } catch (\Exception $e) { @@ -270,7 +277,7 @@ class EmployeeRestController extends AdminController $openForEnrollment = $this->findThePolicyIsOpenForEnrollment($data[0]->client_policy_id); if ($openForEnrollment == false) { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Enrollment closed'], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Enrollment closed'], 200); } if ($data) { @@ -319,7 +326,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200); } else { $result = "No Matches"; - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200); } } } catch (\Exception $e) { @@ -520,7 +527,7 @@ class EmployeeRestController extends AdminController ->update(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); @@ -536,7 +543,7 @@ class EmployeeRestController extends AdminController $openForEnrollment = $this->findThePolicyIsOpenForEnrollment($requestData[0]->client_policy_id); if ($openForEnrollment == false) { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Enrollment closed'], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Enrollment closed'], 200); } foreach ($requestData as $key => $value) { @@ -599,12 +606,13 @@ class EmployeeRestController extends AdminController public function getEmployeeAndDependenceByClientId() { try { - $empData = $this->employeePolicyModel->getEmployeePolicy(client_id: $this->request->getGet('client_id'), policy_id: $this->request->getGet('client_policy_id'), status: 0, branch_id: $this->request->getGet('client_branch_id')); + + $empData = $this->employeePolicyModel->getEmployeePolicy(client_id: $this->request->getGet('client_id'), policy_id: $this->request->getGet('client_policy_id'), status: 0, branch_id: $this->request->getGet('client_branch_id'), status_type: 'inactive'); if ($empData) { return $this->respond(['status' => 'success', 'code' => 200, 'data' => $empData], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); @@ -705,7 +713,7 @@ class EmployeeRestController extends AdminController if ($ClientPolicyData) { return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); @@ -1046,7 +1054,7 @@ class EmployeeRestController extends AdminController $this->fileModel->where('id', $file_id)->set(['status' => 'success', 'reason' => '', 'error_data' => ''])->update(); return $this->respond(['status' => 'success', 'code' => 200, 'message' => "Success"], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'message' => "Client id and Client Policy id is Not Match!"], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'message' => "Client id and Client Policy id is Not Match!"], 200); } } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); @@ -1665,14 +1673,14 @@ class EmployeeRestController extends AdminController { try { - $client = $this->clientModel->where('id', $this->request->getGet('client_id'))->first(); + $client = $this->clientModel->where('md5(id)', $this->request->getGet('client_id'))->first(); if (!empty($client)) { $client['client_logo'] = base_url() . 'public/uploads/logo/' . $client['client_logo']; - $clientPolicy = $this->clientPolicyModel->where('client_id', $this->request->getGet('client_id')) + $clientPolicy = $this->clientPolicyModel->where('md5(client_id)', $this->request->getGet('client_id')) ->where('client_branch_id', $this->request->getGet('client_branch_id'))->findAll(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => ['client' => $client, 'client_policy' => $clientPolicy]], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); @@ -2220,7 +2228,7 @@ class EmployeeRestController extends AdminController // dd($client_policy_id); - $client_id = ($this->clientPolicyModel->select('client_id')->find($client_policy_id))['client_id']; + $client_id = ($this->clientPolicyModel->select('client_id')->find((int)$client_policy_id))['client_id']; // get policy and rack details $policy_terms = $this->clientPolicyModel->getPolicyDetails($client_id, $client_policy_id); $policy_type = $policy_terms[0]->is_addon; @@ -2305,9 +2313,36 @@ class EmployeeRestController extends AdminController } + if($this->request->getGet('policy_status') == 0){ + $emp_policy_status = 'expired'; + }else{ + $emp_policy_status = 'active'; + } - $ClientPolicyData = $this->clientPolicyModel->select('client_policy.id as client_policy_id , client_policy.client_id as client_id, client_policy.policy_type_id as policy_type_id, client_policy.is_addon as is_addon , client_policy.open_for_enrollment as OpenForEnrollment , client_policy.inception_type as inception_type, client_policy.policy_no as policy_no, client_policy.insurer_id as insurer_id, DATE_FORMAT(client_policy.policy_end_date, "%d-%m-%Y") AS policy_expiry_date ') - ->where('client_policy.client_id', $this->request->getGet('client_id')) + $db = \Config\Database::connect(); + $emp_policy_status = $db->escape($emp_policy_status); + + $ClientPolicyData = $this->clientPolicyModel + ->select(" + client_policy.id as client_policy_id , + client_policy.client_id as client_id, + client_policy.policy_type_id as policy_type_id, + client_policy.is_addon as is_addon , + client_policy.open_for_enrollment as OpenForEnrollment , + client_policy.inception_type as inception_type, + client_policy.policy_no as policy_no, + client_policy.insurer_id as insurer_id, + DATE_FORMAT(client_policy.policy_start_date, '%d-%m-%Y') AS policy_start_date, + DATE_FORMAT(client_policy.policy_end_date, '%d-%m-%Y') AS policy_expiry_date, + ( + select COALESCE(ROUND(SUM(rata_premimum + gst)), 0) + from employee_polices + where is_active = 1 + and status = $emp_policy_status + and client_policy_id = client_policy.id + ) as total_premium + ", false) + ->where('md5(client_policy.client_id)', $this->request->getGet('client_id')) ->where('client_policy.client_branch_id', $this->request->getGet('client_branch_id')) ->where('client_policy.is_active', 1) ->where('client_policy.policy_status', $this->request->getGet('policy_status')) @@ -2325,15 +2360,15 @@ class EmployeeRestController extends AdminController $value['policy_name'] = $policyTypeData->long_name; $value['insurer_name'] = $insurerData->name; $value['insurer_short_name'] = $insurerData->short_name; - $employeeDetails = $this->employeePolicyModel->getEmployeePolicy(client_id: $value['client_id'], policy_id: $value['client_policy_id'], status: 0, branch_id: $this->request->getGet('client_branch_id')); + $employeeDetails = $this->employeePolicyModel->getEmployeePolicy(client_id: $value['client_id'], policy_id: $value['client_policy_id'], status: 0, branch_id: $this->request->getGet('client_branch_id'), status_type: 'inactive'); // dd($employeeDetails); $activeCount = 0; $inactiveCount = 0; - if (count($employeeDetails)) { + if (count($employeeDetails)) { foreach ($employeeDetails as $item) { - if ($item['emp_status'] === 'active') { + if ($item['status'] === 'active') { $activeCount++; - } elseif ($item['emp_status'] === 'inactive') { + } elseif ($item['status'] === 'inactive') { $inactiveCount++; } } @@ -2342,6 +2377,8 @@ class EmployeeRestController extends AdminController $value['totalMembersCount'] = count($employeeDetails); $value['membersCountOfActive'] = $activeCount; $value['membersCountOfInactive'] = $inactiveCount; + $value['is_ecard_bulk_download'] = 0; + $value['is_ecard_bulk_download_for_employee'] = 0; array_push($result, $value); @@ -2420,6 +2457,7 @@ class EmployeeRestController extends AdminController $result = $clientController->view_Deposit($insurerId, $requestFrom = 'rest', $param = ['client_id' => $clientId, 'cd_ac_pk' => $cdAccountPrimaryKey]); $data['insurer_name'] = $result['insurerName']->name; + $data['insurer_short_name'] = $result['insurerName']->short_name; $data['account_number'] = $result['insurerName']->cd_master_account_no; $data['total_deposit'] = $result['deposiamount']->total_credit; $data['total_consumed'] = $result['deposiamount']->total_withdraw; @@ -2443,7 +2481,13 @@ class EmployeeRestController extends AdminController if ($this->request->is('get')) { - $data['claim_status'] = $this->claimStatusModel->select('id,ticket_type,claim_status')->where('is_active', 1)->findAll(); + $data['claim_status'] = $this->claimStatusModel + ->select('id,ticket_type, display_name as claim_status') + ->where('is_active', 1) + ->where('display_name IS NOT NULL OR display_name <> ""') + ->groupBy('display_name') + ->findAll(); + $data['ticket_type'] = [ ["ticket_type" => "1", "type_name" => "Claim-GMC"], ["ticket_type" => "2", "type_name" => "Claim-GPA"], @@ -2458,12 +2502,21 @@ class EmployeeRestController extends AdminController $search_data = $this->request->getJSON(true); // Get JSON as associative array + if ( isset($search_data['client_id']) &&is_string($search_data['client_id']) && preg_match('/^[a-f0-9]{32}$/i', $search_data['client_id'])) { + $client_data = $this->clientModel->where('MD5(id)', $search_data['client_id'])->first(); + $search_data['client_id'] = $client_data['id'] ?? null; + } + + $client_id = isset($search_data['client_id']) ? (int) $search_data['client_id'] : 0; unset($search_data['client_id']); + unset($search_data['client_branch_id']); + $policy_number = isset($search_data['policy_no']) ? $search_data['policy_no'] : null; $from_date = isset($search_data['from_date']) ? $search_data['from_date'] : null; $to_date = isset($search_data['to_date']) ? $search_data['to_date'] : null; - unset($search_data['from_date'], $search_data['to_date']); + $claim_status_id = isset($search_data['claim_status_id']) ? $search_data['claim_status_id'] : null; + unset($search_data['from_date'], $search_data['to_date'], $search_data['claim_status_id'], $search_data['policy_no']); $where = []; @@ -2474,6 +2527,11 @@ class EmployeeRestController extends AdminController } } + $claim_status_ids = []; + if(!empty($claim_status_id)){ + $claim_status_ids = $this->getTicketClaimStatusIdBasedOnTheDisplayName($claim_status_id); + } + $builder = $db->table('ticket_master tm'); $builder->select([ 'tm.id', @@ -2485,6 +2543,12 @@ class EmployeeRestController extends AdminController ELSE UPPER(tcs.display_name) END AS status ", + "CASE + WHEN tm.tpa_claim_type IS NOT NULL OR tm.tpa_claim_type != '' + THEN tm.tpa_claim_type + ELSE 'Reimbursement' + END AS cl_type + ", 'tm.claim_number AS claim_no', 'tm.claim_status_id', 'tm.is_head_approved', @@ -2568,6 +2632,14 @@ class EmployeeRestController extends AdminController $builder->where($where); } + if (!empty($claim_status_ids)) { + $builder->whereIn('claim_status_id', $claim_status_ids); + } + + if (!empty($policy_number)) { + $builder->where('cp.policy_no', $policy_number); + } + $builder->orderBy('tm.id', 'DESC'); $data = $builder->get()->getResultArray(); @@ -2929,6 +3001,7 @@ class EmployeeRestController extends AdminController $data['claim_subject'] = "Claim GTLI"; $data['sum_insured_label'] = "Sum Assured"; } else if ($ClientPolicyValue['policy_type_id'] == 72){ + $policyGroup = 'other'; $data['ticket_type_id'] = 72; $data['ticket_settled_status_id'] = 76; $data['claim_subject'] = "Claim OPD"; @@ -3175,7 +3248,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'success', 'code' => 200, 'data' => $feContentData], 200); } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No Data'], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No Data'], 200); } } catch (\Throwable $th) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $th], 500); @@ -3200,7 +3273,7 @@ class EmployeeRestController extends AdminController // return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200); // } else { - // return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No Data'], 404); + // return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No Data'], 200); // } // } catch (\Throwable $th) { // return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $th], 500); @@ -3273,7 +3346,7 @@ class EmployeeRestController extends AdminController // Check if the update was successful if ($db->affectedRows() > 0) { // Fetch the updated employee data - $updated_employee = $this->employeeModel->find($id); + $updated_employee = $this->employeeModel->find((int)$id); return $this->respond(['status' => 'success', 'code' => 200, 'data' => $updated_employee], 200); } else { log_message('error', 'Update failed. No rows affected.'); @@ -3286,7 +3359,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'failed', 'code' => 400, 'message' => 'Firebase Token is required'], 400); } } else { - return $this->respond(['status' => 'failed', 'code' => 404, 'message' => 'Employee not found'], 404); + return $this->respond(['status' => 'failed', 'code' => 404, 'message' => 'Employee not found'], 200); } } catch (\Throwable $th) { log_message('error', 'An error occurred: ' . $th->getMessage()); @@ -3571,6 +3644,26 @@ class EmployeeRestController extends AdminController $get_docs_name = $this->request->getPost('claim_doc_names') ?? []; $policy_transaction_id = $this->request->getPost('policy_transaction_id') ?? null; + + if (is_string($received_data['client_id']) && preg_match('/^[a-f0-9]{32}$/i', $received_data['client_id'])) { + $client_data = $this->clientModel->where('MD5(id)', $received_data['client_id'])->first(); + $received_data['client_id'] = $client_data['id'] ?? null; + } + + $client_policy_data = $this->clientPolicyModel->where('id', $received_data['client_policy_id'] ?? null)->first(); + + $isduplicate = checkDuplicateClaim([ + 'doa' => change_date_format($received_data['doa'] ?? '') ?? null, + 'emp_code' => $received_data['emp_code'] ?? null, + 'claim_amount' => $received_data['claim_amount'] ?? null, + 'policy_no' => $client_policy_data['policy_no'] ?? null + ]); + + if($isduplicate){ + $response = ['status' => false, 'code' => 404, 'message' => 'Claim already exist']; + return $this->respond($response, 200); + } + if(!empty($policy_transaction_id)){ $response = $this->retailClaimInitiate($received_data); return $this->respond($response, 200); @@ -3635,19 +3728,21 @@ class EmployeeRestController extends AdminController from employees emp - left join employees empl on empl.id = $insured_emp_id and empl.is_active = 1 and empl.emp_status = 'active' - left join employee_polices emp_pol on empl.id = emp_pol.employee_id and emp_pol.client_policy_id = $client_policy_id and emp_pol.is_active = 1 + left join employees empl on empl.id = :insured_emp_id: and empl.is_active = 1 and empl.emp_status = 'active' + left join employee_polices emp_pol on empl.id = emp_pol.employee_id and emp_pol.client_policy_id = :client_policy_id: and emp_pol.is_active = 1 left join client_policy cp on cp.id = emp_pol.client_policy_id and cp.is_active = 1 left join client_rm cl_rm on cl_rm.client_id = empl.client_id and cl_rm.is_active = 1 and cl_rm.level = 3 - where emp.id = $employee_id and emp.is_active = 1 and emp.emp_status = 'active' + where emp.id = :employee_id: and emp.is_active = 1 and emp.emp_status = 'active' limit 1 "; - $emp_ticket_data = $this->employeeModel->query($sql)->getResultArray(); + $binds = ['insured_emp_id'=>$insured_emp_id,'client_policy_id'=>(int)$client_policy_id,'employee_id'=>$employee_id ]; + $emp_ticket_data = $this->employeeModel->query($sql,$binds)->getResultArray(); // print_r(db_connect()->getLastQuery()); die; if (!empty($emp_ticket_data)) { + unset($received_data['relationship']); $fetchData = $emp_ticket_data[0]; $claimStatusQuery = $this->claimStatusModel @@ -3671,7 +3766,9 @@ class EmployeeRestController extends AdminController $fetchData['claim_type'] = 1; $fetchData = array_merge($fetchData, $received_data); - $fetchData['relationship'] = strtolower($fetchData['relationship']) ?? $fetchData['relationship']; + // $fetchData['relationship'] = strtolower($fetchData['relationship']) ?? $fetchData['relationship']; + $fetchData['relationship'] = isset($fetchData['relationship']) ? strtolower($fetchData['relationship']) : null; + // print_r($fetchData); die; $insert_status = $this->ticketMaster->insert($fetchData); $ticket_id = $this->ticketMaster->insertID(); @@ -3728,7 +3825,7 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => false, 'code' => 400, 'message' => $message], 200); } } else { - return $this->response->setJSON(['status' => false, 'code' => 200, 'message' => 'The employee is not active.'])->setStatusCode(404); + return $this->response->setJSON(['status' => false, 'code' => 200, 'message' => 'The employee is not active.'])->setStatusCode(200); } } @@ -3840,10 +3937,14 @@ class EmployeeRestController extends AdminController log_message('error', 'Ticket validation | Ticket ID: ' . $ticket_id . ' | Matching active tickets with NULL TPA reference: ' . $count); if ($count > 0 && $pdf_exist_in_the_file && $tpa_claim_push == true) { - // this call for TPA integration - $apiServiceController = new ApiServiceController(); - $apiServiceController->pushClaims($ticket_id); - log_message('error', "pushClaims function called with Ticket ID: {$ticket_id}, In Employee Rest Controller"); + try { + // this call for TPA integration + $apiServiceController = new ApiServiceController(); + $apiServiceController->pushClaims($ticket_id); + log_message('error', "pushClaims function called with Ticket ID: {$ticket_id}, In Employee Rest Controller"); + } catch (\Throwable $e) { + log_message('error', "Error in TPA Claim Push via Benifits or HR : ApiServiceController pushClaims function call for Ticket ID: {$ticket_id}. Error: " . $e->getMessage() . " Trace: " . $e->getTraceAsString()); + } } else { if($tpa_claim_push == false){ log_message('error', 'Skip the TPA claim push for IR DOCUMENTS'); @@ -3910,7 +4011,7 @@ class EmployeeRestController extends AdminController 'status' => false, 'code' => 404, 'message' => 'No policy types found' - ])->setStatusCode(404); + ])->setStatusCode(200); } $this->myLogger->logme('error', "Policy type IDs fetched: " . json_encode($policy_type_ids)); @@ -3983,7 +4084,7 @@ class EmployeeRestController extends AdminController // dd($emp_id); if (empty($emp_id)) { - return $this->response->setJSON(['status' => false, 'code' => 200, 'message' => 'emp_id is required.'])->setStatusCode(404); + return $this->response->setJSON(['status' => false, 'code' => 200, 'message' => 'emp_id is required.'])->setStatusCode(200); } $retail_ticket_data = []; @@ -4434,60 +4535,122 @@ class EmployeeRestController extends AdminController 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_id' => $this->request->getPost('policy_id'), - 'policy_no' => $this->request->getPost('policy_no'), - 'file_name' => $newFileName, - 'file_action' => $this->request->getPost('file_action'), - 'status' => 'Yet to start', - 'created_by' => $this->request->getPost('created_by'), - 'updated_by' => $this->request->getPost('created_by'), + $post_data = [ + 'client_id' => $this->request->getPost('client_id') ?? null, + 'client_branch_id' => $this->request->getPost('client_branch_id') ?? null, + 'policy_id' => $this->request->getPost('policy_id') ?? null, + 'file_action' => $this->request->getPost('file_action') ?? null, + 'created_by' => $this->request->getPost('created_by') ?? null, + 'file_name' => $this->request->getFile('file_name') ?? null, + 'policy_no' => $this->request->getPost('policy_no') ?? null, ]; + // print_r($post_data); die; - // Save into DB - $result = $this->hrFileUploadModel->insert($data); - - if ($result) { - $this->giveNotificationToClientsAccountManager($data); + if(empty($post_data['client_id'])){ + return $this->respondCreated(['status' => false, 'message' => 'Client is required', 'data' => []]); } - return $this->respondCreated([ + // Handle raw client_id vs MD5 + if (is_string($post_data['client_id']) && preg_match('/^[a-f0-9]{32}$/i', $post_data['client_id'])) { + $client_data = $this->clientModel->where('MD5(id)', $post_data['client_id'])->first(); + $post_data['client_id'] = $client_data['id']; + } else { + $client_data = $this->clientModel->where('id', $post_data['client_id'])->first(); + } + + if(empty($client_data)){ + return $this->respondCreated(['status' => false, 'message' => 'Invalid Client Id', 'data' => []]); + } + + // print_r($client_data); die; + + if($client_data['hr_file_processed_by'] == 1){ + $responce = $this->fileUploadInFilesTable($post_data); + }else{ + $responce = $this->fileUploadInHrFileUploadTable($post_data); + } + + return $this->respondCreated($responce); + + } catch (\Exception $e) { + // return $this->failServerError($e->getMessage()); + return $this->respondCreated(['status' => false, 'message' => $e->getMessage(), 'data' => []]); + } + } + + public function fileUploadInHrFileUploadTable($post_data) + { + // Check file + $file = $post_data['file_name']; + + if (!$file) { + return [ + 'status' => false, + 'message' => "Invalid file or file not uploaded.", + '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' => $post_data['client_id'], + 'client_branch_id' => $post_data['client_branch_id'], + 'policy_id' => $post_data['policy_id'], + 'policy_no' => $post_data['policy_no'], + 'file_name' => $newFileName, + 'file_action' => $post_data['file_action'], + 'status' => 'Yet to start', + 'created_by' => $post_data['created_by'], + 'updated_by' => $post_data['created_by'], + ]; + + // Save into DB + $result = $this->hrFileUploadModel->insert($data); + + if ($result) { + $this->giveNotificationToClientsAccountManager($data); + $response = [ 'status' => true, 'message' => 'File uploaded successfully', 'data' => $data - ]); - } catch (\Exception $e) { - return $this->failServerError($e->getMessage()); + ]; + return $response; } } + public function fileUploadInFilesTable($post_data) + { + $employeeController = new EmployeeController(); + $responce = $employeeController->employeesUplodWithEvents($post_data); + // print_r($responce); die; + + return $responce; + + // if(isset($responce['file_id'])){ + // $file_data = $this->getDataFromHrFilesTable(['hr_id' => $responce['created_by']]); + // $responce['data'] = $file_data; + // return $responce; + // }else{ + // $responce['data'] = []; + // return $responce; + // } + } + private function giveNotificationToClientsAccountManager($data) { @@ -4548,7 +4711,7 @@ class EmployeeRestController extends AdminController ]; // Check if record exists - $record = $this->hrFileUploadModel->find($id); + $record = $this->hrFileUploadModel->find((int)$id); if (!$record) { return $this->failNotFound("Record with ID {$id} not found."); } @@ -4572,28 +4735,40 @@ class EmployeeRestController extends AdminController try { $file_id = $this->request->getGet('id') ?? $id; + $client_id = $this->request->getGet('cliend_id') ?? null; + + $client_data = []; + if(!empty($client_id)){ + if (is_string($client_id) && preg_match('/^[a-f0-9]{32}$/i', $client_id)) { + $client_data = $this->clientModel->where('MD5(id)', $client_id)->first(); + } else { + $client_data = $this->clientModel->where('id', $client_id)->first(); + } + } // Find record - $record = $this->hrFileUploadModel->where('id', $file_id)->find(); - - // print_rr( $record);die; + if($client_data && $client_data['hr_file_processed_by'] == 1){ + $record = $this->fileModel->where('id', (int)$file_id)->find(); + $uploadPath = WRITEPATH . 'uploads/excel/'; + }else{ + $record = $this->hrFileUploadModel->where('id', (int)$file_id)->find(); + $uploadPath = WRITEPATH . 'uploads/hr_files/'; + } if (!$record) { return $this->failNotFound("File record not found"); } - $uploadPath = WRITEPATH . 'uploads/hr_files/'; - $filePath = $uploadPath . $record[0]['file_name']; + $filePath = $uploadPath . $record[0]['file_name']; if (!file_exists($filePath)) { - // return $this->failNotFound("File not found on server"); - $data['message'] = 'The Physical File Not Found'; - return view('errors/404', $data); + return $this->failNotFound("File not found on server"); } // Force file download return $this->response->download($filePath, null) ->setFileName($record[0]['file_name']); + } catch (\Exception $e) { return $this->failServerError($e->getMessage()); } @@ -4643,17 +4818,68 @@ class EmployeeRestController extends AdminController public function hrFileList() { try { + $request = service('request'); + $search_data = $request->getGetPost() ?? []; // supports both GET and POST + $table = ""; - // Start builder from model - // $builder = $this->hrFileUploadModel - // ->select('hr_file_upload.* , c.short_name , cb.branch_name , lc.name as first_name') - // ->join('clients c', 'c.id = hr_file_upload.client_id AND c.is_active = 1', 'left') - // ->join('client_branch cb', 'cb.id = hr_file_upload.client_branch_id AND cb.is_active = 1', 'left') - // ->join('level_contacts lc', 'lc.id = hr_file_upload.created_by AND lc.contact_type = "client" AND lc.is_active = 1', 'left'); + if(isset($search_data['hr_file_type']) && $search_data['hr_file_type'] == "CRM"){ + $data = $this->getDataFromHrFileUploadTable($search_data); + $table = "hr_file_upload"; + }else{ - $builder = $this->hrFileUploadModel - ->select(' + if (is_string($search_data['client_id']) && preg_match('/^[a-f0-9]{32}$/i', $search_data['client_id'])) { + $client_data = $this->clientModel->where('MD5(id)', $search_data['client_id'])->first(); + $search_data['client_id'] = $client_data['id']; + } else { + $client_data = $this->clientModel->where('id', $search_data['client_id'])->first(); + } + + if($client_data['hr_file_processed_by'] == 1){ + $data = $this->getDataFromHrFilesTable($search_data); + $table = "files"; + }else{ + $data = $this->getDataFromHrFileUploadTable($search_data); + $table = "hr_file_upload 2"; + + } + } + + return $this->respond([ + 'status' => "success", + 'message' => 'File list fetched successfully', + 'data' => $data, + 'table' => $table + ]); + + } catch (\Exception $th) { + + $errorData = [ + 'message' => $th->getMessage(), + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'code' => $th->getCode(), + 'trace' => $th->getTraceAsString(), + 'trace_array' => $th->getTrace(), // full array version (optional) + 'function' => $th->getTrace()[0]['function'] ?? null, + 'class' => $th->getTrace()[0]['class'] ?? null, + ]; + + return $this->failServerError($th->getMessage()); + } + } + + public function getDataFromHrFileUploadTable($search_data) + { + // Start builder from model + // $builder = $this->hrFileUploadModel + // ->select('hr_file_upload.* , c.short_name , cb.branch_name , lc.name as first_name') + // ->join('clients c', 'c.id = hr_file_upload.client_id AND c.is_active = 1', 'left') + // ->join('client_branch cb', 'cb.id = hr_file_upload.client_branch_id AND cb.is_active = 1', 'left') + // ->join('level_contacts lc', 'lc.id = hr_file_upload.created_by AND lc.contact_type = "client" AND lc.is_active = 1', 'left'); + + $builder = $this->hrFileUploadModel + ->select(" hr_file_upload.id, hr_file_upload.client_id, hr_file_upload.client_branch_id, @@ -4672,51 +4898,119 @@ class EmployeeRestController extends AdminController WHEN f.status IS NULL THEN hr_file_upload.status ELSE CONCAT(UCASE(LEFT(f.status, 1)), LCASE(SUBSTRING(f.status, 2))) - END AS status - ', false) - ->join('clients c', 'c.id = hr_file_upload.client_id AND c.is_active = 1', 'left') - ->join('client_branch cb', 'cb.id = hr_file_upload.client_branch_id AND cb.is_active = 1', 'left') - ->join('level_contacts lc', 'lc.id = hr_file_upload.created_by AND lc.contact_type = "client" AND lc.is_active = 1', 'left') - ->join( - '(SELECT f1.* + END AS status, + '0' as file_error_status, + '' as file_error_status + ", false) + ->join('clients c', 'c.id = hr_file_upload.client_id AND c.is_active = 1', 'left') + ->join('client_branch cb', 'cb.id = hr_file_upload.client_branch_id AND cb.is_active = 1', 'left') + ->join('level_contacts lc', 'lc.id = hr_file_upload.created_by AND lc.contact_type = "client" AND lc.is_active = 1', 'left') + ->join( + '(SELECT f1.* FROM files f1 WHERE f1.is_active = 1 ORDER BY f1.id DESC LIMIT 1) f', - 'f.hr_file_id = hr_file_upload.id', - 'left' - ); + 'f.hr_file_id = hr_file_upload.id', + 'left' + ); - // Allowed filter keys - $filters = [ - 'client_id', - 'client_branch_id', - 'policy_id', - 'policy_no', - 'file_action', - 'status', - 'created_by' - ]; + // Allowed filter keys + $filters = [ + 'client_id', + 'client_branch_id', + 'policy_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("hr_file_upload.$key", $value); - } + // Apply filters dynamically + foreach ($filters as $key) { + $value = $search_data[$key] ?? null; // supports both GET and POST + if (!empty($value)) { + $builder->where("hr_file_upload.$key", $value); } - - // Execute query - $data = $builder->get()->getResultArray(); - - return $this->respond([ - 'status' => "success", - 'message' => 'File list fetched successfully', - 'data' => $data - ]); - } catch (\Exception $e) { - return $this->failServerError($e->getMessage()); } + + // Execute query + $builder->orderBy('hr_file_upload.id', 'DESC'); + $data = $builder->get()->getResultArray(); + + if(!empty($data)){ + return $data; + } + + return []; + } + + public function getDataFromHrFilesTable($search_data) + { + $file_download_base = base_url('downloadFileTableFile/'); + $builder = $this->fileModel + ->select(" + files.id, + files.client_id, + files.client_branch_id, + files.policy_id, + cp.policy_no, + files.file_name, + files.action as file_action, + files.created_at, + files.created_by, + files.updated_at, + files.updated_by, + c.short_name, + cb.branch_name, + lc.name as first_name, + CONCAT(UCASE(LEFT(files.status, 1)), LCASE(SUBSTRING(files.status, 2))) as status, + CASE + WHEN status = 'failed' THEN 1 + ELSE 0 + END AS file_error_status, + CONCAT('{$file_download_base}', files.id, '/api') AS file_download_link + ", false) + ->join('clients c', 'files.client_id = c.id AND c.is_active = 1', 'left') + ->join('client_branch cb', 'files.client_branch_id = cb.id AND cb.is_active = 1', 'left') + ->join('client_policy cp', 'files.policy_id = cp.id AND cp.is_active = 1', 'left') + ->join('level_contacts lc', 'files.hr_id = lc.id AND lc.contact_type = "client" AND lc.is_active = 1', 'left'); + + + if (isset($search_data['policy_id']) && !empty($search_data['policy_id'])) { + $builder->where("files.policy_id", $search_data['policy_id']); + } + + if (isset($search_data['created_by']) && !empty($search_data['created_by'])) { + $builder->where("files.hr_id", $search_data['created_by']); + } + + if (isset($search_data['policy_no']) && !empty($search_data['policy_no'])) { + $builder->where("cp.policy_no", $search_data['policy_no']); + } + + if (isset($search_data['client_id']) && !empty($search_data['client_id'])) { + if (is_string($search_data['client_id']) && preg_match('/^[a-f0-9]{32}$/i', $search_data['client_id'])) { + $builder->where("MD5(files.client_id)", $search_data['client_id']); + } else { + $builder->where("files.client_id", $search_data['client_id']); + } + } + + if (isset($search_data['file_id']) && !empty($search_data['file_id'])) { + $builder->where("files.id", $search_data['file_id']); + } + + + // Execute query + $builder->orderBy('files.id', 'DESC'); + $data = $builder->get()->getResultArray(); + + if (!empty($data)) { + return $data; + } + + return []; } public function hrFileUploadMasters() @@ -5132,6 +5426,209 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => false, 'code' => 400, 'message' => 'Failed to upload the file'], 200); } } + + public function getTicketClaimStatusIdBasedOnTheDisplayName($claim_status_id) + { + $claim_status_data_display_name = $this->claimStatusModel->where('is_active', 1)->where('id', $claim_status_id)->first(); + $claim_status_data_id = $this->claimStatusModel + ->select('id') + ->where('is_active', 1) + ->where('display_name', $claim_status_data_display_name['display_name']) + ->findAll(); + + $claim_status_data_id = array_column($claim_status_data_id, 'id'); + return $claim_status_data_id; + } + + public function downloadCdSplitUpFile() + { + try { + + $file_id = $this->request->getGet('id'); + + // Find record + $record = $this->batchFileModel->where('MD5(id)', $file_id)->first(); + if (!$record) { + $data['message'] = 'File record not found'; + return view('errors/404', $data); + } + + $uploadPath = WRITEPATH . 'uploads/import_excel/'; + $filePath = $uploadPath . $record['file_name']; + + if (!file_exists($filePath)) { + // return $this->failNotFound("File not found on server"); + $data['message'] = 'The Physical File Not Found'; + return view('errors/404', $data); + } + + // Force file download + return $this->response->download($filePath, null)->setFileName($record['file_name']); + } catch (\Exception $e) { + $data['message'] = 'File record not found'; + return view('errors/404', $data); + } + } + + + public function getHrDashboad() + { + // $dashboard_id = $this->request->getGet('dashboard_id') ?? null; + $client_id = $this->request->getPost('client_id') ?? null; + $client_policy_id = $this->request->getPost('client_policy_id') ?? null; + $received_data = $this->request->getJSON(true) ?? null; + // 🔐 Move this to .env in real projects + $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY'); + + $payload = [ + 'resource' => [ + // 'dashboard' => 1 + 'dashboard' => 2 + ], + 'params' => (object)['client_policy' => $received_data['client_policy_id']], // MUST be object for Metabase + 'exp' => time() + (10 * 60) // 10 minutes + ]; + + $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256'); + + // // You can either return token only + // return $this->response->setJSON([ + // 'token' => $token, + // 'iframe_url' => "https://your-metabase-domain/embed/dashboard/{$token}#bordered=true&titled=true" + // ]); + // if($this->request->getGet('api') == 1) + // { + return $this->respond([ + 'status' => 'success', + 'message' => 'Form data received successfully!', + 'data' => [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in'] + ]); + // } + + return view('meta_dashboard_demo_one', [ + 'metabaseToken' => $token, + 'metabaseUrl' => 'https://nsights.nhanceindia.in', + ]); + } + // get policy files + public function getPolicyAndEndorsementFiles() + { + $cd_ac_pk = $this->request->getGet('cd_ac_pk') ?? null; + + $cdModel = new ClientDepositModel(); + $cd_data = $cdModel->where('id', $cd_ac_pk) + ->where('is_active', 1) + ->where('client_policy_id IS NOT NULL') + ->first(); + + if(empty($cd_data)){ + $this->myLogger->logme('error', 'getPolicyAndEndorsementFiles: No Client Deposit data found for cd_ac_pk=' . $cd_ac_pk); + return $this->respond(['status' => false, 'code' => 404, 'message' => 'No file found', 'data' => []], 200); + } + + $client_policy_data = $this->clientPolicyModel + ->where('id', $cd_data['client_policy_id']) + ->where('is_active', 1) + ->first(); + + if(empty($client_policy_data)){ + $this->myLogger->logme('error', 'getPolicyAndEndorsementFiles: No Client Policy data found for client_policy_id=' . $cd_data['client_policy_id']); + return $this->respond(['status' => false, 'code' => 404, 'message' => 'No file found', 'data' => []], 200); + } + + $policyTransactionModel = new PolicyTransactionModel(); + $builder = $policyTransactionModel + ->where('is_active', 1) + ->where('policy_no', $client_policy_data['policy_no']) + ->where('action_type', $cd_data['event_name']); + + if (!empty($cd_data['endorsement_no']) && $cd_data['event_name'] != 'inception') { + $builder->where('endorsement_no', $cd_data['endorsement_no']); + } + + $policy_transaction_data = $builder->findAll(); + + if(empty($policy_transaction_data)){ + $this->myLogger->logme('error', 'getPolicyAndEndorsementFiles: No Policy Transaction data found for policy_no=' . $client_policy_data['policy_no']); + return $this->respond(['status' => false, 'code' => 404, 'message' => 'No file found', 'data' => []], 200); + } + + $policy_transaction_ids = array_column($policy_transaction_data, 'id'); + + + $ptFilesModel = new PTFileModel(); + $pt_files_data = $ptFilesModel->where('is_active', 1)->whereIn('pt_id', $policy_transaction_ids)->findAll(); + + if(empty($pt_files_data)){ + $this->myLogger->logme('error', 'getPolicyAndEndorsementFiles: No PT Files data found for pt_ids=' . implode(',', $policy_transaction_ids)); + return $this->respond(['status' => false, 'code' => 404, 'message' => 'No file found', 'data' => []], 200); + } + + + return $this->respond(['status' => true, 'code' => 200, 'message' => 'Files found', 'data' => $pt_files_data], 200); + + } + + // download policy file + public function downloadPolicyFiles() + { + $pt_file_id = $this->request->getGet('file_id') ?? null; + + if(empty($pt_file_id)){ + return $this->respond(['status' => false, 'code' => 404, 'message' => 'File ID is required'], 200); + } + + $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents'; + + $ptFilesModel = new PTFileModel(); + $pt_files_data = $ptFilesModel->where('is_active', 1)->where('id', $pt_file_id)->first(); + + if(empty($pt_files_data)){ + return $this->respond(['status' => false, 'code' => 404, 'message' => 'The Physical File Not Found in server'], 200); + } + + $filePath = $uploadFilePath . '/' . $pt_files_data['file_name']; + + if (!file_exists($filePath)) { + return $this->respond(['status' => false, 'code' => 404, 'message' => 'The Physical File Not Found in server'], 200); + } + + // Force file download + return $this->response->download($filePath, null)->setFileName($pt_files_data['file_name']); + } + + public function bulkEcardDownloadAsZip() + { + $received_data = $this->request->getJSON(true) ?? null; + $this->myLogger->logme('error', 'bulkEcardDownloadAsZip: Received payload = ' . json_encode($received_data ?? [])); + + if(empty($received_data['client_policy_id']) && empty($received_data['emp_policy_ids'])){ + return $this->respond(['status' => false, 'code' => 404, 'message' => 'client_policy_id or employee_policy_ids is required'], 200); + } + + if(empty($received_data['hr_id'])){ + return $this->respond(['status' => false, 'code' => 404, 'message' => 'hr_id is required'], 200); + } + + $employee_data = $this->employeePolicyModel->getEmployeeDataWithPolicyUsingClientPolicyIdOrEmployeePolicyIds($received_data); + + if(empty($employee_data)){ + return $this->respond(['status' => false, 'code' => 404, 'message' => 'No employee data found for the policy'], 200); + } + + $received_data['folder_name'] = 'bulk_ecards_' . $employee_data[0]['policy_no'] . '_' . date('Y-m-d_H-i-s'); + + // Dispatch background job to process the bulk e-card download + $r = Jobs::addJob(['job_name' => 'bulkEcardDownloadAsZipFromS3', 'payload' => $received_data]); + $this->myLogger->logme('error', 'bulkEcardDownloadAsZip: Job dispatched with result = ' . json_encode($r ?? [])); + + return $this->respond(['status' => true, 'code' => 200, 'message' => 'Bulk E-card download process started. Link share your mail'], 200); + + } + + } diff --git a/app/Controllers/EmployeeServiceController.php b/app/Controllers/EmployeeServiceController.php index 0547b05f..bc4bfa7c 100755 --- a/app/Controllers/EmployeeServiceController.php +++ b/app/Controllers/EmployeeServiceController.php @@ -784,7 +784,7 @@ class EmployeeServiceController extends AdminController //get file name // check_dob_diff('4-APr-1990');die(); $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $return = []; if(!isset($file)) @@ -866,6 +866,14 @@ class EmployeeServiceController extends AdminController // get policy and rack details $policy_details = $this->clientPolicyModel->getPolicyDetails($file['client_id'],$file['policy_id']); + + if(empty($policy_details)){ + $message = "Policy configuration is incomplete. Cannot proceed."; + $this->myLogger->logme('error',($message . ' for file id ' . $file_id)); + $this->fileModel->where('id', $file_id)->set(['status' => 'failed','reason' => json_encode(['error_summary' => array_count_values([5]),'error_data' => $message])])->update(); + return array('error_summary' => [5], 'error_data' => $message); + } + $policy_terms = json_decode($policy_details[0]->policy_terms); $policy_terms = (array) $policy_terms;// convert obj to array $default_age_ratio = isset($policy_terms['age_ratio']) ? json_decode(json_encode($policy_terms['age_ratio']),true) : []; @@ -876,6 +884,13 @@ class EmployeeServiceController extends AdminController $slab_details = $this->policiesModel->getPolicySlabRatesForEmpOnboard($file['policy_id'],$file['client_id']); // dd($slab_details); + if(empty($slab_details) || (isset($slab_details['slab_rates']) && empty($slab_details['slab_rates']))){ + $message = "Policy configuration is incomplete. Cannot proceed."; + $this->myLogger->logme('error',($message . ' for file id ' . $file_id)); + $this->fileModel->where('id', $file_id)->set(['status' => 'failed','reason' => json_encode(['error_summary' => array_count_values([5]),'error_data' => $message])])->update(); + return array('error_summary' => [5], 'error_data' => $message); + } + //remove header unset($excel_data[0]); $relationship = $this->general_relationships; @@ -1062,7 +1077,7 @@ class EmployeeServiceController extends AdminController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $return = []; if(!isset($file)) @@ -1322,7 +1337,7 @@ class EmployeeServiceController extends AdminController { //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $return = []; if(!isset($file)) @@ -1387,6 +1402,8 @@ class EmployeeServiceController extends AdminController $employee_data_group_by_family = data_group_by_family($excel_data, 'excel', '', $current_column_action); // dd($employee_data_group_by_family); + + $employee_insert_count = 0; foreach ($employee_data_group_by_family as $emp_id => $family) { @@ -1410,11 +1427,25 @@ class EmployeeServiceController extends AdminController $data = calculate_premium_new(family_data:$family,policy_terms: $policy_terms,slab_details : $slab_details,fileArr: $file, existing_units:$existing_units); // dd($data); $employee_data_group_by_family[$emp_id] = $data; - $this->employeesOnboardProcess(['familiy_data' => $data,'file' => $file]); + $res = $this->employeesOnboardProcess(['familiy_data' => $data,'file' => $file]); + + if($res > 0){ + $employee_insert_count++; + } } - $this->fileModel->where('id', $file_id)->set(['status' => 'success','reason' => ''])->update(); - $this->myLogger->logme("error",'{file_id} uploaded success',['file_id' => $file_id]); + if($employee_insert_count > 0){ + $this->fileModel->where('id', $file_id)->set(['status' => 'success','reason' => ''])->update(); + $this->myLogger->logme("error",'{file_id} uploaded success',['file_id' => $file_id]); + }else{ + $reason = json_encode([ + 'error_summary' => [5 => 1], + 'error_data' => "Rack rate configuration issue: Please check the slab rates configuration" + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $this->fileModel->where('id', $file_id)->set(['status' => 'failed','reason' => $reason])->update(); + $this->myLogger->logme("error",'{file_id} uploaded failed with reason: Rack rate configuration issue: Please check the slab rates configuration',['file_id' => $file_id]); + + } $this->setPullNotification($this->getFileMetaDataByFileId($file_id,'success')); @@ -1424,7 +1455,7 @@ class EmployeeServiceController extends AdminController { $client_policy_id = $params['client_policy_id']; //get client id - $client_id = ($this->clientPolicyModel->select('client_id')->find($client_policy_id))['client_id']; + $client_id = ($this->clientPolicyModel->select('client_id')->find((int)$client_policy_id))['client_id']; // dd($client_id); // get policy and rack details @@ -1470,7 +1501,7 @@ class EmployeeServiceController extends AdminController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $file_name_with_path = WRITEPATH."/uploads/excel/".$file['file_name']; @@ -1489,7 +1520,7 @@ class EmployeeServiceController extends AdminController $endorsement_data = []; $policy_terms = $this->clientPolicyModel->getPolicyDetails($file['client_id'],$file['policy_id']); $insurer = new InsurerModel(); - $insurer = ($insurer->find($policy_terms[0]->insurer_id)); + $insurer = ($insurer->find((int)$policy_terms[0]->insurer_id)); // kint::dump($insurer); //make closure funciton which is going to use only by this method $endorsement = function($data,$file,$row) use ($insurer){ @@ -1621,7 +1652,7 @@ class EmployeeServiceController extends AdminController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $file_name_with_path = WRITEPATH."/uploads/excel/".$file['file_name']; @@ -1700,7 +1731,7 @@ class EmployeeServiceController extends AdminController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $file_name_with_path = WRITEPATH . "/uploads/excel/" . $file['file_name']; @@ -1827,6 +1858,7 @@ class EmployeeServiceController extends AdminController $policy_details = $this->clientPolicyModel->where('id', $file['policy_id'])->first(); // dd($policy_details); + $employee_insert_count = 0; foreach($familiy_data as $fkey => $value) { if(is_array($value)) @@ -1959,6 +1991,7 @@ class EmployeeServiceController extends AdminController // else{ $emp_policy_id = $employee_policy[0]['id']; } $this->myLogger->logme('error',$log_message); + $employee_insert_count++; }// if end }// is array check end @@ -1967,6 +2000,8 @@ class EmployeeServiceController extends AdminController $this->myLogger->logme('error','######Incoming value is not an array'); } }// for end + + return $employee_insert_count; }//function end @@ -2028,7 +2063,7 @@ class EmployeeServiceController extends AdminController { try { - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); $error_data = json_decode($file['reason']); // dd($error_data); // return $error_data; @@ -2187,7 +2222,7 @@ class EmployeeServiceController extends AdminController helper('excel_util_helper'); //get file name $file_id = $params['file_id']; - $file = $this->fileModel->find($file_id); + $file = $this->fileModel->find((int)$file_id); // dd($file); $return = []; if(!isset($file)) diff --git a/app/Controllers/FhplApiController.php b/app/Controllers/FhplApiController.php new file mode 100644 index 00000000..f2ac8b17 --- /dev/null +++ b/app/Controllers/FhplApiController.php @@ -0,0 +1,790 @@ +db = \Config\Database::connect(); + $this->fhplTpaId = getenv('FHPL_PRIMARY_KEY_CONSTANT'); + } + + public function generateAuthToken() + { + $url = env('FHPL_TOKEN_URL'); + + // x-www-form-urlencoded body + $postData = http_build_query([ + 'UserName' => env('FHPL_USER_NAME'), + 'Password' => env('FHPL_PASSWORD'), + 'grant_type' => env('FHPL_GRANT_TYPE'), + ]); + + $ch = curl_init(); + + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'GET', // SAME AS POSTMAN + CURLOPT_POSTFIELDS => $postData, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/x-www-form-urlencoded', + 'Accept: application/json', + ], + CURLOPT_TIMEOUT => 30, + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + return $this->response->setJSON([ + 'status' => false, + 'error' => curl_error($ch), + ]); + } + + curl_close($ch); + + return $this->response->setJSON([ + 'status' => $httpCode === 200, + 'http_code' => $httpCode, + 'data' => json_decode($response, true), + ]); + } + + public function SubmitClaim($claimId = null) // 515 + { + helper('api'); + + $data = $this->db->table('ticket_master tm') + ->select(' + tm.id, + tm.emp_mobile as mobileNo, + tm.emp_mail as emailId, + tm.doa as admissionDate, + tm.dod as dischargeDate, + tm.hospital_name as hospitalName, + tm.claim_amount as requestedAmount, + tm.tpa_no as dependentUniqueId, + cp.policy_no as policyNo, + e.emp_code as memberId, + tn.note as disease, + cf.url as filePath + ') + ->join('employees e', 'e.id = tm.emp_id', 'left') + ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') + ->join('ticket_notes tn', 'tn.ticket_id = tm.id', 'left') + ->join('claim_files cf', "cf.ticket_id = tm.id AND cf.file_type = 2 AND cf.mime_type='application/pdf'", 'left') + ->where('tm.id', $claimId) + ->get() + ->getRowArray(); + + + if (count($data) && $data['filePath'] == null) { + log_message('error', "TPA CLAIM PUSH FAILED FHPL | claimId: '.$claimId.' - Claim or File Missing"); + return; + } + + // Build absolute file path + $filename = basename($data['filePath']); + $pdfPath = WRITEPATH . 'uploads/claim_files/' . $filename; + + if (!file_exists($pdfPath)) { + log_message('error', "TPA CLAIM PUSH FAILED FHPL | claimId: '.$claimId.' - PDF not found on server"); + return; + } + + // Convert PDF to Base64 + $fileContent = base64_encode(file_get_contents($pdfPath)); + + // Generate FHPL Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['access_token'])) { + log_message('error', "TPA CLAIM PUSH FAILED FHPL | claimId: '.$claimId.' - FHPL Token generation failed"); + return; + } + $token = $tokenResponse['data']['access_token']; + + // Build FHPL Request + $body = [ + "IssueID" => $data['dependentUniqueId'], // this key added after seeing the error in responce have to click with fhpl team + "Userid" => getenv('FHPL_USER_NAME'), + "PolicyNo" => $data['policyNo'], // "111700-TATAMTORS", + "UhidNo" => $data['dependentUniqueId'], // "OIC40830846", + "ClaimID" => (string) $data['id'], + "DOA" => date('Y-m-d', strtotime($data['admissionDate'])), //"2025-10-11", + "DateofDischarge"=> $data['dischargeDate'] ? date('Y-m-d', strtotime($data['dischargeDate'])) : null, + "ClaimedAmount" => (float) $data['requestedAmount'], + "DocumentType" => 20, // Fresh Claim + "PayeeName" => $data['memberId'], + "HospitalName" => $data['hospitalName'], + "MobileNo" => $data['mobileNo'], + "Documents" => [ + [ + "documentName" => $filename, + "documentCategory" => "IRR", + "filecontent" => $fileContent + ] + ] + ]; + + $url = getenv('FHPL_BASE_URL') . "/api/ClaimSubmission"; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + log_message('error', 'TPA CLAIM PUSH FHPL | claimId: '.$claimId.' | payload: '.json_encode($body)); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + log_message('error', 'TPA CLAIM PUSH FHPL RESPONSE | ' . json_encode($response)); + + if($response['status'] != true){ + log_message('error', 'TPA CLAIM PUSH FAILED FHPL | claimId: '.$claimId.' | response: '.json_encode($response)); + $this->db->table('ticket_master') + ->where('id',$claimId) + ->update([ 'tpa_push_response' => json_encode($response) ]); + return; + } + + + if ($response['status'] === true && !empty($response['data'][0]['ClaimsInfo'])) + { + $claimsInfo = json_decode($response['data'][0]['ClaimsInfo'], true); + + if (!empty($claimsInfo[0]['ClaimID'])) { + + $fhplClaimNo = $claimsInfo[0]['ClaimID']; + + $this->db->table('ticket_master') + ->where('id', $claimId) + ->update([ + 'claim_number' => $fhplClaimNo, + 'tpa_claim_id' => $fhplClaimNo, + 'tpa_claim_push_reference_no' => $fhplClaimNo, + 'updated_at' => date('Y-m-d H:i:s') + ]); + + log_message('error', 'TPA CLAIM PUSH SUCCESS FHPL | claimId: '.$claimId.' | claimNO: '.$fhplClaimNo); + + }else { + log_message('error', 'TPA CLAIM PUSH API SUCCESS BUT claimsInfo EMPTY | response: '.json_encode($response)); + return; + } + } + + return; + // return $this->response->setJSON($response); + } + + public function ClaimDetail($claimId = null) //515 + { + helper('api'); + + $ticket = $this->db->table('ticket_master tm') + ->select("tm.id, tm.tpa_claim_id as claimNo, cp.policy_no , cp.policy_start_date , cp.policy_end_date") + ->join('client_policy cp','tm.client_policy_id=cp.id') + ->where('tm.id',$claimId) + ->get()->getRowArray(); + + + if(!$ticket) return ['status'=>false,'message'=>'Invalid claim']; + + // Generate FHPL Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + + if (empty($tokenResponse['data']['access_token'])) { + return ['status' => false,'message' => 'FHPL Token generation failed']; + } + + $token = $tokenResponse['data']['access_token']; + + $url = getenv('FHPL_BASE_URL')."/api/GetTPA_ClaimsDetails"; + + $body = [ + "UserName" => getenv('FHPL_USER_NAME'), + "Password" => getenv('FHPL_PASSWORD'), + "PolicyNumber" => $ticket['policy_no'], //"GHI-81-25-00087313-000", + "Fromdate" => $ticket['policy_start_date'],//"2025-04-26", + "Todate" => $ticket['policy_end_date'],//"2025-04-27", + ]; + + $headers = ["Authorization: Bearer ".$token,"Content-Type: application/json"]; + + $response = call_third_party_api($url,'POST',$headers,$body); + + // dd($response); + + if (empty($response['data'][0])) { + log_message('error', 'CLAIM STATUS FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response)); + return ['status' => false,'message' => 'API call failed.','data' => $response ]; + } + + + $status = null; + + // find this claim + foreach($response['data'] as $row){ + if($row['CLAIM_ID']==$ticket['claimNo']){ + $status = $row['CLAIM_STATUS']; + } + } + + $map = [ + "In-Progress" => 5, + "Under Process" => 5, + "Query" => 4, + "Paid" => 11, + "Rejected" => 8, + "Approved" => 8, + "Required Information" => 4, + ]; + + if( $status != null && isset($map[$status])) + { + $this->db->table('ticket_master')->where('id',$claimId)->update(['claim_status_id'=>$map[$status],'tpa_claim_status'=>$status]); + log_message('error', "CLAIM STATUS SUCCESS | Updated ticket ID $claimId with claim status: $status"); + } + + + return ['status' => true,'message' => 'Claim status updated.','updated_status' => $status,'api_response' => $response]; + + } + + public function ClaimStatusUpdate() + { + helper('api'); + + $tickets = $this->db->table('ticket_master tm') + ->select("tm.id,tm.tpa_claim_id,cp.policy_no") + ->join('client_policy cp','tm.client_policy_id=cp.id') + ->where('tm.tpa_claim_id IS NOT NULL') + ->get()->getResultArray(); + + $count=0; + + foreach($tickets as $t){ + $this->ClaimDetail($t['id']); + $count++; + } + + return $this->response->setJSON(['status'=>true,'updated'=>$count]); + } + + public function EcardRequest($employeeId = null,$policyNo = null,$uhid = null) + { + helper('api'); + + // Generate FHPL Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['access_token'])) { + log_message('error', 'Ecard Request FAILED | employeeId: '.$employeeId.' | policyNo: '.$policyNo.' | Message: FHPL Token generation failed'); + return null; + } + $token = $tokenResponse['data']['access_token']; + + $url = getenv('FHPL_BASE_URL')."/api/GetEcard"; + + $body = [ + "UserName" => getenv('FHPL_USER_NAME'), + "Password" => getenv('FHPL_PASSWORD'), + "PolicyNumber" => $policyNo, //'10/12/2025/16/17', + "EmployeeID" => $employeeId, //'101225', + ]; + + $headers = ["Authorization: Bearer ".$token,"Content-Type: application/json"]; + + $response = call_third_party_api($url,'POST',$headers,$body); + // dd($response); + + if (($response['status'] ?? false) !== true) { + log_message('error', 'Ecard Request FAILED | response: ' . json_encode($response)); + return null; + } + + if (empty($response['data'][0])) { + log_message('error', 'Ecard Request FAILED | Empty data | response: ' . json_encode($response)); + return null; + } + + $apiData = $response['data'][0]; + + if (($apiData['STATUS'] ?? '') === 'SUCCESS') { + + $ecardUrl = $apiData['E_Card'] ?? ''; + + if (!empty($ecardUrl)) { + log_message( + 'info', + 'Ecard Request PUSH SUCCESS | employeeId: ' . $employeeId . + ' | policyNo: ' . $policyNo . + ' | ecardUrl: ' . $ecardUrl + ); + return $ecardUrl; + } + } + + log_message('error', 'Ecard Request FAILED | response: ' . json_encode($response)); + return null; + } + + public function FhplGetBenefDetails($requestData = null) + { + + $function_calling_type = $requestData['return_type'] ?? 'job'; + + try { + + helper('api'); + + $policyNo = $requestData['policy_no'] ?? null; + $client_policy_id = $requestData['client_policy_id'] ?? null; + + if (empty($policyNo)) { + log_message('error', 'TPA ID PULL | policy_no missing in request'); + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'policy_no required']; + }else{ + return $this->respond(['status' => false, 'message' => 'policy_no required']); + } + } + + if (empty($client_policy_id)) { + log_message('error', 'TPA ID PULL | client_policy_id missing in request'); + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'client_policy_id required']; + }else{ + return $this->respond(['status' => false, 'message' => 'client_policy_id required']); + } + } + + log_message('error', "TPA ID PULL | called for policy_no: {$policyNo} , client_policy_id: {$client_policy_id}"); + + // Fetch file download dates + $batchFiles = $this->db->table('batch_files f') + ->select("f.created_at") + ->where('f.client_policy_id', $client_policy_id) + ->where('f.insurer_or_tpa', 'tpa') + ->where('f.actions', 'export') + ->get() + ->getResultArray(); + + if (empty($batchFiles)) { + log_message('error', 'TPA ID PULL FAILED | batchFiles is empty for this tpa id pull request'); + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'batchFiles not found']; + }else{ + return $this->respond(['status' => false, 'message' => 'batchFiles not found']); + } + } + + // Generate FHPL Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['access_token'])) { + log_message('error', 'TPA ID PULL | FHPL Token generation failed'); + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'FHPL Token generation failed']; + }else{ + return $this->respond(['status' => false, 'message' => 'FHPL Token generation failed']); + } + } + $token = $tokenResponse['data']['access_token']; + + $url = getenv('FHPL_BASE_URL') . "/api/GetEnrollmentDetailsPolicy"; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + $startIndex = 0; + $range = 100; + $allMembers = []; + + while (true) { + + $body = [ + "UserName" => getenv('FHPL_USER_NAME'), + "Password" => getenv('FHPL_PASSWORD'), + "PolicyNumber" => $policyNo, + "StartIndex" => $startIndex, + "Range" => $range + ]; + + log_message('error', "TPA ID PULL | API parems " . json_encode([$url, 'POST', $headers, $body])); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + if (($response['status'] ?? false) !== true) { + log_message('error', 'FHPL API FAILED | response: ' . json_encode($response)); + break; + } + + if ( + !isset($response['data']) || + !is_array($response['data']) || + count($response['data']) === 0 || + !array_is_list($response['data']) // PHP 8+ safe check + ) { + // STOP when data is not a valid list + break; + } + + $allMembers = array_merge($allMembers, $response['data']); + + // Move to next page + $startIndex += $range; + } + + if(empty($allMembers)) + { + + // update file table status after the tpa id failed to update + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); + log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + log_message('error', 'TPA ID PULL API FAILED | API failed: Empty menber data for this pull request'); + + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'API call failed', 'data' => 'Empty menber data for this pull request']; + }else{ + return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => 'Empty menber data for this pull request']); + } + + } + + // dd($allMembers); + + //save API data as JSON for analysis + $json = json_encode($allMembers, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + // $filename = time() . '.json'; + $filePath = WRITEPATH . 'tmp/'.time().'_'.$requestData['file_id'].'.json'; + file_put_contents($filePath, $json); + + //call a job for dump JSON data to DB + $job_details = new Jobs(); + $r = Jobs::addJob(['job_name' => 'saveFhplAPIData', 'payload' => [ 'file_id' => $requestData['file_id'],'json_file_path' => $filePath ]]); + + // ================= MATCHING LOGIC ================= + + $employeePolicyModel = new EmployeePolicyModel(); + + $employeePolicyData = $employeePolicyModel + ->join('employees', 'employees.id = employee_polices.employee_id') + ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('employee_polices.tpa_id IS NULL') + ->findAll(); + + $updated = 0; + + foreach ($employeePolicyData as $policy) { + $hasMatchForThisPolicy = false; + foreach ($allMembers as $m) { + + if ( + strtolower(trim($policy['name'])) === strtolower(trim($m['EMPLOYEE_NAME'] ?? '')) && + ($policy['emp_code'] ?? '') == ($m['EMPLOYEE_ID'] ?? '') && + strtolower($policy['relationship']) === strtolower($m['RELATION'] ?? '') + ) { + + $hasMatchForThisPolicy = true; + + + $sql = "UPDATE employee_polices SET tpa_id = ? WHERE id = ?"; + $this->db->query($sql, [$m['TPA_TPADETAIL_ID'], $policy['emp_policy_id']]); + + // for e-card send + if(strtolower(trim($policy['relationship'])) == 'self'){ + $employee_policy_ids[] = $policy['emp_policy_id']; + } + + if ($this->db->affectedRows() > 0) { + $updated++; + log_message('error', "✅ Updated tpa_id={$m['TPA_TPADETAIL_ID']} for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}"); + } else { + log_message('error', "⚠️ No update (already set or not matched) for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}"); + } + } + } + } + + // Handle NO MATCH for this policy + if (!$hasMatchForThisPolicy) { + + $nhanceSideData = [ + 'name' => $policy_data['name'] ?? null, + 'emp_code' => $policy_data['emp_code'] ?? null, + 'relationship' => $policy_data['relationship'] ?? null, + 'gender' => $policy_data['gender'] ?? null, + 'dob' => $policy_data['dob'] ?? null, + ]; + $batch_file_success = 'partially success'; + + log_message( + 'error', + "❌ No match for Nhance = " . json_encode($nhanceSideData) + ); + } + + // send e-card + if(!empty($employee_policy_ids)){ + log_message('error', "sendMailForDownloadingECard JOB PUSHED."); + Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $employee_policy_ids, 'client_policy_id' => $client_policy_id]]); + } + + // update file table status after the tpa id successfully updated + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + + $file_model->where('id', $requestData['file_id'])->set('status', $batch_file_success)->update(); + log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + $totalCount = count($allMembers); + + log_message('error', "TPA ID PULL SUCCESS | completed. Total fetched={$totalCount}, updated={$updated}"); + + if($function_calling_type == "job"){ + return [ + 'status' => true, + 'message' => 'Updated successfully', + 'total_fetched' => $totalCount, + 'total_updated' => $updated + ]; + }else{ + return $this->respond([ + 'status' => true, + 'message' => 'Updated successfully', + 'total_fetched' => $totalCount, + 'total_updated' => $updated + ]); + } + } catch (\Throwable $th) { + + // update file table status after the tpa id failed to update + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); + log_message('error', "Files table status updated for the file id : {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + $errorData = [ + 'message' => $th->getMessage(), + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'code' => $th->getCode(), + 'trace' => $th->getTraceAsString(), + 'trace_array' => $th->getTrace(), // full array version (optional) + 'function' => $th->getTrace()[0]['function'] ?? null, + 'class' => $th->getTrace()[0]['class'] ?? null, + ]; + + log_message('error', 'Exception thrown while calling GetBenefDetails API: ' . json_encode($errorData)); + if($function_calling_type == "job"){ + return ['status' => false, 'message' => 'API call failed', 'data' => $errorData]; + }else{ + return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $errorData]); + } + } + } + + + // public function FhplGetBenefDetails($requestData = null) + // { + // helper('api'); + + // $policyNo = $requestData['policy_no'] ?? "10/12/2025/16/17"; + // $client_policy_id = $requestData['client_policy_id'] ?? 0; + + // // Generate FHPL Token + // $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + // if (empty($tokenResponse['data']['access_token'])) { + // return $this->response->setJSON(['status' => false,'message' => 'FHPL Token generation failed']); + // } + // $token = $tokenResponse['data']['access_token']; + + // $url = getenv('FHPL_BASE_URL')."/api/GetEnrollmentDetailsPolicy"; + + // $headers = [ + // "Authorization: Bearer ".$token, + // "Content-Type: application/json" + // ]; + + // $startIndex = 0; + // $range = 100; + // $allMembers = []; + + // do { + // $body = [ + // "UserName" => getenv('FHPL_USER_NAME'), + // "Password" => getenv('FHPL_PASSWORD'), + // "PolicyNumber" => $policyNo, + // "StartIndex" => $startIndex, + // "Range" => $range + // ]; + + // $response = call_third_party_api($url,'POST',$headers,$body); + + // dd($response); + + // if(empty($response['data']['Members'])){ + // break; + // } + + // $allMembers = array_merge($allMembers,$response['data']['Members']); + + // $startIndex += $range; + + // } while($startIndex < ($response['data']['Total'] ?? 0)); + + + // dd($allMembers); + + // // Now same matching logic you already have + // $employeePolicyModel = new EmployeePolicyModel(); + + // $employeePolicyData = $employeePolicyModel + // ->join('employees','employees.id=employee_polices.employee_id') + // ->where('employee_polices.client_policy_id',$client_policy_id) + // ->where('employee_polices.tpa_id IS NULL') + // ->findAll(); + + // $updated = 0; + + // foreach($employeePolicyData as $policy){ + // foreach($allMembers as $m){ + + // if( + // strtolower(trim($policy['name']))==strtolower(trim($m['Name'])) && + // $policy['emp_code']==$m['MemberID'] && + // strtolower($policy['relationship'])==strtolower($m['Relation']) + // ){ + // $this->db->table('employee_polices') + // ->where('id',$policy['emp_policy_id']) + // ->update(['tpa_id'=>$m['UHID']]); + + // $updated++; + // } + // } + // } + + // return [ + // 'status'=>true, + // 'total_fetched'=>count($allMembers), + // 'updated'=>$updated + // ]; + // } + + public function syncFhplClaimsToNhance() + { + helper('api'); + + // Generate FHPL Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + + if (empty($tokenResponse['data']['access_token'])) { + return $this->response->setJSON(['status' => false,'message' => 'FHPL Token generation failed']); + } + + $token = $tokenResponse['data']['access_token']; + + $url = getenv('FHPL_BASE_URL')."/api/GetTPA_ClaimsDetails"; + + $headers = [ + "Authorization: Bearer ".$token, + "Content-Type: application/json" + ]; + + $policies = $this->db->table('client_policy') + ->where('tpa_id',$this->fhplTpaId) + ->get()->getResultArray(); + + $finalResult=[]; + + foreach($policies as $policy){ + + $body = [ + "UserName" => getenv('FHPL_USER_NAME'), + "Password" => getenv('FHPL_PASSWORD'), + "PolicyNumber" => $policy['policy_no'], + "Fromdate" => $policy['policy_start_date'], + "Todate" => $policy['policy_end_date'] + ]; + + $response = call_third_party_api($url,'POST',$headers,$body); + + if(!empty($response['data'])){ + $finalResult = array_merge($finalResult,$response['data']); + } + } + + // Insert / update ticket_master same way you already do for MediAssist + foreach($finalResult as $row){ + + $status = $row['CLAIM_STATUS']; + + $map = [ + "Under Process"=>5, + "Paid"=>11, + "Rejected"=>8, + "Approved"=>8 + ]; + + $claimStatus = $map[$status] ?? 1; + + $this->db->table('ticket_master')->insert([ + 'policy_no'=>$row['POLICY_NO'], + 'claim_number'=>$row['CLAIM_ID'], + 'tpa_claim_id'=>$row['CLAIM_ID'], + 'emp_code'=>$row['EMPLOYEE_NO'], + 'insured_name'=>$row['PATIENT_NAME'], + 'claim_amount'=>$row['CLAIM_AMOUNT'], + 'hospital_name'=>$row['HOSPITAL_NAME'], + 'doa'=>$row['DATE_OF_ADMISSION'], + 'dod'=>$row['DATE_OF_DISCHARGE'], + 'claim_status_id'=>$claimStatus, + 'tpa_id'=>$this->fhplTpaId + ]); + } + + return ['status'=>true,'total'=>count($finalResult)]; + } + + + + + + + + + + +} diff --git a/app/Controllers/GoogleDriveController.php b/app/Controllers/GoogleDriveController.php index bf689112..31c0a6a7 100644 --- a/app/Controllers/GoogleDriveController.php +++ b/app/Controllers/GoogleDriveController.php @@ -116,7 +116,7 @@ class GoogleDriveController extends BaseController } //if not in cache then get it from grdrive - $client = $this->clientModel->find($client_id); + $client = $this->clientModel->find((int)$client_id); $clientShortName = $client['short_name']; // Assuming short_name is the column for client's short name } else if($client_policy_id) diff --git a/app/Controllers/HealthIndiaApiController.php b/app/Controllers/HealthIndiaApiController.php new file mode 100644 index 00000000..9ac26461 --- /dev/null +++ b/app/Controllers/HealthIndiaApiController.php @@ -0,0 +1,785 @@ +db = \Config\Database::connect(); + $this->healthIndiaTpaId = getenv('HEALTH_INDIA_PRIMARY_KEY_CONSTANT'); + } + + public function generateAuthToken() + { + $url = getenv('HEALTH_INDIA_TOKEN_URL'); // https://software.healthindiatpa.com/HIITPABROKERAPI/JWT/GenerateJWTAuth + + // Basic Authentication credentials + $username = getenv('HEALTH_INDIA_USERNAME'); // /MU4gwfBYC71M1QnczasegH0vPM5IMbESO4iy4wbUrQ= + $password = getenv('HEALTH_INDIA_PASSWORD'); // KoVi+w2WGA+ET6fHN3kdBamHNsDKbA+kUtuF++4jWcg= + + $ch = curl_init(); + + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_USERPWD => $username . ':' . $password, + CURLOPT_POSTFIELDS => '{}', + + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'Accept: application/json', + ], + + CURLOPT_TIMEOUT => 30, + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Error: ' . curl_error($ch)); + curl_close($ch); + return $this->response->setJSON([ + 'status' => false, + 'error' => curl_error($ch), + ]); + } + + curl_close($ch); + + $responseData = json_decode($response, true); + + if ($httpCode === 200 && isset($responseData['status']) && $responseData['status'] === true) { + log_message('error', 'HEALTH_INDIA TOKEN GENERATION SUCCESS | Token: ' . ($responseData['result'][0]['access_token'] ?? 'N/A')); + } else { + log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Response: ' . $response); + } + + return $this->response->setJSON([ + 'status' => $httpCode === 200, + 'http_code' => $httpCode, + 'data' => $responseData, + ]); + } + + public function SubmitClaim($claimId = 515) + { + helper('api'); + + log_message('error', 'TPA CLAIM PUSH HEALTH_INDIA | Started for claimId: ' . $claimId); + + $data = $this->db->table('ticket_master tm') + ->select(' + tm.id, + tm.emp_mobile as mobileNo, + tm.emp_mail as emailId, + tm.doa as admissionDate, + tm.dod as dischargeDate, + tm.hospital_pin_code as hospitalCode, + tm.hospital_name as hospitalName, + tm.hospital_address as hospitalAddress, + tm.hospital_phone_no as hospitalNumber, + tm.claim_amount as claimedAmount, + tm.tpa_no as memberId, + tm.claim_description as ailmentDescription, + cp.policy_no as policyNumber, + e.emp_code as employeeCode, + tn.note as disease, + cf.url as filePath, + tm.claim_type as claimType, + tm.claim_type as benefitType + ') + ->join('employees e', 'e.id = tm.emp_id', 'left') + ->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left') + ->join('ticket_notes tn', 'tn.ticket_id = tm.id', 'left') + ->join('claim_files cf', "cf.ticket_id = tm.id AND cf.file_type = 2 AND cf.mime_type='application/pdf'", 'left') + ->where('tm.id', $claimId) + ->get() + ->getRowArray(); + + // dd($data); + + if (!$data) { + log_message('error', "TPA CLAIM PUSH FAILED HEALTH_INDIA | claimId: {$claimId} - Claim not found"); + return; + } + + if ($data['filePath'] == null) { + log_message('error', "TPA CLAIM PUSH FAILED HEALTH_INDIA | claimId: {$claimId} - File Missing"); + return; + } + + // Build absolute file path + $filename = basename($data['filePath']); + $pdfPath = WRITEPATH . 'uploads/claim_files/' . $filename; + + if (!file_exists($pdfPath)) { + log_message('error', "TPA CLAIM PUSH FAILED HEALTH_INDIA | claimId: {$claimId} - PDF not found on server at path: {$pdfPath}"); + return; + } + + // Convert PDF to Base64 + $fileContent = base64_encode(file_get_contents($pdfPath)); + + // Generate Health India Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['result'][0]['access_token'])) { + log_message('error', "TPA CLAIM PUSH FAILED HEALTH_INDIA | claimId: {$claimId} - Token generation failed"); + return; + } + $token = $tokenResponse['data']['result'][0]['access_token']; + + // Map claim type: Reimbursement or Cashless + $claimTypeMap = [ + 'reimbursement' => 'Reimbursement', + 'cashless' => 'Cashless', + ]; + $mappedClaimType = $claimTypeMap[strtolower($data['claimType'] ?? 'reimbursement')] ?? 'Reimbursement'; + + // Map benefit type: IPD or OPD + $benefitTypeMap = [ + 'ipd' => 'IPD', + 'opd' => 'OPD', + ]; + $mappedBenefitType = $benefitTypeMap[strtolower($data['benefitType'] ?? 'ipd')] ?? 'IPD'; + + // Build Health India Claim Submission Request (API Section 5) + $body = [ + "policY_NUMBER" => $data['policyNumber'], + "employeE_CODE" => $data['employeeCode'], + "membeR_ID" => $data['memberId'], + "claiM_TYPE" => $mappedClaimType, + "benefiT_TYPE" => $mappedBenefitType, + "claimeD_AMOUNT" => (string) $data['claimedAmount'], + "datE_OF_ADMISSION" => date('Y-m-d', strtotime($data['admissionDate'])), + "ailmenT_DESCRIPTION" => $data['ailmentDescription'] ?? 'NA', + "hospitaL_CODE" => $data['hospitalCode'] ?? '', + "hospitaL_NAME" => $data['hospitalName'] ?? '', + "hospitaL_ADDRESS" => $data['hospitalAddress'] ?? '', + "hospitaL_NUMBER" => $data['hospitalNumber'] ?? '', + "pdF_BYTES" => [$fileContent] + ]; + + // $body = [ + // 'policY_NUMBER' => '141600_POLICY_AWAITED', + // 'employeE_CODE' => 'DECP000', + // 'membeR_ID' => '10662420SE', + // 'claiM_TYPE' => 'Reimbursement', + // 'benefiT_TYPE' => 'IPD', + // 'claimeD_AMOUNT' => '102', + // 'datE_OF_ADMISSION' => '2026-01-19', + // 'ailmenT_DESCRIPTION' => 'Fever', + // 'hospitaL_CODE' => '100900', + // 'hospitaL_NAME' => 'Test', + // 'hospitaL_ADDRESS' => 'YEst', + // 'hospitaL_NUMBER' => '2233445566', + // "pdF_BYTES" => [$fileContent] + // ]; + + + $url = getenv('HEALTH_INDIA_BASE_URL') . "/Intimation/GetClaimIntimation"; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + log_message('error', 'TPA CLAIM PUSH HEALTH_INDIA | claimId: ' . $claimId . ' | URL: ' . $url ); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + dd($response); + + log_message('error', 'TPA CLAIM PUSH HEALTH_INDIA RESPONSE | claimId: ' . $claimId . ' | response: ' . json_encode($response)); + + if ($response['status'] != true) { + log_message('error', 'TPA CLAIM PUSH FAILED HEALTH_INDIA | claimId: ' . $claimId . ' | response: ' . json_encode($response)); + $this->db->table('ticket_master') + ->where('id', $claimId) + ->update(['tpa_push_response' => json_encode($response)]); + return; + } + + if ($response['status'] === true && !empty($response['data']['result'][0]['ccn'])) { + $ccn = $response['data']['result'][0]['ccn']; + $ccnExt = $response['data']['result'][0]['ccN_EXT'] ?? '0'; + + $this->db->table('ticket_master') + ->where('id', $claimId) + ->update([ + 'claim_number' => $ccn, + 'tpa_claim_id' => $ccn, + 'tpa_claim_push_reference_no' => $ccn . '(' . $ccnExt . ')', + 'updated_at' => date('Y-m-d H:i:s') + ]); + + log_message('error', 'TPA CLAIM PUSH SUCCESS HEALTH_INDIA | claimId: ' . $claimId . ' | CCN: ' . $ccn . ' | CCN_EXT: ' . $ccnExt); + } else { + log_message('error', 'TPA CLAIM PUSH HEALTH_INDIA API SUCCESS BUT CCN EMPTY | claimId: ' . $claimId . ' | response: ' . json_encode($response)); + return; + } + + return; + } + + public function ClaimDetail($claimId = null) + { + helper('api'); + + log_message('error', 'CLAIM STATUS HEALTH_INDIA | Started for claimId: ' . $claimId); + + $ticket = $this->db->table('ticket_master tm') + ->select("tm.id, tm.tpa_claim_id as ccn, cp.policy_no, cp.policy_start_date, cp.policy_end_date, tm.emp_code") + ->join('client_policy cp', 'tm.client_policy_id=cp.id') + ->where('tm.id', $claimId) + ->get()->getRowArray(); + + if (!$ticket) { + log_message('error', 'CLAIM STATUS FAILED HEALTH_INDIA | claimId: ' . $claimId . ' - Invalid claim'); + return ['status' => false, 'message' => 'Invalid claim']; + } + + // Generate Health India Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + + if (empty($tokenResponse['data']['result'][0]['access_token'])) { + log_message('error', 'CLAIM STATUS FAILED HEALTH_INDIA | claimId: ' . $claimId . ' - Token generation failed'); + return ['status' => false, 'message' => 'Token generation failed']; + } + + $token = $tokenResponse['data']['result'][0]['access_token']; + + $url = getenv('HEALTH_INDIA_BASE_URL') . "/Claims/GetClaims"; + + // Using claim number wise approach (Section 7.3 - option 3) + $body = [ + "policY_NUMBER" => $ticket['policy_no'], + "CCN" => $ticket['ccn'], + "CCN_EXT" => "0" + ]; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + log_message('error', 'CLAIM STATUS HEALTH_INDIA | claimId: ' . $claimId . ' | Request: ' . json_encode($body)); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + log_message('error', 'CLAIM STATUS HEALTH_INDIA | claimId: ' . $claimId . ' | Response: ' . json_encode($response)); + + if (empty($response['data']['result'][0])) { + log_message('error', 'CLAIM STATUS FAILED HEALTH_INDIA | claimId: ' . $claimId . ' | Empty response data'); + return ['status' => false, 'message' => 'API call failed.', 'data' => $response]; + } + + $claimData = $response['data']['result'][0]; + $status = $claimData['claiM_STATUS'] ?? null; + + // Status mapping based on API documentation + $map = [ + "Under Process" => 5, + "Pending for Bill Entry" => 5, + "Query" => 4, + "Paid" => 11, + "Rejected" => 8, + "Approved" => 8, + "Outstanding" => 5, + ]; + + if ($status != null && isset($map[$status])) { + $this->db->table('ticket_master') + ->where('id', $claimId) + ->update([ + 'claim_status_id' => $map[$status], + 'tpa_claim_status' => $status + ]); + log_message('error', "CLAIM STATUS SUCCESS HEALTH_INDIA | Updated claimId: {$claimId} with status: {$status}"); + } else { + log_message('error', "CLAIM STATUS HEALTH_INDIA | claimId: {$claimId} | Unknown status: {$status}"); + } + + return [ + 'status' => true, + 'message' => 'Claim status updated.', + 'updated_status' => $status, + 'api_response' => $response + ]; + } + + public function ClaimStatusUpdate() + { + helper('api'); + + log_message('error', 'CLAIM STATUS UPDATE BULK HEALTH_INDIA | Started'); + + $tickets = $this->db->table('ticket_master tm') + ->select("tm.id, tm.tpa_claim_id, cp.policy_no") + ->join('client_policy cp', 'tm.client_policy_id=cp.id') + ->where('tm.tpa_claim_id IS NOT NULL') + ->where('cp.tpa_id', $this->healthIndiaTpaId) + ->get()->getResultArray(); + + $count = 0; + + foreach ($tickets as $t) { + $this->ClaimDetail($t['id']); + $count++; + } + + log_message('error', 'CLAIM STATUS UPDATE BULK HEALTH_INDIA | Completed | Updated: ' . $count); + + return $this->response->setJSON(['status' => true, 'updated' => $count]); + } + + public function EcardRequest($employeeId = null, $policyNo = null, $memberId = null) + { + helper('api'); + + log_message('error', "ECARD REQUEST HEALTH_INDIA | Started | employeeId: {$employeeId} | policyNo: {$policyNo} | memberId: {$memberId}"); + + // Generate Health India Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['result'][0]['access_token'])) { + log_message('error', "ECARD REQUEST FAILED HEALTH_INDIA | employeeId: {$employeeId} | policyNo: {$policyNo} | Message: Token generation failed"); + return null; + } + $token = $tokenResponse['data']['result'][0]['access_token']; + + // API Section 3.3 - Individual member e-card + $url = getenv('HEALTH_INDIA_BASE_URL') . "/Member/GetMemberEcard"; + + $body = [ + "policY_NUMBER" => $policyNo, + "employeE_CODE" => $employeeId, + "membeR_ID" => $memberId + ]; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + log_message('error', 'ECARD REQUEST HEALTH_INDIA | Request: ' . json_encode($body)); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + log_message('error', 'ECARD REQUEST HEALTH_INDIA | Response: ' . json_encode($response)); + + if (($response['status'] ?? false) !== true) { + log_message('error', 'ECARD REQUEST FAILED HEALTH_INDIA | response: ' . json_encode($response)); + return null; + } + + if (empty($response['data']['result'][0])) { + log_message('error', 'ECARD REQUEST FAILED HEALTH_INDIA | Empty data | response: ' . json_encode($response)); + return null; + } + + $apiData = $response['data']['result'][0]; + + if (($apiData['message'] ?? '') === 'SUCCESS') { + $ecardUrl = $apiData['membeR_ECARD'] ?? ''; + + if (!empty($ecardUrl)) { + log_message('error', "ECARD REQUEST SUCCESS HEALTH_INDIA | employeeId: {$employeeId} | policyNo: {$policyNo} | memberId: {$memberId} | ecardUrl: {$ecardUrl}"); + return $ecardUrl; + } + } + + log_message('error', 'ECARD REQUEST FAILED HEALTH_INDIA | response: ' . json_encode($response)); + return null; + } + + public function HealthIndiaGetBenefDetails($requestData = null) + { + $function_calling_type = $requestData['return_type'] ?? 'job'; + + try { + helper('api'); + + $policyNo = $requestData['policy_no'] ?? '141600_POLICY_AWAITED'; + $client_policy_id = $requestData['client_policy_id'] ?? 6473; + + + + if (empty($policyNo)) { + log_message('error', 'TPA ID PULL HEALTH_INDIA | policy_no missing in request'); + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'policy_no required']; + } else { + return $this->respond(['status' => false, 'message' => 'policy_no required']); + } + } + + if (empty($client_policy_id)) { + log_message('error', 'TPA ID PULL HEALTH_INDIA | client_policy_id missing in request'); + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'client_policy_id required']; + } else { + return $this->respond(['status' => false, 'message' => 'client_policy_id required']); + } + } + + log_message('error', "TPA ID PULL HEALTH_INDIA | called for policy_no: {$policyNo}, client_policy_id: {$client_policy_id}"); + + // Fetch file download dates + // $batchFiles = $this->db->table('batch_files f') + // ->select("f.created_at") + // ->where('f.client_policy_id', $client_policy_id) + // ->where('f.insurer_or_tpa', 'tpa') + // ->where('f.actions', 'export') + // ->get() + // ->getResultArray(); + + + + // if (empty($batchFiles)) { + // log_message('error', 'TPA ID PULL FAILED HEALTH_INDIA | batchFiles is empty for this tpa id pull request'); + // if ($function_calling_type == "job") { + // return ['status' => false, 'message' => 'batchFiles not found']; + // } else { + // return $this->respond(['status' => false, 'message' => 'batchFiles not found']); + // } + // } + + // Generate Health India Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + if (empty($tokenResponse['data']['result'][0]['access_token'])) { + log_message('error', 'TPA ID PULL HEALTH_INDIA | Token generation failed'); + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'Token generation failed']; + } else { + return $this->respond(['status' => false, 'message' => 'Token generation failed']); + } + } + $token = $tokenResponse['data']['result'][0]['access_token']; + + // API Section 2.3 - Bulk Enrolment data Fetch + $url = getenv('HEALTH_INDIA_BASE_URL') . "/Enrollment/GetEnrollmentData"; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + $body = [ + "policY_NUMBER" => $policyNo + ]; + + log_message('error', "TPA ID PULL HEALTH_INDIA | API params: " . json_encode(['url' => $url, 'method' => 'POST', 'body' => $body])); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + dd($response); + + log_message('error', "TPA ID PULL HEALTH_INDIA | API Response: " . json_encode($response)); + + if (($response['status'] ?? false) !== true) { + log_message('error', 'HEALTH_INDIA API FAILED | response: ' . json_encode($response)); + + // update file table status after the tpa id failed to update + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); + log_message('error', "Files table status updated for the file id: {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'API call failed', 'data' => $response]; + } else { + return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $response]); + } + } + + if ( + !isset($response['data']['result']) || + !is_array($response['data']['result']) || + count($response['data']['result']) === 0 + ) { + log_message('error', 'TPA ID PULL API FAILED HEALTH_INDIA | Empty member data for this pull request'); + + // update file table status after the tpa id failed to update + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); + log_message('error', "Files table status updated for the file id: {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'API call failed', 'data' => 'Empty member data for this pull request']; + } else { + return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => 'Empty member data for this pull request']); + } + } + + $allMembers = $response['data']['result']; + + // Save API data as JSON for analysis + $json = json_encode($allMembers, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $filePath = WRITEPATH . 'tmp/' . time() . '_' . $requestData['file_id'] . '.json'; + file_put_contents($filePath, $json); + + log_message('error', "TPA ID PULL HEALTH_INDIA | Saved JSON to: {$filePath}"); + + // Call a job for dump JSON data to DB + $job_details = new Jobs(); + $r = Jobs::addJob([ + 'job_name' => 'saveHealthIndiaAPIData', + 'payload' => [ + 'file_id' => $requestData['file_id'], + 'json_file_path' => $filePath + ] + ]); + + log_message('error', "TPA ID PULL HEALTH_INDIA | Job added for saving API data"); + + // ================= MATCHING LOGIC ================= + + $employeePolicyModel = new EmployeePolicyModel(); + + $employeePolicyData = $employeePolicyModel + ->join('employees', 'employees.id = employee_polices.employee_id') + ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('employee_polices.tpa_id IS NULL') + ->findAll(); + + $updated = 0; + $batch_file_success = 'success'; + $employee_policy_ids = []; + + foreach ($employeePolicyData as $policy) { + $hasMatchForThisPolicy = false; + + foreach ($allMembers as $m) { + // Match based on: employee name, employee code, and relation + if ( + strtolower(trim($policy['name'])) === strtolower(trim($m['insured_Name'] ?? '')) && + ($policy['emp_code'] ?? '') == ($m['employeeCode'] ?? '') && + strtolower($policy['relationship']) === strtolower($m['relation'] ?? '') + ) { + $hasMatchForThisPolicy = true; + + $sql = "UPDATE employee_polices SET tpa_id = ? WHERE id = ?"; + $this->db->query($sql, [$m['memberId'], $policy['emp_policy_id']]); + + // For e-card send + if (strtolower(trim($policy['relationship'])) == 'self') { + $employee_policy_ids[] = $policy['emp_policy_id']; + } + + if ($this->db->affectedRows() > 0) { + $updated++; + log_message('error', "✅ HEALTH_INDIA | Updated tpa_id={$m['memberId']} for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}"); + } else { + log_message('error', "⚠️ HEALTH_INDIA | No update (already set or not matched) for emp_policy_id={$policy['emp_policy_id']} policy={$policyNo}"); + } + } + } + + // Handle NO MATCH for this policy + if (!$hasMatchForThisPolicy) { + $nhanceSideData = [ + 'name' => $policy['name'] ?? null, + 'emp_code' => $policy['emp_code'] ?? null, + 'relationship' => $policy['relationship'] ?? null, + 'gender' => $policy['gender'] ?? null, + 'dob' => $policy['dob'] ?? null, + ]; + $batch_file_success = 'partially success'; + + log_message('error', "❌ HEALTH_INDIA | No match for Nhance = " . json_encode($nhanceSideData)); + } + } + + // Send e-card + if (!empty($employee_policy_ids)) { + log_message('error', "HEALTH_INDIA | sendMailForDownloadingECard JOB PUSHED."); + Jobs::addJob([ + 'job_name' => 'sendMailForDownloadingECard', + 'payload' => [ + 'ids' => $employee_policy_ids, + 'client_policy_id' => $client_policy_id + ] + ]); + } + + // Update file table status after the tpa id successfully updated + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', $batch_file_success)->update(); + log_message('error', "Files table status updated for the file id: {$requestData['file_id']} with status: {$batch_file_success}"); + } else { + log_message('error', "Failed to update file table status."); + } + + $totalCount = count($allMembers); + + log_message('error', "TPA ID PULL SUCCESS HEALTH_INDIA | completed. Total fetched={$totalCount}, updated={$updated}"); + + if ($function_calling_type == "job") { + return [ + 'status' => true, + 'message' => 'Updated successfully', + 'total_fetched' => $totalCount, + 'total_updated' => $updated + ]; + } else { + return $this->respond([ + 'status' => true, + 'message' => 'Updated successfully', + 'total_fetched' => $totalCount, + 'total_updated' => $updated + ]); + } + } catch (\Throwable $th) { + // Update file table status after the tpa id failed to update + if (isset($requestData['file_id']) && !empty($requestData['file_id'])) { + $file_model = new BatchFileModel(); + $file_model->where('id', $requestData['file_id'])->set('status', 'failed-8')->update(); + log_message('error', "Files table status updated for the file id: {$requestData['file_id']}"); + } else { + log_message('error', "Failed to update file table status."); + } + + $errorData = [ + 'message' => $th->getMessage(), + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'code' => $th->getCode(), + 'trace' => $th->getTraceAsString(), + 'trace_array' => $th->getTrace(), + 'function' => $th->getTrace()[0]['function'] ?? null, + 'class' => $th->getTrace()[0]['class'] ?? null, + ]; + + log_message('error', 'Exception thrown while calling HealthIndiaGetBenefDetails API: ' . json_encode($errorData)); + + if ($function_calling_type == "job") { + return ['status' => false, 'message' => 'API call failed', 'data' => $errorData]; + } else { + return $this->respond(['status' => false, 'message' => 'API call failed', 'data' => $errorData]); + } + } + } + + public function syncHealthIndiaClaimsToNhance() + { + helper('api'); + + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Started'); + + // Generate Health India Token + $tokenResponse = json_decode($this->generateAuthToken()->getBody(), true); + + if (empty($tokenResponse['data']['result'][0]['access_token'])) { + log_message('error', 'SYNC CLAIMS FAILED HEALTH_INDIA | Token generation failed'); + return $this->response->setJSON(['status' => false, 'message' => 'Token generation failed']); + } + + $token = $tokenResponse['data']['result'][0]['access_token']; + + $url = getenv('HEALTH_INDIA_BASE_URL') . "/ClaimsMIS/GetClaimsMIS"; + + $headers = [ + "Authorization: Bearer " . $token, + "Content-Type: application/json" + ]; + + $policies = $this->db->table('client_policy') + ->where('tpa_id', $this->healthIndiaTpaId) + ->get()->getResultArray(); + + $finalResult = []; + + foreach ($policies as $policy) { + $body = [ + "policY_NUMBER" => $policy['policy_no'] + ]; + + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Fetching for policy: ' . $policy['policy_no']); + + $response = call_third_party_api($url, 'POST', $headers, $body); + + if (!empty($response['data']['result'])) { + $finalResult = array_merge($finalResult, $response['data']['result']); + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Fetched ' . count($response['data']['result']) . ' claims for policy: ' . $policy['policy_no']); + } + } + + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Total claims fetched: ' . count($finalResult)); + + // Insert / update ticket_master + $insertedCount = 0; + foreach ($finalResult as $row) { + $status = $row['Claim_Status'] ?? 'Under Process'; + + $map = [ + "Under Process" => 5, + "Pending for Bill Entry" => 5, + "Paid" => 11, + "Rejected" => 8, + "Approved" => 8, + "Outstanding" => 5, + ]; + + $claimStatus = $map[$status] ?? 1; + + // Check if claim already exists + $existing = $this->db->table('ticket_master') + ->where('tpa_claim_id', $row['CLAIM_NUMBER']) + ->get()->getRowArray(); + + if (!$existing) { + $this->db->table('ticket_master')->insert([ + 'policy_no' => $row['Policy_No'] ?? '', + 'claim_number' => $row['CLAIM_NUMBER'] ?? '', + 'tpa_claim_id' => $row['CLAIM_NUMBER'] ?? '', + 'emp_code' => $row['Employee_Code'] ?? '', + 'insured_name' => $row['PATIENT_NAME'] ?? '', + 'claim_amount' => $row['INTIMATED_AMOUNT'] ?? 0, + 'hospital_name' => $row['HOSPITAL_NAME'] ?? '', + 'doa' => !empty($row['DATEOF_ADMISSION']) ? date('Y-m-d', strtotime($row['DATEOF_ADMISSION'])) : null, + 'dod' => !empty($row['DATEOF_DISCHARGE']) ? date('Y-m-d', strtotime($row['DATEOF_DISCHARGE'])) : null, + 'claim_status_id' => $claimStatus, + 'tpa_id' => $this->healthIndiaTpaId, + 'created_at' => date('Y-m-d H:i:s') + ]); + $insertedCount++; + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Inserted claim: ' . ($row['CLAIM_NUMBER'] ?? 'N/A')); + } else { + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Skipped existing claim: ' . ($row['CLAIM_NUMBER'] ?? 'N/A')); + } + } + + log_message('error', 'SYNC CLAIMS HEALTH_INDIA | Completed | Total: ' . count($finalResult) . ' | Inserted: ' . $insertedCount); + + return $this->response->setJSON([ + 'status' => true, + 'total' => count($finalResult), + 'inserted' => $insertedCount + ]); + } +} \ No newline at end of file diff --git a/app/Controllers/ICICILombardController.php b/app/Controllers/ICICILombardController.php index a91cc147..e9cbdf32 100644 --- a/app/Controllers/ICICILombardController.php +++ b/app/Controllers/ICICILombardController.php @@ -86,33 +86,64 @@ class ICICILombardController extends AdminController // dd($body); - $body = [ - "PolicyNumber" => "4016/PPN/A/O/53167743/00/000", + // $body = [ + // "PolicyNumber" => "4016/PPN/A/O/53167743/00/000", + // "CDBGAccountNumber" => "CD-MUM-0026", + // "CorrelationId" => "550e8400-e29b-41d4-a716-446655440016", + // "MemberDetails" => [ + // [ + // "MemberEmpId" => "EMPID3625562", + // "DOJ" => "21-MAR-2019", + // "InsuredName" => "Jeeva", + // "DOB" => "7-JUL-1993", + // "Relationship" => "SELF", + // "Gender" => "MALE", + // "DOC" => '28-Oct-2025', + // "SumInsured" => "500000", + // "EmailId" => "Jeeva@GMAIL.COM", + // "FlagStatus" => "A" + // ], + // [ + // "MemberEmpId" => "EMPID3625562", + // "DOJ" => "21-MAR-2019", + // "InsuredName" => "Muthu", + // "DOB" => "8-AUG-1970", + // "Relationship" => "MOTHER", + // "Gender" => "FEMALE", + // "DOC" => '28-Oct-2025', + // "EmailId" => "Muthu@GMAIL.COM", + // "FlagStatus" => "A" + // ], + // ] + // ]; + + + $body = [ + "PolicyNumber" => "4016/PPN/A/O/53185987/00/000", "CDBGAccountNumber" => "CD-MUM-0026", - "CorrelationId" => "550e8400-e29b-41d4-a716-446655440012", + "CorrelationId" => "550e8400-e29b-41d4-a716-446655440021", "MemberDetails" => [ [ - "MemberEmpId" => "EMPID3625559", + "MemberEmpId" => "EMPID3625564", "DOJ" => "21-MAR-2019", - "InsuredName" => "Gowtham", + "InsuredName" => "Jeeva", "DOB" => "7-JUL-1993", "Relationship" => "SELF", "Gender" => "MALE", - "DOC" => '2025-10-27', - "SumInsured" => "400000", - "EmailId" => "Gowtham@GMAIL.COM", + "DOC" => '18-Nov-2025', + "SumInsured" => "500000", + "EmailId" => "Jeeva@GMAIL.COM", "FlagStatus" => "A" ], [ - "MemberEmpId" => "EMPID3625559", + "MemberEmpId" => "EMPID3625564", "DOJ" => "21-MAR-2019", - "InsuredName" => "Lavanya", + "InsuredName" => "Muthu", "DOB" => "8-AUG-1970", "Relationship" => "MOTHER", "Gender" => "FEMALE", - "DOC" => '2025-10-27', - "SumInsured" => "400000", - "EmailId" => "Lavanya@GMAIL.COM", + "DOC" => '18-Nov-2025', + "EmailId" => "Muthu@GMAIL.COM", "FlagStatus" => "A" ], ] @@ -147,10 +178,10 @@ class ICICILombardController extends AdminController // dd($headers); - $body = [ - "PolicyNumber" => "4016/PPN/A/O/53167743/00/000", - "BatchId" => "3672146", - "CorrelationId" => "550e8400-e29b-41d4-a716-446655440012" + $body = [ + "PolicyNumber" => "4016/PPN/A/O/53185987/00/000", + "BatchId" => "3727144", + "CorrelationId" => "550e8400-e29b-41d4-a716-446655440021" ]; $response = call_third_party_api($url, 'POST', $headers, $body, true); @@ -181,10 +212,10 @@ class ICICILombardController extends AdminController 'Content-Type: application/json' ]; - $body = [ - "PolicyNumber" => "4016/PPN/A/O/53167743/00/000", - "IMID" => "3672145", - "CorrelationId" => "550e8400-e29b-41d4-a716-446655440011" + $body = [ + "PolicyNumber" => "4016/PPN/A/O/53185987/00/000", + "IMID" => "201580517901", + "CorrelationId" => "550e8400-e29b-41d4-a716-446655440020" ]; $response = call_third_party_api($url, 'POST', $headers, $body, true); diff --git a/app/Controllers/JobWorker.php b/app/Controllers/JobWorker.php index 9cc001dd..d6ae550f 100755 --- a/app/Controllers/JobWorker.php +++ b/app/Controllers/JobWorker.php @@ -155,6 +155,14 @@ class JobWorker extends AdminController 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\TicketServiceController', ], + 'tpaClaimDumpImporter' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\TicketServiceController', + ], + 'tpaClaimDumpToTicketMasterImporters' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\TicketServiceController', + ], 'excelMultieventFileFormateValidation' => [ 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\EmployeeMultiEventServiceController', @@ -183,6 +191,10 @@ class JobWorker extends AdminController 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\VidalApiController', ], + 'FhplGetBenefDetails' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\FhplApiController', + ], 'bdsDumpExcelFileFormatValidation' => [ 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\PolicyTransactionController', @@ -201,6 +213,14 @@ class JobWorker extends AdminController 'bulkGenerateEcardAndStoreinS3' => [ 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\EmployeeController', + ], + 'bulkEcardDownloadAsZipFromS3' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\EmployeeController', + ], + 'getEmployeeEcardFromTmpFolderAndZipToS3' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\EmployeeController', ] ]; diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index a3ebe469..2deca1f1 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -378,7 +378,154 @@ class LeadsController extends BaseController private function prepareLeadData() { - $data = $this->request->getPost(); + $rules = [ + + 'lead_type' => [ + 'rules' => 'integer', + 'errors' => ['required' => 'Lead Type is required'] + ], + 'issuer' => [ + 'rules' => 'required', + 'errors' => ['required' => 'Issuer is required'] + ], + 'entity_type_id' => [ + 'rules' => 'required', + 'errors' => ['required' => 'Entity Type is required'] + ], + 'client_name' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]', + 'errors' => [ + 'required' => 'Client Name is required', + 'regex_match' => 'Client Name only letters, numbers and characters _ - and space are allowed' + ] + ], + 'client_short_name' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'errors' => ['required' => 'Client Short Name is required','regex_match' => 'Client Short Name only letters, numbers and characters _ and - are allowed'] + ], + 'gst' => [ + 'rules' => 'required', + 'errors' => ['required' => 'GST Number is required'] + ], + 'gst' => [ + 'rules' => 'required|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]', + 'errors' => [ + 'required' => 'GST Number is required.', + 'regex_match' => 'Invalid GST format. Example: 22AAAAA0000A1Z5' + ] + ], + 'branch_name' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[3]', + 'errors' => [ + 'required' => 'Branch Name is required.', + 'regex_match' => 'Branch Name can only contain letters, numbers, spaces, - and _.', + 'min_length' => 'Branch Name must be at least 3 characters long.' + ] + ], + 'branch_code' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'errors' => [ + 'required' => 'Branch Code is required.', + 'regex_match' => 'Branch Code can only contain letters, numbers, - and _.' + ] + ], + 'contact_person_name' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]', + 'errors' => [ + 'required' => 'Contact Person Name is required', + 'regex_match' => 'Contact person name only letters, numbers and spaces, - and _.' + ] + ], + 'contact_person_mobile' => [ + 'rules' => 'required', + 'rules' => 'required|regex_match[/^[0-9]+$/]', + 'errors' => ['required' => 'Contact Person Mobile is required' , + 'regex_match' => 'Contact Person Mobile Only numbers' + ] + ], + 'contact_person_email' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Contact Person Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).' + ] + // 'rules' => 'required|valid_email|regex_match[/^[a-zA-Z0-9_-]+$/]', + // 'regex_match' => 'Contact Person Email Only letters, numbers and characters _ - @ . are allowed' + + ], + 'salse_person_id' => [ + 'rules' => 'required', + 'errors' => ['required' => 'Sales Person is required'] + ], + 'status' => [ + 'rules' => 'required', + 'errors' => ['required' => 'Status is required'] + ] + ]; + $request_data = $this->request->getPost(); + $data = sanitizeInputArrayAdvanced($request_data); + if (isset($data['lead_form_type']) && (int)$data['lead_form_type'] === 2) { + $rules['client_type'] = [ + 'rules' => 'required', + 'errors' => ['required' => 'Client Type is required'] + ]; + $rules['policy_type_id'] = [ + 'rules' => 'required', + 'errors' => ['required' => 'Policy Type is required'] + ]; + $rules['policy_start_date'] = [ + 'rules' => 'required', + 'errors' => ['required' => 'Date of Commencement is required'] + ]; + $rules['policy_end_date'] = [ + 'rules' => 'required', + 'errors' => ['required' => 'Date of Expiry is required'] + ]; + } + + // 1. MANUALLY VALIDATE FILES BEFORE PROCESSING + // $allFiles = $this->request->getFiles(); + // foreach ($allFiles as $inputName => $files) { + // // If it's a single file, wrap it in an array to use the same logic + // $fileArray = is_array($files) ? $files : [$files]; + + // foreach ($fileArray as $index => $file) { + // if ($file->isValid() && !$file->hasMoved()) { + // $extension = strtolower($file->getExtension()); + + // // Logic: index 0 is Excel only, others are mixed + // if ($index === 0) { + // $allowed = ['xls', 'xlsx']; + // } else { + // $allowed = ['xls', 'xlsx', 'pdf', 'jpg', 'jpeg', 'png']; + // } + + // if (!in_array($extension, $allowed)) { + // // Return 400 Error immediately + // return $this->response->setStatusCode(400)->setJSON([ + // 'status' => false, + // 'message' => 'Input validation failed', + // 'code' => 400, + // 'errors' => [ + // $inputName => "File " . ($index + 1) . " has an invalid extension ($extension). Allowed: " . implode(',', $allowed) + // ] + // ]); + // } + // } + // } + // } + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + + $data['client_type'] = 1; $data['pan'] = ""; @@ -1136,7 +1283,7 @@ class LeadsController extends BaseController // 12. Conditional rendering based on lead_form_type if ($lead_data['lead_form_type'] == 1) { - $data['demogrphy_html_data'] = $this->generateDemographyDataTable(['lead_id' => $id]); + // $data['demogrphy_html_data'] = $this->generateDemographyDataTable(['lead_id' => $id]); $this->loadLayout('view_rfq.php', $data); } else if ($lead_data['lead_form_type'] == 2) { $data['occupancy'] = $this->occupancyModel->findAll(); @@ -2538,7 +2685,7 @@ class LeadsController extends BaseController public function calculateMembersDemography($params, $returnType = null) { $lead_id = $params['lead_id']; - $lead_data = $this->leadsModel->find($lead_id); + $lead_data = $this->leadsModel->find((int)$lead_id); $file_name_with_path = WRITEPATH . "/uploads/lead_files/" . $lead_data['file_name']; if (!$lead_data) { @@ -2999,7 +3146,7 @@ class LeadsController extends BaseController // print_r($params); die; - $lead_id = $params['lead_id']; + $lead_id = (int)$params['lead_id']; $file_type = $params['file_type']; //rfq or qcr $recipient_type = $params['recipient_type']; //insurer or client or internal or placement $recipient_mail = $params['recipient_mail']; // - only primary key of contacts @@ -3033,7 +3180,7 @@ class LeadsController extends BaseController if ($recipient_type === 'placement') { - $lead_data = $this->leadsModel->find($lead_id); + $lead_data = $this->leadsModel->find((int)$lead_id); $data = []; @@ -5320,7 +5467,7 @@ class LeadsController extends BaseController public function handleMemberDataGPATotalSumInsurerFromExcel($params) { $lead_id = $params['lead_id']; - $lead_data = $this->leadsModel->find($lead_id); + $lead_data = $this->leadsModel->find((int)$lead_id); // dd($lead_data); $file_name_with_path = WRITEPATH . "/uploads/lead_files/" . $lead_data['file_name']; // dd($file_name_with_path); @@ -5384,8 +5531,13 @@ class LeadsController extends BaseController return []; } - public function generateDemographyDataTable($param) - { + public function generateDemographyDataTable($param = []) + { + + if ($this->request !== null) { + $param['lead_id'] = $this->request->getGet('lead_id'); + } + $returnData = $this->calculateMembersDemography($param, "internal"); $html = ""; @@ -5431,6 +5583,14 @@ class LeadsController extends BaseController } } + if ($this->request !== null) { + if(!empty($html)){ + return $this->respond(['status' => true, 'data' => $html], 200); + }else{ + return $this->respond(['status' => false, 'data' => $html], 200); + } + } + return $html; } @@ -5726,7 +5886,7 @@ class LeadsController extends BaseController $first_file_name = $isFirstField ? 'Member List' : ''; $member_data_link = $isFirstField ? $sample_dwn_link : ''; $read_only = $isFirstField ? 'readonly' : ''; - $accept = $isFirstField ? '.xls,.xlsx' : ''; + $accept = $isFirstField ? '.xls,.xlsx' : '.xls,.xlsx,.pdf,.jpg,.jpeg,.png'; $displayIndex = $index + 1; $html .= ' diff --git a/app/Controllers/LoginController.php b/app/Controllers/LoginController.php index 451765c2..fb073f21 100755 --- a/app/Controllers/LoginController.php +++ b/app/Controllers/LoginController.php @@ -11,6 +11,8 @@ use CodeIgniter\API\ResponseTrait; use App\Models\UserModel; use App\Models\AuthHistoryModel; +use App\Libraries\AuthLogout; + class LoginController extends BaseController { use ResponseTrait; @@ -45,7 +47,7 @@ class LoginController extends BaseController $user_team = $UserModel->getUserTeamsByUserID($user->id); // dd($user_team); - + session()->regenerate(true); $session_data = [ 'isLoggedIn' => True , 'userid' => $user->id, @@ -56,9 +58,12 @@ class LoginController extends BaseController $path = getenv('cookie.Path'); $domain = getenv('cookie.Domain'); $https = getenv('ccokie.secure'); - setcookie('session_data', json_encode($session_data), time() + 12 * 60 * 60, $path, $domain, $https, true); + // setcookie('session_data', json_encode($session_data), time() + 12 * 60 * 60, $path, $domain, $https, true); set_session_data($session_data); + // Bind session to device + set_session_data(['fingerprint' => generateFingerprint()]); + log_message('error', 'Set The UserId : `'. $user->id .'` in Session'); log_message('error', 'User Login Sucessfully'); @@ -91,14 +96,23 @@ class LoginController extends BaseController public function logout() { - $path = getenv('cookie.Path'); - session()->destroy(); - // setcookie('session_data', '', time() - 3600, $path); - $path = getenv('cookie.Path'); - $domain = getenv('cookie.Domain'); - $https = getenv('cookie.secure'); - setcookie('session_data',null, time() -3600, $path, $domain, $https, true); - return redirect()->to(base_url('login')); + // $path = getenv('cookie.Path'); + // session()->regenerate(true); + // session()->destroy(); + + // $path = getenv('cookie.Path'); + // $domain = getenv('cookie.Domain'); + // $https = getenv('cookie.secure'); + + // setcookie('session_data',null, time() - 42000, $path, $domain, $https, true); + // // return redirect()->to(base_url('login')); + // return redirect()->to(base_url('login')) + // ->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + // ->setHeader('Pragma', 'no-cache') + // ->setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT'); + + + return AuthLogout::logout(); } public function getUserDeviceInfo($userId, $type_of_user) diff --git a/app/Controllers/MasterController.php b/app/Controllers/MasterController.php index 79f5753b..cff68f18 100755 --- a/app/Controllers/MasterController.php +++ b/app/Controllers/MasterController.php @@ -283,26 +283,53 @@ class MasterController extends AdminController { $this->myLogger->logme('error','Insurer general info function called'); + $rules = [ + 'name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Name is required' + ] + ], + 'short_name' => [ + 'rules' => 'required|min_length[3]|max_length[8]', + 'errors' => [ + 'required' => 'Short name is required', + 'min_length' => 'Short name must be at least 3 characters', + 'max_length' => 'Short name cannot exceed 8 characters' + ] + ], + 'insurer_logo' => [ + 'rules' => 'if_exist|is_image[insurer_logo]|max_size[insurer_logo,200]|ext_in[insurer_logo,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'The uploaded file must be an image', + 'max_size' => 'File size should not exceed 200 KB', + 'ext_in' => 'Allowed file types: jpg, jpeg, png', + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $uploadFilePath = ROOTPATH . 'public/uploads/logo/'; $file_name = file_Upload($this->request->getFile('insurer_logo'), $uploadFilePath); $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); - if($this->request->getPost('addition_add_day')){ - $data['addition_add_day'] = 1; - } - - if($this->request->getPost('deletion_add_day')){ - $data['deletion_add_day'] = 1; - } - - if($this->request->getPost('is_multi_event')){ - $data['is_multi_event'] = 1; - } - - $data['created_by'] = get_session_userid(); - $data['insurer_logo'] = $file_name; + $sanitized_post_data['addition_add_day'] = (!empty($sanitized_post_data['addition_add_day'])) ? 1 : 0; + $sanitized_post_data['deletion_add_day'] = (!empty($sanitized_post_data['deletion_add_day'])) ? 1 : 0; + $sanitized_post_data['is_multi_event'] = (!empty($sanitized_post_data['is_multi_event'])) ? 1 : 0; + $sanitized_post_data['created_by'] = get_session_userid(); + $sanitized_post_data['insurer_logo'] = $file_name; - $insert = $this->insurerModel->insert($data); + $insert = $this->insurerModel->insert($sanitized_post_data); + if($insert){ $insurer_data = $this->insurerModel->where(['id' => $insert, 'is_active' => 1])->first(); $insurer_templete_count = $this->insurerTemplateModel->where(['insurer_id' => $insert, 'is_active' => 1])->countAllResults(); @@ -316,28 +343,130 @@ class MasterController extends AdminController { $this->myLogger->logme('error','Client branch CREATE function called'); + $rules = [ + + // ====================== + // Branch Details + // ====================== + 'branch_name' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch name is required' + ] + ], + + 'branch_code' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch code is required' + ] + ], + + 'address1' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Address Line 1 is required' + ] + ], + + 'state' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'State is required' + ] + ], + + 'district' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'District is required' + ] + ], + + 'city' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'City is required' + ] + ], + + 'pincode' => [ + 'rules' => 'required|numeric|exact_length[6]', + 'errors' => [ + 'required' => 'Pincode is required', + 'numeric' => 'Pincode must contain only numbers', + 'exact_length' => 'Pincode must be exactly 6 digits' + ] + ], + + // ====================== + // Contact Details (Array) + // ====================== + 'name.*' => [ + 'rules' => 'required|trim|min_length[2]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'errors' => [ + 'required' => 'Contact name is required', + 'min_length' => 'Contact name must be at least 2 characters', + 'regex_match' => 'Contact name is invalid Format' + ] + ], + + 'designation.*' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Designation is required' + ] + ], + + 'email.*' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@company.com).' + ] + ], + + 'mobile.*' => [ + 'rules' => 'required|numeric|exact_length[10]', + 'errors' => [ + 'required' => 'Mobile number is required', + 'numeric' => 'Mobile must contain only digits', + 'exact_length' => 'Mobile number must be exactly 10 digits' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $data = $this->request->getPost(); - $data['created_by'] = get_session_userid(); - $insert = $this->insurerBranchModel->insert($data); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $sanitized_post_data['created_by'] = get_session_userid(); + $insert = $this->insurerBranchModel->insert($sanitized_post_data); if($insert){ - for ($i = 0; $i < count($this->request->getPost('name')); $i++) { + for ($i = 0; $i < count($sanitized_post_data['name']); $i++) { // Prepare data to insert - $data = [ + $sanitized_post_data_for_level = [ 'contact_type' => 'insurer', 'ref_id' => $insert, 'created_by' => get_session_userid(), - 'name' => $this->request->getPost('name')[$i], - 'email' => $this->request->getPost('email')[$i], - 'mobile' => $this->request->getPost('mobile')[$i], - 'designation' => $this->request->getPost('designation')[$i] + 'name' => $sanitized_post_data['name'][$i] ?? null, + 'email' => $sanitized_post_data['email'][$i] ?? null, + 'mobile' => $sanitized_post_data['mobile'][$i] ?? null, + 'designation' => $sanitized_post_data['designation'][$i] ?? null ]; - $contacts = $this->levelContactModel->insert($data); + $contacts = $this->levelContactModel->insert($sanitized_post_data_for_level); } } if($insert){ - $branchData = $this->insurerBranchModel->where('insurer_id', $this->request->getPost('insurer_id'))->findAll(); + $branchData = $this->insurerBranchModel->where('insurer_id', $sanitized_post_data['insurer_id'])->findAll(); echo json_encode(array("status" => true , 'data' => $branchData)); }else{ echo json_encode(array("status" => false)); @@ -367,40 +496,72 @@ class MasterController extends AdminController public function editInsurerGeneralInfo() { - $this->myLogger->logme('error','edit Insurer general info function called'); + + $this->myLogger->logme('error','Edit Insurer general info function called'); + $postData = $this->request->getPost(); + $logoFile = $this->request->getFile('insurer_logo'); + + $rules = [ + 'name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Name is required' + ] + ], + 'short_name' => [ + 'rules' => 'required|min_length[3]|max_length[8]', + 'errors' => [ + 'required' => 'Short name is required', + 'min_length' => 'Short name must be at least 3 characters', + 'max_length' => 'Short name cannot exceed 8 characters' + ] + ], + 'insurer_logo' => [ + 'rules' => 'permit_empty|is_image[insurer_logo]|max_size[insurer_logo,200]|ext_in[insurer_logo,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'The uploaded file must be an image', + 'max_size' => 'File size should not exceed 200 KB', + 'ext_in' => 'Only JPG, JPEG, and PNG files are allowed.', + ] + ], + ]; + + if (!$this->validateData($postData, $rules)) { + $errors = $this->validator->getErrors(); + + $mainMessage = 'Input validation failed'; + + if (isset($errors['insurer_logo']) && (strpos($errors['insurer_logo'], 'Security') !== false || strpos($errors['insurer_logo'], 'Forbidden') !== false)) { + $mainMessage = 'File upload rejected: Security policy violation.'; + } + + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => $mainMessage, // This will change based on the error + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $uploadFilePath = ROOTPATH . 'public/uploads/logo/'; - $file_name = file_Upload($this->request->getFile('insurer_logo'), $uploadFilePath); - - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); - $data['updated_by'] = get_session_userid(); - + $file_name = file_Upload($this->request->getFile('insurer_logo'), $uploadFilePath); + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey']; + + $sanitized_post_data['addition_add_day'] = (!empty($sanitized_post_data['addition_add_day'])) ? 1 : 0; + $sanitized_post_data['deletion_add_day'] = (!empty($sanitized_post_data['deletion_add_day'])) ? 1 : 0; + $sanitized_post_data['is_multi_event'] = (!empty($sanitized_post_data['is_multi_event'])) ? 1 : 0; + $sanitized_post_data['updated_by'] = get_session_userid(); + if(!empty($file_name)){ - $data['insurer_logo'] = $file_name; + $sanitized_post_data['insurer_logo'] = $file_name; } - if($this->request->getPost('addition_add_day')){ - $data['addition_add_day'] = 1; - }else{ - $data['addition_add_day'] = 0; - } + $update = $this->insurerModel->update($id,$sanitized_post_data); - if($this->request->getPost('deletion_add_day')){ - $data['deletion_add_day'] = 1; - }else{ - $data['deletion_add_day'] = 0; - } - - if($this->request->getPost('is_multi_event')){ - $data['is_multi_event'] = 1; - }else{ - $data['is_multi_event'] = 0; - } - - $update = $this->insurerModel->update($id,$data); if($update){ - echo json_encode(array("status" => true , 'data' => $data)); + echo json_encode(array("status" => true , 'data' => $sanitized_post_data)); }else{ echo json_encode(array("status" => false)); } @@ -422,33 +583,133 @@ class MasterController extends AdminController public function editInsurerBranch() { - $this->myLogger->logme('error','Insurer branch CREATE function called'); - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); - $update = $this->insurerBranchModel->update($id, $data); + $this->myLogger->logme('error','Insurer branch EDIT function called'); + $rules = [ + + // ====================== + // Branch Details + // ====================== + 'branch_name' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch name is required' + ] + ], + + 'branch_code' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch code is required' + ] + ], + + 'address1' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Address Line 1 is required' + ] + ], + + 'state' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'State is required' + ] + ], + + 'district' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'District is required' + ] + ], + + 'city' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'City is required' + ] + ], + + 'pincode' => [ + 'rules' => 'required|numeric|exact_length[6]', + 'errors' => [ + 'required' => 'Pincode is required', + 'numeric' => 'Pincode must contain only numbers', + 'exact_length' => 'Pincode must be exactly 6 digits' + ] + ], + + // ====================== + // Contact Details (Array) + // ====================== + 'name.*' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'Contact name is required', + 'min_length' => 'Contact name must be at least 2 characters' + ] + ], + + 'designation.*' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Designation is required' + ] + ], + + 'email.*' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@company.com).' + ] + ], + + 'mobile.*' => [ + 'rules' => 'required|numeric|exact_length[10]', + 'errors' => [ + 'required' => 'Mobile number is required', + 'numeric' => 'Mobile must contain only digits', + 'exact_length' => 'Mobile number must be exactly 10 digits' + ] + ], + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + $update = $this->insurerBranchModel->update($id, $sanitized_post_data); if($update){ $contactsToDelete = $this->levelContactModel->where(['ref_id' => $id,'contact_type' => 'insurer', 'is_active' => 1])->get()->getResult(); foreach ($contactsToDelete as $contact) { $this->levelContactModel->delete($contact->id); } - for ($i = 0; $i < count($this->request->getPost('name')); $i++) { + for ($i = 0; $i < count($sanitized_post_data['name']); $i++) { // Prepare data to update - $data = [ + $sanitized_post_data_for_level = [ 'contact_type' => 'insurer', 'ref_id' => $id, 'created_by' => get_session_userid(), - 'name' => $this->request->getPost('name')[$i], - 'email' => $this->request->getPost('email')[$i], - 'mobile' => $this->request->getPost('mobile')[$i], - 'designation' => $this->request->getPost('designation')[$i] - ]; - $contacts = $this->levelContactModel->insert($data); + 'name' => $sanitized_post_data['name'][$i] ?? null, + 'email' => $sanitized_post_data['email'][$i] ?? null, + 'mobile' => $sanitized_post_data['mobile'][$i] ?? null, + 'designation' => $sanitized_post_data['designation'][$i] ?? null + ]; + $contacts = $this->levelContactModel->insert($sanitized_post_data_for_level); } } if($update){ - $branchData = $this->insurerBranchModel->where('insurer_id', $this->request->getPost('insurer_id'))->findAll(); + $branchData = $this->insurerBranchModel->where('insurer_id', $sanitized_post_data['insurer_id'])->findAll(); echo json_encode(array("status" => true , 'data' => $branchData, 'edit')); }else{ echo json_encode(array("status" => false, 'edit')); @@ -594,6 +855,74 @@ class MasterController extends AdminController { $this->myLogger->logme('error','TPA general info function called'); + $rules = [ + + // ====================== + // TPA Basic Details + // ====================== + 'name' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'TPA name is required', + 'min_length' => 'TPA name must be at least 2 characters' + ] + ], + + 'short_name' => [ + 'rules' => 'required|trim|min_length[3]', + 'errors' => [ + 'required' => 'TPA short name is required', + 'min_length' => 'Short name must be at least 3 characters', + ] + ], + + 'network_hospitals' => [ + 'rules' => 'required|valid_url', + 'errors' => [ + 'required' => 'Network hospitals URL is required', + 'valid_url' => 'Please enter a valid URL' + ] + ], + 'tpa_logo' => [ + 'rules' => 'if_exist|is_image[tpa_logo]|max_size[tpa_logo,200]|ext_in[tpa_logo,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'TPA logo must be an image', + 'max_size' => 'TPA logo should not exceed 200 KB', + 'ext_in' => 'Allowed logo types: jpg, jpeg, png' + ] + ], + + 'fc' => [ + 'rules' => 'if_exist|is_image[fc]|max_size[fc,500]|ext_in[fc,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'Front card must be an image', + 'max_size' => 'Front card image should not exceed 500 KB', + 'ext_in' => 'Allowed types: jpg, jpeg, png' + ] + ], + + 'bc' => [ + 'rules' => 'if_exist|is_image[bc]|max_size[bc,500]|ext_in[bc,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'Back card must be an image', + 'max_size' => 'Back card image should not exceed 500 KB', + 'ext_in' => 'Allowed types: jpg, jpeg, png' + ] + ], + ]; + + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $uploadFilePath = ROOTPATH . 'public/uploads/logo/'; $file_name = file_Upload($this->request->getFile('tpa_logo'), $uploadFilePath); @@ -603,18 +932,18 @@ class MasterController extends AdminController $back_card_file_name = file_Upload($this->request->getFile('bc'), $template_bg_path); - $eCardTemplate = $this->request->getPost('ecard_content'); - $data = $this->request->getPost(); - $data['created_by'] = get_session_userid(); - $data['tpa_logo'] = $file_name; - $data['front_card'] = $front_card_file_name; - $data['back_card'] = $back_card_file_name; - $data['network_hospitals'] = $this->request->getPost('network_hospitals'); + $eCardTemplate = $sanitized_post_data['ecard_content']; - $insert = $this->tpaModel->insert($data); + $sanitized_post_data['created_by'] = get_session_userid(); + $sanitized_post_data['tpa_logo'] = $file_name; + $sanitized_post_data['front_card'] = $front_card_file_name; + $sanitized_post_data['back_card'] = $back_card_file_name; + $sanitized_post_data['network_hospitals'] = $sanitized_post_data['network_hospitals']; + + $insert = $this->tpaModel->insert($sanitized_post_data); - $tpa_name = (string) $this->request->getPost('name'); - $short_name = (string) $this->request->getPost('short_name'); + $tpa_name = ((string) $sanitized_post_data['name']) ?? null; + $short_name = ((string) $sanitized_post_data['short_name']) ?? null; $filename = strtolower(str_replace(' ', '_', $short_name)) . '.html'; $file_directory = WRITEPATH . 'e_card_template/'; @@ -629,10 +958,10 @@ class MasterController extends AdminController header('Content-type:text/html; charset=utf-8'); // Write the HTML content to the file - $data = file_put_contents($file_path, $eCardTemplate); + $html_data = file_put_contents($file_path, $eCardTemplate); - if ($data !== false) { + if ($html_data !== false) { $this->myLogger->logme('error', 'TPA: {tpa}, e-Card HTML template file saved successfully: {data}, filepath is: {path}', ['data' => $filename, 'tpa' => $tpa_name, 'path' => $file_path]); } else { $this->myLogger->logme('error', 'TPA: {tpa}, e-Card HTML template file unable to save: {data}', ['data' => $filename, 'tpa' => $tpa_name]); @@ -651,28 +980,129 @@ class MasterController extends AdminController { $this->myLogger->logme('error','TPA branch CREATE function called'); + $rules = [ + + // ====================== + // Branch Details + // ====================== + 'branch_name' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch name is required' + ] + ], + + 'branch_code' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch code is required' + ] + ], + + 'address1' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Address Line 1 is required' + ] + ], + + 'state' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'State is required' + ] + ], + + 'district' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'District is required' + ] + ], + + 'city' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'City is required' + ] + ], + + 'pincode' => [ + 'rules' => 'required|numeric|exact_length[6]', + 'errors' => [ + 'required' => 'Pincode is required', + 'numeric' => 'Pincode must contain only numbers', + 'exact_length' => 'Pincode must be exactly 6 digits' + ] + ], + + // ====================== + // Contact Details (Array) + // ====================== + 'name.*' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'Contact name is required', + 'min_length' => 'Contact name must be at least 2 characters' + ] + ], + + 'designation.*' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Designation is required' + ] + ], + + 'email.*' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@company.com).' + ] + ], + + 'mobile.*' => [ + 'rules' => 'required|numeric|exact_length[10]', + 'errors' => [ + 'required' => 'Mobile number is required', + 'numeric' => 'Mobile must contain only digits', + 'exact_length' => 'Mobile number must be exactly 10 digits' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $data = $this->request->getPost(); - $data['created_by'] = get_session_userid(); - $insert = $this->tpaBranchModel->insert($data); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $sanitized_post_data['created_by'] = get_session_userid(); + $insert = $this->tpaBranchModel->insert($sanitized_post_data); if($insert){ - for ($i = 0; $i < count($this->request->getPost('name')); $i++) { + for ($i = 0; $i < count($sanitized_post_data['name']); $i++) { // Prepare data to insert - $data = [ + $sanitized_post_data_for_level = [ 'contact_type' => 'tpa', 'ref_id' => $insert, 'created_by' => get_session_userid(), - 'name' => $this->request->getPost('name')[$i], - 'email' => $this->request->getPost('email')[$i], - 'mobile' => $this->request->getPost('mobile')[$i], - 'designation' => $this->request->getPost('designation')[$i] + 'name' => $sanitized_post_data['name'][$i] ?? null, + 'email' => $sanitized_post_data['email'][$i] ?? null, + 'mobile' => $sanitized_post_data['mobile'][$i] ?? null, + 'designation' => $sanitized_post_data['designation'][$i] ?? null ]; - $contacts = $this->levelContactModel->insert($data); + $contacts = $this->levelContactModel->insert($sanitized_post_data_for_level); } } if($insert){ - $branchData = $this->tpaBranchModel->where('tpa_id', $this->request->getPost('tpa_id'))->findAll(); + $branchData = $this->tpaBranchModel->where('tpa_id', $sanitized_post_data['tpa_id'])->findAll(); echo json_encode(array("status" => true , 'data' => $branchData)); }else{ echo json_encode(array("status" => false)); @@ -715,6 +1145,74 @@ class MasterController extends AdminController public function editTPAGeneralInfo() { $this->myLogger->logme('error','edit TPA general info function called'); + $rules = [ + + // ====================== + // TPA Basic Details + // ====================== + 'name' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'TPA name is required', + 'min_length' => 'TPA name must be at least 2 characters' + ] + ], + + 'short_name' => [ + 'rules' => 'required|trim|min_length[3]', + 'errors' => [ + 'required' => 'TPA short name is required', + 'min_length' => 'Short name must be at least 3 characters', + ] + ], + + 'network_hospitals' => [ + 'rules' => 'required|valid_url', + 'errors' => [ + 'required' => 'Network hospitals URL is required', + 'valid_url' => 'Please enter a valid URL' + ] + ], + 'tpa_logo' => [ + 'rules' => 'if_exist|is_image[tpa_logo]|max_size[tpa_logo,200]|ext_in[tpa_logo,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'TPA logo must be an image', + 'max_size' => 'TPA logo should not exceed 200 KB', + 'ext_in' => 'Allowed logo types: jpg, jpeg, png' + ] + ], + + 'fc' => [ + 'rules' => 'if_exist|is_image[fc]|max_size[fc,500]|ext_in[fc,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'Front card must be an image', + 'max_size' => 'Front card image should not exceed 500 KB', + 'ext_in' => 'Allowed types: jpg, jpeg, png' + ] + ], + + 'bc' => [ + 'rules' => 'if_exist|is_image[bc]|max_size[bc,500]|ext_in[bc,jpg,jpeg,png]', + 'errors' => [ + 'is_image' => 'Back card must be an image', + 'max_size' => 'Back card image should not exceed 500 KB', + 'ext_in' => 'Allowed types: jpg, jpeg, png' + ] + ], + ]; + + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $uploadFilePath = ROOTPATH . 'public/uploads/logo/'; $file_name = file_Upload($this->request->getFile('tpa_logo'), $uploadFilePath); @@ -723,29 +1221,29 @@ class MasterController extends AdminController $front_card_file_name = file_Upload($this->request->getFile('fc'), $template_bg_path); $back_card_file_name = file_Upload($this->request->getFile('bc'), $template_bg_path); - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); - $data['updated_by'] = get_session_userid(); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + + $sanitized_post_data['updated_by'] = get_session_userid(); if(!empty($file_name)){ - $data['tpa_logo'] = $file_name; + $sanitized_post_data['tpa_logo'] = $file_name; } if(!empty($front_card_file_name)){ - $data['front_card'] = $front_card_file_name; + $sanitized_post_data['front_card'] = $front_card_file_name; } if(!empty($back_card_file_name)){ - $data['back_card'] = $back_card_file_name; + $sanitized_post_data['back_card'] = $back_card_file_name; } - $data['network_hospitals'] = $this->request->getPost('network_hospitals'); - $update = $this->tpaModel->update($id,$data); + $sanitized_post_data['network_hospitals'] = $sanitized_post_data['network_hospitals'] ?? null; + $update = $this->tpaModel->update($id,$sanitized_post_data); - $tpa_name = (string) $this->request->getPost('name'); - $short_name = (string) $this->request->getPost('short_name'); - $eCardTemplate = $this->request->getPost('ecard_content'); + $tpa_name = ((string) $sanitized_post_data['name']) ?? null; + $short_name = ((string) $sanitized_post_data['short_name']) ?? null; + $eCardTemplate = $sanitized_post_data['ecard_content'] ?? null; $filename = strtolower(str_replace(' ', '_', $short_name)) . '.html'; $file_directory = WRITEPATH . 'e_card_template/'; @@ -760,10 +1258,10 @@ class MasterController extends AdminController header('Content-type:text/html; charset=utf-8'); // Write the HTML content to the file - $data = file_put_contents($file_path, $eCardTemplate); + $html_data = file_put_contents($file_path, $eCardTemplate); - if ($data !== false) { + if ($html_data !== false) { $this->myLogger->logme('error', 'TPA: {tpa}, e-Card HTML template file saved successfully: {data}, filepath is: {path}', ['data' => $filename, 'tpa' => $tpa_name, 'path' => $file_path]); } else { $this->myLogger->logme('error', 'TPA: {tpa}, e-Card HTML template file unable to save: {data}', ['data' => $filename, 'tpa' => $tpa_name]); @@ -772,7 +1270,7 @@ class MasterController extends AdminController if($update){ - echo json_encode(array("status" => true , 'data' => $data)); + echo json_encode(array("status" => true , 'data' => $sanitized_post_data)); }else{ echo json_encode(array("status" => false)); } @@ -795,9 +1293,110 @@ class MasterController extends AdminController public function editTPABranch() { $this->myLogger->logme('error','TPA branch CREATE function called'); - $id = $this->request->getPost('PrimaryKey'); + $rules = [ + + // ====================== + // Branch Details + // ====================== + 'branch_name' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch name is required' + ] + ], + + 'branch_code' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Branch code is required' + ] + ], + + 'address1' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Address Line 1 is required' + ] + ], + + 'state' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'State is required' + ] + ], + + 'district' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'District is required' + ] + ], + + 'city' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'City is required' + ] + ], + + 'pincode' => [ + 'rules' => 'required|numeric|exact_length[6]', + 'errors' => [ + 'required' => 'Pincode is required', + 'numeric' => 'Pincode must contain only numbers', + 'exact_length' => 'Pincode must be exactly 6 digits' + ] + ], + + // ====================== + // Contact Details (Array) + // ====================== + 'name.*' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'Contact name is required', + 'min_length' => 'Contact name must be at least 2 characters' + ] + ], + + 'designation.*' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Designation is required' + ] + ], + + 'email.*' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@company.com).' + ] + ], + + 'mobile.*' => [ + 'rules' => 'required|numeric|exact_length[10]', + 'errors' => [ + 'required' => 'Mobile number is required', + 'numeric' => 'Mobile must contain only digits', + 'exact_length' => 'Mobile number must be exactly 10 digits' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $data = $this->request->getPost(); - $update = $this->tpaBranchModel->update($id, $data); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + $update = $this->tpaBranchModel->update($id, $sanitized_post_data); // print_r($this->request->getPost('name[]')); // print_r($this->request->getPost('email[]')); @@ -809,18 +1408,18 @@ class MasterController extends AdminController foreach ($contactsToDelete as $contact) { $this->levelContactModel->delete($contact->id); } - for ($i = 0; $i < count($this->request->getPost('name')); $i++) { + for ($i = 0; $i < count($sanitized_post_data['name']); $i++) { // Prepare data to update - $data = [ + $sanitized_post_data_for_level = [ 'contact_type' => 'tpa', 'ref_id' => $id, 'created_by' => get_session_userid(), - 'name' => $this->request->getPost('name')[$i], - 'email' => $this->request->getPost('email')[$i], - 'mobile' => $this->request->getPost('mobile')[$i], - 'designation' => $this->request->getPost('designation')[$i] + 'name' => $sanitized_post_data['name'][$i] ?? null, + 'email' => $sanitized_post_data['email'][$i] ?? null, + 'mobile' => $sanitized_post_data['mobile'][$i] ?? null, + 'designation' => $sanitized_post_data['designation'][$i] ?? null ]; - $contacts = $this->levelContactModel->insert($data); + $contacts = $this->levelContactModel->insert($sanitized_post_data_for_level); } } @@ -926,10 +1525,28 @@ class MasterController extends AdminController { $this->myLogger->logme('error','KYC Entity Type general info function called'); + $rules = [ + 'name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'KYC Entity Type Name is required' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $data = $this->request->getPost(); - $data['created_by'] = get_session_userid(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $sanitized_post_data['created_by'] = get_session_userid(); - $insert = $this->kycEntityTypeModel->insert($data); + $insert = $this->kycEntityTypeModel->insert($sanitized_post_data); if($insert){ $kyc_data = $this->kycEntityTypeModel->where(['id' => $insert, 'is_active' => 1])->first(); echo json_encode(array("status" => true , 'data' => $kyc_data)); @@ -942,20 +1559,40 @@ class MasterController extends AdminController { $this->myLogger->logme('error','Kyc Docs CREATE function called'); + $rules = [ + 'file_name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'KYC Docs File Name is required' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + // print_r($data);die; - if($data['kyc_type_id'] == ''){ + if($sanitized_post_data['kyc_type_id'] == ''){ // PrimaryKey - $data['kyc_type_id'] =$data['PrimaryKey']; + $sanitized_post_data['kyc_type_id'] =$data['PrimaryKey']; } // print_r($data);die; - $data['created_by'] = get_session_userid(); - $insert = $this->kycDocsModel->insert($data); + $sanitized_post_data['created_by'] = get_session_userid(); + $insert = $this->kycDocsModel->insert($sanitized_post_data); if($insert){ - $kycDocsData = $this->kycDocsModel->where('kyc_type_id', $data['kyc_type_id'])->where('is_active',1)->findAll(); + $kycDocsData = $this->kycDocsModel->where('kyc_type_id', $sanitized_post_data['kyc_type_id'])->where('is_active',1)->findAll(); echo json_encode(array("status" => true , 'data' => $kycDocsData)); }else{ echo json_encode(array("status" => false)); @@ -999,12 +1636,31 @@ class MasterController extends AdminController public function editKYCInfo() { $this->myLogger->logme('error','edit KYC general info function called'); - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); - $data['updated_by'] = get_session_userid(); - $update = $this->kycEntityTypeModel->update($id,$data); + $rules = [ + 'file_name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'KYC Docs File Name is required' + ] + ], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + $sanitized_post_data['updated_by'] = get_session_userid(); + $update = $this->kycEntityTypeModel->update($id,$sanitized_post_data); if($update){ - echo json_encode(array("status" => true , 'data' => $data)); + echo json_encode(array("status" => true , 'data' => $sanitized_post_data)); }else{ echo json_encode(array("status" => false)); } @@ -1138,11 +1794,97 @@ class MasterController extends AdminController public function createPolicyType() { $this->myLogger->logme('error','Policy Type CREATE function called'); + $rules = [ + 'policy_type' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'Policy type name is required', + 'min_length' => 'Policy type name must be at least 2 characters' + ] + ], + + 'bap' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'BAP is required' + ] + ], + + 'allocg' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Category is required' + ] + ], + + 'alloci' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'BAP category is required' + ] + ], + + + 'ebp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group base premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'etp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group third-party premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'etep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group terrorism premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'iep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual base premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'itp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual third-party premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'itep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual terrorism premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } $data = $this->request->getPost(); - - $data['created_by'] = get_session_userid(); - $insert = $this->policyTypeModel->insert($data); - + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $sanitized_post_data['created_by'] = get_session_userid(); + $insert = $this->policyTypeModel->insert($sanitized_post_data); if($insert){ $policyTypeData = $this->policyTypeModel->where('id', $insert)->first(); echo json_encode(array("status" => true , 'data' => $policyTypeData)); @@ -1175,12 +1917,101 @@ class MasterController extends AdminController public function editPolicyType() { $this->myLogger->logme('error','edit Policy general info function called'); - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); - $data['updated_by'] = get_session_userid(); - $update = $this->policyTypeModel->update($id,$data); + $rules = [ + 'policy_type' => [ + 'rules' => 'required|trim|min_length[2]', + 'errors' => [ + 'required' => 'Policy type name is required', + 'min_length' => 'Policy type name must be at least 2 characters' + ] + ], + + 'bap' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'BAP is required' + ] + ], + + 'allocg' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Category is required' + ] + ], + + 'alloci' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'BAP category is required' + ] + ], + + + 'ebp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group base premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'etp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group third-party premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'etep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Group terrorism premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'iep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual base premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'itp' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual third-party premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + + 'itep' => [ + 'rules' => 'permit_empty|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'numeric' => 'Individual terrorism premium must be numeric', + 'greater_than_equal_to' => 'Value cannot be negative' + ] + ], + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + + $id = $sanitized_post_data['PrimaryKey'] ?? null; + $sanitized_post_data['updated_by'] = get_session_userid(); + $update = $this->policyTypeModel->update($id,$sanitized_post_data); if($update){ - echo json_encode(array("status" => true , 'data' => $data)); + echo json_encode(array("status" => true , 'data' => $sanitized_post_data)); }else{ echo json_encode(array("status" => false)); } @@ -1197,7 +2028,7 @@ class MasterController extends AdminController // print_r($id);die; foreach ($policies as $policy) { // Retrieve the insurer information - $insurer = $this->insurerModel->find($policy['insurer_id']); + $insurer = $this->insurerModel->find((int)$policy['insurer_id']); // print_r($insurer);die; // Add the insurer information to the editData array @@ -1229,8 +2060,10 @@ class MasterController extends AdminController public function editPolicies() { $this->myLogger->logme('error','edit Policy general info function called'); - $id = $this->request->getPost('PrimaryKey'); - $data = $this->request->getPost(); + $request_post_data = $this->request->getPost(); + $data = sanitizeInputArrayAdvanced($request_post_data); + + $id = $data['PrimaryKey'] ?? null; $data['updated_by'] = get_session_userid(); $update = $this->policesModel->update($id,$data); if($update){ @@ -1347,12 +2180,76 @@ class MasterController extends AdminController public function createCDMasterData() { $this->myLogger->logme("error", 'Create CD Master Data API called.'); - $data = $this->request->getPost(); + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + if($id === null){ + $rules = [ + + // ====================== + // Client / Insurer Mapping + // ====================== + 'client_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Client is required' + ] + ], + + 'insurer_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Insurer is required' + ] + ], + + 'insurer_branch_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Insurer branch is required' + ] + ], + + // ====================== + // CD Account Details + // ====================== + 'opening_date' => [ + 'rules' => 'required|valid_date[d-m-Y]', + 'errors' => [ + 'required' => 'Opening date is required', + 'valid_date' => 'Opening date must be in DD-MM-YYYY format' + ] + ], + + 'cd_ac_no' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'CD account number is required' + ] + ], + + 'opening_bal' => [ + 'rules' => 'required|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'required' => 'Opening amount is required', + 'numeric' => 'Opening amount must be numeric', + 'greater_than_equal_to' => 'Opening amount cannot be negative' + ] + ], + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + } $this->myLogger->logme("error", 'Received POST data: ' . json_encode($data)); - $id = $data['PrimaryKey'] ?? null; - $date = (string) ($data['opening_date'] ?? ''); - $data['opening_date'] = date('Y-m-d', strtotime($date)); + $date = (string) ($sanitized_post_data['opening_date'] ?? ''); + $sanitized_post_data['opening_date'] = date('Y-m-d', strtotime($date)); $this->myLogger->logme("error", 'Formatted opening_date: ' . $data['opening_date']); $loggedInUserID = get_session_userid(); @@ -1362,9 +2259,9 @@ class MasterController extends AdminController if (empty($id)) { $cd_acc_count = $this->CDMasterModel->where('is_active', 1) - ->where('client_id', $data['client_id']) - ->where('insurer_id', $data['insurer_id']) - ->where('insurer_branch_id', $data['insurer_branch_id']) + ->where('client_id', $sanitized_post_data['client_id']) + ->where('insurer_id', $sanitized_post_data['insurer_id']) + ->where('insurer_branch_id', $sanitized_post_data['insurer_branch_id']) ->countAllResults(); if($cd_acc_count > 0){ @@ -1372,19 +2269,19 @@ class MasterController extends AdminController } $this->myLogger->logme("error", 'Performing INSERT operation.'); - $insert = $this->CDMasterModel->insert($data); + $insert = $this->CDMasterModel->insert($sanitized_post_data); $this->myLogger->logme("error", 'Insert result: ' . json_encode($insert)); if ($insert) { $cd_tranction_data = [ - 'amount' => $data['opening_bal'], + 'amount' => $sanitized_post_data['opening_bal'] ?? null, 'sub_type_id' => 7, - 'client_id' => $data['client_id'], + 'client_id' => $sanitized_post_data['client_id'] ?? null, 'client_policy_id' => null, - 'cd_ac_no' => $data['cd_ac_no'], + 'cd_ac_no' => $sanitized_post_data['cd_ac_no'] ?? null, 'endorsement_no' => null, - 'insurer_id' => $data['insurer_id'], + 'insurer_id' => $sanitized_post_data['insurer_id'] ?? null, 'description' => 'Opening Amount', 'transaction_type' => 'Credit', 'event_name' => null, @@ -1400,9 +2297,9 @@ class MasterController extends AdminController $this->myLogger->logme("error", 'Inserted CD Master data: ' . json_encode($cd_master_data)); $cd_master_full_data = $this->CDMasterModel->where('is_active', 1) - ->where('client_id', $data['client_id']) - ->where('insurer_id', $data['insurer_id']) - ->where('insurer_branch_id', $data['insurer_branch_id']) + ->where('client_id', $sanitized_post_data['client_id']) + ->where('insurer_id', $sanitized_post_data['insurer_id']) + ->where('insurer_branch_id', $sanitized_post_data['insurer_branch_id']) ->findAll(); return $this->respond([ @@ -1427,7 +2324,7 @@ class MasterController extends AdminController // === UPDATE === $this->myLogger->logme("error", 'Performing UPDATE operation for ID: ' . $id); - $updated = $this->CDMasterModel->where('id', $id)->set($data)->update(); + $updated = $this->CDMasterModel->where('id', $id)->set($sanitized_post_data)->update(); $this->myLogger->logme("error", 'Update result: ' . json_encode($updated)); $existingData = $this->CDMasterModel->where('is_active', 1)->where('id', $id)->first(); @@ -1465,27 +2362,89 @@ class MasterController extends AdminController public function editCDMasterData($id = null) { + $this->myLogger->logme("error", 'Edit CD Master Data API called.'); + $rules = [ - $id = $this->request->getPost('PrimaryKey'); - $date = (string) $this->request->getPost('opening_date'); - $data = $this->request->getPost(); - $data['opening_date'] = date('Y-m-d', strtotime($date)); + // ====================== + // Client / Insurer Mapping + // ====================== + 'client_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Client is required' + ] + ], + + 'insurer_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Insurer is required' + ] + ], + + 'insurer_branch_id' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Insurer branch is required' + ] + ], + + // ====================== + // CD Account Details + // ====================== + 'opening_date' => [ + 'rules' => 'required|valid_date[d-m-Y]', + 'errors' => [ + 'required' => 'Opening date is required', + 'valid_date' => 'Opening date must be in DD-MM-YYYY format' + ] + ], + + 'cd_ac_no' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'CD account number is required' + ] + ], + + 'opening_bal' => [ + 'rules' => 'required|numeric|greater_than_equal_to[0]', + 'errors' => [ + 'required' => 'Opening amount is required', + 'numeric' => 'Opening amount must be numeric', + 'greater_than_equal_to' => 'Opening amount cannot be negative' + ] + ], + ]; + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['PrimaryKey'] ?? null; + $date = ((string) $sanitized_post_data['opening_date']) ?? null; + $sanitized_post_data['opening_date'] = date('Y-m-d', strtotime($date)); if ($data) { - $insert = $this->CDMasterModel->where('id', $id)->set($data)->update(); + $insert = $this->CDMasterModel->where('id', $id)->set($sanitized_post_data)->update(); - $data = $this->CDMasterModel->where('id', $id)->first(); + $get_data = $this->CDMasterModel->where('id', $id)->first(); $cd_transaction_updated_data = [ - 'balance' => $data['opening_bal'], - 'amount' => $data['opening_bal'] + 'balance' => $get_data['opening_bal'], + 'amount' => $get_data['opening_bal'] ]; $cd_tranction = $this->clientDepositModel - ->where('client_id', $data['client_id']) - ->where('insurer_id', $data['insurer_id']) - ->where('cd_ac_no', $data['cd_ac_no']) + ->where('client_id', $sanitized_post_data['client_id']) + ->where('insurer_id', $sanitized_post_data['insurer_id']) + ->where('cd_ac_no', $sanitized_post_data['cd_ac_no']) ->where('sub_type', 7) ->set($cd_transaction_updated_data)->update(); @@ -1731,35 +2690,72 @@ class MasterController extends AdminController { // return $this->respond($this->request->getPost()); $template_id = $this->request->getPost('template_id'); + + $rules = [ + 'policy_type' => [ + 'rules' => 'required|is_natural_no_zero', + 'errors' => [ + 'required' => 'Policy type is required' + ] + ], + + 'event' => [ + 'rules' => 'required|trim', + 'errors' => [ + 'required' => 'Event is required' + ] + ], + + + 'json_data' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Template mapping data is required' + ] + ], + ]; + + if (! $this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + + - $data = [ - 'insurer_id' => $this->request->getPost('insurer_id'), - 'policy_type_id' => $this->request->getPost('policy_type'), - 'event_name' => $this->request->getPost('event'), + $fetch_data = [ + 'insurer_id' => $sanitized_post_data['insurer_id'] ?? null , + 'policy_type_id' => $sanitized_post_data['policy_type'] ?? null, + 'event_name' => $sanitized_post_data['event'] ?? null, 'type_name' => 'export', - 'jsoncolumns' => $this->request->getPost('json_data'), + 'jsoncolumns' => $sanitized_post_data['json_data'] ?? null, 'is_active' => 1, 'created_by' => get_session_userid(), ]; if($template_id){ - $update = $this->insurerTemplateModel->where('id', $template_id)->set($data)->update(); + $update = $this->insurerTemplateModel->where('id', $template_id)->set($fetch_data)->update(); if($update){ - return $this->respond(['status' => true, 'message' => 'Template updated successfully', $data]); + return $this->respond(['status' => true, 'message' => 'Template updated successfully', $fetch_data]); }else{ - return $this->respond(['status' => true, 'message' => 'Failed to update template', $data]); + return $this->respond(['status' => true, 'message' => 'Failed to update template', $fetch_data]); } }else{ - $insert = $this->insurerTemplateModel->insert($data); + $insert = $this->insurerTemplateModel->insert($fetch_data); if($insert){ - return $this->respond(['status' => true, 'message' => 'Template created successfully', $data]); + return $this->respond(['status' => true, 'message' => 'Template created successfully', $fetch_data]); }else{ - return $this->respond(['status' => true, 'message' => 'Failed to create template', $data]); + return $this->respond(['status' => true, 'message' => 'Failed to create template', $fetch_data]); } } @@ -1983,7 +2979,9 @@ class MasterController extends AdminController 'files' => WRITEPATH . 'uploads/commission/files', 'rules' => WRITEPATH . 'uploads/commission/rules', 'claim_sample_forms' => ROOTPATH . 'public/claim_sample_forms/', + 'tmp' => ROOTPATH . 'public/tmp/', 'bds_dump_excel' => WRITEPATH . 'uploads/bds_dump_excel/', + 'claims_mis' => WRITEPATH . 'uploads/claims_mis/', ]; foreach ($folders as $folderName => $folderPath) { @@ -2160,13 +3158,34 @@ class MasterController extends AdminController if ($method === 'post') { - $id = $this->request->getPost('pk') ?? null; - $data = $this->request->getPost(); + + $rules = [ + 'branch_name' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Branch Name is required' + ] + ] + ]; + if (! $this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['pk'] ?? null; + unset($sanitized_post_data['pk']); + if (empty($id)) { - $update_status = $nhanceBranchModel->insert($data); + $update_status = $nhanceBranchModel->insert($sanitized_post_data); } else { - $update_status = $nhanceBranchModel->where('id', $id)->set($data)->update(); + $update_status = $nhanceBranchModel->where('id', $id)->set($sanitized_post_data)->update(); } if ($update_status) { @@ -2242,13 +3261,34 @@ class MasterController extends AdminController if ($method === 'post') { - $id = $this->request->getPost('pk') ?? null; - $data = $this->request->getPost(); + + $rules = [ + 'vehicle_type' => [ + 'rules' => 'required', + 'errors' => [ + 'required' => 'Vehicle Type is required' + ] + ] + ]; + + if (! $this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['pk'] ?? null; + unset($sanitized_post_data['pk']); if (empty($id)) { - $update_status = $vehicleTypeModel->insert($data); + $update_status = $vehicleTypeModel->insert($sanitized_post_data); } else { - $update_status = $vehicleTypeModel->where('id', $id)->set($data)->update(); + $update_status = $vehicleTypeModel->where('id', $id)->set($sanitized_post_data)->update(); } if ($update_status) { @@ -2256,14 +3296,14 @@ class MasterController extends AdminController 'status' => true, 'code' => 200, 'message' => 'Vehicle Type Master updated successfully', - 'data' => $data + 'data' => $sanitized_post_data ], 200); } else { return $this->respond([ 'status' => false, 'code' => 400, 'message' => 'Failed to update', - 'data' => $data + 'data' => $sanitized_post_data ], 200); } @@ -2323,13 +3363,62 @@ class MasterController extends AdminController if ($method === 'post') { - $id = $this->request->getPost('pk') ?? null; - $data = $this->request->getPost(); + $rules = [ + // ====================== + // RTO Office Name + // ====================== + 'rto_name' => [ + 'rules' => 'required|trim|min_length[3]|max_length[100]|alpha_numeric_space', + 'errors' => [ + 'required' => 'RTO Office Name is required', + 'min_length' => 'RTO Office Name must be at least 3 characters' + ] + ], + // ====================== + // RTO Code (2 digits only) + // ====================== + 'rto_code' => [ + 'rules' => 'required|exact_length[2]|numeric', + 'errors' => [ + 'required' => 'RTO Code is required', + 'exact_length' => 'RTO Code must be exactly 2 digits', + 'numeric' => 'RTO Code must contain only numbers' + ] + ], + // ====================== + // RTO State (2 letters only) + // ====================== + 'rto_state' => [ + 'rules' => 'required|exact_length[2]|alpha', + 'errors' => [ + 'required' => 'RTO State is required', + 'exact_length' => 'RTO State must be exactly 2 letters', + 'alpha' => 'RTO State must contain only alphabets' + ] + ], + + ]; + + if (! $this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['pk'] ?? null; + unset($sanitized_post_data['pk']); + if (empty($id)) { - $update_status = $rtoModel->insert($data); + $update_status = $rtoModel->insert($sanitized_post_data); } else { - $update_status = $rtoModel->where('id', $id)->set($data)->update(); + $update_status = $rtoModel->where('id', $id)->set($sanitized_post_data)->update(); } if ($update_status) { @@ -2337,14 +3426,14 @@ class MasterController extends AdminController 'status' => true, 'code' => 200, 'message' => 'RTO Master updated successfully', - 'data' => $data + 'data' => $sanitized_post_data ], 200); } else { return $this->respond([ 'status' => false, 'code' => 400, 'message' => 'Failed to update', - 'data' => $data + 'data' => $sanitized_post_data ], 200); } @@ -2404,47 +3493,172 @@ class MasterController extends AdminController if ($this->request->getMethod() === 'post') { - $id = $this->request->getPost('pk'); - $data = $this->request->getPost(); + $rules = [ + 'manager_id' => [ + 'rules' => 'required|integer', + 'errors' => [ + 'required' => 'Manager is required', + 'integer' => 'Invalid Manager selected' + ] + ], + 'name' => [ + 'rules' => 'required|min_length[3]|max_length[100]|alpha_space', + 'errors' => [ + 'required' => 'Name is required', + 'min_length' => 'Name must be at least 3 characters', + 'max_length' => 'Name cannot exceed 100 characters', + 'alpha_space'=> 'Name can contain only letters and spaces' + ] + ], + 'pos_code' => [ + 'rules' => 'required|alpha_numeric|max_length[20]', + 'errors' => [ + 'required' => 'POS Code is required', + 'alpha_numeric' => 'POS Code must be alphanumeric', + 'max_length' => 'POS Code cannot exceed 20 characters' + ] + ], + 'email' => [ + 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', + 'errors' => [ + 'required' => 'Email address is required.', + 'regex_match' => 'Please enter a valid email format (e.g., name@company.com).' + ] + ], + 'mobile' => [ + 'rules' => 'required|numeric|exact_length[10]', + 'errors' => [ + 'required' => 'Mobile number is required', + 'numeric' => 'Mobile number must contain only digits', + 'exact_length' => 'Mobile number must be exactly 10 digits' + ] + ], + 'address' => [ + 'rules' => 'required|min_length[5]', + 'errors' => [ + 'required' => 'Address is required', + 'min_length' => 'Address must be at least 5 characters' + ] + ], + 'city' => [ + 'rules' => 'required|alpha_space', + 'errors' => [ + 'required' => 'City is required', + 'alpha_space' => 'City must contain only letters and spaces' + ] + ], + 'state' => [ + 'rules' => 'required|alpha_space', + 'errors' => [ + 'required' => 'State is required', + 'alpha_space' => 'State must contain only letters and spaces' + ] + ], + 'pincode' => [ + 'rules' => 'required|numeric|exact_length[6]', + 'errors' => [ + 'required' => 'Pincode is required', + 'numeric' => 'Pincode must contain only digits', + 'exact_length' => 'Pincode must be exactly 6 digits' + ] + ], + 'aadhar' => [ + 'rules' => 'required|numeric|exact_length[12]', + 'errors' => [ + 'required' => 'Aadhaar number is required', + 'numeric' => 'Aadhaar must contain only digits', + 'exact_length' => 'Aadhaar must be exactly 12 digits' + ] + ], + 'pan' => [ + 'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/]', + 'errors' => [ + 'required' => 'PAN number is required', + 'regex_match' => 'Enter a valid PAN number (ABCDE1234F)' + ] + ], + 'gst' => [ + 'rules' => 'permit_empty|max_length[15]', + 'errors' => [ + 'max_length' => 'GST number cannot exceed 15 characters' + ] + ], + 'aadhar_file_name' => [ + 'rules' => 'permit_empty|uploaded[aadhar_file_name]|max_size[aadhar_file_name,5120]|ext_in[aadhar_file_name,pdf,jpg,jpeg,png]', + 'errors' => [ + 'uploaded' => 'Invalid Aadhaar file', + 'max_size' => 'Aadhaar file size should not exceed 5MB', + 'ext_in' => 'Aadhaar must be PDF or image (jpg, jpeg, png)' + ] + ], + 'pan_file_name' => [ + 'rules' => 'permit_empty|uploaded[pan_file_name]|max_size[pan_file_name,5120]|ext_in[pan_file_name,pdf,jpg,jpeg,png]', + 'errors' => [ + 'uploaded' => 'Invalid PAN file', + 'max_size' => 'PAN file size should not exceed 5MB', + 'ext_in' => 'PAN must be PDF or image (jpg, jpeg, png)' + ] + ], + 'certificate_file_name' => [ + 'rules' => 'permit_empty|uploaded[certificate_file_name]|max_size[certificate_file_name,5120]|ext_in[certificate_file_name,pdf,jpg,jpeg,png]', + 'errors' => [ + 'uploaded' => 'Invalid Certificate file', + 'max_size' => 'Certificate file size should not exceed 5MB', + 'ext_in' => 'Certificate must be PDF or image (jpg, jpeg, png)' + ] + ], + ]; - unset($data['pk']); + if (! $this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $data = $this->request->getPost(); + $sanitized_post_data = sanitizeInputArrayAdvanced($data); + $id = $sanitized_post_data['pk'] ?? null; + unset($sanitized_post_data['pk']); try { // Certificate $certificate = $this->uploadPOSFile('certificate_file_name', 'pos_certificate_files'); - if ($certificate !== null) { $data['certificate_file_name'] = $certificate; } else { unset($data['certificate_file_name']); } + if ($certificate !== null) { $sanitized_post_data['certificate_file_name'] = $certificate; } else { unset($sanitized_post_data['certificate_file_name']); } // PAN file $panFile = $this->uploadPOSFile('pan_file_name', 'pos_certificate_files'); - if ($panFile !== null) { $data['pan_file_name'] = $panFile; } else { unset($data['pan_file_name']);} + if ($panFile !== null) { $sanitized_post_data['pan_file_name'] = $panFile; } else { unset($sanitized_post_data['pan_file_name']);} // Aadhaar file $aadharFile = $this->uploadPOSFile('aadhar_file_name', 'pos_certificate_files'); - if ($aadharFile !== null) { $data['aadhar_file_name'] = $aadharFile; } else { unset($data['aadhar_file_name']);} + if ($aadharFile !== null) { $sanitized_post_data['aadhar_file_name'] = $aadharFile; } else { unset($sanitized_post_data['aadhar_file_name']);} } catch (\RuntimeException $e) { - return $this->respond([ 'status' => false, 'code' => 400, 'message' => $e->getMessage(), 'data' => $data ], 400); + return $this->respond([ 'status' => false, 'code' => 400, 'message' => $e->getMessage(), 'data' => $sanitized_post_data ], 400); } - foreach ($data as $k => $v) { + foreach ($sanitized_post_data as $k => $v) { if ($v === '' || $v === null) { - unset($data[$k]); + unset($sanitized_post_data[$k]); } } // INSERT / UPDATE if (empty($id)) { - $status = $posModel->insert($data); + $status = $posModel->insert($sanitized_post_data); } else { - $status = $posModel->update($id, $data); + $status = $posModel->update($id, $sanitized_post_data); } if ($status) { - return $this->respond([ 'status' => true, 'code' => 200, 'message' => 'POS updated successfully', 'data' => $data ], 200); + return $this->respond([ 'status' => true, 'code' => 200, 'message' => 'POS updated successfully', 'data' => $sanitized_post_data ], 200); } - return $this->respond([ 'status' => false, 'code' => 400, 'message' => 'Failed to update', 'data' => $data ], 400); + return $this->respond([ 'status' => false, 'code' => 400, 'message' => 'Failed to update', 'data' => $sanitized_post_data ], 400); } elseif ($method === 'get') { diff --git a/app/Controllers/MediAssistApiController.php b/app/Controllers/MediAssistApiController.php index b019850a..0452c797 100644 --- a/app/Controllers/MediAssistApiController.php +++ b/app/Controllers/MediAssistApiController.php @@ -138,6 +138,9 @@ class MediAssistApiController extends BaseController if($response['status'] != true){ log_message('error', 'TPA CLAIM PUSH FAILED | claimId: '.$claimId.' | response: '.json_encode($response)); + $this->db->table('ticket_master') + ->where('id',$claimId) + ->update([ 'tpa_push_response' => json_encode($response) ]); return; } @@ -515,7 +518,7 @@ class MediAssistApiController extends BaseController ->getRowArray(); if (!$ticket) { - return $this->response->setJSON(['status' => false,'message' => 'Invalid Claim ID' ]); + return ['status' => false,'message' => 'Invalid Claim ID' ]; } // REQUEST BODY @@ -545,8 +548,6 @@ class MediAssistApiController extends BaseController } - - // $body = [ // "policyNo" => "97000063250400000031", // "startDate" => "01/11/2025", @@ -565,11 +566,7 @@ class MediAssistApiController extends BaseController if ($response['status'] != true || empty($response['data']['claimsData'][0])) { log_message('error', 'CLAIM STATUS FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response)); - return $this->response->setJSON([ - 'status' => false, - 'message' => 'API call failed.', - 'data' => $response - ]); + return ['status' => false,'message' => 'API call failed.','data' => $response ]; } // Extract claim status @@ -631,12 +628,7 @@ class MediAssistApiController extends BaseController // LOG UPDATE log_message('error', "CLAIM STATUS SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus"); - return $this->response->setJSON([ - 'status' => true, - 'message' => 'Claim status updated.', - 'updated_status' => $currentStatus, - 'api_response' => $response - ]); + return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response]; } public function IRSubmission($claimId = null) // 585 this id for test @@ -1106,7 +1098,9 @@ class MediAssistApiController extends BaseController $ticket = $this->db->table('ticket_master tm')->select("tm.id") ->where('tm.policy_no', $value['policY_NUMBER']) ->where('tm.emp_code', $value['employeE_NO']) - ->where('tm.claim_number', $value['tpA_CLAIM_NO']) + ->where('tm.claim_amount', $value['estimateD_CLAIM_AMOUNT']) + ->where('tm.doa', $this->mediDate($value['datE_OF_ADMISSION'] ?? null)) + // ->where('tm.claim_number', $value['tpA_CLAIM_NO']) ->get() ->getRowArray(); @@ -1173,6 +1167,7 @@ class MediAssistApiController extends BaseController ") ->join('client_rm', 'client_rm.client_id = cp.client_id AND client_rm.level = 3', 'left') ->where('cp.policy_no', $value['policY_NUMBER']) + ->orderBy('client_rm.id','DESC') ->get() ->getRowArray(); @@ -1181,12 +1176,14 @@ class MediAssistApiController extends BaseController e.id as emp_id, e.emp_code , e2.id as insured_emp_id, + ep.tpa_id as tpa_no, ") ->join( 'employees e2', "e2.emp_code = e.emp_code AND e2.relationship = ".$this->db->escape($relationship), 'left' ) + ->join( 'employee_polices ep', "ep.employee_id = e2.id ", 'left' ) ->where('e.emp_code', $value['employeE_NO']) ->where('e.relationship', 'self') ->get() @@ -1200,7 +1197,7 @@ class MediAssistApiController extends BaseController 'claim_status_id' => $claimStatusId, 'policy_no' => $value['policY_NUMBER'] ?? null, 'claim_number' => $value['tpA_CLAIM_NO'] ?? null, - 'tpa_claim_id' => $value['tpA_CLAIM_NO'] ?? null, + 'tpa_claim_id' => $value['tpA_CLAIM_NO'] ?? null, // local promary id 'tpa_id' => $clientpolicy['tpa_id'] ?? null, @@ -1212,6 +1209,7 @@ class MediAssistApiController extends BaseController // Employee / Insured 'emp_id' => $employee['emp_id'] ?? null, 'insured_emp_id' => $employee['insured_emp_id'] ?? null, + 'tpa_no' => $employee['tpa_no'] ?? null, 'emp_code' => $value['employeE_NO'] ?? null, 'emp_name' => $value['employeE_NAME'] ?? null, 'insured_name' => $value['beneficiarY_NAME'] ?? null, @@ -1250,9 +1248,8 @@ class MediAssistApiController extends BaseController $this->db->table('ticket_master')->insert($claimData); log_message( - 'info', - 'New claim created | Policy: '.$claimData['policy_no']. - ' | Claim: '.$claimData['claim_number'] + 'error', + 'New claim created | Policy: '.$claimData['policy_no'].' | Claim: '.$claimData['claim_number'] ); } diff --git a/app/Controllers/NotificationController.php b/app/Controllers/NotificationController.php index 0d85df18..8014f0c5 100755 --- a/app/Controllers/NotificationController.php +++ b/app/Controllers/NotificationController.php @@ -52,6 +52,7 @@ class NotificationController extends AdminController // Create Or Update the Notification public function createNotification() { + $form_data['template_name'] = $this->camelCaseToSnakeCase($this->request->getPost('template_name')); $form_data['subject'] = $this->request->getPost('subject'); $form_data['mail_content'] = $this->request->getPost('mailContent'); @@ -93,7 +94,8 @@ class NotificationController extends AdminController 'member_ecard_mail_btn', 'member_review_and_summary_mail_btn', 'account_maneger_summary_mail_btn', - 'client_hr_summary_mail_btn' + 'client_hr_summary_mail_btn', + 'hr_cd_insufficient_balance_mail_btn', ]; $data = []; $emptyTemplate = []; diff --git a/app/Controllers/PayoutController.php b/app/Controllers/PayoutController.php index 90d501dc..bf992d88 100644 --- a/app/Controllers/PayoutController.php +++ b/app/Controllers/PayoutController.php @@ -147,7 +147,7 @@ class PayoutController extends BaseController $summary_data = $this->invoiceModel->utrSummary($invoice_id); if($summary_data['invoice_amount'] == $summary_data['total_utr_amount']){ $sql = "UPDATE partner_invoice SET payout_status = 2 WHERE id = ?"; - db_connect()->query($sql, [$invoice_id]); + db_connect()->query($sql, [(int)$invoice_id]); } return $this->respond(['status' => true, 'code' => 200, 'data' => $payout_edit_data, "message" => "UTR successfully updated"], 200); @@ -166,7 +166,7 @@ class PayoutController extends BaseController $summary_data = $this->invoiceModel->utrSummary($invoice_id); if($summary_data['invoice_amount'] == $summary_data['total_utr_amount']){ $sql = "UPDATE partner_invoice SET payout_status = 2 WHERE id = ?"; - db_connect()->query($sql, [$invoice_id]); + db_connect()->query($sql, [(int)$invoice_id]); } return $this->respond(['status' => true, 'code' => 200, 'data' => $payout_data, "message" => "UTR added successfully"], 200); @@ -281,13 +281,17 @@ class PayoutController extends BaseController //... Payout-invoice Mapping - Save/update/soft Delete/Hard Delete Data public function saveInvoice() { - $json = $this->request->getJSON(true); - // print_rr($json);die(); - - if (!$json) { + $raw_json = $this->request->getJSON(true); + + // 1. Check if the JSON was actually valid/parsed before sanitizing + if (is_null($raw_json)) { return $this->response->setJSON(['error' => 'Invalid JSON','message' => 'Invalid JSON received.'])->setStatusCode(400); } + // 2. Sanitize the data + $json = sanitizeInputArrayAdvanced($raw_json); + + // 3. Now you can safely use $json (even if it is an empty array) $id = $json['invoice_id'] ?? null; try { @@ -335,7 +339,7 @@ class PayoutController extends BaseController $invoiceData = [ 'invoice_no' => $json['invoice_no'], - 'agent_id' => $json['agent_id'], + // 'agent_id' => $json['agent_id'] ?? null, 'invoice_date' => $json['invoice_date'], 'invoice_amount' => $json['invoice_amount'], ]; diff --git a/app/Controllers/PolicyTransactionController.php b/app/Controllers/PolicyTransactionController.php index f106f274..4a5d3d04 100644 --- a/app/Controllers/PolicyTransactionController.php +++ b/app/Controllers/PolicyTransactionController.php @@ -309,7 +309,7 @@ 'format' => null, 'allowed_values' => null, 'custom' => 'check_agent_exist', - 'params' => ['row', 'agent_data'] + 'params' => ['row', 'pos_data'] ], 'base_premium' => [ @@ -917,12 +917,152 @@ // policy Transaction Create function start public function createInceptionPolicy() { - $post_data = $this->request->getPost() ?? []; + $post_data = $this->request->getPost(); + + $rules = [ + // ========================================== + // 1. CLIENT SECTION (5 Fields) + // ========================================== + 'client_id' => ['label' => 'Client','rules' => 'required','errors' => ['required' => 'Please select a Client.']], + 'client_type' => ['label' => 'Client Type','rules' => 'required','errors' => ['required' => 'Client Type must be selected.']], + 'industry_type' => ['label' => 'Industry','rules' => 'permit_empty','errors' => []], // No errors needed for permit_empty unless adding other rules + 'gst_no' => ['label' => 'GST Number','rules' => 'permit_empty|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]','errors' => ['alpha_numeric' => 'GST Number should only contain letters and numbers.','regex_match' => 'Invalid GST format. (Example: 22AAAAA0000A1Z5)']], + 'pan_no' => ['label' => 'PAN Number','rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/]','errors' => ['alpha_numeric' => 'PAN Number should only contain letters and numbers.','regex_match' => 'Invalid PAN format. (Example: ABCDE1234F)']], + // ========================================== + // 2. PRE-SALES SECTION (3 Fields) + // ========================================== + 'lead_source' => ['label' => 'Lead Source', 'rules' => 'permit_empty', 'errors' => []], + 'opportunity_id' => ['label' => 'Opportunity', 'rules' => 'permit_empty', 'errors' => []], + 'quotation_ref' => ['label' => 'Quotation Ref', 'rules' => 'permit_empty', 'errors' => []], + + // ========================================== + // 3. POLICY DOCUMENT (2 Fields) + // ========================================== + 'policy_copy' => ['label' => 'Policy Copy', 'rules' => 'permit_empty', 'errors' => []], + 'proposal_form' => ['label' => 'Proposal Form', 'rules' => 'permit_empty', 'errors' => []], + + // ========================================== + // 4. VEHICLE DOCUMENT (2 Fields) + // ========================================== + 'rc_copy' => ['label' => 'RC Copy', 'rules' => 'permit_empty', 'errors' => []], + 'inspection_report' => ['label' => 'Inspection Report', 'rules' => 'permit_empty', 'errors' => []], + + // ========================================== + // 5. SALES SECTION (12 Fields) + // ========================================== + 'base_policy' => ['label' => 'Base Policy', 'rules' => 'permit_empty','errors' => []], + 'policy_no' => ['label' => 'Policy Number', 'rules' => 'required|regex_match[/^[a-zA-Z0-9\/_-]+$/]', + 'errors' => [ + 'required' => 'Policy Number is Must.', + 'regex_match' => 'Policy Number can only contain letters, numbers, and symbols like / - or _' + ]], + 'policy_issue_date' => ['label' => 'Issue Date', 'rules' => 'required|valid_date[Y-m-d]|regex_match[/^\d{4}-\d{2}-\d{2}$/]', + 'errors' => [ + 'required' => 'Policy Issue Date is required.', + 'valid_date' => 'Please provide a valid Policy Issue Date.', + 'regex_match' => 'The Policy Issue Date format is incorrect. ' + ]], + 'month' => ['label' => 'Issue Month', 'rules' => 'required','errors' => [ + 'required' => 'Please select or enter the Policy Issue Month.' + ]], + 'policy_start_date' => ['label' => 'D.O.C', + 'rules' => 'required|valid_date[Y-m-d]|regex_match[/^\d{4}-\d{2}-\d{2}$/]', + 'errors' => [ + 'required' => 'D.O.C (Start Date) is required.', + 'valid_date' => 'Please provide a valid D.O.C date.', + 'regex_match' => 'The D.O.C date format is incorrect. ' + ]], + 'policy_end_date' => ['label' => 'D.O.E', 'rules' => 'required|valid_date[Y-m-d]|regex_match[/^\d{4}-\d{2}-\d{2}$/]','errors' => [ + 'required' => 'D.O.E (Expiry Date) is required.', + 'valid_date' => 'Please provide a valid D.O.E date.', + 'regex_match' => 'The D.O.E date format is incorrect. ' + ]], + 'emp_count' => ['label' => 'No of Insured', 'rules' => 'permit_empty|numeric','errors' => [ + 'numeric' => 'No of Insured must contain only numbers.' + ]], + 'dependent_count' => ['label' => 'No of Dependents', 'rules' => 'permit_empty|numeric','errors' => [ + 'numeric' => 'No of Dependents must contain only numbers.' + ]], + 'installment' => ['label' => 'Installment', 'rules' => 'permit_empty','errors' => []], + 'installment_data' => ['label' => 'Installment Data', 'rules' => 'permit_empty','errors' => []], + 'co_share' => ['label' => 'CoP Yes', 'rules' => 'permit_empty','errors' => []], + 'bro_payable_by' => ['label' => 'Remuneration Pay By', 'rules' => 'permit_empty','errors' => []], + 'renewal_date' => ['label' => 'Renewal Date', 'rules' => 'required|valid_date[Y-m-d]|regex_match[/^\d{4}-\d{2}-\d{2}$/]','errors' => [ + 'required' => 'Renewal Date is required.', + 'valid_date' => 'Please provide a valid Renewal Date.', + 'regex_match' => 'The Renewal Date format is incorrect. ' + ]], + 'sales_generated_by' => ['label' => 'Sales Generated By', 'rules' => 'permit_empty','errors' => [ + // 'required' => 'Please select the person who generated this sale.' + ]], + 'serviced_by' => ['label' => 'Serviced By', 'rules' => 'permit_empty','errors' => [ + // 'required' => 'Please select the service person for this policy.' + ]], + 'pos_id' => ['label' => 'POS', 'rules' => 'permit_empty','errors' => []], + + // ========================================== + // 6. PREMIUM DETAILS (Array Fields - 25 Fields) + // Use .* because these come from the dynamic table + // ========================================== + 'follow_insurer_id.*' => ['label' => 'Insurer', 'rules' => 'required','errors' => ['required' => 'Please select an Insurer.']], + 'co_share_type.*' => ['label' => 'Is Leader', 'rules' => 'permit_empty','errors' => []], + 'cd_ac_no_for_child.*' => ['label' => 'CD Acc No', 'rules' => 'permit_empty','errors' => []], + 'cd_current_balance.*' => ['label' => 'CD Amount', 'rules' => 'permit_empty','errors' => []], + 'follower_policy_no.*' => ['label' => 'Follower Policy No', 'rules' => 'permit_empty','errors' => []], + 'calc_policy_issue_date.*' => ['label' => 'Policy Issue Date', 'rules' => 'required','errors' => [ 'required' => 'Policy Issue Date is required' ]], + 'co_share_per.*' => ['label' => 'Co-Share %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Co-Share % must be a valid decimal.']], + 'non_comm_per_amt.*' => ['label' => 'Non-Comm Premium', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Amount must be numeric.']], + 'base_premium.*' => ['label' => 'Base Premium', 'rules' => 'permit_empty|regex_match[/^-?\d+(\.\d+)?$/]|differs[0]', + 'errors' => [ + 'regex_match' => 'Base Premium must be a valid positive or negative number.', + 'differs' => 'Base Premium cannot be zero.' + ]], + 'tp_premium.*' => ['label' => 'TP Premium', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'TP Premium must be numeric.']], + 'ter_premium.*' => ['label' => 'TEP Premium', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'TEP Premium must be numeric.']], + 'co_premium.*' => ['label' => 'Co-Premium', 'rules' => 'permit_empty|decimal','errors'=> ['decimal' => 'Co-Premium must be numeric.']], + 'co_tp_premium.*' => ['label' => 'Co-TP Premium', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Co-TP Premium must be numeric.']], + 'co_ter_premium.*' => ['label' => 'Co-TEP Premium', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Co-TEP Premium must be numeric.']], + 'cgst.*' => ['label' => 'CGST', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'CGST must be numeric.']], + 'sgst.*' => ['label' => 'SGST', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'SGST must be numeric.']], + 'igst.*' => ['label' => 'IGST', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'IGST must be numeric.']], + 'gst_amount.*' => ['label' => 'GST Amount', 'rules' => 'permit_empty|decimal|greater_than[0]','errors' => [ + 'decimal' => 'GST Amount must be a valid numeric value.', + 'greater_than' => 'GST Amount cannot be zero; please enter a value greater than 0.' + ]], + 'stamp_duty.*' => ['label' => 'Stamp Duty', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Stamp Duty must be numeric.']], + 'total.*' => ['label' => 'Total', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Total must be a numeric value.']], + 'agreed_bp.*' => ['label' => 'Agreed BP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Agreed BP % must be numeric.']], + 'agreed_tp.*' => ['label' => 'Agreed TP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Agreed TP % must be numeric.']], + 'agreed_ter.*' => ['label' => 'Agreed TEP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Agreed TEP % must be numeric.']], + 'agreed_amount.*' => ['label' => 'Agreed Amount', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Agreed Amount must be numeric.']], + 'actual_bp_amt.*' => ['label' => 'Actual BP Amount', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual BP Amount must be numeric.']], + 'actual_tp_amt.*' => ['label' => 'Actual TP Amount', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual TP Amount must be numeric.']], + 'actual_tep_amt.*' => ['label' => 'Actual TEP Amount', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual TEP Amount must be numeric.']], + 'actual_bp_per.*' => ['label' => 'Actual BP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual BP % must be numeric.']], + 'actual_tp_per.*' => ['label' => 'Actual TP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual TP % must be numeric.']], + 'actual_tep_per.*' => ['label' => 'Actual TEP %', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'Actual TEP % must be numeric.']], + 'actual_bp_brokerage_amt.*'=> ['label' => 'BP Remuneration', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'BP Remuneration must be numeric.']], + 'actual_tp_brokerage_amt.*'=> ['label' => 'TP Remuneration', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'TP Remuneration must be numeric.']], + 'actual_tep_brokerage_amt.*'=>['label' => 'TEP Remuneration', 'rules' => 'permit_empty|decimal','errors' => ['decimal' => 'TEP Remuneration must be numeric.']], + 'exp_amt.*' => ['label' => 'Expected Amount', 'rules' => 'permit_empty|decimal','errors' => ['decimal'=> 'The Expected Amount field must contain a valid number.']] + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + + $post_data = $post_data ? sanitizeInputArrayAdvanced($post_data) : []; + $this->myLogger->logme('error', 'Policy Trancaction form data : '. json_encode($post_data)); - $id = $this->request->getPost('id'); + $id = $post_data['id'] ?? null; $data = $this->preparePolicyData(); - $data['cd_ac_pk'] = $this->request->getPost('cd_ac_no'); + $data['cd_ac_pk'] = $post_data['cd_ac_no']; $data['issuer'] = 2; $data['status'] = 'completed'; $this->myLogger->logme('error', 'Policy Trancaction modified form data ( insert data ) : '. json_encode($data)); @@ -937,7 +1077,8 @@ private function preparePolicyData() { - $data = $this->request->getPost(); + $request_data = $this->request->getPost(); + $data = sanitizeInputArrayAdvanced($request_data); $file_data = $this->request->getFiles() ?? null; $data['file_data'] = $file_data ?? null; @@ -1094,6 +1235,7 @@ private function updateInceptionPolicy($id, $data) { // print_r($data); die; + $data['updated_by'] = get_session_userid(); $old_pt_data = $this->policyTransactionModel->where('is_active', 1)->where("id", $id)->first(); $old_pt_co_share_data = $this->PTCOShareDetailsModel->where('is_active', 1)->where("id", $id)->first(); if ($this->policyTransactionModel->update($id, $data)) { @@ -2007,6 +2149,7 @@ { if ($id) { $data['is_active'] = 0; + $data['updated_by'] = get_session_userid(); $policy_transaction_data = $this->policyTransactionModel->where('id', $id)->first(); if($policy_transaction_data['policy_type_id'] > 7 && $type == 0){ @@ -2247,26 +2390,277 @@ public function createEndorsementPolicy() { - $id = $this->request->getPost('id'); + // $id = $this->request->getPost('id'); + $rules = [ + // ========================================== + // 1. CLIENT SECTION + // ========================================== + 'client_id' => [ + 'label' => 'Client', + 'rules' => 'required', + 'errors' => ['required' => 'Client selection is mandatory.'] + ], + 'client_policy_id' => [ + 'label' => 'Policy', + 'rules' => 'required', + 'errors' => ['required' => 'Please select the policy to endorse.'] + ], + 'client_type' => ['rules' => 'permit_empty','errors' => []], + 'client_branch_id' => ['rules' => 'permit_empty','errors' => []], + + // ========================================== + // 2. ENDORSEMENT SECTION + // ========================================== + 'action_type' => [ + 'label' => 'Endorsement Type', + 'rules' => 'required', + 'errors' => ['required' => 'Select an Endorsement Type.'] + ], + 'endorsement_no' => ['label' => 'Endorsement No','rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]', + 'errors' => [ 'regex_match' => 'Endorsement No can only contain letters, numbers, and symbols like / - or _' ]], + 'data_received_date' => [ + 'label' => 'Data Received Date', + 'rules' => 'required|valid_date[d/m/Y]', + 'errors' => ['required' => 'Data Received Date is required.', 'valid_date' => 'Please provide a valid Data Received Date.'] + ], + 'policy_issue_date' => [ + 'label' => 'Endorsement Issue Date', + 'rules' => 'required|valid_date[d/m/Y]', + 'errors' => ['required' => 'Endorsement Issue Date is required.', 'valid_date' => 'Please provide a valid Endorsement Issue Date.'] + ], + // ========================================== + // ENDORSEMENT SECTION + // ========================================== + 'month' => [ + 'label' => 'Endorsement Issue Month', + 'rules' => 'required', + 'errors' => ['required' => 'Endorsement Issue Month (MM/YYYY) is required.'] + ], + 'endorse_eff_date' => [ + 'label' => 'Endorsement Effective Date', + 'rules' => 'permit_empty|valid_date[d/m/Y]', + 'errors' => ['valid_date' => 'Please provide a valid Endorsement Effective Date.'] + ], + 'emp_count' => [ + 'label' => 'No of Insured', + 'rules' => 'permit_empty|numeric', + 'errors' => ['numeric' => 'No of Insured must be a number.'] + ], + 'dependent_count' => [ + 'label' => 'No of Dependents', + 'rules' => 'permit_empty|numeric', + 'errors' => ['numeric' => 'No of Dependents must be a number.'] + ], + 'last_action_date' => [ + 'label' => 'Latest Action Date', + 'rules' => 'permit_empty|valid_date[d/m/Y]', + 'errors' => ['valid_date' => 'Please provide a valid Latest Action Date.'] + ], + 'install_due_date' => [ + 'label' => 'Installment Due Date', + 'rules' => 'permit_empty|valid_date[d/m/Y]', + 'errors' => ['valid_date' => 'Please provide a valid Installment Due Date.'] + ], + + // ========================================== + // PREMIUM DETAILS (ARRAY FIELDS .*) + // ========================================== + 'follow_insurer_id.*' => [ + 'label' => 'Insurer', + 'rules' => 'required', + 'errors' => ['required' => 'Please select an Insurer.'] + ], + 'calc_policy_issue_date.*' => [ + 'label' => 'Premium Endorsement Issue Date', + 'rules' => 'required', + 'errors' => ['required' => 'Endorsement Issue Date is required.'] + ], + 'co_share_per.*' => [ + 'label' => 'Co-Share %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Co-Share % must be a valid decimal.'] + ], + 'base_premium.*' => [ + 'label' => 'Base Premium', + 'rules' => 'permit_empty|regex_match[/^-?\d+(\.\d+)?$/]|differs[0]', + 'errors' => [ + 'regex_match' => 'Base Premium must be a valid positive or negative number.', + 'differs' => 'Base Premium cannot be zero.' + ] + ], + 'total.*' => [ + 'label' => 'Total Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Total Premium must be a valid amount.'], + ], + + // Decimal validation with Labels + 'non_comm_per_amt.*' => [ + 'label' => 'Non-Comm Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Non-Comm Premium must be numeric.'] + ], + 'tp_premium.*' => [ + 'label' => 'TP Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'TP Premium must be numeric.'] + ], + 'ter_premium.*' => [ + 'label' => 'TEP Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'TEP Premium must be numeric.'] + ], + 'co_premium.*' => [ + 'label' => 'Co-Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Co-Premium must be numeric.'] + ], + 'co_tp_premium.*' => [ + 'label' => 'Co-TP Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Co-TP Premium must be numeric.'] + ], + 'co_ter_premium.*' => [ + 'label' => 'Co-TEP Premium', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Co-TEP Premium must be numeric.'] + ], + 'cgst.*' => [ + 'label' => 'CGST', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'CGST must be numeric.'] + ], + 'sgst.*' => [ + 'label' => 'SGST', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'SGST must be numeric.'] + ], + 'igst.*' => [ + 'label' => 'IGST', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'IGST must be numeric.'] + ], + 'gst_amount.*' => [ + 'label' => 'GST Amount', + 'rules' => 'permit_empty|decimal|greater_than[0]', + 'errors' => ['decimal' => 'GST Amount must be numeric.','greater_than' => 'GST Amount cannot be zero; please enter a value greater than 0.'] + ], + 'stamp_duty.*' => [ + 'label' => 'Stamp Duty', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Stamp Duty must be numeric.'] + ], + 'agreed_bp.*' => [ + 'label' => 'Agreed BP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Agreed BP % must be numeric.'] + ], + 'agreed_tp.*' => [ + 'label' => 'Agreed TP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Agreed TP % must be numeric.'] + ], + 'agreed_ter.*' => [ + 'label' => 'Agreed TEP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Agreed TEP % must be numeric.'] + ], + 'agreed_amount.*' => [ + 'label' => 'Agreed Amount', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Agreed Amount must be numeric.'] + ], + + // Finance/Management Specific Labels + 'actual_bp_amt.*' => [ + 'label' => 'Actual BP Amount', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual BP Amount must be numeric.'] + ], + 'actual_tp_amt.*' => [ + 'label' => 'Actual TP Amount', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual TP Amount must be numeric.'] + ], + 'actual_tep_amt.*' => [ + 'label' => 'Actual TEP Amount', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual TEP Amount must be numeric.'] + ], + 'actual_bp_per.*' => [ + 'label' => 'Actual BP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual BP % must be numeric.'] + ], + 'actual_tp_per.*' => [ + 'label' => 'Actual TP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual TP % must be numeric.'] + ], + 'actual_tep_per.*' => [ + 'label' => 'Actual TEP %', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Actual TEP % must be numeric.'] + ], + 'actual_bp_brokerage_amt.*' => [ + 'label' => 'BP Remuneration', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'BP Remuneration must be numeric.'] + ], + 'actual_tp_brokerage_amt.*' => [ + 'label' => 'TP Remuneration', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'TP Remuneration must be numeric.'] + ], + 'actual_tep_brokerage_amt.*' => [ + 'label' => 'TEP Remuneration', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'TEP Remuneration must be numeric.'] + ], + 'exp_amt.*' => [ + 'label' => 'Expected Amount', + 'rules' => 'permit_empty|decimal', + 'errors' => ['decimal' => 'Expected Amount must be numeric.'] + ], + 'co_share_type.*' => ['label' => 'Is Leader', 'rules' => 'permit_empty','errors' => []], + 'follower_policy_no.*' => ['label' => 'Follower Policy No', 'rules' => 'permit_empty','errors' => []], + 'co_share_id.*' => ['label' => 'Record ID', 'rules' => 'permit_empty','errors' => []], + ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); + } + $data = $this->preparePolicyTransactionData(); + + $id = (isset($data['id']) && !empty($data['id'])) ? $data['id'] : null; + $data['status'] = 'completed'; - // print_r($data); die; if (!$id) { return $this->insertEndorsementTransaction($data); } else { return $this->updateEndorsementTransaction($id, $data); } + } private function preparePolicyTransactionData() { - $data = $this->request->getPost(); - + $request_post_data = $this->request->getPost(); + $data = sanitizeInputArrayAdvanced($request_post_data); // echo '
';
             // print_r($data); 
             // die;
 
+            $file_data = $this->request->getFiles() ?? null;
+            $data['file_data'] = $file_data ?? null;
+
             if (!isset($data['policy_with_corr'])) {
                 $data['policy_with_corr'] = 0;
             } elseif ($data['policy_with_corr']) {
@@ -2410,6 +2804,11 @@
                 $this->handleCompletedStatus($data, $insert);
                 $this->insertOrUpdateCoShareDetails($data, $insert);
 
+                // policy file upload 
+                if(isset($data['doc_name']) && isset($data['file_data']) && !empty($data['doc_name']) && !empty($data['file_data'])){
+                    $this->uploadFile($data['doc_name'], $data['file_data'], $insert);
+                }
+
                 return $this->respond(['status' => true, 'message' => 'Endorsement transaction created successfully'], 200);
             }
 
@@ -2418,6 +2817,7 @@
 
         private function updateEndorsementTransaction($id, $data)
         {   
+            $data['updated_by']  = get_session_userid();
             $old_endorse_data =  $this->policyTransactionModel->where('id', $id)->where('is_active', 1)->first();
             $update = $this->policyTransactionModel->where('id', $id)->set($data)->update();
 
@@ -2681,6 +3081,20 @@
             // dd(db_connect()->getLastQuery() ,$pt_bp_amt);
             $data['base_cd_amount'] = $pt_bp_amt['amount'] ?? null;
 
+            $ptFileQuery = $this->PTFileModel
+                ->join('policy_transaction', 'pt_files.pt_id = policy_transaction.id')
+                ->where('pt_files.pt_id', $id)
+                ->where('pt_files.is_active', 1);
+
+            if (!in_array(get_role_id(), [1, 5]) && empty(array_intersect(user_team(), [MANAGEMENT_TEAM_ID, FINANCE_TEAM_ID, BUSINESS_TEAM_ID]))) {
+
+                if (get_role_id() == 4 && in_array(POS_TEAM_ID, user_team())) {
+                    $ptFileQuery->where('pt_files.created_by', get_session_userid());
+                }
+            }
+
+            $data['pt_files'] = $ptFileQuery->findAll();
+
             if ($data) {
                 return $this->respond(['status' => true, 'data' => $data, 'pt_id' => $pt_id], 200);
             } else {
@@ -3250,10 +3664,12 @@
             $client_policy_id = (!isset($client_policy_id) || $client_policy_id === '' || $client_policy_id === null) ? 0 : $client_policy_id;
             $user_id = (!isset($user_id) || $user_id === '' || $user_id === null) ? 0 : $user_id;
             if ($this->request->is('post')) {
-                $isFromDashboard = $this->request->getPost("is_dashboard");
+                $request_post_data   = $this->request->getPost();
+                $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
+                $isFromDashboard = $sanitized_post_data["is_dashboard"];
 
                 if (isset($isFromDashboard) && !empty($isFromDashboard) && $isFromDashboard == 1) {
-                    $ids = $this->request->getPost('ids');
+                    $ids = $sanitized_post_data['ids'];
 
                     $ids = array_filter(explode(',', $ids));
 
@@ -3639,7 +4055,7 @@
             helper('excel_util_helper');
             //get file info
             $file_id = $params['file_id'];
-            $file = $this->insurerStatements->find($file_id);
+            $file = $this->insurerStatements->find((int)$file_id);
             // dd($file);
             $date = new \DateTime($file['month']);
 
@@ -3757,7 +4173,7 @@
             try {
 
                 //get file info
-                $file = $this->insurerStatements->find($file_id);
+                $file = $this->insurerStatements->find((int)$file_id);
                 // dd($file);
 
                 $date = new \DateTime($file['month']);
@@ -3910,7 +4326,7 @@
             helper('excel_util_helper');
             //get file info
             $file_id = $params['file_id'];
-            $file = $this->insurerStatements->find($file_id);
+            $file = $this->insurerStatements->find((int)$file_id);
             // dd($file);
             $date = new \DateTime($file['month']);
 
@@ -4075,7 +4491,7 @@
         {
             $statement_id = $this->request->getUri()->getSegment(4);
 
-            $inv_details = $this->insurerStatements->find($statement_id);
+            $inv_details = $this->insurerStatements->find((int)$statement_id);
             $inv_payment_details = $this->invPaymentDetailsModel
                 ->where('statement_id', $statement_id)
                 ->where('is_active', 1)
@@ -4212,7 +4628,7 @@
         public function getFileErr()
         {
             $file_id = $this->request->getUri()->getSegment(4);
-            $file = $this->insurerStatements->find($file_id);
+            $file = $this->insurerStatements->find((int)$file_id);
             return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => $file['reason']], 200);
         }
 
@@ -4221,10 +4637,12 @@
             // echo 'scbsc';die();
             if ($this->request->is('post')) {
                 // $jsonData = (array)$this->request->getJSON();
-                $customer_id = $this->request->getPost('customer_id');
-                $policy_id = $this->request->getPost('policy_id');
-                $cus_doc_name = $this->request->getPost('cus_doc_name');
-                $policy_doc_name = $this->request->getPost('policy_doc_name');
+                $request_data   = $this->request->getPost();
+                $data = sanitizeInputArrayAdvanced($request_data);
+                $customer_id = $data['customer_id'];
+                $policy_id = $data['policy_id'];
+                $cus_doc_name = $data['cus_doc_name'];
+                $policy_doc_name = $data['policy_doc_name'];
                 $pt_files = [];
                 $kyc_files = [];
                 $batch_files = [];
@@ -4933,6 +5351,7 @@
             $pt_data = $this->policyTransactionModel->where('is_active', 1)->findAll();
             $user_data = $this->userModel->where('is_active', 1)->findAll();
             $agent_data = db_connect()->table('partner_agent')->where('is_active', 1)->get()->getResultArray();
+            $pos_data = db_connect()->table('partner_pos')->where('is_active', 1)->get()->getResultArray();
             $rto_master = db_connect()->table('rto_master')->where('is_active', 1)->get()->getResultArray();
             $vehicle_type = db_connect()->table('vehicle_type')->where('is_active', 1)->get()->getResultArray();
             $nhance_branch_data = db_connect()->table('nhance_branch')->where('is_active', 1)->get()->getResultArray();
@@ -5098,6 +5517,7 @@
             $vehicle_data = $this->vehicleModel->where('is_active', 1)->findAll();
             $user_data = $this->userModel->where('is_active', 1)->findAll();
             $agent_data = db_connect()->table('partner_agent')->where('is_active', 1)->get()->getResultArray();
+            $pos_data = db_connect()->table('partner_pos')->where('is_active', 1)->get()->getResultArray();
             $rto_master = db_connect()->table('rto_master')->where('is_active', 1)->get()->getResultArray();
             $vehicle_type_data = db_connect()->table('vehicle_type')->where('is_active', 1)->get()->getResultArray();
             $nhance_branch_data = db_connect()->table('nhance_branch')->where('is_active', 1)->get()->getResultArray();
@@ -5180,7 +5600,8 @@
                     $current_insurer_branch_data = check_insurer_branch_exist($row, $insurer_branch_data, $current_insurer_data)['branch'] ?? null;
 
                     $current_nhance_branch = check_nhance_branch($row, $nhance_branch_data)['branch'] ?? null;
-                    $current_agent_data = check_agent_exist($row, $agent_data)['agent'] ?? null;
+                    // $current_agent_data = check_agent_exist($row, $agent_data)['agent'] ?? null;
+                    $current_agent_data = check_agent_exist($row, $pos_data)['agent'] ?? null;
 
                     $salse = check_user_exist($row, 13, $user_data)['user'] ?? null;
                     $service = check_user_exist($row, 14, $user_data)['user'] ?? null;
@@ -5218,8 +5639,9 @@
                         'salse_person_manager_id'   => $salse['rm_id'] ?? null,
                         'service_person_manager_id' => $service['rm_id'] ?? null,
                         'service_person_branch_id'  => $service['nhance_branch_id'] ?? null,
-                        'agent_id'              => $current_agent_data['id'] ?? null,
-                        'agent_code'            => $current_agent_data['agent_code'] ?? null,
+                        // 'agent_id'              => $current_agent_data['id'] ?? null,
+                        // 'agent_code'            => $current_agent_data['agent_code'] ?? null,
+                        'pos_id'                => $current_agent_data['id'] ?? null,
                         'file_id'               => $params['file_id'],
                         'endorsement_no'        => null,
                         'client_branch_id'      => null,
diff --git a/app/Controllers/RestAuthenticationController.php b/app/Controllers/RestAuthenticationController.php
index bad20540..e4c474a1 100755
--- a/app/Controllers/RestAuthenticationController.php
+++ b/app/Controllers/RestAuthenticationController.php
@@ -110,6 +110,14 @@ class RestAuthenticationController extends AdminController
         
             if (isset($employeeData['employee_id'])) 
             {
+
+                //check the resend otp (with in 60 seconds don't allow another otp to send)
+                $check = canSendOtp($employeeData);
+                if (!$check['allowed']) {
+                    return $this->respond(['status'  => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after']], 429); // Too Many Requests
+
+                }
+
                 $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithMobileNumber: Employee verified with ID = " . $employeeData['employee_id']);
                 log_message('error', '  ');
                 log_message('error', '************************ POST END ********************************');
@@ -217,6 +225,12 @@ class RestAuthenticationController extends AdminController
 
             if (isset($employeeData['employee_id'])) {
 
+                //check the resend otp (with in 60 seconds don't allow another otp to send)
+                $check = canSendOtp($employeeData);
+                if (!$check['allowed']) {
+                    return $this->respond(['status'  => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after']], 429); // Too Many Requests
+                }
+
 
                 $builder = $this->employeeModel
                     ->where('email_corporate', $email)
@@ -518,6 +532,12 @@ class RestAuthenticationController extends AdminController
 
 
             if ($HrData) {
+
+                    //check the resend otp (with in 60 seconds don't allow another otp to send)
+                    $check = canSendOtp($HrData);
+                    if (!$check['allowed']) {
+                        return $this->respond(['status'  => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after']], 429); // Too Many Requests
+                    }
                 
                     $sql = "UPDATE level_contacts SET otp = ? WHERE mobile = ? AND contact_type = 'client' AND is_active = 1";
 
@@ -569,6 +589,12 @@ class RestAuthenticationController extends AdminController
           
             if ($HrData) {
 
+                //check the resend otp (with in 60 seconds don't allow another otp to send)
+                $check = canSendOtp($HrData);
+                if (!$check['allowed']) {
+                    return $this->respond(['status'  => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after']], 429); // Too Many Requests
+                }
+
                 $sql = "
                         UPDATE level_contacts
                         SET otp = ?
@@ -734,6 +760,8 @@ class RestAuthenticationController extends AdminController
 
                             $decoded = json_decode($HRAccessData['allowed_modules'], true);
                             $getAllhrData[$key]['allowed_modules'] = $decoded;
+                            $HRAccessData['pre_client_id'] = md5($HRAccessData['pre_client_id']);
+                            $HRAccessData['post_client_id'] = md5($HRAccessData['post_client_id']);
 
                             $token = JWTToken::encode($HRAccessData);
                             $getAllhrData[$key]['token'] = $token;
@@ -749,7 +777,7 @@ class RestAuthenticationController extends AdminController
 
                 }else {
 
-                    return $this->respond(['status' => 'failed','code' => 404,'data' => "" , 'message' => 'User not found' ],200);
+                    return $this->respond(['status' => 'failed','code' => 404,'data' => "" , 'message' => 'Invalid OTP' ],200);
                 }
 
                
@@ -759,7 +787,7 @@ class RestAuthenticationController extends AdminController
 
             } else {
 
-                return $this->respond(['status' => 'failed','code' => 404,'data' => "" , 'message' => 'User not found' ],200);
+                return $this->respond(['status' => 'failed','code' => 404,'data' => "" , 'message' => 'Invalid OTP' ],200);
             }
         } catch (\Exception $e) {
             return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getMessage()],500);
@@ -919,6 +947,12 @@ class RestAuthenticationController extends AdminController
             $mpin = $this->request->getJSON()->new_mpin;
             $client_id = $this->request->getJSON()->client_id ?? null;
 
+            if($old_mpin == $mpin)
+            {
+                $result = ['mpin_verification' => false , 'message' => "New MPIN must be different from the old MPIN"];
+                return $this->respond(['status' => 'failed','code' => 404,'data' => $result],200);
+            }
+
             // if (isset($mobile_number)) 
             // {
             //     $employeeData = $this->employeeModel->where('mobile', $mobile_number)->where('mpin', $old_mpin)->where('relationship', 'self')->first();
@@ -990,11 +1024,11 @@ class RestAuthenticationController extends AdminController
                
                
             } else {
-                $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - updateMpin: Employee not found in the POST. Wrong MPIN");
+                $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - updateMpin: Employee not found in the POST. Old MPIN is incorrect");
                 log_message('error', '  ');
                 log_message('error', ' ************************************* POST END **************************************** ');
                 log_message('error', '  ');
-                $result = ['mpin_verification' => false , 'message' => "Wrong Mpin"];
+                $result = ['mpin_verification' => false , 'message' => "Old MPIN is incorrect"];
                 return $this->respond(['status' => 'failed','code' => 404,'data' => $result],200);
 
             }
@@ -1237,7 +1271,7 @@ class RestAuthenticationController extends AdminController
                 log_message('error', '  ');
                 log_message('error', ' ************************************* POST END **************************************** ');
                 log_message('error', '  ');
-                return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'], 'is_biometric_enabled' => $employeeData['is_biometric_enabled']],200);
+                return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? 0, 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? 0],200);
             } else {
                 $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - not found");
                 log_message('error', '  ');
@@ -2289,4 +2323,53 @@ class RestAuthenticationController extends AdminController
         }
     }
 
+    public function logout()
+    {
+        $authHeader = $this->request->getHeaderLine('Authorization');
+
+        if (!$authHeader) {
+            return $this->respond([
+                'status'  => false,
+                'message' => 'Authorization token missing'
+            ], 401);
+        }
+
+        // Validate JWT (your hardened function)
+        $result = JWTToken::validateJWT($authHeader);
+
+        if ($result['status'] !== true) {
+            return $this->respond([
+                'status'  => false,
+                'message' => 'Invalid or expired token'
+            ], 401);
+        }
+
+        $decoded = $result['decoded'];
+        $userId  = $decoded['id'] ?? null;
+
+        if (!$userId) {
+            return $this->respond([
+                'status'  => false,
+                'message' => 'Invalid token payload'
+            ], 401);
+        }
+
+        // Identify user type
+        if (isset($decoded['emp_code'])) {
+            $model = new EmployeeModel();
+        } else {
+            $model = new LevelContactModel();
+        }
+
+        // Invalidate token server-side
+        $model->update($userId, [
+            'token_time_out' => null
+        ]);
+
+        return $this->respond([
+            'status'  => true,
+            'message' => 'Logged out successfully'
+        ], 200);
+    }
+
 }
\ No newline at end of file
diff --git a/app/Controllers/SalesController.php b/app/Controllers/SalesController.php
index 17a60b0e..33a422f6 100644
--- a/app/Controllers/SalesController.php
+++ b/app/Controllers/SalesController.php
@@ -128,7 +128,7 @@ class SalesController extends BaseController
     public function updateLead($id)
     {
         try {
-            if (!$this->leadModel->find($id)) {
+            if (!$this->leadModel->find((int)$id)) {
                 return $this->failNotFound('Lead not found');
             }
 
@@ -158,7 +158,7 @@ class SalesController extends BaseController
     public function deleteLead($id)
     {
         try {
-            if (!$this->leadModel->find($id)) {
+            if (!$this->leadModel->find((int)$id)) {
                 return $this->failNotFound('Lead not found');
             }
 
@@ -228,7 +228,7 @@ class SalesController extends BaseController
             }
 
             $contactId = $this->contactModel->getInsertID();
-            $contact = $this->contactModel->find($contactId);
+            $contact = $this->contactModel->find((int)$contactId);
 
             return $this->respondCreated([
                 'status' => 'success',
@@ -247,18 +247,18 @@ class SalesController extends BaseController
     public function updateContact($id)
     {
         try {
-            if (!$this->contactModel->find($id)) {
+            if (!$this->contactModel->find((int)$id)) {
                 return $this->failNotFound('Contact not found');
             }
 
             $data = $this->request->getJSON(true);
             $data['updated_by'] = $this->getUserId();
 
-            if (!$this->contactModel->update($id, $data)) {
+            if (!$this->contactModel->update((int)$id, $data)) {
                 return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
             }
 
-            $contact = $this->contactModel->find($id);
+            $contact = $this->contactModel->find((int)$id);
 
             return $this->respond([
                 'status' => 'success',
@@ -277,11 +277,11 @@ class SalesController extends BaseController
     public function deleteContact($id)
     {
         try {
-            if (!$this->contactModel->find($id)) {
+            if (!$this->contactModel->find((int)$id)) {
                 return $this->failNotFound('Contact not found');
             }
 
-            $this->contactModel->delete($id);
+            $this->contactModel->delete((int)$id);
 
             return $this->respondDeleted([
                 'status' => 'success',
@@ -299,7 +299,7 @@ class SalesController extends BaseController
     public function setPrimaryContact($id)
     {
         try {
-            $contact = $this->contactModel->find($id);
+            $contact = $this->contactModel->find((int)$id);
             
             if (!$contact) {
                 return $this->failNotFound('Contact not found');
@@ -425,7 +425,7 @@ class SalesController extends BaseController
             }
 
             $activityId = $this->activityModel->getInsertID();
-            $activity = $this->activityModel->find($activityId);
+            $activity = $this->activityModel->find((int)$activityId);
 
             return $this->respondCreated([
                 'status' => 'success',
@@ -444,7 +444,7 @@ class SalesController extends BaseController
     public function updateActivity($id)
     {
         try {
-            if (!$this->activityModel->find($id)) {
+            if (!$this->activityModel->find((int)$id)) {
                 return $this->failNotFound('Activity not found');
             }
 
@@ -455,7 +455,7 @@ class SalesController extends BaseController
                 return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
             }
 
-            $activity = $this->activityModel->find($id);
+            $activity = $this->activityModel->find((int)$id);
 
             return $this->respond([
                 'status' => 'success',
@@ -474,7 +474,7 @@ class SalesController extends BaseController
     public function completeActivity($id)
     {
         try {
-            $activity = $this->activityModel->find($id);
+            $activity = $this->activityModel->find((int)$id);
             
             if (!$activity) {
                 return $this->failNotFound('Activity not found');
@@ -487,7 +487,7 @@ class SalesController extends BaseController
             $data = $this->request->getJSON(true);
             $data['updated_by'] = $this->getUserId();
 
-            $this->activityModel->completeActivity($id, $data);
+            $this->activityModel->completeActivity((int)$id, $data);
 
             // Create follow-up activity if requested
             if (!empty($data['create_followup']) && $data['create_followup'] === true) {
@@ -505,7 +505,7 @@ class SalesController extends BaseController
                 $this->activityModel->insert($followupData);
             }
 
-            $updatedActivity = $this->activityModel->find($id);
+            $updatedActivity = $this->activityModel->find((int)$id);
 
             return $this->respond([
                 'status' => 'success',
@@ -524,11 +524,11 @@ class SalesController extends BaseController
     public function deleteActivity($id)
     {
         try {
-            if (!$this->activityModel->find($id)) {
+            if (!$this->activityModel->find((int)$id)) {
                 return $this->failNotFound('Activity not found');
             }
 
-            $this->activityModel->delete($id);
+            $this->activityModel->delete((int)$id);
 
             return $this->respondDeleted([
                 'status' => 'success',
@@ -595,7 +595,7 @@ class SalesController extends BaseController
             }
 
             $noteId = $this->noteModel->getInsertID();
-            $note = $this->noteModel->find($noteId);
+            $note = $this->noteModel->find((int)$noteId);
 
             return $this->respondCreated([
                 'status' => 'success',
@@ -614,7 +614,7 @@ class SalesController extends BaseController
     public function updateNote($id)
     {
         try {
-            $note = $this->noteModel->find($id);
+            $note = $this->noteModel->find((int)$id);
             
             if (!$note) {
                 return $this->failNotFound('Note not found');
@@ -632,7 +632,7 @@ class SalesController extends BaseController
                 return $this->fail($this->noteModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
             }
 
-            $updatedNote = $this->noteModel->find($id);
+            $updatedNote = $this->noteModel->find((int)$id);
 
             return $this->respond([
                 'status' => 'success',
@@ -651,7 +651,7 @@ class SalesController extends BaseController
     public function deleteNote($id)
     {
         try {
-            $note = $this->noteModel->find($id);
+            $note = $this->noteModel->find((int)$id);
             
             if (!$note) {
                 return $this->failNotFound('Note not found');
@@ -662,7 +662,7 @@ class SalesController extends BaseController
                 return $this->failUnauthorized('You are not authorized to delete this note');
             }
 
-            $this->noteModel->delete($id);
+            $this->noteModel->delete((int)$id);
 
             return $this->respondDeleted([
                 'status' => 'success',
diff --git a/app/Controllers/TestingController.php b/app/Controllers/TestingController.php
index 75d4d578..7cfc5ab2 100644
--- a/app/Controllers/TestingController.php
+++ b/app/Controllers/TestingController.php
@@ -17,6 +17,8 @@ use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Style\Fill;
 use PhpOffice\PhpSpreadsheet\Style\Border;
 
+use Firebase\JWT\JWT;
+
 class TestingController extends BaseController
 {
     use ResponseTrait;
@@ -983,5 +985,46 @@ class TestingController extends BaseController
     }
 
 
+    public function metaDashboardDemo()
+    {
+
+        // 🔐 Move this to .env in real projects
+        $METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY');
+        $policy_id = $this->request->getGet('client_policy');
+        $policy_id = $policy_id ? $policy_id : 4687;
+        $payload = [
+            'resource' => [
+                // 'dashboard' => 1
+                'dashboard' => 2
+            ],
+            'params' => (object)['client_policy' => $policy_id],   // MUST be object for Metabase
+            'exp'    => time() + (10 * 60) // 10 minutes
+        ];
+        // dd($payload);
+        $token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256');
+
+        // // You can either return token only
+        // return $this->response->setJSON([
+        //     'token' => $token,
+        //     'iframe_url' => "https://your-metabase-domain/embed/dashboard/{$token}#bordered=true&titled=true"
+        // ]);
+        if($this->request->getGet('api') == 1)
+        {
+             return $this->respond([
+                'status' => 'success',
+                'message' => 'Form data received successfully!',
+                'data' => [
+                'metabaseToken' => $token,
+                'metabaseUrl'   => 'https://nsights.nhanceindia.in']
+            ]);
+        }
+
+       return view('meta_dashboard_demo_one', [
+            'metabaseToken' => $token,
+            'metabaseUrl'   => 'https://nsights.nhanceindia.in',
+        ]);
+    }
+
+
 
 }
diff --git a/app/Controllers/ThzController.php b/app/Controllers/ThzController.php
index a2427b07..a4bde6dd 100644
--- a/app/Controllers/ThzController.php
+++ b/app/Controllers/ThzController.php
@@ -62,8 +62,108 @@ class ThzController extends BaseController
 
     public function ticketSave()
     {
+            $rules = [
+                    'client_id' => [
+                        'rules'  => 'permit_empty|integer',
+                        'errors' => [
+                            'integer' => 'Invalid client selected'
+                        ]
+                    ],
+                    
+
+                    'mobile' => [
+                        'rules'  => 'required|regex_match[/^[0-9]{10}$/]',
+                        'errors' => [
+                            'required'     => 'Mobile number is required',
+                            'regex_match'  => 'Mobile number must be exactly 10 digits'
+                        ]
+                    ],
+
+                    'name' => [
+                        'rules'  => 'required|min_length[3]|max_length[100]|alpha_space',
+                        'errors' => [
+                            'required'   => 'Name is required',
+                            'min_length' => 'Name must be at least 3 characters',
+                            'alpha_space'=> 'Name can contain only letters and spaces'
+                        ]
+                    ],
+
+                    'email' => [
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                        ]
+                    ],
+
+                    'empcode' => [
+                        'rules'  => 'permit_empty|alpha_numeric_punct|max_length[50]',
+                        'errors' => [
+                            'alpha_numeric_punct' => 'Employee code contains invalid characters.',
+                            'max_length' => 'Employee code is too long'
+                        ]
+                    ],
+
+                    'ticket_type' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Please select a Ticket Type.'
+                        ]
+                    ],
+
+                    'ticket_type' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Ticket Type is required',
+                        ]
+                    ],
+
+                    'assign_to' => [
+                        'rules'  => 'permit_empty|integer',
+                        'errors' => [
+                            'integer' => 'Invalid assignee selected'
+                        ]
+                    ],
+
+                    'subject' => [
+                        'rules'  => 'required|min_length[3]|max_length[150]|regex_match[/^[a-zA-Z0-9 .,\-_()&\/\?]+$/]',
+                        'errors' => [
+                            'required'    => 'Subject is required.',
+                            'min_length'  => 'Subject must be at least 3 characters long.',
+                            'max_length'  => 'Subject cannot exceed 150 characters',
+                            'regex_match' => 'Subject contains invalid characters (Avoid quotes and special symbols).'
+                        ]
+                    ],
+
+                    'message' => [
+                        'rules'  => 'required|min_length[10]|max_length[1500]',
+                        'errors' => [
+                            'required'   => 'Message is required',
+                            'min_length' => 'Message must be at least 10 characters',
+                            'max_length' => 'Message cannot exceed 1500 characters'
+                        ]
+                    ],
+                    'status' => [
+                        'rules'  => 'permit_empty|in_list[Open,In Progress,Resolved,Closed]',
+                        'errors' => [
+                            'in_list' => 'Invalid ticket status'
+                        ]
+                    ],
+            ];
+
+            if (!$this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status' => false,
+                    'message' => 'Input validation failed',
+                    'code' => 400,
+                    'errors' => $this->validator->getErrors()
+                ]);
+            }
         try {
-            $data = $this->request->getPost();
+
+            $request_post_data = $this->request->getPost();
+            $data = sanitizeInputArrayAdvanced($request_post_data);
+                
             $references   = "";
             if (!empty($data['thz_id'])) {
 
@@ -157,7 +257,30 @@ class ThzController extends BaseController
     {
 
         try {
-            $data = $this->request->getPost();
+            
+            $rules = [
+                'notes' => [
+                    'rules'  => 'required|string|min_length[1]|max_length[1500]',
+                    'errors' => [
+                        'required'   => 'Notes is required',
+                        'string'     => 'Notes must be valid text',
+                        'min_length' => 'Notes cannot be empty',
+                        'max_length' => 'Notes cannot exceed 1500 characters'
+                    ]
+                ],
+            ];
+
+            if (!$this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status' => false,
+                    'message' => 'Input validation failed',
+                    'code' => 400,
+                    'errors' => $this->validator->getErrors()
+                ]);
+            }
+
+            $request_post_data = $this->request->getPost();
+            $data = sanitizeInputArrayAdvanced($request_post_data);
 
             $data['notes_type'] = $data['notes_type'] ?? 'External';
 
@@ -238,7 +361,7 @@ class ThzController extends BaseController
                 
                 $accManagerId = $this->getAcmIdUsingClientId($clientId ?? null);
                
-                $accManager = $this->userModel->find($accManagerId);
+                $accManager = $this->userModel->find((int)$accManagerId);
 
             if (!empty($result['master'])) {
                 $notes = $this->thzMasterNotesModel->ticketConversationList($thz_id, $returnType);
diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php
index 07b8db0e..2a265f4c 100644
--- a/app/Controllers/TicketController.php
+++ b/app/Controllers/TicketController.php
@@ -24,6 +24,7 @@ use App\Models\ClaimFilesModel;
 use App\Models\ClaimDumpFileModel;
 use App\Models\VehicleModel;
 use App\Models\PartnerPolicyModel;
+use App\Models\ClaimMisFileModel;
 
 use DOMDocument;
 use DOMXPath;
@@ -67,6 +68,7 @@ class TicketController extends BaseController
     protected $claimDumpFileModel;
     protected $vehicleModel;
     protected $partnerPolicyModel;
+    protected $claimmisFileModel;
 
     public function __construct()
     {
@@ -398,6 +400,7 @@ class TicketController extends BaseController
         $this->claimDumpFileModel = new ClaimDumpFileModel();
         $this->vehicleModel = new VehicleModel();
         $this->partnerPolicyModel = new PartnerPolicyModel();
+        $this->claimmisFileModel = new ClaimMisFileModel();
     }
 
     public function ticketList()
@@ -421,7 +424,6 @@ class TicketController extends BaseController
 
             return $this->loadLayout('ticket_search', $data);
         } else {
-
             $isFromDashboard = $this->request->getPost("is_dashboard");
             log_message('error', 'Is From Dashboard: ' . $isFromDashboard);
 
@@ -443,7 +445,6 @@ class TicketController extends BaseController
                 }
                 $data['claim_status'] = $this->claimStatus->select('id,ticket_type,claim_status')->where('is_active', 1)->findAll();
                 $data['client_list'] = $this->clientModel->select('id,client_name')->where('is_active', 1)->findAll();
-                // dd($data);
                 return $this->loadLayout('ticket_search', $data);
 
             }
@@ -951,6 +952,13 @@ class TicketController extends BaseController
     {     
         // $ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first();
         $ticket_data = $this->ticketMasterModel->getTicketDataByTicketID($ticket_id);
+     
+
+        // Security Check: Verify if record exists
+        if (empty($ticket_data)) {
+            // Redirect or show a 404 to prevent "Undefined array key" errors
+            return redirect()->to(base_url('ticket/list'))->with('error', 'Ticket not found');
+        }
     
         $ticket_data = $this->formatDateForClaim($ticket_data, 'd/m/Y');
 
@@ -1119,11 +1127,307 @@ class TicketController extends BaseController
 
     public function createTicket()
     {
-        $ticket_data = $this->request->getPost();
-        $ticket_data = $this->formatDateForClaim($ticket_data);
+        $request_data = $this->request->getPost();
+        $selected_ticket_type = $this->request->getPost('ticket_type_id');
+
+        // 1. Initialize an empty rules array
+        $rules = [];
+        if ($selected_ticket_type == 1 || $selected_ticket_type == 72) {
+            $rules = [
+                // --- EMPLOYEE DETAILS ---
+                'emp_code' => [
+                    'label'  => 'Employee Code',
+                    'rules'  => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Code is required.',
+                        'regex_match' => 'Employee Code cannot contain spaces or special characters (only letters, numbers, - and _ allowed).'
+                    ]
+                ],
+                'emp_name' => [
+                    'label'  => 'Employee Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Name is required',
+                        'min_length'  => 'Employee Name must be at least 3 characters long',
+                        'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                'insured_name' => [
+                    'label'  => 'Insured Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Insured Name is required',
+                        'min_length'  => 'Insured Name must be at least 3 characters long',
+                        'regex_match' => 'Insured Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                'relationship' => ['label'  => 'Relationship','rules'  => 'required',
+                      'errors' => ['required' => 'Please select a Relationship']
+                ],
+                'policy_no' => [
+                    'label'  => 'Policy Number',
+                    // Allows: A-Z, a-z, 0-9, spaces (\s), hyphens (\-), underscores (_), and slashes (\/)
+                    'rules'  => 'permit_empty|min_length[3]|max_length[50]|regex_match[/^[a-zA-Z0-9\s\-\/_]+$/]',
+                    'errors' => [
+                        'min_length'  => 'Policy Number appears too short (min 5 characters).',
+                        'regex_match' => 'Policy Number can only contain letters, numbers, spaces, hyphens(-), underscores(_), and slashes(/).'
+                    ]
+                ],
+                'tpa_no'    => ['label'  => 'TPA ID','rules'  => 'permit_empty','errors' => []],
+                'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'emp_personal_mail' => ['label'  => 'Personal Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Personal Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required',
+                    'errors' => ['required' => 'Please select a Policy']
+                ],
+                'acm_id' => ['label'  => 'Account Manager','rules'  => 'required',
+                    'errors' => ['required' => 'Select an Account Manager']
+                ],
+
+                // --- CLAIM DETAILS ---
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required',
+                    'errors' => ['required' => 'Claim Status is required']
+                ],
+                'priority' => ['label'  => 'Priority','rules'  => 'required',
+                    'errors' => ['required' => 'Priority is required']
+                ],
+                'mode_of_intimation' => ['label'  => 'Mode of Submission','rules'  => 'required',
+                    'errors' => ['required' => 'Mode of Submission is required']
+                ],
+                'claim_type' => ['label'  => 'Claim Type','rules'  => 'required',
+                    'errors' => ['required' => 'Claim Type is required']
+                ],
+                'hospital_name' => ['label'  => 'Hospital Name','rules'  => 'required',
+                    'errors' => ['required' => 'Hospital Name is required']
+                ],
+                'hospital_address' => ['label'  => 'Hospital Address','rules'  => 'required',
+                    'errors' => ['required' => 'Hospital Address is required']
+                ],
+                'hospital_city' => ['label'  => 'Hospital City','rules'  => 'required',
+                    'errors' => ['required' => 'City is required']
+                ],
+                'hospital_state' => ['label'  => 'Hospital State','rules'  => 'required',
+                    'errors' => ['required' => 'State is required']
+                ],
+                'hospital_pin_code' => ['label'  => 'Hospital Pincode','rules'  => 'required|numeric|exact_length[6]',
+                    'errors' => ['exact_length' => 'Pincode must be 6 digits']
+                ],
+                'hospital_phone_no' => ['label'  => 'Hospital Phone','rules'  => 'required|numeric|min_length[10]',
+                    'errors' => ['min_length' => 'Phone number too short']
+                ],
+                'doa' => ['label'  => 'DOA', 'rules'  => 'required',
+                    'errors' => ['required' => 'Date of Admission is required']
+                ],
+                'dod' => [ 'label'  => 'DOD', 'rules'  => 'required',
+                    'errors' => ['required' => 'Date of Discharge is required']
+                ],
+                'claim_amount' => [
+                    'label'  => 'Claim Amount',
+                    'rules'  => 'permit_empty|numeric',
+                    'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
+                ],
+                'pod_no' => ['label'  => 'POD No','rules'  => 'permit_empty','errors' => []],
+
+                // --- ADDITIONAL / CONDITIONAL FIELDS ---
+                'claim_number' => ['label'  => 'Claim Number','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
+                'denial_date' => ['label'  => 'Denial Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
+                'approved_amount' => ['label'  => 'Approved Amount','rules'  => 'permit_empty|numeric',
+                    'errors' => ['numeric' => 'Approved amount must be numeric']
+                ],
+                'approved_date' => ['label'  => 'Approved Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
+                'approved_letter' => ['label'  => 'Approved Letter','rules'  => 'permit_empty','errors' => []],
+                'approved_description' => ['label'  => 'Approved Description','rules'  => 'permit_empty','errors' => []],
+                'utr_details' => ['label'  => 'UTR Details','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
+                'settled_date' => ['label'  => 'Settled Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
+                'settle_letter' => ['label'  => 'Settle Letter','rules'  => 'permit_empty','errors' => []],
+                'pay_initiate_date' => ['label'  => 'Payment Initiate Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
+                'cancel_remark' => ['label'  => 'Cancel Remark','rules'  => 'permit_empty','errors' => []],
+                'return_remark' => ['label'  => 'Return Remark','rules'  => 'permit_empty','errors' => []],
+                'awb_no_courier_name' => ['label'  => 'AWB No','rules'  => 'permit_empty','errors' => []],
+                'non_id_reason' => ['label'  => 'Non ID Reason','rules'  => 'permit_empty','errors' => []],
+                'client_id' => ['rules'  => 'required','errors' => ['required' => 'Client ID is required']],
+                'insurer_id' => ['rules'  => 'required','errors' => ['required' => 'Insurer is required'],
+                'si_amt' => [ 'label'  => 'Sum Insured', 'rules'  => 'permit_empty|numeric', 'errors' => ['numeric' => 'Sum Insured must be a valid number.']],
+                'registration_date' => ['label'  => 'Registration Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Registration Date must be dd/mm/yyyy.']],
+                'denial_reason' => ['label'  => 'Denial Reason', 'rules'  => 'permit_empty|string','errors' => ['string' => 'Invalid characters in Denial Reason.']],
+                ],
+            ];   
+        } else if ($selected_ticket_type == 8) {
+            $rules = [
+                // --- GENERAL DETAILS ---
+                'client_name' => ['label'  => 'Client Name','rules'  => 'required|is_not_list[0]',
+                    'errors' => [
+                        'required'    => 'Please select a Client Name.',
+                        'is_not_list' => 'Please select a valid Client from the list.'
+                    ]
+                ],
+                'vehicle_id' => ['label'  => 'Vehicle',
+                    'rules'  => 'required',
+                    'errors' => [
+                        'required'    => 'Vehicle selection is required.'
+                    ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required',
+                    'errors' => ['required'    => 'Please select a Policy.',]
+                ],
+                'insurer_id' => ['label'  => 'Insurer ID','rules'  => 'required',
+                    'errors' => ['required' => 'Insurer data is missing. Please re-select the policy.']
+                ],
+                'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                // --- CLAIM DETAILS ---
+                'ticket_type_id' => ['label'  => 'Policy Type','rules'  => 'required','errors' => ['required' => 'Policy Type is required.']],
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required','errors' => ['required'    => 'Status is required.']],
+
+                // --- HIDDEN KEYS (Integrity Checks) ---
+                'ticket_master_id' => ['label'  => 'Ticket Master ID','rules'  => 'permit_empty','errors' => []],
+                'client_id' => ['label'  => 'Client ID','rules'  => 'required','errors' => ['required' => 'System error: Client ID is missing.']]
+            ];
+        } else {
+            $rules = [
+                // --- EMPLOYEE DETAILS ---
+                'emp_code' => [
+                    'label'  => 'Employee Code',
+                    'rules'  => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Code is required.',
+                        'regex_match' => 'Employee Code cannot contain spaces or special characters (only letters, numbers, - and _ allowed).',
+                    ]
+                ],
+                'emp_name' => [
+                    'label'  => 'Employee Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Name is required',
+                        'min_length'  => 'Employee Name must be at least 3 characters long',
+                        'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                 'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'emp_personal_mail' => ['label'  => 'Personal Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Personal Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required','errors' => ['required' => 'Please select a Policy']],
+                'acm_id' => ['label'  => 'Account Manager','rules'  => 'required','errors' => ['required' => 'Account Manager must be selected']],
+
+                // --- CLAIM DETAILS ---
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required','errors' => ['required' => 'Claim status is required']],
+                'claim_type' => ['label'  => 'Claim Type','rules'  => 'required','errors' => ['required' => 'Claim type is required']],
+                'dob' => ['label'  => 'Date of Birth','rules'  => 'required','errors' => ['required' => 'Date of Birth is required']],
+                'date_of_intimat' => ['label'  => 'Date of Intimation','rules'  => 'required','errors' => ['required' => 'Intimation date is required']],
+                'si_amt' => ['label'  => 'Sum Insured','rules'  => 'required|numeric','errors' => ['numeric' => 'Sum insured must be a number']],
+                'approved_amount' => ['label'  => 'Approved Amount','rules'  => 'permit_empty|numeric','errors' => ['numeric' => 'Approved amount must be numeric']],
+                'approved_date' => ['label'  => 'Approved Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
+                'approved_letter' => ['label'  => 'Approved Letter','rules'  => 'permit_empty','errors' => []],
+                'approved_description' => ['label'  => 'Approved Description','rules'  => 'permit_empty','errors' => []],
+                'utr_details' => ['label'  => 'UTR Details','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
+                'settled_date' => ['label'  => 'Settled Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
+                'settle_letter' => ['label'  => 'Settle Letter','rules'  => 'permit_empty','errors' => []],
+                'pay_initiate_date' => ['label'  => 'Payment Initiate Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
+                'cancel_remark' => ['label'  => 'Cancel Remark','rules'  => 'permit_empty','errors' => []],
+                'return_remark' => ['label'  => 'Return Remark','rules'  => 'permit_empty','errors' => []],
+            ];
+        }
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $request_data = $this->request->getPost();
+        $sanitized_data = sanitizeInputArrayAdvanced($request_data);
+        $ticket_data = $this->formatDateForClaim($sanitized_data);
         // $ticket_data = $this->getLastMatchedStatus($ticket_data, );
         // print_rr($ticket_data); die;
 
+        $isduplicate = checkDuplicateClaim([
+            'doa' => change_date_format($ticket_data['doa'] ?? '') ?? null, 
+            'emp_code' => $ticket_data['emp_code'] ?? null, 
+            'claim_amount' => $ticket_data['claim_amount'] ?? null, 
+            'policy_no' => $ticket_data['policy_no'] ?? null
+        ]);
+
+        if ($isduplicate) {
+
+            $errorDetails = [];
+
+            if (!empty($ticket_data['doa'])) {
+                $errorDetails[] = 'DOA: ' . change_date_format($ticket_data['doa']);
+            }
+
+            if (!empty($ticket_data['claim_amount'])) {
+                $errorDetails[] = 'Claim Amount: ₹' . number_format($ticket_data['claim_amount'], 2);
+            }
+
+            if (!empty($ticket_data['emp_code'])) {
+                $errorDetails[] = 'Emp Code: ' . $ticket_data['emp_code'];
+            }
+
+            if (!empty($ticket_data['policy_no'])) {
+                $errorDetails[] = 'Policy No: ' . $ticket_data['policy_no'];
+            }
+
+            $response = [
+                'status'  => false,
+                'code'    => 409,
+                'message' => 'Duplicate claim found for ' . implode(', ', $errorDetails)
+            ];
+
+            return $this->respond($response, 200);
+        }
+
+
         if ($ticket_data) {
             $return_value = $this->ticketMasterModel->insert($ticket_data);
             if ($return_value) {
@@ -1143,9 +1447,267 @@ class TicketController extends BaseController
 
     public function updateTicket()
     {
+        
+        $request_data = $this->request->getPost();
+        $selected_ticket_type = $this->request->getPost('ticket_type_id');
+
+        // 1. Initialize an empty rules array
+        $rules = [];
+        if ($selected_ticket_type == 1 || $selected_ticket_type == 72) {
+            $rules = [
+                // --- EMPLOYEE DETAILS ---
+                'emp_code' => [
+                    'label'  => 'Employee Code',
+                    'rules'  => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Code is required.',
+                        'regex_match' => 'Employee Code cannot contain spaces or special characters (only letters, numbers, - and _ allowed).',
+                    ]
+                ],
+                'emp_name' => [
+                    'label'  => 'Employee Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Name is required',
+                        'min_length'  => 'Employee Name must be at least 3 characters long',
+                        'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                'insured_name' => [
+                    'label'  => 'Insured Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Insured Name is required',
+                        'min_length'  => 'Insured Name must be at least 3 characters long',
+                        'regex_match' => 'Insured Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                'relationship' => ['label'  => 'Relationship','rules'  => 'required',
+                      'errors' => ['required' => 'Please select a Relationship']
+                ],
+                'policy_no' => [
+                    'label'  => 'Policy Number',
+                    // Allows: A-Z, a-z, 0-9, spaces (\s), hyphens (\-), underscores (_), and slashes (\/)
+                    'rules'  => 'permit_empty|min_length[3]|max_length[50]|regex_match[/^[a-zA-Z0-9\s\-\/_]+$/]',
+                    'errors' => [
+                        'min_length'  => 'Policy Number appears too short (min 5 characters).',
+                        'regex_match' => 'Policy Number can only contain letters, numbers, spaces, hyphens(-), underscores(_), and slashes(/).'
+                    ]
+                ],
+                'tpa_no'    => ['label'  => 'TPA ID','rules'  => 'permit_empty','errors' => []],
+                'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'emp_personal_mail' => ['label'  => 'Personal Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Personal Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required',
+                    'errors' => ['required' => 'Please select a Policy']
+                ],
+                'acm_id' => ['label'  => 'Account Manager','rules'  => 'required',
+                    'errors' => ['required' => 'Select an Account Manager']
+                ],
+
+                // --- CLAIM DETAILS ---
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required',
+                    'errors' => ['required' => 'Claim Status is required']
+                ],
+                'priority' => ['label'  => 'Priority','rules'  => 'required',
+                    'errors' => ['required' => 'Priority is required']
+                ],
+                'mode_of_intimation' => ['label'  => 'Mode of Submission','rules'  => 'required',
+                    'errors' => ['required' => 'Mode of Submission is required']
+                ],
+                'claim_type' => ['label'  => 'Claim Type','rules'  => 'required',
+                    'errors' => ['required' => 'Claim Type is required']
+                ],
+                'hospital_name' => ['label'  => 'Hospital Name','rules'  => 'required',
+                    'errors' => ['required' => 'Hospital Name is required']
+                ],
+                'hospital_address' => ['label'  => 'Hospital Address','rules'  => 'required',
+                    'errors' => ['required' => 'Hospital Address is required']
+                ],
+                'hospital_city' => ['label'  => 'Hospital City','rules'  => 'required',
+                    'errors' => ['required' => 'City is required']
+                ],
+                'hospital_state' => ['label'  => 'Hospital State','rules'  => 'required',
+                    'errors' => ['required' => 'State is required']
+                ],
+                'hospital_pin_code' => ['label'  => 'Hospital Pincode','rules'  => 'required|numeric|exact_length[6]',
+                    'errors' => ['exact_length' => 'Pincode must be 6 digits']
+                ],
+                'hospital_phone_no' => ['label'  => 'Hospital Phone','rules'  => 'required|numeric|min_length[10]',
+                    'errors' => ['min_length' => 'Phone number too short']
+                ],
+                'doa' => ['label'  => 'DOA', 'rules'  => 'required',
+                    'errors' => ['required' => 'Date of Admission is required']
+                ],
+                'dod' => [ 'label'  => 'DOD', 'rules'  => 'required',
+                    'errors' => ['required' => 'Date of Discharge is required']
+                ],
+                'claim_amount' => [
+                    'label'  => 'Claim Amount',
+                    'rules'  => 'permit_empty|numeric',
+                    'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
+                ],
+                'pod_no' => ['label'  => 'POD No','rules'  => 'permit_empty','errors' => []],
+
+                // --- ADDITIONAL / CONDITIONAL FIELDS ---
+                'claim_number' => ['label'  => 'Claim Number','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
+                'denial_date' => ['label'  => 'Denial Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
+                'approved_amount' => ['label'  => 'Approved Amount','rules'  => 'permit_empty|numeric',
+                    'errors' => ['numeric' => 'Approved amount must be numeric']
+                ],
+                'approved_date' => ['label'  => 'Approved Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
+                'approved_letter' => ['label'  => 'Approved Letter','rules'  => 'permit_empty','errors' => []],
+                'approved_description' => ['label'  => 'Approved Description','rules'  => 'permit_empty','errors' => []],
+                'utr_details' => ['label'  => 'UTR Details','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
+                'settled_date' => ['label'  => 'Settled Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
+                'settle_letter' => ['label'  => 'Settle Letter','rules'  => 'permit_empty','errors' => []],
+                'pay_initiate_date' => ['label'  => 'Payment Initiate Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
+                'cancel_remark' => ['label'  => 'Cancel Remark','rules'  => 'permit_empty','errors' => []],
+                'return_remark' => ['label'  => 'Return Remark','rules'  => 'permit_empty','errors' => []],
+                'awb_no_courier_name' => ['label'  => 'AWB No','rules'  => 'permit_empty','errors' => []],
+                'non_id_reason' => ['label'  => 'Non ID Reason','rules'  => 'permit_empty','errors' => []],
+                'client_id' => ['rules'  => 'required','errors' => ['required' => 'Client ID is required']],
+                'insurer_id' => ['rules'  => 'required','errors' => ['required' => 'Insurer is required'],
+                'si_amt' => [ 'label'  => 'Sum Insured', 'rules'  => 'permit_empty|numeric', 'errors' => ['numeric' => 'Sum Insured must be a valid number.']],
+                'registration_date' => ['label'  => 'Registration Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Registration Date must be dd/mm/yyyy.']],
+                'denial_reason' => ['label'  => 'Denial Reason', 'rules'  => 'permit_empty|string','errors' => ['string' => 'Invalid characters in Denial Reason.']],
+                ],
+            ];   
+        } else if ($selected_ticket_type == 8) {
+            $rules = [
+                // --- GENERAL DETAILS ---
+                'client_name' => ['label'  => 'Client Name','rules'  => 'required|is_not_list[0]',
+                    'errors' => [
+                        'required'    => 'Please select a Client Name.',
+                        'is_not_list' => 'Please select a valid Client from the list.'
+                    ]
+                ],
+                'vehicle_id' => ['label'  => 'Vehicle',
+                    'rules'  => 'required',
+                    'errors' => [
+                        'required'    => 'Vehicle selection is required.'
+                    ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required',
+                    'errors' => ['required'    => 'Please select a Policy.',]
+                ],
+                'insurer_id' => ['label'  => 'Insurer ID','rules'  => 'required',
+                    'errors' => ['required' => 'Insurer data is missing. Please re-select the policy.']
+                ],
+                'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                // --- CLAIM DETAILS ---
+                'ticket_type_id' => ['label'  => 'Policy Type','rules'  => 'required','errors' => ['required' => 'Policy Type is required.']],
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required','errors' => ['required'    => 'Status is required.']],
+
+                // --- HIDDEN KEYS (Integrity Checks) ---
+                'ticket_master_id' => ['label'  => 'Ticket Master ID','rules'  => 'permit_empty','errors' => []],
+                'client_id' => ['label'  => 'Client ID','rules'  => 'required','errors' => ['required' => 'System error: Client ID is missing.']]
+            ];
+        } else {
+            $rules = [
+                // --- EMPLOYEE DETAILS ---
+                'emp_code' => [
+                    'label'  => 'Employee Code',
+                    'rules'  => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Code is required.',
+                        'regex_match' => 'Employee Code cannot contain spaces or special characters (only letters, numbers, - and _ allowed).',
+                    ]
+                ],
+                'emp_name' => [
+                    'label'  => 'Employee Name',
+                    'rules'  => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]',
+                    'errors' => [
+                        'required'    => 'Employee Name is required',
+                        'min_length'  => 'Employee Name must be at least 3 characters long',
+                        'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.'
+                    ]
+                ],
+                 'emp_mobile' => ['label'  => 'Employee Mobile No','rules'  => 'required|numeric|exact_length[10]',
+                    'errors' => [
+                        'required' => 'Mobile number is required',
+                        'numeric'  => 'Mobile must contain only numbers',
+                        'exact_length' => 'Mobile must be exactly 10 digits'
+                    ]
+                ],
+                'emp_mail' => ['label'  => 'Employee Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Employee Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'emp_personal_mail' => ['label'  => 'Personal Mail ID',
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Personal Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
+                        ]
+                ],
+                'client_policy_id' => ['label'  => 'Policy','rules'  => 'required','errors' => ['required' => 'Please select a Policy']],
+                'acm_id' => ['label'  => 'Account Manager','rules'  => 'required','errors' => ['required' => 'Account Manager must be selected']],
+
+                // --- CLAIM DETAILS ---
+                'claim_status_id' => ['label'  => 'Status','rules'  => 'required','errors' => ['required' => 'Claim status is required']],
+                'claim_type' => ['label'  => 'Claim Type','rules'  => 'required','errors' => ['required' => 'Claim type is required']],
+                'dob' => ['label'  => 'Date of Birth','rules'  => 'required','errors' => ['required' => 'Date of Birth is required']],
+                'date_of_intimat' => ['label'  => 'Date of Intimation','rules'  => 'required','errors' => ['required' => 'Intimation date is required']],
+                'si_amt' => ['label'  => 'Sum Insured','rules'  => 'required|numeric','errors' => ['numeric' => 'Sum insured must be a number']],
+                'approved_amount' => ['label'  => 'Approved Amount','rules'  => 'permit_empty|numeric','errors' => ['numeric' => 'Approved amount must be numeric']],
+                'approved_date' => ['label'  => 'Approved Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
+                'approved_letter' => ['label'  => 'Approved Letter','rules'  => 'permit_empty','errors' => []],
+                'approved_description' => ['label'  => 'Approved Description','rules'  => 'permit_empty','errors' => []],
+                'utr_details' => ['label'  => 'UTR Details','rules'  => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
+                'settled_date' => ['label'  => 'Settled Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
+                'settle_letter' => ['label'  => 'Settle Letter','rules'  => 'permit_empty','errors' => []],
+                'pay_initiate_date' => ['label'  => 'Payment Initiate Date','rules'  => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
+                'cancel_remark' => ['label'  => 'Cancel Remark','rules'  => 'permit_empty','errors' => []],
+                'return_remark' => ['label'  => 'Return Remark','rules'  => 'permit_empty','errors' => []],
+            ];
+        }
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
+        $sanitized_data = sanitizeInputArrayAdvanced($request_data);
+        $ticket_data = $this->formatDateForClaim($sanitized_data);
         $ticket_id = $this->request->getPost('ticket_master_id');
-        $ticket_data = $this->request->getPost();
-        $ticket_data = $this->formatDateForClaim($ticket_data);
         $old_ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first();
         $ticket_data['claim_status_id'] = $this->getLastMatchedStatus($ticket_data, $old_ticket_data);
         // print_rr($ticket_data); die;
@@ -1210,7 +1772,49 @@ class TicketController extends BaseController
     {
         //action 1 is create. action 2 is edit and action 3 is delete
         if ($action == 1) {
-            $received_data = $this->request->getPost();
+                $rules = [
+                    'template_name' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Template Name is required'
+                        ]
+                    ],
+                    'ticket_type' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Policy Type is required'
+                        ]
+                    ],
+                    'trigger_type' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Trigger Type is required'
+                        ]
+                    ],
+                    'subject' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Subject is required'
+                        ]
+                    ],
+                    'mail_content' => [
+                        'rules'  => 'required',
+                        'errors' => [
+                            'required' => 'Mail Content is required'
+                        ]
+                    ]
+                ];
+                 if (!$this->validate($rules)) {
+                    return $this->response->setStatusCode(400)->setJSON([
+                        'status' => false,
+                        'message' => 'Input validation failed',
+                        'code' => 400,
+                        'errors' => $this->validator->getErrors()
+                    ]);
+                }
+                $data   = $this->request->getPost();
+                $received_data = sanitizeInputArrayAdvanced($data);
+
             if (isset($received_data['id']) && $received_data['id'] != '') {
                 $status = $this->ticketMailTemplateModel->save($received_data);
                 if ($status) {
@@ -1252,8 +1856,9 @@ class TicketController extends BaseController
         } elseif ($action == 3) {
             $table_id = (int)$this->request->getPost('id');
 
-            $sql = "update ticket_mail_template set is_active = 0 where id = $table_id ";
-            $status = $this->ticketMailTemplateModel->query($sql);
+            $sql = "update ticket_mail_template set is_active = 0 where id = :table_id: ";
+            $binds = ['table_id'=>$table_id];
+            $status = $this->ticketMailTemplateModel->query($sql,$binds);
             if ($status) {
                 return $this->respond(['status' => true], 200);
             } else {
@@ -1278,12 +1883,34 @@ class TicketController extends BaseController
                 }
             }
         } elseif ($action == 2) {
-            $data = $this->request->getPost();
+            $request_data = $this->request->getPost();
+            
+            $rules = [
+                'note' => [
+                    'label'  => 'Add Notes',
+                    'rules'  => 'required|min_length[3]|max_length[1000]',
+                    'errors' => [
+                        'required' => 'The note field cannot be empty.',
+                        'min_length' => 'The note is too short (minimum 3 characters).',
+                        'max_length' => 'The note cannot exceed 1000 characters.'
+                    ]
+                ],
+            ];
+        
+            if (!$this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status' => false,
+                    'message' => 'Input validation failed',
+                    'code' => 400,
+                    'errors' => $this->validator->getErrors()
+                ]);
+            }
+
+            $data = sanitizeInputArrayAdvanced($request_data);
             // if (isset($data['id']) && $data['id'] != ''){
             // print_rr($data);die();
             $status = $this->ticketNoteModel->save($data);
             $id = isset($data['id']) ? $data['id'] : $this->ticketNoteModel->insertID();
-
             // }
             if ($status) {
                 return $this->respond(['status' => true, 'id' => $id], 200);
@@ -1297,6 +1924,33 @@ class TicketController extends BaseController
     {
 
         $received_data = $this->request->getPost();
+        $rules =  [
+            'emp_mail' => [
+                'label'  => 'To',
+                'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                'errors' => [
+                    'required'    => 'Email address is required.',
+                    'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                ]
+            ],
+            'mail_subject' => [
+                'label'  => 'Subject',
+                'rules'  => 'required|min_length[5]',
+                'errors' => [
+                    'required' => 'Please enter a Subject for the mail.',
+                    'min_length' => 'Subject should be at least 5 characters long.'
+                ]
+            ]
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
         // print_r($received_data); die;
         $received_data['sender'] = 'staff';
         $status = $this->ticketMessageModel->insert($received_data);
@@ -1734,11 +2388,12 @@ class TicketController extends BaseController
             
         
         WHERE 
-            th.ticket_id = $ticket_id
+            th.ticket_id = :ticket_id:
             AND th.is_active = 1
         ORDER BY 
             th.created_at DESC";
-        $data = $this->ticketHistoryModel->query($sql)->getResultArray();
+        $binds = ['ticket_id'=>(int)$ticket_id];
+        $data = $this->ticketHistoryModel->query($sql,$binds)->getResultArray();
         // dd(db_connect()->getLastQuery());
         $priorityType = $this->priorityType;
         $relationshipType = $this->relationshipType;
@@ -2446,7 +3101,45 @@ class TicketController extends BaseController
     public function upload_url()
     {
 
-        $data = $this->request->getPost();
+        
+        $request_data   = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($request_data);
+
+        $rules = [
+                'ticket_id_url' => [
+                    'label'  => 'Ticket ID URL',
+                    'rules'  => 'required|numeric',
+                    'errors' => [
+                        'required' => 'System could not identify the Number.',
+                        'numeric'  => 'Invalid format.'
+                    ]
+                ],
+                'docs_name.*' => [
+                    'label'  => 'Required Documents',
+                    'rules'  => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]',
+                    'errors' => [
+                        'required'    => 'Document Name is required',
+                        'regex_match' => 'Document Name can only contain letters, numbers, hyphens, and underscores'
+                    ]
+                ],
+                'url.*' => [
+                    'label'  => 'URL',
+                    'rules'  => 'required',
+                    'errors' => [
+                        'required'    => 'URL is required',
+                    ]
+                ]
+            ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
+
         $get_file_data = $this->request->getFiles('file_upload') ?? [];
         $ticket_id = $data['ticket_id_url'];
 
@@ -2460,7 +3153,7 @@ class TicketController extends BaseController
 
                     $insertData = [
                         'doc_name' => $data['docs_name'][$key] ?? '',
-                        'url'      => $data['url'][$key] ?? '',
+                        'url'      => convertGoogleDriveToDownloadLink($data['url'][$key] ?? ''),
                         'ticket_id' => $data['ticket_id_url'] ?? '',
                         'created_by' => get_session_userid(),
                         'is_active'  => 1,
@@ -2769,8 +3462,10 @@ class TicketController extends BaseController
 
     public function claimDumpUpload()
     {   
-        $data['tab_name'] = "Claim Dupm Upload";
+        $data['tab_name'] = "Claim Dump Upload";
         $data['page_name'] = "Claims";
+        $data['tpa_list'] = $this->TPAModel->where('is_active', 1)->findAll();
+
 
         if($this->request->is('get')){
 
@@ -2835,17 +3530,25 @@ class TicketController extends BaseController
             $status = 'inprogress';
             $insert_data = [
                 'file_name' => $filename, 
-                'status' => $status
+                'status' => $status,
+                'client_id' => !empty($this->request->getPost('client_id')) ? $this->request->getPost('client_id') : null,
+                'client_policy_id' => !empty($this->request->getPost('client_policy_id')) ? $this->request->getPost('client_policy_id') : null,
+                'tpa_id' => !empty($this->request->getPost('tpa_id')) ? $this->request->getPost('tpa_id') : null,
             ];
             
             $file_id = $this->claimDumpFileModel->insert($insert_data); 
             $this->myLogger->logme("error", 'claim_dumb_file_id : {file_id}, uploaded success', ['file_id' => $file_id]);
 
             //after file upload success than call the file formate validation in service controller
-            $ticketServiceController = new TicketServiceController();
-            // $response = $ticketServiceController->claimDumpExcelFileFormatValidation(["file_id" => $file_id]);
-            $r = Jobs::addJob(['job_name' => 'claimDumpExcelFileFormatValidation', 'payload' => ['file_id' => $file_id]]);
 
+            // $ticketServiceController = new TicketServiceController();
+            if(!empty($insert_data['tpa_id'])){
+                $r = Jobs::addJob(['job_name' => 'tpaClaimDumpImporter', 'payload' => ['file_id' => $file_id]]);
+                // $response = $ticketServiceController->claimDumpExcelFileFormatValidation(["file_id" => $file_id]);
+            }else{
+                $r = Jobs::addJob(['job_name' => 'claimDumpExcelFileFormatValidation', 'payload' => ['file_id' => $file_id]]);
+                // $response = $ticketServiceController->claimDumpExcelFileFormatValidation(["file_id" => $file_id]);
+            }
             return $this->respond(['status' => true, 'code' => 200, 'message' => 'File uploaded successfully. File being validated'], 200);
 
         }
@@ -2917,12 +3620,194 @@ class TicketController extends BaseController
     }
 
     // -------- END CLAIM DUMP UPLOAD ----------------------------------------------------------------------------------------------
-    
-
     public function saveIRDocsJson()
     {
-        $ticket_id = $this->request->getPost('ticket_id');
-        $required_docs = $this->request->getPost('required_docs');
+        // -----------------------------------------------------
+        // 1) GET INPUT
+        // -----------------------------------------------------
+        $request_data = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($request_data);
+
+        $ticket_id     = $data['ticket_id'] ?? '';
+        $required_docs = $data['required_docs'] ?? '';
+
+        // -----------------------------------------------------
+        // 2) BASIC VALIDATION (CI4)
+        // -----------------------------------------------------
+        $rules = [
+            'ticket_id' => [
+                'label'  => 'Ticket ID',
+                'rules'  => 'required|numeric',
+                'errors' => [
+                    'required' => 'System could not identify the Number.',
+                    'numeric'  => 'Invalid format.'
+                ]
+            ],
+
+            'required_docs' => [
+                'label'  => 'Required Documents',
+                'rules'  => 'required',
+                'errors' => [
+                    'required' => 'Document data is required'
+                ]
+            ]
+        ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status'  => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors'  => $this->validator->getErrors()
+            ]);
+        }
+
+        // -----------------------------------------------------
+        // 3) VALIDATE JSON FORMAT
+        // -----------------------------------------------------
+        $docsArray = json_decode($required_docs, true);
+
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status'  => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors'  => [
+                    'required_docs' => 'Invalid JSON format'
+                ]
+            ]);
+        }
+
+        // -----------------------------------------------------
+        // 4) VALIDATE JSON STRUCTURE
+        // -----------------------------------------------------
+        if (!isset($docsArray['docs']) || !is_array($docsArray['docs'])) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status'  => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors'  => [
+                    'required_docs' => 'Invalid document structure'
+                ]
+            ]);
+        }
+
+        // -----------------------------------------------------
+        // 5) VALIDATE EACH DOCUMENT NAME
+        // -----------------------------------------------------
+        foreach ($docsArray['docs'] as $index => $doc) {
+
+            // Check document_name exists
+            if (!isset($doc['document_name'])) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status'  => false,
+                    'code' => 400,
+                    'message' => 'Input validation failed',
+                    'errors'  => [
+                        'required_docs' =>
+                            "Document name missing at row " . ($index + 1)
+                    ]
+                ]);
+            }
+
+            // YOUR REQUIRED REGEX RULE
+            if (!preg_match('/^[a-zA-Z0-9_\- ]+$/', $doc['document_name'])) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status'  => false,
+                    'code' => 400,
+                    'message' => 'Input validation failed',
+                    'errors'  => [
+                        'required_docs' =>
+                            "Document name '{$doc['document_name']}' can only contain letters, numbers, hyphens and underscores"
+                    ]
+                ]);
+            }
+
+            // Check document_received exists
+            if (!array_key_exists('document_received', $doc)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status'  => false,
+                    'code' => 400,
+                    'message' => 'Input validation failed',
+                    'errors'  => [
+                        'required_docs' =>
+                            "document_received key missing at row " . ($index + 1)
+                    ]
+                ]);
+            }
+        }
+
+        // -----------------------------------------------------
+        // 6) EXTRA SAFETY CHECKS
+        // -----------------------------------------------------
+        if (empty($ticket_id) || empty($required_docs)) {
+            return $this->respond([
+                'status'  => false,
+                'code'    => 404,
+                'message' => 'Failed to save Docs'
+            ], 200);
+        }
+
+        // -----------------------------------------------------
+        // 7) UPDATE DATABASE
+        // -----------------------------------------------------
+        db_connect()->query(
+            "UPDATE ticket_master SET required_docs = ? WHERE id = ?",
+            [$required_docs, $ticket_id]
+        );
+
+        // -----------------------------------------------------
+        // 8) RETURN UPDATED DATA
+        // -----------------------------------------------------
+        $saved = $this->ticketMasterModel
+            ->select('required_docs')
+            ->where('id', $ticket_id)
+            ->first();
+
+        $savedDocs = json_decode($saved['required_docs'] ?? '{}', true) ?? [];
+
+        return $this->respond([
+            'status'  => true,
+            'code'    => 200,
+            'message' => 'IR docs saved successfully',
+            'data'    => $savedDocs
+        ], 200);
+    }
+
+    public function saveIRDocsJson_old()
+    {
+        $request_data   = $this->request->getPost();
+        $data = sanitizeInputArrayAdvanced($request_data);
+        $ticket_id = $data['ticket_id'];
+        $required_docs = $data['required_docs'];
+
+            $rules = [
+                'ticket_id' => [
+                    'label'  => 'Ticket ID',
+                    'rules'  => 'required|numeric',
+                    'errors' => [
+                        'required' => 'System could not identify the Number.',
+                        'numeric'  => 'Invalid format.'
+                    ]
+                ],
+                'required_docs' => [
+                    'label'  => 'Required Documents',
+                    'rules'  => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]',
+                    'errors' => [
+                        'required'    => 'Document Name is required',
+                        'regex_match' => 'Document Name can only contain letters, numbers, hyphens, and underscores'
+                    ]
+                ]
+            ];
+
+        if (!$this->validate($rules)) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status' => false,
+                'message' => 'Input validation failed',
+                'code' => 400,
+                'errors' => $this->validator->getErrors()
+            ]);
+        }
 
         if(empty($ticket_id)){
             return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to save Docs'], 200);
@@ -2943,4 +3828,153 @@ class TicketController extends BaseController
         return $this->respond(['status' => true, 'code' => 200, 'message' => 'IR docs saved successfully', 'data' => $required_docs], 200);
 
     }
+
+
+    // -------- CLAIM MIS UPLOAD ----------------------------------------------------------------------------------------------
+
+    public function claimMisFileList()
+    {   
+        $data['tab_name'] = "Claim MIS Upload";
+        $data['page_name'] = "Cliam MIS Files";
+        $data['claim_mis_file_list'] = $this->claimmisFileModel
+                                ->select('claims_mis_files.*, user_profiles.first_name as user_name')
+                                ->join('user_profiles', 'claims_mis_files.created_by = user_profiles.id')
+                                ->where('claims_mis_files.is_active', 1)
+                                ->orderBy('claims_mis_files.id', 'desc')
+                                ->findAll();
+        $data['tpa_list'] = $this->TPAModel->where('is_active', 1)->findAll();
+
+        return $this->loadLayout('claim_mis_file_list', $data);
+    }
+
+    public function uploadClaimMisFile()
+    {
+
+        $filename = '';
+        $fileSize = '';
+
+            //validate uploaded file
+            $validated = $this->validate([
+                'file' => [
+                    'uploaded[file]',
+                    'mime_in[file,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
+                    'max_size[file,16384]',
+                ],
+            ]);
+            
+            if ($validated) 
+            {
+                    
+                    $file = $this->request->getFile('file');
+                    if (!$file) {
+                        $this->myLogger->logme("error", 'File not found');
+                        return $this->respond(['status' => false, 'code' => 400, 'message' => 'File not found'], 400);
+                    }
+
+                    $is_moved = $file->move(WRITEPATH . 'uploads/claims_mis/');
+                
+                    if ($is_moved) {
+                        $file_path = WRITEPATH.'uploads/claims_mis';
+                        $filename = file_Upload_for_lead($file, $file_path);
+                        $fileSize = $file->getSize(); // File size in bytes
+                        $fileSize = $fileSize / (1024 * 1024); // Convert to MB
+
+                        $this->myLogger->logme("error", 'File move successful');
+                        
+                            $request_data   = $this->request->getPost();
+                            $data = sanitizeInputArrayAdvanced($request_data);
+
+                            if(isset($data['from_date']) && !empty($data['from_date'])){
+                                $data['from_date'] = change_date_format($data['from_date'], 'd/m/Y', 'Y-m-d');
+                            }
+
+                            if(isset($data['to_date']) && !empty($data['to_date'])){
+                                $data['to_date'] = change_date_format($data['to_date'], 'd/m/Y', 'Y-m-d');
+                            }
+                            if(!empty($file_name)){
+                                $data['file_name'] = $file;
+                            }
+
+                            $response = $this->claimmisFileModel->insert($data);
+                        
+                            if($response){
+                                return $this->respond(['status'=>true, 'code'=>200, 'message'=>'MIS file uploaded successfully'], 200);
+                            }else{
+                                return $this->respond(['status'=>true, 'code'=>500, 'message'=>'Failed to upload'], 200);
+                            }
+                        
+                    } else {
+                        $this->myLogger->logme("error", 'File move failed');
+                        return $this->respond(['status' => false, 'code' => 500, 'message' => 'File move failed'], 500);
+                    }
+
+            } else {
+                $this->myLogger->logme("error", 'Upload failed Invalid file');
+                return $this->respond(['status' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
+            }
+
+        /*** OLD CODE KEEP it Safe 
+        $file = $this->request->getFile('file');
+        $data = $this->request->getPost();
+
+        if(isset($data['from_date']) && !empty($data['from_date'])){
+            $data['from_date'] = change_date_format($data['from_date'], 'd/m/Y', 'Y-m-d');
+        }
+
+        if(isset($data['to_date']) && !empty($data['to_date'])){
+            $data['to_date'] = change_date_format($data['to_date'], 'd/m/Y', 'Y-m-d');
+        }
+
+        $file_path = WRITEPATH.'uploads/claims_mis';
+        $file_name = file_Upload_for_lead($file, $file_path);
+
+        if(!empty($file_name)){
+            $data['file_name'] = $file_name;
+        }
+
+        $response = $this->claimmisFileModel->insert($data);
+    
+        if($response){
+            return $this->respond(['status'=>true, 'code'=>200, 'message'=>'MIS file uploaded successfully'], 200);
+        }else{
+            return $this->respond(['status'=>true, 'code'=>500, 'message'=>'Failed to upload'], 200);
+        }
+        ****/
+    }
+
+    public function downloadClaimMisFile()
+    {
+        try {
+
+            $file_id = $this->request->getGet('id');
+
+            // Find record
+            $record = $this->claimmisFileModel->where('id', $file_id)->first();
+            // dd($record);
+
+            if (!$record) {
+                $data['message'] = 'File record not found';
+                return view('errors/404', $data);
+            }
+
+            $uploadPath = WRITEPATH . 'uploads/claims_mis/';
+            $filePath   = $uploadPath . $record['file_name'];
+            // dd($filePath);
+
+            if (!file_exists($filePath)) {
+                $data['message'] = 'The Physical File Not Found';
+                return view('errors/404', $data);
+            }
+
+            // Force file download
+            return $this->response->download($filePath, null)->setFileName($record['file_name']);
+
+        } catch (\Exception $e) {
+            // return $this->failServerError($e->getMessage());
+            $this->myLogger->logme('error', 'Error occoured in downloadClaimMisFile : ' . $e->getMessage());
+            $data['message'] = 'File record not found';
+            return view('errors/404', $data);
+        }
+    }
+
 }
diff --git a/app/Controllers/TicketServiceController.php b/app/Controllers/TicketServiceController.php
index d2265378..f95f547d 100644
--- a/app/Controllers/TicketServiceController.php
+++ b/app/Controllers/TicketServiceController.php
@@ -7,6 +7,14 @@ use CodeIgniter\HTTP\ResponseInterface;
 use CodeIgniter\API\ResponseTrait;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use Kint\Kint;
+use App\Libraries\TpaClaimsImportFactory;
+use App\Libraries\TPAClaimsImportServices\FhplClaimImportService;
+use App\Libraries\TPAClaimsImportServices\MediAssistClaimImportService;
+use App\Libraries\TPAClaimsImportServices\AbhiClaimImportService;
+use App\Libraries\TPAClaimsImportServices\RcareClaimImportService;
+use App\Libraries\TPAClaimsImportServices\VidalClaimImportService;
+use App\Libraries\TPAClaimsImportServices\IciciClaimImportService;
+use App\Libraries\TPAClaimsImportServices\BaseTpaClaimImportService;
 
 use App\Models\ClaimDumpFileModel;
 use App\Models\EmployeeModel;
@@ -15,10 +23,14 @@ use App\Models\TPAModel;
 use App\Models\ClientModel;
 use App\Models\TicketClaimStatusModel;
 use App\Models\ClientPolicyModel;
+use App\Models\ClaimsDumpFhplModel;
 
 use App\Helpers\ExcelSanitizeHelper;
 use App\Models\TicketMasterModel;
 
+use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
 class TicketServiceController extends BaseController
 {
     use ResponseTrait;
@@ -35,6 +47,10 @@ class TicketServiceController extends BaseController
     protected $ticketClaimStatusModel;
     protected $clientPolicyModel;
 
+    protected $medi_assist_primary_key;
+    protected $vidal_primary_key;
+    protected $icici_primary_key;
+
 
     public function __construct()
     {   
@@ -50,6 +66,11 @@ class TicketServiceController extends BaseController
         $this->ticketClaimStatusModel = new TicketClaimStatusModel();
         $this->clientPolicyModel = new ClientPolicyModel();
 
+        $this->medi_assist_primary_key = getenv('MEDI_ASSIST_PRIMARY_KEY_CONSTANT');
+        $this->vidal_primary_key = getenv('VIDAL_PRIMARY_KEY_CONSTANT');
+        $this->icici_primary_key = getenv('ICICI_PRIMARY_KEY_CONSTANT');
+
+
         $this->claim_dump_excel_columns = [
 
             // Mandatory Fields
@@ -1144,7 +1165,7 @@ class TicketServiceController extends BaseController
     {
         //get file name
         $file_id = $params['file_id'];
-        $file = $this->claimDumpFileModel->find($file_id);
+        $file = $this->claimDumpFileModel->find((int)$file_id);
         $this->myLogger->logme('error', "Start the claim dump File Data Validataion with the file id : " . $file_id);
         // dd($file);
 
@@ -1329,7 +1350,7 @@ class TicketServiceController extends BaseController
     {
         //get file name
         $file_id = $params['file_id'];
-        $file = $this->claimDumpFileModel->find($file_id);
+        $file = $this->claimDumpFileModel->find((int)$file_id);
         $this->myLogger->logme('error', "Start the claim dump OnBoard Process with the file id : " . $file_id);
         // dd($file);
 
@@ -1819,5 +1840,176 @@ class TicketServiceController extends BaseController
     {
 
     }
+
+
+    // --------------------------------------------------------------------------------------------------------------------------------
+
+    public function tpaClaimDumpImporter($params)
+    {
+        $file_id = $params['file_id'] ?? null; // Move outside try to ensure catch can see it
+
+        try {
+            $file_path = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR;
+
+            if (!$file_id) {
+                return ['status' => false, 'message' => 'File ID is missing'];
+            }
+
+            $fileData = $this->claimDumpFileModel->find((int)$file_id);
+            if (!$fileData) {
+                return ['status' => false, 'message' => 'Invalid file ID. No file data found'];
+            }
+
+            $file_full_path = $file_path . $fileData['file_name'];
+            if (!is_file($file_full_path)) {
+                return ['status' => false, 'message' => 'Claim dump file not found'];
+            }
+
+            $handler = TpaClaimsImportFactory::make($fileData['tpa_id'] ?? 0);
+            $result  = $handler->runTpaClaimDumpInsert($file_full_path, $file_id);
+
+            if (!empty($result['status']) && $result['status'] === true) {
+                Jobs::addJob(['job_name' => 'tpaClaimDumpToTicketMasterImporters', 'payload' => ['file_id' => $file_id]]);
+            } else {
+                // FORCE FAIL LOGIC
+                $this->markAsFailed($file_id, $result['message'] ?? 'System error contact admin', $fileData['created_by'] ?? null);
+            }
+
+            return $result;
+        } catch (\Throwable $th) {
+            $this->myLogger->logme("error", 'TPA_CLAIM_IMPORTER_JOB : ' . $th->getMessage());
+
+            if (!empty($file_id)) {
+                $this->markAsFailed($file_id, 'System error contact admin');
+            }
+
+            return [
+                'status' => false,
+                'message' => 'TPA Claim dump import failed',
+                'error_data' => $th->getMessage()
+            ];
+        }
+    }
+
+    private function markAsFailed($file_id, $message, $user_id = null)
+    {
+        $reason = json_encode([
+            'error_summary' => [5 => 1],
+            'error_data'    => $message
+        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
+
+        $data = [
+            'status'     => 'failed',
+            'reason'     => $reason,
+            'updated_by' => $user_id
+        ];
+
+        // Using the direct update(id, data) method is often more reliable inside try/catch
+        return $this->claimDumpFileModel->update($file_id, $data);
+    }
+
+    public function tpaClaimDumpToTicketMasterImporters($params)
+    {
+        $file_id = $params['file_id'] ?? null;
+        $fileData = null;
+
+        try {
+            if (!$file_id) {
+                return ['status' => false, 'message' => 'File ID is missing'];
+            }
+
+            $fileData = $this->claimDumpFileModel->where('id', $file_id)->first();
+
+            if (!$fileData) {
+                return ['status' => false, 'message' => 'Invalid file ID. No file data found to import'];
+            }
+
+            $handler = TpaClaimsImportFactory::make($fileData['tpa_id'] ?? 0);
+            $result = $handler->runTicketMasterInsert($params);
+
+            if (!empty($result['status']) && $result['status'] === true) {
+                // Success: Update the status to success
+                $this->claimDumpFileModel->update($file_id, [
+                    'status' => 'success',
+                    'reason' => null
+                ]);
+            } else {
+                // Logic failure: The runTicketMasterInsert returned status false
+                $this->markAsFailed(
+                    $file_id,
+                    $result['message'] ?? 'System error contact admin',
+                    $fileData['created_by'] ?? null
+                );
+            }
+
+            return $result;
+        } catch (\Throwable $th) {
+            // Log the full error
+            $this->myLogger->logme("error", 'TICKET_MASTER_CLAIM_IMPORTER_JOB :' . $th->getMessage() . ' at line ' . $th->getLine());
+
+            // CRITICAL: Even if the code crashes, try to mark the file as failed
+            if ($file_id) {
+                $this->markAsFailed($file_id, 'Fatal Crash: ' . $th->getMessage(), $fileData['created_by'] ?? null);
+            }
+
+            return [
+                'status'  => false,
+                'message' => $th->getMessage(),
+                'error_data' => [
+                    'line' => $th->getLine(),
+                    'file' => $th->getFile()
+                ]
+            ];
+        }
+    }
+
+    public function readExcelBySheetName(string $filePath, string $sheetName): array
+    {
+        if (!file_exists($filePath)) {
+           return [];
+        }
+
+        $spreadsheet = IOFactory::load($filePath);
+
+        // Get sheet by name
+        $sheet = $spreadsheet->getSheetByName($sheetName);
+
+        if ($sheet === null) {
+            return [];
+        }
+
+        $rows = $sheet->toArray(null, true, true, true);
+
+        // Need at least header + one row
+        if (count($rows) < 2) {
+            return [];
+        }
+
+        // First row = headers
+        $headers = array_shift($rows);
+        $headers = array_map('trim', $headers);
+
+        $data = [];
+
+        foreach ($rows as $row) {
+            // Skip completely empty rows
+            if (!array_filter($row)) {
+                continue;
+            }
+
+            $item = [];
+
+            foreach ($headers as $key => $headerName) {
+                if ($headerName !== '') {
+                    $item[$headerName] = $row[$key] ?? null;
+                }
+            }
+
+            $data[] = $item;
+        }
+
+        return $data;
+    }
+
    
 }
diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php
index f6b4cc60..44b44e51 100755
--- a/app/Controllers/UserController.php
+++ b/app/Controllers/UserController.php
@@ -83,7 +83,110 @@ class UserController extends AdminController
             return redirect()->to(base_url('/user/list'));
         } else {
 
-            $userData = $this->request->getPost();
+            // $userData = $this->request->getPost();
+            $data      = $this->request->getPost();
+            $userData  = sanitizeInputArrayAdvanced($data);
+            $rules = [
+
+                // ======================
+                // Nhance Branch
+                // ======================
+                'nhance_branch_id' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'Nhance Branch is required',
+                        'integer'  => 'Invalid Nhance Branch selected'
+                    ]
+                ],
+
+                // ======================
+                // Reporting Manager
+                // ======================
+                'rm_id' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'Reporting Manager is required',
+                        'integer'  => 'Invalid Reporting Manager selected'
+                    ]
+                ],
+
+                // ======================
+                // Employee Code
+                // ======================
+                'emp_code' => [
+                    'rules'  => 'required|min_length[3]|max_length[15]',
+                    'errors' => [
+                        'required'      => 'Employee Code is required',
+                        'min_length'    => 'Employee Code must be at least 3 characters',
+                        'max_length'    => 'Employee Code cannot exceed 15 characters',
+                    ]
+                ],
+
+                // ======================
+                // First Name
+                // ======================
+                'first_name' => [
+                    'rules'  => 'required|alpha_space|min_length[2]|max_length[100]',
+                    'errors' => [
+                        'required'    => 'Name is required',
+                        'alpha_space' => 'Name can contain only letters and spaces',
+                        'min_length'  => 'Name must be at least 2 characters',
+                        'max_length'  => 'Name cannot exceed 100 characters'
+                    ]
+                ],
+
+                // ======================
+                // Email
+                // ======================
+                'email' => [
+                    'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                    'errors' => [
+                        'required'    => 'Email address is required.',
+                        'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                    ]
+                ],
+
+                // ======================
+                // Mobile
+                // ======================
+                'mobile' => [
+                    'rules'  => 'required|regex_match[/^[6-9][0-9]{9}$/]',
+                    'errors' => [
+                        'required'     => 'Mobile number is required',
+                        'regex_match'  => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
+                    ]
+                ],
+
+                // ======================
+                // Role
+                // ======================
+                'role' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'User Role is required',
+                        'integer'  => 'Invalid User Role selected'
+                    ]
+                ],
+
+                // ======================
+                // Team (Multiple select)
+                // ======================
+                'team' => [
+                    'rules'  => 'required',
+                    'errors' => [
+                        'required' => 'At least one User Team must be selected'
+                    ]
+                ],
+            ];
+            if (!$this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status' => false,
+                    'message' => 'Input validation failed',
+                    'code' => 400,
+                    'errors' => $this->validator->getErrors()
+                ]);
+            }
+
             $userData['created_by'] =  get_session_userid();
             $temp_team = $userData['team'];
             unset($userData['team']);
@@ -167,9 +270,112 @@ class UserController extends AdminController
             return redirect()->to(base_url('/user/list'));
         } else {
             // echo ":/ in 163";
-            $id = $this->request->getPost('PrimaryKey');
-            $teams = $this->request->getPost('team');
-            $userData = $this->request->getPost();
+            $rules = [
+
+                // ======================
+                // Nhance Branch
+                // ======================
+                'nhance_branch_id' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'Nhance Branch is required',
+                        'integer'  => 'Invalid Nhance Branch selected'
+                    ]
+                ],
+
+                // ======================
+                // Reporting Manager
+                // ======================
+                'rm_id' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'Reporting Manager is required',
+                        'integer'  => 'Invalid Reporting Manager selected'
+                    ]
+                ],
+
+                // ======================
+                // Employee Code
+                // ======================
+                'emp_code' => [
+                    'rules'  => 'required|min_length[3]|max_length[15]',
+                    'errors' => [
+                        'required'      => 'Employee Code is required',
+                        'min_length'    => 'Employee Code must be at least 3 characters',
+                        'max_length'    => 'Employee Code cannot exceed 15 characters',
+                    ]
+                ],
+
+                // ======================
+                // First Name
+                // ======================
+                'first_name' => [
+                    'rules'  => 'required|alpha_space|min_length[2]|max_length[100]',
+                    'errors' => [
+                        'required'    => 'Name is required',
+                        'alpha_space' => 'Name can contain only letters and spaces',
+                        'min_length'  => 'Name must be at least 2 characters',
+                        'max_length'  => 'Name cannot exceed 100 characters'
+                    ]
+                ],
+
+                // ======================
+                // Email
+                // ======================
+                'email' => [
+                    'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                    'errors' => [
+                        'required'    => 'Email address is required.',
+                        'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                    ]
+                ],
+
+                // ======================
+                // Mobile
+                // ======================
+                'mobile' => [
+                    'rules'  => 'required|regex_match[/^[6-9][0-9]{9}$/]',
+                    'errors' => [
+                        'required'     => 'Mobile number is required',
+                        'regex_match'  => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
+                    ]
+                ],
+
+                // ======================
+                // Role
+                // ======================
+                'role' => [
+                    'rules'  => 'required|integer',
+                    'errors' => [
+                        'required' => 'User Role is required',
+                        'integer'  => 'Invalid User Role selected'
+                    ]
+                ],
+
+                // ======================
+                // Team (Multiple select)
+                // ======================
+                'team' => [
+                    'rules'  => 'required',
+                    'errors' => [
+                        'required' => 'At least one User Team must be selected'
+                    ]
+                ],
+            ];
+            if (!$this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status' => false,
+                    'message' => 'Input validation failed',
+                    'code' => 400,
+                    'errors' => $this->validator->getErrors()
+                ]);
+            }
+            
+            $data      = $this->request->getPost();
+            $userData  = sanitizeInputArrayAdvanced($data);
+            $id = $userData['PrimaryKey'];
+            $teams = $userData['team'];
+            
             unset($userData['csrf_test_name']);
             unset($userData['PrimaryKey']);
 
@@ -594,29 +800,100 @@ class UserController extends AdminController
             
             // add/update
             if ($method === 'post') {
-                $data = $this->request->getPost();
-                $id = !empty($data['PrimaryKey']) ? $data['PrimaryKey'] : null;
+
+                $rules = [
+                    // ======================
+                    // Partner Name
+                    // ======================
+                    'name' => [
+                        'rules'  => 'required|alpha_space|min_length[2]|max_length[100]',
+                        'errors' => [
+                            'required'    => 'Partner name is required',
+                            'alpha_space' => 'Partner name can contain only letters and spaces',
+                            'min_length'  => 'Partner name must be at least 2 characters',
+                            'max_length'  => 'Partner name cannot exceed 100 characters'
+                        ]
+                    ],
+                    // ======================
+                    // Mobile Number
+                    // ======================
+                    'mobile' => [
+                        'rules'  => 'required|regex_match[/^[6-9][0-9]{9}$/]',
+                        'errors' => [
+                            'required'    => 'Mobile number is required',
+                            'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
+                        ]
+                    ],
+
+                    // ======================
+                    // Email
+                    // ======================
+                    'email' => [
+                        'rules'  => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
+                        'errors' => [
+                            'required'    => 'Email address is required.',
+                            'regex_match' => 'Please enter a valid email format (e.g., name@company.com).'
+                        ]
+                    ],
+                    // ======================
+                    // Retention Rate
+                    // ======================
+                     'retention_rate' => [
+        'rules'  => [
+            'required',
+            'regex_match[/^(100(\.0{1,2})?|([0-9]{1,2})(\.[0-9]{1,2})?)$/]'
+        ],
+        'errors' => [
+            'required'    => 'Retention Rate is required',
+            'regex_match' => 'Retention Rate must be between 0 and 100 with up to 2 decimal places'
+        ]
+        ],
+                    // ======================
+                    // Nhance Branch
+                    // ======================
+                    'nhance_branch_id' => [
+                        'rules'  => 'required|integer',
+                        'errors' => [
+                            'required' => 'Nhance Branch is required',
+                            'integer'  => 'Invalid Nhance Branch selected'
+                        ]
+                    ],
+                ];
+                if (! $this->validate($rules)) {
+                return $this->response->setStatusCode(400)->setJSON([
+                    'status'  => false,
+                    'message' => 'Input validation failed',
+                    'code'    => 400,
+                    'errors'  => $this->validator->getErrors()
+                ]);
+            }
+
+
+                $data           = $this->request->getPost();
+                $sanitized_post_data = sanitizeInputArrayAdvanced($data);
+                $id = !empty($sanitized_post_data['PrimaryKey']) ? $sanitized_post_data['PrimaryKey'] : null;
+                unset($sanitized_post_data['pk']); 
                 
                 // don't forgot same means just unset the key because partner_staff some UNIQUE KEY sets in table thats why
                 if ($id) {
-                    $existing = $this->partnerStaffModel->find($id);
+                    $existing = $this->partnerStaffModel->find((int)$id);
                     if ($existing) {
-                        if ($data['email'] === $existing['email']) { unset($data['email']); }
-                        if ($data['mobile'] === $existing['mobile']) { unset($data['mobile']); }
+                        if ($sanitized_post_data['email'] === $existing['email']) { unset($sanitized_post_data['email']); }
+                        if ($sanitized_post_data['mobile'] === $existing['mobile']) { unset($sanitized_post_data['mobile']); }
                     }
                 }
 
                 $errors = [];
 
                 // Check Email Duplicate (if it wasn't unset)
-                if (isset($data['email'])) {
-                    $count = $this->partnerStaffModel->where('email', $data['email'])->countAllResults();
+                if (isset($sanitized_post_data['email'])) {
+                    $count = $this->partnerStaffModel->where('email', $sanitized_post_data['email'])->countAllResults();
                     if ($count > 0) $errors['email'] = "This email is already taken by another user.";
                 }
 
                 // Check Mobile Duplicate (if it wasn't unset)
-                if (isset($data['mobile'])) {
-                    $count = $this->partnerStaffModel->where('mobile', $data['mobile'])->countAllResults();
+                if (isset($sanitized_post_data['mobile'])) {
+                    $count = $this->partnerStaffModel->where('mobile', $sanitized_post_data['mobile'])->countAllResults();
                     if ($count > 0) $errors['mobile'] = "This mobile is already taken by another user.";
                 }
 
@@ -632,14 +909,14 @@ class UserController extends AdminController
                 // --- Save/Update ---
                 if ($id) {
                     $text   = "update";            
-                    $data['updated_by'] =  get_session_userid();
+                    $sanitized_post_data['updated_by'] =  get_session_userid();
                     
-                    $result = $this->partnerStaffModel->update($id, $data);
+                    $result = $this->partnerStaffModel->update($id, $sanitized_post_data);
                 } else {
                     $text = "create";
-                    $data['role_id'] = 1;
-                    $data['created_by'] = get_session_userid();
-                    $id = $this->partnerStaffModel->insert($data);
+                    $sanitized_post_data['role_id'] = 1;
+                    $sanitized_post_data['created_by'] = get_session_userid();
+                    $id = $this->partnerStaffModel->insert($sanitized_post_data);
                     if($id){
                         $details['manager_id'] = $id;
                         $details['updated_by'] =  get_session_userid();
@@ -667,7 +944,7 @@ class UserController extends AdminController
                         return $this->response->setJSON(['status'  => 'error', 'message' => 'ID is required'])->setStatusCode(400);
                     }
 
-                    $staff = $this->partnerStaffModel->find($id);
+                    $staff = $this->partnerStaffModel->find((int)$id);
                     if (!$staff) {
                         return $this->response->setJSON(['status'  => 'error','message' => 'Staff not found'])->setStatusCode(404);
                     }
@@ -783,7 +1060,7 @@ class UserController extends AdminController
                         return $this->response->setJSON(['status'  => 'error', 'message' => 'ID is required'])->setStatusCode(400);
                     }
 
-                    $files = $this->partnerManagerIncentiveFileModel->find($id);
+                    $files = $this->partnerManagerIncentiveFileModel->find((int)$id);
                     if (!$files) {
                         return $this->response->setJSON(['status'  => 'error','message' => 'No Records found'])->setStatusCode(404);
                     }
diff --git a/app/Controllers/VidalApiController.php b/app/Controllers/VidalApiController.php
index 27a68399..eac51552 100644
--- a/app/Controllers/VidalApiController.php
+++ b/app/Controllers/VidalApiController.php
@@ -29,7 +29,7 @@ class VidalApiController extends BaseController
         $apiUrl    = getenv('VIDAL_API_BASE_URL').'/files/upload-url';
         $subscriptionKey = getenv('VIDAL_API_SUBSCRIPTION_KEY'); 
 
-        log_message('error', "Starting file upload process for filename: $filename | Path: $filePath");
+        log_message('error', "TPA CLAIM PUSH  | Starting file upload process for filename: $filename | Path: $filePath");
 
         // Step 1: Get signed URL from Vidal API
         // $filePath = '/opt/lampp/htdocs/nhance/writable/uploads/claim_files/1760013019_73d94af7b96ddc2e3d51.png';
@@ -39,7 +39,7 @@ class VidalApiController extends BaseController
             "Ocp-Apim-Subscription-Key: $subscriptionKey"
         ];
 
-        log_message('error', "Requesting signed URL from Vidal API: $apiUrl | Payload: $payload");
+        log_message('error', "TPA CLAIM PUSH  | Requesting signed URL from Vidal API: $apiUrl | Payload: $payload");
 
         $ch = curl_init($apiUrl);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@@ -57,7 +57,7 @@ class VidalApiController extends BaseController
         $responseData = json_decode($response, true);
 
         if (!isset($responseData['data']['signedUrl']) || !isset($responseData['data']['fileId'])) {
-            log_message('error', "Invalid signed URL response received: " . json_encode($responseData));
+            log_message('error', "TPA CLAIM PUSH  | Invalid signed URL response received: " . json_encode($responseData));
             return ["status" => false, "message" => "Invalid signed URL response", "response" => $responseData];
         }
 
@@ -88,12 +88,12 @@ class VidalApiController extends BaseController
         curl_close($ch2);
 
          if ($curlErr) {
-            log_message('error', "Curl error during file upload: $curlErr");
+            log_message('error', "TPA CLAIM PUSH  | Curl error during file upload: $curlErr");
             return ["status" => false, "message" => "File upload failed", "data" => $curlErr];
         }
 
         if ($httpCode !== 200 && $httpCode !== 201) {
-            log_message('error', "File upload failed with HTTP Code: $httpCode | Response: $uploadResponse");
+            log_message('error', "TPA CLAIM PUSH  | File upload failed with HTTP Code: $httpCode | Response: $uploadResponse");
             return [
                 "status"   => false,
                 "message"  => "File upload failed",
@@ -112,7 +112,7 @@ class VidalApiController extends BaseController
         ];
     }
 
-    public function  SubmitClaim ($claimId = 515)
+    public function  SubmitClaim ($claimId = null) //515
     {
         helper('api');
 
@@ -153,7 +153,7 @@ class VidalApiController extends BaseController
             ->getRowArray(); // single record
 
         if (count($data) && $data['filePath'] == null) {
-            log_message('error', "Submit claim failed - Claim or File Missing");
+            log_message('error', "TPA CLAIM PUSH  | Submit claim failed - Claim or File Missing");
             return $this->response->setJSON(['status' => false,'message' => 'Claim or File Missing', ]);
         }
 
@@ -166,7 +166,7 @@ class VidalApiController extends BaseController
        
         $upload = $this->uploadFileToVidal($filePath,$filename);
         if ($upload['status'] !== true) {
-            log_message('error', "Submit claim failed - File upload failed");
+            log_message('error', "TPA CLAIM PUSH  | Submit claim failed - File upload failed");
             return $this->response->setJSON([
                 'status' => false,
                 'message' => 'File upload failed',
@@ -244,6 +244,9 @@ class VidalApiController extends BaseController
 
         if($response['status'] != true){
             log_message('error', 'TPA CLAIM PUSH FAILED | claimId: '.$claimId.' | response: '.json_encode($response));
+            $this->db->table('ticket_master')
+            ->where('id',$claimId)
+            ->update([ 'tpa_push_response' => json_encode($response) ]);
             return;
         }
 
@@ -251,8 +254,8 @@ class VidalApiController extends BaseController
 
         if($response['data']['status'] == 'SUCCESS')
         {
-            $claimNO = $response['data']['data']['claimNO'] ?? null;
-            $claimInwardNO = $response['data']['data']['claimInwardNO'] ?? null;
+            $claimNO = $response['data']['data']['claimNo'] ?? null;
+            $claimInwardNO = $response['data']['data']['claimInwardNo'] ?? null;
 
             if(!empty($claimNO) && !empty($claimInwardNO)){
 
@@ -260,7 +263,7 @@ class VidalApiController extends BaseController
 
                 $this->db->table('ticket_master')
                 ->where('id',$claimId)
-                ->update([ 'tpa_claim_push_reference_no' => $claimInwardNO , 'tpa_claim_id' => $claimNO ]);
+                ->update([ 'tpa_claim_push_reference_no' => $claimInwardNO , 'tpa_claim_id' => $claimNO , 'claim_number' => $claimNO ]);
 
                 return;
 
@@ -439,7 +442,7 @@ class VidalApiController extends BaseController
                         ->getRowArray();
 
         if (!$ticket) {
-            return $this->response->setJSON(['status' => false,'message' => 'Invalid Claim ID' ]);
+            return ['status' => false,'message' => 'Invalid Claim ID' ];
         }
 
         $body = [];
@@ -464,11 +467,7 @@ class VidalApiController extends BaseController
         if ($response['status'] != true || empty($response['data']['data']['claims'][0])) {
             log_message('error', 'CLAIM STATUS FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response));
 
-            return $this->response->setJSON([
-                'status'  => false,
-                'message' => 'API call failed.',
-                'data'    => $response
-            ]);
+            return ['status'  => false, 'message' => 'API call failed.','data'    => $response];
         }
 
         // Extract claim status
@@ -501,12 +500,7 @@ class VidalApiController extends BaseController
         // LOG UPDATE
         log_message('error', "CLAIM STATUS SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
 
-        return $this->response->setJSON([
-            'status' => true,
-            'message' => 'Claim status updated.',
-            'updated_status' => $currentStatus,
-            'api_response' => $response
-        ]);
+        return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response];
     }
 
     public function ClaimStatusUpdate()
@@ -801,7 +795,7 @@ class VidalApiController extends BaseController
 
             //call a job for dump JSON data to DB
             $job_details  = new Jobs();
-            $r = Jobs::addJob(['job_name' => 'saveMediAssitAPIData', 'payload' => [ 'file_id' => $requestData['file_id'],'json_file_path'    => $filePath ]]);
+            $r = Jobs::addJob(['job_name' => 'saveVidalAPIData', 'payload' => [ 'file_id' => $requestData['file_id'],'json_file_path'    => $filePath ]]);
 
 
             // now update DB
@@ -842,9 +836,9 @@ class VidalApiController extends BaseController
                         // log_message('error', "✅ Match found: emp_code={$row['empNo']} policy={$row['policyNumber']}");
 
                         $sql = "UPDATE employee_polices 
-                                SET tpa_id = ? 
-                                WHERE id = ?";
-                        $this->db->query($sql, [$row['enrollmentId'], $policy_data['emp_policy_id']]);
+                                SET tpa_id = :tpa_id:,
+                                WHERE id = :emp_policy_id:";
+                        $this->db->query($sql, ["tpa_id"=>$row['enrollmentId'], "emp_policy_id"=>$policy_data['emp_policy_id']]);
 
                         // for e-card send
                         if(strtolower(trim($policy_data['relationship'])) == 'self'){
diff --git a/app/Filters/AclFilter.php b/app/Filters/AclFilter.php
new file mode 100644
index 00000000..c3b052b8
--- /dev/null
+++ b/app/Filters/AclFilter.php
@@ -0,0 +1,156 @@
+getPath(), '/');
+
+        // Base path: /PHP828APPS/ruc/nhance
+        $basePath = rtrim(parse_url(base_url(), PHP_URL_PATH), '/');
+
+        // Remove base path
+        if ($basePath && str_starts_with($fullPath, $basePath)) {
+            $path = substr($fullPath, strlen($basePath));
+        } else {
+            $path = $fullPath;
+        }
+
+        // Remove index.php if present
+        if (str_starts_with($path, '/index.php')) {
+            $path = substr($path, strlen('/index.php'));
+        }
+
+        // Normalize
+        $path = '/' . ltrim($path, '/');
+
+        // Fallback
+        if ($path === '') {
+            $path = '/';
+        }
+
+        // echo'
BASH PATH: ' . base_url(); + // echo'
ACL RAW PATH: ' . $fullPath; + // echo'
ACL BASE PATH: ' . $basePath; + // echo'
ACL FINAL PATH: ' . $path; + // ===================== LOAD ACL ===================== + $acl = new Acl(); + $rules = $acl->rules; + // print_rr($rules);die; + // ===================== MATCH RULE ===================== + $matchedRule = null; + + foreach ($rules as $pattern => $rule) { + // echo "$pattern".'---------
'; + if (preg_match($pattern, $path)) { + // echo "matched - $pattern"; + $matchedRule = $rule; + break; // FIRST MATCH WINS + } + } + + // print_r($matchedRule);//die; + // ===================== NO RULE = DENY ===================== + if ($matchedRule === null) { + return $this->deny($path, 'No ACL rule matched'); + } + + // ===================== PUBLIC ROUTE ===================== + if (!empty($matchedRule['public'])) { + return; // ALLOW + } + + // ===================== AUTH CHECK ===================== + if (!check_session()) { + // For API requests return 401 JSON + if ($request->isAJAX() || str_starts_with($path, '/api') || str_starts_with($path, '/employeeRest')) { + return service('response') + ->setStatusCode(401) + ->setJSON(['error' => 'Unauthorized Resource Access']); + } + + // For web redirect to login + return AuthLogout::logout(); + } + + // ===================== GET USER CONTEXT ===================== + + $userRole = check_role(); // + $userTeams = user_team(); // must return array of TEAM IDs + + $allowedRoles = $matchedRule['roles'] ?? []; + $allowedTeams = $matchedRule['teams'] ?? []; + // ===================== ROLE FIRST ===================== + if (!empty($allowedRoles) && in_array((int)$userRole, $allowedRoles, true)) { + return; // ALLOW + } + // ===================== TEAM FALLBACK ===================== + if (!empty($allowedTeams) && is_array($userTeams)) { + foreach ($userTeams as $teamId) { + if (in_array($teamId, $allowedTeams, true)) { + return; // ALLOW + } + } + } + // ===================== DENY ===================== + return $this->deny($path, 'Role/Team not permitted'); + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + // nothing + } + + // ===================== DENY HANDLER ===================== + protected function deny(string $path, string $reason) + { + log_message('error', 'ACL BLOCKED: {user} {path} - {reason}', [ + 'user' => session()->get('userid') ?? 'guest', + 'path' => $path, + 'reason' => $reason, + ]); + + // API / AJAX → JSON + $request = service('request'); + if ($request->isAJAX() || str_starts_with($path, '/api') || str_starts_with($path, '/employeeRest')) { + return service('response') + ->setStatusCode(403) + ->setJSON([ + 'error' => 'Forbidden', + 'message' => 'You do not have permission to access this resource' + ]); + } + + $response = service('response'); + $response->setStatusCode(403); + $response->setBody(view('errors/404', [ + 'message' => '403 Access denied - You do not have permission to access this resource' + ])); + + return $response; + // Web → nice 403 page or simple text + return service('response') + ->setStatusCode(403) + ->setBody('403 Forbidden - Access denied - You do not have permission to access this resource'); + } +} diff --git a/app/Filters/AuthJWT.php b/app/Filters/AuthJWT.php index 484372a7..ec34d902 100755 --- a/app/Filters/AuthJWT.php +++ b/app/Filters/AuthJWT.php @@ -18,67 +18,120 @@ use App\Models\LevelContactModel; class AuthJWT implements FilterInterface { + // public function before(RequestInterface $request, $arguments = null) + // { + // $jwt = $request->getHeader('Authorization'); + + // if ($jwt) { + // if (JWTToken::validateJWT($jwt)) { + // $data = JWTToken::validateJWT($jwt); + // $data = json_decode($data); + + // $id = $data->decoded->id; + // if(isset($data->decoded->emp_code)){ + // $model = new EmployeeModel(); + // $user_data = $model->where('id', $id)->first(); + + // if($user_data['token_time_out'] > time()){ + // $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; + // $model->update($id, $data); + // return true; + // }else{ + + // // if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL) + + // $data =["token_time_out" => '']; + // $model->update($id, $data); + // header('Content-Type: application/json'); + // http_response_code(401); + // // $error = json_encode(["status" => 401, "message" => $data->message]); + // $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); + // echo $error; + // exit; + // } + // }else{ + // $model = new LevelContactModel(); + // $hr_data = $model->where('id', $id)->first(); + + // if($hr_data['token_time_out'] > time()){ + // $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; + // $model->update($id, $data); + // return true; + // }else{ + // $data =["token_time_out" => '']; + // $model->update($id, $data); + // header('Content-Type: application/json'); + // http_response_code(401); + // // $error = json_encode(["status" => 401, "message" => $data->message]); + // $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); + // echo $error; + // exit; + // } + // } + + // } + // } else { + // header('Content-Type: application/json'); + // http_response_code(403); + // $error = json_encode(["status" => 403, "message" => "Access Forbidden!"]); + // echo $error; + // exit(); + // } + // } + public function before(RequestInterface $request, $arguments = null) { - $jwt = $request->getHeader('Authorization'); + $authHeader = $request->getHeaderLine('Authorization'); - if ($jwt) { - if (JWTToken::validateJWT($jwt)) { - $data = JWTToken::validateJWT($jwt); - $data = json_decode($data); - - $id = $data->decoded->id; - if(isset($data->decoded->emp_code)){ - $model = new EmployeeModel(); - $user_data = $model->where('id', $id)->first(); - - if($user_data['token_time_out'] > time()){ - $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; - $model->update($id, $data); - return true; - }else{ - - // if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL) - - $data =["token_time_out" => '']; - $model->update($id, $data); - header('Content-Type: application/json'); - http_response_code(401); - // $error = json_encode(["status" => 401, "message" => $data->message]); - $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); - echo $error; - exit; - } - }else{ - $model = new LevelContactModel(); - $hr_data = $model->where('id', $id)->first(); - - if($hr_data['token_time_out'] > time()){ - $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; - $model->update($id, $data); - return true; - }else{ - $data =["token_time_out" => '']; - $model->update($id, $data); - header('Content-Type: application/json'); - http_response_code(401); - // $error = json_encode(["status" => 401, "message" => $data->message]); - $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); - echo $error; - exit; - } - } - - } - } else { - header('Content-Type: application/json'); - http_response_code(403); - $error = json_encode(["status" => 403, "message" => "Access Forbidden!"]); - echo $error; - exit(); + if (!$authHeader) { + return $this->reject(403, 'Access Forbidden'); } + + $result = JWTToken::validateJWT($authHeader); + + if ($result['status'] !== true) { + return $this->reject(401, $result['message']); + } + + $decoded = $result['decoded']; + $id = $decoded['id'] ?? null; + + if (!$id) { + return $this->reject(401, 'Invalid token payload'); + } + + if (isset($decoded['emp_code'])) { + $model = new EmployeeModel(); + } else { + $model = new LevelContactModel(); + $id = $decoded['post_hr_id'] ?? null; + } + + $user = $model->find($id); + + if (!$user || $user['token_time_out'] <= time()) { + $model->update($id, ['token_time_out' => null]); + return $this->reject(401, 'Token expired'); + } + + // Refresh sliding expiration + $model->update($id, [ + 'token_time_out' => time() + getenv('TOKENTIMEOUT') + ]); + + return true; } + private function reject(int $code, string $message) + { + return service('response') + ->setStatusCode($code) + ->setJSON(['status' => $code, 'message' => $message]) + ->send(); + } + + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // Do something here after the response is sent diff --git a/app/Filters/AuthMVC.php b/app/Filters/AuthMVC.php index 1e0be969..ef2da046 100755 --- a/app/Filters/AuthMVC.php +++ b/app/Filters/AuthMVC.php @@ -5,13 +5,28 @@ use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; +use App\Libraries\AuthLogout; + class AuthMVC implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { - if (!check_session() && !check_cookie()) { - - return redirect()->to(base_url('/login')); + + if (!check_session()) + { + return AuthLogout::logout(); + } + + // if (!check_cookie()) + // { + // return AuthLogout::logout(); + // } + + // Fingerprint validation + $fp = generateFingerprint(); + // log_message('error',$fp); + if (session()->get('fingerprint') !== $fp) { + return AuthLogout::logout(); } } diff --git a/app/Filters/Cors.php b/app/Filters/Cors.php index 6aae35d9..60a97a89 100644 --- a/app/Filters/Cors.php +++ b/app/Filters/Cors.php @@ -38,7 +38,7 @@ class Cors implements FilterInterface * * @var string */ - protected string $allowedMethods = 'GET,POST,PUT,PATCH,DELETE,OPTIONS'; + protected string $allowedMethods = 'GET,POST,OPTIONS'; /** * HTTP headers allowed in CORS requests @@ -143,7 +143,7 @@ class Cors implements FilterInterface // If wildcard present in configuration, allow any origin if (in_array('*', $this->allowedOrigins, true)) { - $this->log('Origin allowed: wildcard match', ['origin' => $origin]); + // $this->log('Origin allowed: wildcard match', ['origin' => $origin]); return true; } @@ -159,10 +159,10 @@ class Cors implements FilterInterface // 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 - ]); + // $this->log('Origin allowed: exact match', [ + // 'origin' => $origin, + // 'matched_rule' => $allowed + // ]); return true; } @@ -178,11 +178,11 @@ class Cors implements FilterInterface // 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 - ]); + // $this->log('Origin allowed: wildcard subdomain match', [ + // 'origin' => $origin, + // 'matched_rule' => $allowed, + // 'origin_host' => $originHost + // ]); return true; } } @@ -191,11 +191,11 @@ class Cors implements FilterInterface // 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 - ]); + // $this->log('Origin allowed: host match (scheme-less)', [ + // 'origin' => $origin, + // 'matched_rule' => $allowed, + // 'origin_host' => $originHost + // ]); return true; } } @@ -274,6 +274,7 @@ class Cors implements FilterInterface // Handle allowed headers if ($isPreflight) { + $response->setHeader('Access-Control-Allow-Methods', ['OPTIONS']); // 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'); @@ -320,11 +321,11 @@ class Cors implements FilterInterface // 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() - ]); + // $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)) { @@ -346,10 +347,10 @@ class Cors implements FilterInterface $response->setStatusCode(204); $response->setBody(''); - $this->log('Preflight approved', [ - 'origin' => $origin, - 'allowed_methods' => $this->allowedMethods - ]); + // $this->log('Preflight approved', [ + // 'origin' => $origin, + // 'allowed_methods' => $this->allowedMethods + // ]); return $response; } @@ -392,10 +393,10 @@ class Cors implements FilterInterface // Add CORS headers to the response $this->addCorsHeaders($response, $request, $origin, false); - $this->log('CORS headers added to response', [ - 'origin' => $origin, - 'status' => $response->getStatusCode() - ]); + // $this->log('CORS headers added to response', [ + // 'origin' => $origin, + // 'status' => $response->getStatusCode() + // ]); } /** diff --git a/app/Filters/GlobalPostFileUploadGuard.php b/app/Filters/GlobalPostFileUploadGuard.php new file mode 100644 index 00000000..76117240 --- /dev/null +++ b/app/Filters/GlobalPostFileUploadGuard.php @@ -0,0 +1,147 @@ + ['jpg', 'jpeg'], + 'image/png' => ['png'], + 'image/gif' => ['gif'], + 'image/webp' => ['webp'], + 'image/svg+xml' => ['svg'], + 'application/pdf' => ['pdf'], + 'application/msword' => ['doc'], + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['docx'], + 'application/vnd.oasis.opendocument.text' => ['odt'], + 'text/rtf' => ['rtf'], + 'application/rtf' => ['rtf'], + 'application/vnd.ms-excel' => ['xls'], + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => ['xlsx'], + 'application/vnd.oasis.opendocument.spreadsheet' => ['ods'], + 'text/csv' => ['csv'], + 'application/csv' => ['csv'], + 'text/plain' => ['txt', 'csv'], + ]; + + protected array $blockedExtensions = [ + 'php', 'phtml', 'html', 'pht', 'phar', 'php3', 'php4', 'php5', 'php7', 'php8', 'phps', + 'cgi', 'fcgi', 'pl', 'py', 'rb', 'lua', 'tcl', 'go', 'rs', 'jar', 'class', + 'exe', 'dll', 'com', 'bat', 'cmd', 'msi', 'vbs', 'ps1', 'scr', + 'sh', 'bash', 'zsh', 'apk', 'app', 'deb', 'rpm', 'bin', 'run', + 'js', 'mjs', 'jsp', 'asp', 'aspx', 'cer', 'swf', + 'env', 'ini', 'user.ini', 'htaccess', 'htpasswd', 'conf', 'config', 'log', 'sql', + 'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'iso', + 'lnk', 'url', 'reg', 'sys', 'drv', 'vxd', 'tmp', 'bak', 'old', 'backup', 'key', 'pem' + ]; + + public function before(RequestInterface $request, $arguments = null) + { + if ($request->getMethod() !== 'post') { + return; + } + + $files = $request->getFiles(); + if (empty($files)) { + return; + } + + foreach ($files as $inputName => $fileData) { + $this->validateFileInput($fileData, $inputName); + } + } + + private function validateFileInput($fileData, string $inputName): void + { + if (is_array($fileData)) { + foreach ($fileData as $file) { + $this->validateSingleFile($file, $inputName); + } + } else { + $this->validateSingleFile($fileData, $inputName); + } + } + + private function validateSingleFile($file, string $inputName): void + { + $request = Services::request(); + $clientIp = $request->getIPAddress(); + $uri = $request->getUri()->getPath(); + + if (!$file->isValid()) { + if ($file->getError() === UPLOAD_ERR_INI_SIZE || $file->getError() === UPLOAD_ERR_FORM_SIZE) { + $this->block("File exceeds server-side size limit", $clientIp, $uri, $inputName, $file->getClientName(), 'unknown', 'unknown', 0); + } + return; + } + + $originalName = $file->getClientName(); + $extension = strtolower($file->getExtension()); + $mime = $file->getMimeType(); + $size = $file->getSize(); + + // --- 1. Fixed Null Byte & Path Traversal Check --- + if (preg_match('/\0|[\/\\\]/', $originalName)) { + $this->block("Malicious filename characters", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + + // --- 2. Double Extension Attack Check --- + if (preg_match('/\.(php|html|phtml|phar|exe|sh|bat|cmd|js|jsp|asp|aspx|py|pl)\./i', $originalName)) { + $this->block("Double extension attack", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + + // --- 3. Forbidden Extension --- + if (in_array($extension, $this->blockedExtensions, true)) { + $this->block("Forbidden extension", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + + // --- 4. File Size Limit --- + if ($size > $this->maxFileSize) { + $this->block("File too large", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + + // --- 5. MIME Allow-list Check --- + if (!array_key_exists($mime, $this->allowedMimeMap)) { + $this->block("MIME type not allowed ($mime)", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + + // --- 6. MIME-Extension Consistency --- + if (!in_array($extension, $this->allowedMimeMap[$mime], true)) { + $this->block("MIME-extension mismatch", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size); + } + } + + private function block(string $reason, string $ip, string $uri, string $field, string $filename, string $mime, string $ext, int $size): void + { + log_message('critical', + '[UPLOAD_BLOCKED] {reason} | IP: {ip} | URI: {uri} | Field: {field} | File: {file} | MIME: {mime} | EXT: {ext} | SIZE: {size}', + ['reason'=>$reason, 'ip'=>$ip, 'uri'=>$uri, 'field'=>$field, 'file'=>$filename, 'mime'=>$mime, 'ext'=>$ext, 'size'=>$size] + ); + + $response = Services::response(); + $response->setStatusCode(403) + ->setJSON([ + 'status' => 'error', + 'message' => 'File upload rejected: Security policy violation.', + 'debug' => (ENVIRONMENT === 'development') ? $reason : null + ]) + ->send(); + exit; + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {} +} \ No newline at end of file diff --git a/app/Filters/RateLimitFilter.php b/app/Filters/RateLimitFilter.php new file mode 100644 index 00000000..31efe81e --- /dev/null +++ b/app/Filters/RateLimitFilter.php @@ -0,0 +1,34 @@ +getIPAddress()); + + if ($throttler->check($key, 25, MINUTE) === false) { + return service('response') + ->setStatusCode(429) + ->setJSON([ + 'status' => 'error', + 'message' => 'Too many requests. Try again later.' + ]); + } + } + + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + // nothing + } +} diff --git a/app/Filters/SecurityInputFilter.php b/app/Filters/SecurityInputFilter.php new file mode 100644 index 00000000..c23f7907 --- /dev/null +++ b/app/Filters/SecurityInputFilter.php @@ -0,0 +1,130 @@ +/i', + + // JavaScript execution vectors + '/javascript\s*:/i', + '/vbscript\s*:/i', + '/data\s*:\s*text\/html/i', + + // Inline event handlers (strong signal) + '/on\w+\s*=\s*["\']?/i', + + // Dangerous HTML tags + '/<\s*iframe\b/i', + '/<\s*object\b/i', + '/<\s*embed\b/i', + '/<\s*applet\b/i', + '/<\s*img\b/i', + + // Image-based execution + '/<\s*img\b[^>]*on\w+/i', + + // SVG-based execution (modern bypass) + '/<\s*svg\b/i', + '/<\s*math\b/i', + + // Meta refresh redirect + '/<\s*meta\b[^>]*http-equiv\s*=\s*["\']?refresh/i', + + // HTML injection via src/href + '/<\s*\w+\b[^>]*(src|href)\s*=\s*["\']?\s*(javascript|data)\s*:/i' + ]; + + + public function before(RequestInterface $request, $arguments = null) + { + $logger = Services::mylogger(); + $response = Services::response(); + + // Collect all user-controlled input + $inputs = array_merge( + $request->getGet(), + $request->getPost() + ); + + if (empty($inputs)) { + return; + } + + foreach ($inputs as $field => $value) { + if (is_array($value)) { + $value = json_encode($value); + } + + // Step 1: Canonicalization (VERY IMPORTANT) + $canonical = $this->canonicalize($value); + + // Step 2: Trim (hygiene) + $canonical = trim($canonical); + + // Step 3: Detection (signal-only) + if ($this->detectXss($canonical)) { + + // 🔐 Log intent, not data + $logger->logme('critical','SECURITY_BLOCKED_REQUEST - '. json_encode([ + 'ip' => $request->getIPAddress(), + 'method' => $request->getMethod(), + 'uri' => current_url(), + 'field' => $field, + 'attack' => 'XSS_PATTERN', + 'length' => strlen($canonical), + 'hash' => hash('sha256', $canonical), + ])); + + // ⛔ Block request + return $response + ->setStatusCode(403) + ->setJSON([ + 'status' => 403, + 'error' => 'Forbidden', + 'message' => 'Malicious input detected' + ]); + } + } + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + // no-op + } + + /** + * Canonicalization prevents encoded bypass + */ + private function canonicalize(string $value): string + { + $value = urldecode($value); + $value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // Remove invisible control characters + return preg_replace('/[\x00-\x1F\x7F]/u', '', $value); + } + + private function detectXss(string $value): bool + { + foreach ($this->xssPatterns as $pattern) { + if (preg_match($pattern, $value)) { + return true; + } + } + return false; + } +} diff --git a/app/Helpers/ChatbotHelper.php b/app/Helpers/ChatbotHelper.php index 72632251..3947ef7e 100644 --- a/app/Helpers/ChatbotHelper.php +++ b/app/Helpers/ChatbotHelper.php @@ -59,7 +59,7 @@ class ChatbotHelper // $policy_id = 0; // $relationship ='Father'; $EmployeeModel = new EmployeeModel(); - $data = $EmployeeModel->find($emp_id); + $data = $EmployeeModel->find((int)$emp_id); if(isset($data) && isset($data['email_corporate'])) { $message = SELF::ReimbursementProcessMailTemplate(); diff --git a/app/Helpers/DepositHelper.php b/app/Helpers/DepositHelper.php index 495797f3..cc8fbde0 100755 --- a/app/Helpers/DepositHelper.php +++ b/app/Helpers/DepositHelper.php @@ -67,7 +67,8 @@ class DepositHelper 'balance' => $newBalance, // Include the new balance in the data array 'cd_ac_pk'=> isset($data['cd_ac_pk'])?$data['cd_ac_pk']:null, 'record_date' => $data['record_date'] ?? null, - 'policy_transaction_id' => $data['pt_id'] ?? null + 'policy_transaction_id' => $data['pt_id'] ?? null, + 'file_id' => $data['file_id'] ?? null, ]; // Insert data and get the insert ID diff --git a/app/Helpers/HttpRequestHelper.php b/app/Helpers/HttpRequestHelper.php index a3a3da64..3e072e8a 100755 --- a/app/Helpers/HttpRequestHelper.php +++ b/app/Helpers/HttpRequestHelper.php @@ -4,24 +4,175 @@ namespace App\Helpers; class HttpRequestHelper { - public static function getRequestInfo() + public static function getRequestInfo(): array { $request = service('request'); + + $uaString = $request->getHeaderLine('User-Agent'); + + // Detect platform & browser using robust fallback logic + [$platform, $browser] = self::detectFromUserAgent($uaString); + $data = [ - 'ip' => $request->getIPAddress(), - 'platform' => $request->getUserAgent()->getPlatform(), - 'browser' => ($request->getUserAgent()->getBrowser().' '.$request->getUserAgent()->getVersion()), - 'method' => $request->getMethod(), - 'endpoint' => $request->uri->getPath(), - 'getparams' => $request->uri->getSegments(), - 'postparams' => $request->getPost() + 'ip' => $request->getIPAddress(), + 'platform' => $platform, + 'browser' => $browser, + 'method' => strtoupper($request->getMethod()), + 'endpoint' => $request->uri->getPath(), + 'getparams' => json_encode($request->uri->getSegments(), JSON_UNESCAPED_UNICODE), + 'postparams' => self::sanitizePostForLog($request->getPost()), ]; - $data['method'] = (isset($data['method']) ? strtoupper($data['method']) : $data['method']); - $data['getparams'] = is_array($data['getparams']) ? json_encode($data['getparams']) : $data['getparams']; - $data['postparams'] = is_array($data['postparams']) ? json_encode($data['postparams']) : $data['postparams']; return $data; } - public static function add($payload) - {return $payload['a'] + $payload['b'];} + + /** + * Detect platform & browser from UA string (reliable fallback) + */ + private static function detectFromUserAgent(string $ua): array + { + $uaLower = strtolower($ua); + + // ========================= + // PLATFORM DETECTION + // ========================= + $platform = 'Unknown'; + + if (str_contains($uaLower, 'windows nt 11') || str_contains($uaLower, 'windows 11')) { + $platform = 'Windows 11'; + } elseif (str_contains($uaLower, 'windows nt 10')) { + $platform = 'Windows 10'; + } elseif (str_contains($uaLower, 'windows nt 6.3')) { + $platform = 'Windows 8.1'; + } elseif (str_contains($uaLower, 'windows nt 6.2')) { + $platform = 'Windows 8'; + } elseif (str_contains($uaLower, 'windows nt 6.1')) { + $platform = 'Windows 7'; + } elseif (str_contains($uaLower, 'windows nt 6.0')) { + $platform = 'Windows Vista'; + } elseif (str_contains($uaLower, 'windows nt 5.1') || str_contains($uaLower, 'windows xp')) { + $platform = 'Windows XP'; + } elseif (str_contains($uaLower, 'android')) { + $platform = 'Android'; + } elseif (str_contains($uaLower, 'iphone')) { + $platform = 'iOS (iPhone)'; + } elseif (str_contains($uaLower, 'ipad')) { + $platform = 'iOS (iPad)'; + } elseif (str_contains($uaLower, 'ipod')) { + $platform = 'iOS (iPod)'; + } elseif (str_contains($uaLower, 'mac os') || str_contains($uaLower, 'macintosh')) { + $platform = 'Mac OS'; + } elseif (str_contains($uaLower, 'cros')) { + $platform = 'Chrome OS'; + } elseif (str_contains($uaLower, 'linux')) { + $platform = 'Linux'; + } elseif (str_contains($uaLower, 'freebsd')) { + $platform = 'FreeBSD'; + } elseif (str_contains($uaLower, 'openbsd')) { + $platform = 'OpenBSD'; + } elseif (str_contains($uaLower, 'netbsd')) { + $platform = 'NetBSD'; + } elseif (str_contains($uaLower, 'unix')) { + $platform = 'Unix'; + } elseif (str_contains($uaLower, 'symbian')) { + $platform = 'Symbian'; + } elseif (str_contains($uaLower, 'blackberry')) { + $platform = 'BlackBerry'; + } elseif (str_contains($uaLower, 'tizen')) { + $platform = 'Tizen'; + } elseif (str_contains($uaLower, 'webos')) { + $platform = 'WebOS'; + } elseif (str_contains($uaLower, 'kaios')) { + $platform = 'KaiOS'; + } elseif (str_contains($uaLower, 'harmonyos')) { + $platform = 'HarmonyOS'; + } elseif (str_contains($uaLower, 'watchos')) { + $platform = 'watchOS'; + } elseif (str_contains($uaLower, 'tv os') || str_contains($uaLower, 'tvos')) { + $platform = 'tvOS'; + } + + // ========================= + // BROWSER / CLIENT DETECTION + // ========================= + $browser = 'Unknown'; + + // Bots & tools first + if (preg_match('/googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|sogou|exabot|facebot|ia_archiver/i', $ua)) { + $browser = 'Search Bot'; + } elseif (preg_match('/postman/i', $ua)) { + $browser = 'Postman'; + } elseif (preg_match('/insomnia/i', $ua)) { + $browser = 'Insomnia'; + } elseif (preg_match('/curl/i', $ua)) { + $browser = 'curl'; + } elseif (preg_match('/wget/i', $ua)) { + $browser = 'wget'; + } + + // Real browsers + elseif (preg_match('/edg\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Edge ' . $m[1]; + } elseif (preg_match('/opr\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Opera ' . $m[1]; + } elseif (preg_match('/vivaldi\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Vivaldi ' . $m[1]; + } elseif (preg_match('/brave\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Brave ' . $m[1]; + } elseif (preg_match('/chrome\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Chrome ' . $m[1]; + } elseif (preg_match('/firefox\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Firefox ' . $m[1]; + } elseif (preg_match('/safari\/([\d\.]+)/i', $ua, $m)) { + $browser = 'Safari ' . $m[1]; + } elseif (preg_match('/msie\s([\d\.]+)/i', $ua, $m) || preg_match('/trident\/.*rv:([\d\.]+)/i', $ua, $m)) { + $browser = 'Internet Explorer ' . $m[1]; + } + + // In-app browsers + elseif (preg_match('/fbav|fban/i', $ua)) { + $browser = 'Facebook In-App Browser'; + } elseif (preg_match('/instagram/i', $ua)) { + $browser = 'Instagram In-App Browser'; + } elseif (preg_match('/linkedinapp/i', $ua)) { + $browser = 'LinkedIn In-App Browser'; + } elseif (preg_match('/twitter/i', $ua)) { + $browser = 'Twitter/X In-App Browser'; + } + + return [$platform, $browser]; + } + + + /** + * Remove sensitive fields before logging POST + */ + private static function sanitizePostForLog(array $post): string + { + if (empty($post)) { + return json_encode([]); + } + + $sensitiveKeys = [ + 'password', 'pass', 'pwd', + 'token', 'access_token', 'refresh_token', + 'secret', 'api_key', 'authorization', + 'otp', 'pin' + ]; + + foreach ($post as $k => $v) { + foreach ($sensitiveKeys as $sk) { + if (stripos($k, $sk) !== false) { + $post[$k] = '***MASKED***'; + } + } + } + + return json_encode($post, JSON_UNESCAPED_UNICODE); + } + + public static function add($payload) + { + return $payload['a'] + $payload['b']; + } } diff --git a/app/Helpers/JWTToken.php b/app/Helpers/JWTToken.php index 41d76b5c..c964bac7 100755 --- a/app/Helpers/JWTToken.php +++ b/app/Helpers/JWTToken.php @@ -18,24 +18,28 @@ use App\Models\LevelContactModel; class JWTToken { + + private const ALLOWED_ALG = 'HS512'; + public static function encode($data =null) { - $secret_Key ="secret"; + $secret_Key = env('JWT_SECRET'); $request_data = (array)$data; try{ $token = JWT::encode($request_data ,$secret_Key,'HS512'); $id = $request_data['id']; - $data["token_time_out"] = time() + getenv('TOKENTIMEOUT'); + $update["token_time_out"] = time() + getenv('TOKENTIMEOUT'); if(isset($data['emp_code'])){ $model = new EmployeeModel(); - $model->update($id, $data); + $model->update($id, $update); }else{ $models = new LevelContactModel(); - $models->update($id, $data); + $id = $request_data['post_hr_id']; + $models->update($id, $update); } @@ -47,30 +51,79 @@ class JWTToken } } - public static function validateJWT($jwt) + // public static function validateJWT($jwt) + // { + // $jwtParts = explode(' ', $jwt); + + // // print_r($jwtParts); + // if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') { + // return false; + // } + + // $token = $jwtParts[1]; + + // try { + // $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS512')); + // return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]); + // } catch (ExpiredException $e) { + // return json_encode(['status' => false, 'message' => 'Token has expired']); + // } catch (BeforeValidException $e) { + // return json_encode(['status' => false, 'message' => 'Token is not yet valid']); + // } catch (SignatureInvalidException $e) { + // return json_encode(['status' => false, 'message' => 'Token signature is invalid']); + // } catch (\Exception $e) { + // return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']); + // } + + // } + + public static function validateJWT(string $authHeader) { - $jwtParts = explode(' ', $jwt); + // 1️⃣ Validate Authorization header + if (!preg_match('/^Bearer\s(\S+)$/', $authHeader, $matches)) { + return ['status' => false, 'message' => 'Invalid Authorization header']; + } - // print_r($jwtParts); - if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') { - return false; - } + $token = $matches[1]; - $token = $jwtParts[1]; + // 2️⃣ Decode JWT header manually + $jwtParts = explode('.', $token); + if (count($jwtParts) !== 3) { + return ['status' => false, 'message' => 'Malformed JWT']; + } - try { - $decoded = JWT::decode($token, new Key("secret", 'HS512')); - return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]); - } catch (ExpiredException $e) { - return json_encode(['status' => false, 'message' => 'Token has expired']); - } catch (BeforeValidException $e) { - return json_encode(['status' => false, 'message' => 'Token is not yet valid']); - } catch (SignatureInvalidException $e) { - return json_encode(['status' => false, 'message' => 'Token signature is invalid']); - } catch (\Exception $e) { - return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']); - } + $header = json_decode(base64_decode(strtr($jwtParts[0], '-_', '+/')), true); + // 3️⃣ Reject missing or NONE algorithm + if ( + empty($header['alg']) || + $header['alg'] === 'none' || + $header['alg'] !== self::ALLOWED_ALG + ) { + return ['status' => false, 'message' => 'Invalid or unsupported JWT algorithm']; + } + + // 4️⃣ Enforce signature validation + try { + $decoded = JWT::decode( + $token, + new Key(env('JWT_SECRET'), self::ALLOWED_ALG) + ); + + return [ + 'status' => true, + 'decoded' => (array) $decoded + ]; + + } catch (ExpiredException $e) { + return ['status' => false, 'message' => 'Token expired']; + } catch (BeforeValidException $e) { + return ['status' => false, 'message' => 'Token not yet valid']; + } catch (SignatureInvalidException $e) { + return ['status' => false, 'message' => 'Invalid token signature']; + } catch (\Exception $e) { + return ['status' => false, 'message' => 'Token validation failed']; + } } diff --git a/app/Helpers/excel_util_helper.php b/app/Helpers/excel_util_helper.php index 98b171e5..5b5d6b7b 100755 --- a/app/Helpers/excel_util_helper.php +++ b/app/Helpers/excel_util_helper.php @@ -1128,7 +1128,7 @@ if (!function_exists('premium_calculation_manager_old')) { //if curent action is dependent addition OR addition then pull insurer master to set whether add one day from employee date of coverage if ($emp_data['temp']['action'] == 'DA' || $emp_data['temp']['action'] == 'A') { $insurer = new InsurerModel(); - $insurer = ($insurer->find($policy_terms['insurer_id'])); + $insurer = ($insurer->find((int)$policy_terms['insurer_id'])); if (isset($insurer['addition_add_day']) && $insurer['addition_add_day'] == true) { // $emp_data['policy_details']['date_coverage'] = (new DateTime($emp_data['policy_details']['date_coverage']))->modify('+1 day')->format('Y-m-d'); @@ -1535,7 +1535,7 @@ if (!function_exists('premium_calculation_manager')) { //if curent action is dependent addition OR addition then pull insurer master to set whether add one day from employee date of coverage if ($emp_data['temp']['action'] == 'DA' || $emp_data['temp']['action'] == 'A') { $insurer = new InsurerModel(); - $insurer = ($insurer->find($policy_terms['insurer_id'])); + $insurer = ($insurer->find((int)$policy_terms['insurer_id'])); if (isset($insurer['addition_add_day']) && $insurer['addition_add_day'] == true) { // $emp_data['policy_details']['date_coverage'] = (new DateTime($emp_data['policy_details']['date_coverage']))->modify('+1 day')->format('Y-m-d'); @@ -1884,7 +1884,7 @@ if (!function_exists('premium_calculation_manager')) { } // if the family floater case first self add first the process completed, after spouse or any dependent add the rata premium not added this change will handle this - if (strtolower($emp_data['relationship']) != 'self' && $temp_slab_rates[0]['premium_type'] == 1 && $emp_data['temp']['action'] == 'DA') { + if (strtolower($emp_data['relationship']) != 'self' && empty($emp_data['temp']['acting_self']) && $temp_slab_rates[0]['premium_type'] == 1 && $emp_data['temp']['action'] == 'DA') { //set dependent si to 0 $emp_data['policy_details']['basic_cover_si'] = 0; $emp_data['policy_details']['premium'] = 0; @@ -1897,7 +1897,7 @@ if (!function_exists('premium_calculation_manager')) { $emp_data['policy_details']['gst'] = (float) number_format(($emp_data['policy_details']['rata_premimum'] * ($gst / 100)), 2, '.', ''); } - }else if(strtolower($emp_data['relationship']) != 'self' && $temp_slab_rates[0]['premium_type'] == 1 && ($emp_data['temp']['action'] == 'I' || $emp_data['temp']['action'] == 'A' || $emp_data['temp']['action'] == 'MI')){ + }else if(strtolower($emp_data['relationship']) != 'self' && empty($emp_data['temp']['acting_self']) && $temp_slab_rates[0]['premium_type'] == 1 && ($emp_data['temp']['action'] == 'I' || $emp_data['temp']['action'] == 'A' || $emp_data['temp']['action'] == 'MI')){ $emp_data['policy_details']['basic_cover_si'] = 0; $emp_data['policy_details']['premium'] = 0; @@ -2000,8 +2000,9 @@ if (!function_exists('get_emp_records_from_audit_history')) { function get_emp_records_from_audit_history($employee_id) { $db = db_connect(); - $query = "SELECT min(id) as id,pk,table_name,field_name,old_value FROM auditing_history where pk = $employee_id and table_name = 'employees' and field_name in ('name','dob','gender','mobile','email_corporate','relationship') group by field_name order by id asc"; - $res = $db->query($query); + $query = "SELECT min(id) as id,pk,table_name,field_name,old_value FROM auditing_history where pk = :employee_id: and table_name = 'employees' and field_name in ('name','dob','gender','mobile','email_corporate','relationship') group by field_name order by id asc"; + $binds = ['employee_id'=>(int)$employee_id]; + $res = $db->query($query,$binds); // echo $db->getLastQuery(); $res = $res->getResultArray(); if (count($res)) { @@ -2019,8 +2020,9 @@ if (!function_exists('get_emp_policy_records_from_audit_history')) { function get_emp_policy_records_from_audit_history($emp_policy_id) { $db = db_connect(); - $query = "SELECT min(id) as id,pk,table_name,field_name,old_value FROM auditing_history where pk = $emp_policy_id and table_name = 'employee_polices' and field_name in ('basic_cover_si','premium','gst') group by field_name order by id asc"; - $res = $db->query($query); + $query = "SELECT min(id) as id,pk,table_name,field_name,old_value FROM auditing_history where pk = :emp_policy_id: and table_name = 'employee_polices' and field_name in ('basic_cover_si','premium','gst') group by field_name order by id asc"; + $binds = ['emp_policy_id'=>(int)$emp_policy_id]; + $res = $db->query($query,$binds); // echo $db->getLastQuery(); $res = $res->getResultArray(); if (count($res)) { @@ -3147,7 +3149,7 @@ if (!function_exists('check_agent_exist')) { // Loop agent data from DB foreach ($agent_data as $agent) { // Assuming DB keys: agent_code - if (isset($agent['agent_code']) && $agent['agent_code'] == $agent_code) { + if (isset($agent['pos_code']) && (strtolower($agent['pos_code']) == strtolower($agent_code))) { return [ 'status' => true, 'error' => null, @@ -3171,6 +3173,24 @@ if (!function_exists('check_rto_data')) { $vehicle_no = strtoupper(trim($row[2])); // Example: TN10AB1234 $new_vehicle = strtolower(trim($row[2])); + if(!validate_indian_vehicle_number($vehicle_no)['status']){ + return [ + 'status' => false, + 'error' => 'Invalid Vehicle Number. Format should be like TN82AX2024 (no spaces or special characters).' + ]; + }; + + // BH Series: 22BH1234AA + $bhPattern = '/^[0-9]{2}BH[0-9]{4}[A-Z]{2}$/'; + + if (preg_match($bhPattern, $vehicle_no)) { + return [ + 'status' => true, + 'error' => null, + 'rto_data' => [] + ]; + } + if($new_vehicle == "new"){ return [ 'status' => true, diff --git a/app/Helpers/sanitizeInputArrayAdvanced_helper.php b/app/Helpers/sanitizeInputArrayAdvanced_helper.php new file mode 100644 index 00000000..79d7df9b --- /dev/null +++ b/app/Helpers/sanitizeInputArrayAdvanced_helper.php @@ -0,0 +1,84 @@ + $v) { + + if (is_array($v)) { + $data[$k] = sanitizeInputArrayAdvanced($v, $htmlAllowedFields); + continue; + } + + if (!is_string($v)) { + continue; + } + + // 1. Unicode normalization (prevents homoglyph attacks) + if (class_exists('Normalizer')) { + $v = \Normalizer::normalize($v, \Normalizer::FORM_C); + } + + // 2. Remove NULL bytes & control chars + $v = preg_replace('/[\x00-\x1F\x7F]/u', '', $v); + + // 3. Remove invisible unicode chars (zero width, etc) + $v = preg_replace('/[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{206F}]/u', '', $v); + + // 4. Decode HTML entities (so hidden payloads are exposed) + $v = html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // 5. Trim + $v = trim($v); + + // 6. If this field is NOT allowed to contain HTML → strip aggressively + if (!in_array($k, $htmlAllowedFields, true)) { + + // Remove all tags + $v = strip_tags($v); + + // Kill any leftover JS protocol + $v = preg_replace('/(javascript:|data:|vbscript:)/i', '', $v); + + } else { + // This is HTML-allowed field → run HTML sanitizer + $v = sanitizeTrustedHtml($v); + } + + $data[$k] = $v; + } + + return $data; +} + +function sanitizeTrustedHtml(string $html): string +{ + // Allowed tags for email templates + $allowedTags = '