diff --git a/.env.sample b/.env.sample index ebc9ab1d..f9ab9b80 100755 --- a/.env.sample +++ b/.env.sample @@ -108,3 +108,5 @@ CORS_MAX_AGE=7200 CORS_DEBUG=true APP_SIGNATURE = +TOKENTIMEOUT = +JWT_SECRET = \ No newline at end of file diff --git a/app/Config/Acl.php b/app/Config/Acl.php new file mode 100644 index 00000000..f2f4ecca --- /dev/null +++ b/app/Config/Acl.php @@ -0,0 +1,157 @@ + ['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]], + + // ===================== 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' => [] + ], + + // ===================== 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..572f5fdd 100755 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -16,6 +16,9 @@ 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\AuthJWT; @@ -43,6 +46,9 @@ class Filters extends BaseConfig 'CommissionApiFilter'=> CommissionApiFilter::class, 'appSignature' => VerifyAppSignature::class, 'Cors' => Cors::class, + 'SecurityInputFilter' => SecurityInputFilter::class, + 'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class, + 'AclFilter' => AclFilter::class, ]; /** @@ -56,6 +62,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']], + 'SecurityInputFilter' => ['except' => ['notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail'] ], + 'GlobalPostFileUploadGuard' // 'csrf', // 'invalidchars', ], diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 531d5836..c402419a 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -600,6 +600,8 @@ $routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($route $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], function ($routes) { + $routes->post('logout', 'RestAuthenticationController::logout'); + $routes->post("ecardRequest", "ApiServiceController::ecardRequest"); $routes->get("getWellnessURL", "ApiServiceController::getWellnessURL"); @@ -736,6 +738,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"); @@ -754,7 +762,7 @@ $routes->post("retrieveWebhookDataClaim","ClientWebHooksController::pullData_cla //Third party ICICILombard Api Call -$routes->get('generateAuthToken','ICICILombardController::generateAuthToken'); +$routes->get('generateAuthToken','FhplApiController::generateAuthToken'); $routes->get('createEnrollmentBatch','ICICILombardController::createEnrollmentBatch'); $routes->get('getEnrollmentBatchStatus','ICICILombardController::getEnrollmentBatchStatus'); $routes->get('fetchUHIDDetails','ICICILombardController::fetchUHIDDetails'); diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php index f59de0f0..7c256f40 100644 --- a/app/Controllers/ApiServiceController.php +++ b/app/Controllers/ApiServiceController.php @@ -106,10 +106,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(); diff --git a/app/Controllers/AppContentManagementController.php b/app/Controllers/AppContentManagementController.php index 33d592e9..54f8cbb0 100755 --- a/app/Controllers/AppContentManagementController.php +++ b/app/Controllers/AppContentManagementController.php @@ -264,9 +264,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..84702df7 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()); 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..385d1adf 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -519,6 +519,7 @@ 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']]); } @@ -664,7 +665,7 @@ class ClientController extends AdminController $headerData['tab_name'] = 'Client Deposit'; $headerData['page_name'] = 'Clients'; - $data['clientName'] = $this->clientModel->where('id', $id)->find(); + $data['clientName'] = $this->clientModel->where('id', (int)$id)->find(); $data['clientData'] = $this->clientPolicyModel->getinsurerswithclientid($id, $policyId); $data['depositsummary'] = $this->clientPolicyModel->getDepositlistsummary($id); @@ -735,14 +736,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|is_natural_no_zero', + 'errors' => [ + 'required' => 'Account number is required', + 'is_natural_no_zero' => 'must be a positive integer', + ] + ], + + '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']; + $insurer_id = $sanitized_post_data['insurer_id']; + $record_date = $sanitized_post_data['record_date']; + $cd_ac_pk = $sanitized_post_data['cd_ac_pk']; + $cd_ac_no = $sanitized_post_data['cd_ac_no']; - $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 +849,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'], + 'sub_type_id' => $sanitized_post_data['sub_type_id'], + 'client_id' => $sanitized_post_data['client_id'], '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'], + 'description' => $sanitized_post_data['description'], + 'transaction_type' => $sanitized_post_data['transaction_type'] ?: 'Credit', 'updated_by' => 1, 'record_date' => $record_date ]; @@ -1370,7 +1459,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 +1483,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); @@ -3761,9 +3850,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; 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..ff86177d 100755 --- a/app/Controllers/EmpDataServiceController.php +++ b/app/Controllers/EmpDataServiceController.php @@ -487,13 +487,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()); @@ -1095,13 +1100,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 +1938,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 +1947,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 +1965,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); @@ -2500,7 +2507,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) { @@ -3135,7 +3142,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) { @@ -3709,7 +3716,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) { @@ -4193,7 +4200,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) { @@ -4454,11 +4461,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) { diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 118b5330..1f996e53 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -177,7 +177,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 +188,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 +242,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 +330,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' => true, '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 +344,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 +379,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 +387,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 +412,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') @@ -420,7 +463,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 +478,22 @@ class EmployeeController extends AdminController } } - public function getExcelFileErrors() + public function getExcelFileErrors($file_id, $retun_type = null) { - $file_id = $this->request->uri->getSegment(3); + // $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; @@ -1116,7 +1167,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 +1517,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 +1543,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 +1779,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 +1837,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 +1933,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 +2063,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 +2127,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 +2193,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 +2427,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;
 
@@ -3346,7 +3403,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);
     }
 
@@ -3377,7 +3435,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 +3464,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 +3497,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';
@@ -3495,6 +3553,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 +3563,7 @@ class EmployeeController extends AdminController
         ];
 
         foreach ($familiesPayload as $empCode => &$family) {
-
+             // print_rr($family);die();
             try {
                 $response = $client->post($endpointUrl, [
                     'headers' => $headers,
@@ -3522,6 +3581,8 @@ class EmployeeController extends AdminController
                     'rawBody'    => $body,
                     'data'       => $decoded,
                 ];
+
+
             } catch (\Throwable $e) {
                 // In case of exception, store error info
                 $family['apiResponse'] = [
@@ -3531,6 +3592,8 @@ class EmployeeController extends AdminController
                     'error'      => $e->getMessage(),
                 ];
             }
+
+              // print_rr($body );die();
         }
 
         unset($family); // break reference
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..c2f9f397 100755
--- a/app/Controllers/EmployeeRestController.php
+++ b/app/Controllers/EmployeeRestController.php
@@ -159,9 +159,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')) {
@@ -2220,7 +2220,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;
@@ -2929,6 +2929,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";
@@ -3273,7 +3274,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.');
@@ -3571,6 +3572,20 @@ class EmployeeRestController extends AdminController
         $get_docs_name = $this->request->getPost('claim_doc_names') ?? [];
         $policy_transaction_id = $this->request->getPost('policy_transaction_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 +3650,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 +3688,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();
@@ -4434,57 +4453,105 @@ 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'),
+            $post_data = [
+                'client_id'        => $this->request->getPost('client_id') ?? null,
                 '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'),
+                'file_name'        => $this->request->getFile('file_name')
             ];
+            // 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([
+            $client_data = $this->clientModel->where('id',  $post_data['client_id'])->first();
+            // 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'        => $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'),
+        ];
+
+        // 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;
+
+        if($responce['status']){
+            $file_data = $this->getDataFromHrFilesTable(['file_id' => $responce['file_id']]);
+            $responce['data'] = $file_data;
+            return $responce;
+        }else{
+            $responce['data'] = [];
+            return $responce;
         }
     }
 
@@ -4548,7 +4615,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.");
             }
@@ -4574,7 +4641,7 @@ class EmployeeRestController extends AdminController
             $file_id = $this->request->getGet('id') ?? $id;
 
             // Find record
-            $record = $this->hrFileUploadModel->where('id', $file_id)->find();
+            $record = $this->hrFileUploadModel->where('id', (int)$file_id)->find();
 
             // print_rr( $record);die;
 
@@ -4643,17 +4710,48 @@ 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{
+                $client_data = $this->clientModel->where('id', $search_data['client_id'])->first();
+                if($client_data['hr_file_processed_by'] == 1){
+                    $data = $this->getDataFromHrFileUploadTable($search_data);
+                    $table = "hr_file_upload 2";
+                }else{
+                    $data = $this->getDataFromHrFilesTable($search_data);
+                    $table = "files";
+                }
+            }
 
-            $builder = $this->hrFileUploadModel
-                ->select('
+            return $this->respond([
+                'status'  => "success",
+                'message' => 'File list fetched successfully',
+                'data'    => $data,
+                'table'   => $table
+            ]);
+
+        } catch (\Exception $e) {
+            return $this->failServerError($e->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 +4770,112 @@ 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,
+                    '1' 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
+        $data = $builder->get()->getResultArray();
+
+        if(!empty($data)){
+            return $data;
+        }
+
+        return [];
+    }
+
+    public function getDataFromHrFilesTable($search_data)
+    {   
+        $file_download_base = base_url('util/download-file-list/');
+        $builder = $this->fileModel
+            ->select("
+                    files.id, 
+                    files.client_id, 
+                    files.client_branch_id, 
+                    files.policy_id, 
+                    cp.policy_no, 
+                    files.file_name, 
+                    files.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'])) {
+            $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
+        $data = $builder->get()->getResultArray();
+
+        if (!empty($data)) {
+            return $data;
+        }
+
+        return [];
     }
 
     public function hrFileUploadMasters()
diff --git a/app/Controllers/EmployeeServiceController.php b/app/Controllers/EmployeeServiceController.php
index 0547b05f..78bf3162 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))
@@ -1062,7 +1062,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 +1322,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))
@@ -1424,7 +1424,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 +1470,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 +1489,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 +1621,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 +1700,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'];
@@ -2028,7 +2028,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 +2187,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..33037652
--- /dev/null
+++ b/app/Controllers/FhplApiController.php
@@ -0,0 +1,73 @@
+db = \Config\Database::connect();
+        $this->fhplTpaId = getenv('FHPL_PRIMARY_KEY_CONSTANT');
+    }
+
+    public function generateAuthToken()
+    {
+        $url = env('FHPL_TOKEN_URL'); // example: https://uat.fhpl.net/token
+
+        // x-www-form-urlencoded body
+        $postData = http_build_query([
+            'UserName'   => 'TestApi@fhpl',  
+            'Password'   => 'Fhpl@12345',
+            'grant_type' => 'password',
+        ]);
+
+        $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,
+            'response'  => json_decode($response, true),
+        ]);
+    }
+
+
+}
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/LeadsController.php b/app/Controllers/LeadsController.php
index a3ebe469..c9150148 100644
--- a/app/Controllers/LeadsController.php
+++ b/app/Controllers/LeadsController.php
@@ -2538,7 +2538,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 +2999,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 +3033,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 +5320,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);
diff --git a/app/Controllers/LoginController.php b/app/Controllers/LoginController.php
index 451765c2..779c788b 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,14 @@ 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' =>  hash('sha256',
+                            ($this->request->getUserAgent()->getAgentString() . '|' . ($this->request->getIPAddress()
+                        )))]);
+
                         log_message('error', 'Set The UserId : `'. $user->id .'` in Session');
                         log_message('error', 'User Login Sucessfully');
 
@@ -91,14 +98,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..475c8a97 100755
--- a/app/Controllers/MasterController.php
+++ b/app/Controllers/MasterController.php
@@ -1197,7 +1197,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
@@ -1984,6 +1984,7 @@ class MasterController extends AdminController
             'rules' => WRITEPATH . 'uploads/commission/rules',
             'claim_sample_forms' => ROOTPATH . 'public/claim_sample_forms/',
             'bds_dump_excel' => WRITEPATH . 'uploads/bds_dump_excel/',
+            'claims_mis' => WRITEPATH . 'uploads/claims_mis/',
         ];
 
         foreach ($folders as $folderName => $folderPath) {
diff --git a/app/Controllers/MediAssistApiController.php b/app/Controllers/MediAssistApiController.php
index b019850a..9ce9be18 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;
         }
 
@@ -1106,7 +1109,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 +1178,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 +1187,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 +1208,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 +1220,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 +1259,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/PayoutController.php b/app/Controllers/PayoutController.php
index 90d501dc..bba35e99 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);
@@ -335,7 +335,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..f0d6fc56 100644
--- a/app/Controllers/PolicyTransactionController.php
+++ b/app/Controllers/PolicyTransactionController.php
@@ -3639,7 +3639,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 +3757,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 +3910,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 +4075,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 +4212,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);
         }
 
diff --git a/app/Controllers/RestAuthenticationController.php b/app/Controllers/RestAuthenticationController.php
index bad20540..9bfb87f9 100755
--- a/app/Controllers/RestAuthenticationController.php
+++ b/app/Controllers/RestAuthenticationController.php
@@ -110,6 +110,13 @@ 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'] ])->setStatusCode(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 +224,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'] ])->setStatusCode(429); // Too Many Requests
+                }
+
 
                 $builder = $this->employeeModel
                     ->where('email_corporate', $email)
@@ -518,6 +531,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'] ])->setStatusCode(429); // Too Many Requests
+                    }
                 
                     $sql = "UPDATE level_contacts SET otp = ? WHERE mobile = ? AND contact_type = 'client' AND is_active = 1";
 
@@ -569,6 +588,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'] ])->setStatusCode(429); // Too Many Requests
+                }
+
                 $sql = "
                         UPDATE level_contacts
                         SET otp = ?
@@ -919,6 +944,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 +1021,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 +1268,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 +2320,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/ThzController.php b/app/Controllers/ThzController.php
index a2427b07..df886d2a 100644
--- a/app/Controllers/ThzController.php
+++ b/app/Controllers/ThzController.php
@@ -238,7 +238,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..09021a99 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()
@@ -1124,6 +1127,43 @@ class TicketController extends BaseController
         // $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) {
@@ -1252,8 +1292,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 {
@@ -1734,11 +1775,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;
@@ -2943,4 +2985,83 @@ class TicketController extends BaseController
         return $this->respond(['status' => true, 'code' => 200, 'message' => 'IR docs saved successfully', 'data' => $required_docs], 200);
 
     }
+
+    public function claimMisFileList()
+    {
+        $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()
+    {
+        $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..5cfa99f4 100644
--- a/app/Controllers/TicketServiceController.php
+++ b/app/Controllers/TicketServiceController.php
@@ -1144,7 +1144,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 +1329,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);
 
diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php
index f6b4cc60..668144a6 100755
--- a/app/Controllers/UserController.php
+++ b/app/Controllers/UserController.php
@@ -599,7 +599,7 @@ class UserController extends AdminController
                 
                 // 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']); }
@@ -667,7 +667,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 +783,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..a6c45242 100644
--- a/app/Controllers/VidalApiController.php
+++ b/app/Controllers/VidalApiController.php
@@ -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;
         }
 
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..efda2976 100755 --- a/app/Filters/AuthMVC.php +++ b/app/Filters/AuthMVC.php @@ -5,13 +5,30 @@ 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 = hash('sha256', + $request->getUserAgent()->getAgentString() . '|' . $request->getIPAddress() + ); + + if (session()->get('fingerprint') !== $fp) { + return AuthLogout::logout(); } } diff --git a/app/Filters/Cors.php b/app/Filters/Cors.php index 6aae35d9..3cfd1252 100644 --- a/app/Filters/Cors.php +++ b/app/Filters/Cors.php @@ -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..4cc82ba8 --- /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', '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|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/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/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..73978ddb 100755 --- a/app/Helpers/JWTToken.php +++ b/app/Helpers/JWTToken.php @@ -18,9 +18,12 @@ 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; @@ -47,30 +50,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..ef882326 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'); @@ -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)) { 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 = '