From f1bc4fddea394b47a6a9e3ecf56335f8806487f0 Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Tue, 12 May 2026 11:27:12 +0530 Subject: [PATCH 1/7] FIX_STATEMENT_UPLOAD_ISSUE --- app/Config/Routes.php | 1 + app/Controllers/FhplApiController.php | 2 +- .../PolicyTransactionController.php | 374 +++++++++++++++++- app/Models/PTCOShareDetailsModel.php | 36 ++ app/Views/insurer_statement_list.php | 10 +- 5 files changed, 418 insertions(+), 5 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 081b44cd..566935a0 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -508,6 +508,7 @@ $routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) { $routes->post("saveInvoicePaymentDetails", "PolicyTransactionController::saveInvoicePaymentDetails"); $routes->get("deletePaymentEntry/(:any)", "PolicyTransactionController::deletePaymentEntry/$1"); $routes->get("downloadSampleInsurerStatement", "PolicyTransactionController::downloadSampleInsurerStatement"); + $routes->get("downloadInsurerStatement/(:num)", "PolicyTransactionController::downloadInsurerStatement/$1"); $routes->get("getFileErr/(:any)", "PolicyTransactionController::getFileErr/$1"); $routes->get("getInsurerStatementMonth", "PolicyTransactionController::getInsurerStatementMonth"); $routes->get("deleteStatement/(:any)", "PolicyTransactionController::deleteStatement/$1"); diff --git a/app/Controllers/FhplApiController.php b/app/Controllers/FhplApiController.php index dfa7db1b..2d6cc36c 100644 --- a/app/Controllers/FhplApiController.php +++ b/app/Controllers/FhplApiController.php @@ -1068,7 +1068,7 @@ class FhplApiController extends BaseController 'is_active' => 1, 'created_by' => $file_info[0]['created_by'] ?? null, - 'action_flag_status' => $row['IsActive'] == 1 ? 'A' : 'D' + 'action_flag_status' => $row['IsActive'] == 1 ? 'A' : 'D', 'si' => $row['BASE_SUMINSURED'], 'doj' => change_date_format($row['DATE_OF_JOINING'],'Y-m-d\TH:i:s'), 'endorsement_no' => $row['ENDORSEMENT_NO'] diff --git a/app/Controllers/PolicyTransactionController.php b/app/Controllers/PolicyTransactionController.php index 1c4c347b..0f15f31d 100644 --- a/app/Controllers/PolicyTransactionController.php +++ b/app/Controllers/PolicyTransactionController.php @@ -4236,7 +4236,7 @@ class PolicyTransactionController extends BaseController return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']); } - public function validateInsurerStatement($params) + public function validateInsurerStatementOLD1($params) { helper('excel_util_helper'); @@ -4392,8 +4392,7 @@ class PolicyTransactionController extends BaseController } } - - public function updateInsurerStatement($params) + public function updateInsurerStatementOLD($params) { helper('excel_util_helper'); //get file info @@ -4559,6 +4558,375 @@ class PolicyTransactionController extends BaseController return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']); } + public function validateInsurerStatement($params) + { + /* + * Changes made: 12-06-2024 + * - Added safe file_id handling and DB file record validation before using file details. + * - Fixed physical file missing response and error message. + * - Skips empty Excel rows and collects unique policy numbers from the uploaded statement. + * - Fetches NHance source records only for those uploaded policy numbers. + * - Sanitizes policy and endorsement numbers before comparison to avoid hidden-space mismatch. + * - Validates each row by policy number + endorsement number combination. + * - Detects duplicate policy + endorsement rows and returns row-wise validation errors. + */ + helper('excel_util_helper'); + + $file_id = $params['file_id'] ?? 0; + + try { + + $error_data = ['error_code' => '', 'error_data' => []]; + $status = 'success'; + $ret_status = true; + + //get file info + $file = $this->insurerStatements->find((int)$file_id); + // dd($file); + + if (empty($file)) { + //file not found in DB + $this->insurerStatements->where('id', $file_id)->set(['file_status' => 'failed', 'reason' => json_encode(['error_code' => 0, 'error_data' => 'statement file not found in DB'])])->update(); + return array('status' => false, 'error_code' => 0, 'error_data' => 'statement file not found in DB'); + } + $file_name_with_path = WRITEPATH . "/uploads/statements/" . $file['file_name']; + + //check physical file + if (!file_exists($file_name_with_path)) { + //file not found update status and reason + $message = "Physical file not found"; + // echo $message; + $this->myLogger->logme('error', ($message . ' for statement file id ' . $file_id)); + $this->insurerStatements->where('id', $file_id)->set(['file_status' => 'failed', 'reason' => json_encode(['error_code' => 0, 'error_data' => $message])])->update(); + return array('status' => false, 'error_code' => 0, 'error_data' => $message); //0 - Physcial file not found + } + + //get excel data to php array + $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path); + $sheet = $spreadsheet->getActiveSheet(); + + $highestRow = $sheet->getHighestRow(); + $highestColumn = $sheet->getHighestColumn(); + + $excel_data = $sheet->rangeToArray('A1:' . $highestColumn . $highestRow); + unset($excel_data[0]); + $excel_data = ExcelSanitizeHelper::sanitizeArrayData($excel_data); + // dd($excel_data); + + //get no of line items and update in DB + $line_items = 0; + + $policyNos = []; + + foreach ($excel_data as $row) { + if (check_row_is_empty_or_null($row)) { + continue; + } + + // row[1] => second column (B column) + $policyNo = $this->sanitizeStatementLookupValue($row[1] ?? ''); + + if ($policyNo !== '') { + $policyNos[] = $policyNo; + } + } + $policyNos = array_values(array_unique($policyNos)); + + // get uploaded month transactions data + // $source_data = $this->PTCOShareDetailsModel->getNonReconcileredPolicyTransactions(insurer_id: $file['insurer_id'], insurer_branch_id: $file['branch_id'], month: $file['month']); + $source_data = !empty($policyNos) ? $this->PTCOShareDetailsModel->getNonReconcileredPolicyTransactionByPolicyAndEndorsement(insurer_id: $file['insurer_id'], insurer_branch_id: $file['branch_id'], policy_no: $policyNos) : []; + // dd($source_data); + + $source_lookup = []; + $source_policy_lookup = []; + $source_endorsement_lookup = []; + + foreach ($source_data as $source_row) { + $source_policy_no = $this->sanitizeStatementLookupValue($source_row['policy_no'] ?? ''); + $source_endorsement_no = $this->sanitizeStatementLookupValue($source_row['endorsement_no'] ?? ''); + $source_entry_key = $source_policy_no . '|' . $source_endorsement_no; + + $source_lookup[$source_entry_key] = true; + $source_policy_lookup[$source_policy_no] = true; + $source_endorsement_lookup[$source_policy_no][$source_endorsement_no] = true; + } + + // check policy no,insurer and etc in DB for this month + // if all good return true, otherwise return false with messssage + $matched_entry = []; + $error_messages = []; // row-wise error storage + + foreach ($excel_data as $excel_key => $excel_row) { + + $row_number = $excel_key; + $is_row_empty = check_row_is_empty_or_null($excel_row); + $policy_source_found = 0; + $endorsement_source_found = 0; + $duplicate_found = 0; + + if (!$is_row_empty) { + + $policy_no = $this->sanitizeStatementLookupValue($excel_row[1] ?? ''); //policy_number from excel + $endorsement_no = $this->sanitizeStatementLookupValue($excel_row[2] ?? ''); //endorsement number from excel + $entry_key = $policy_no . '|' . $endorsement_no; + + if (isset($matched_entry[$entry_key])) { + $duplicate_found = 1; + } elseif (isset($source_lookup[$entry_key])) { + $line_items = $line_items + 1; + $matched_entry[$entry_key] = true; + continue; + } + + $policy_source_found = isset($source_policy_lookup[$policy_no]) ? 1 : 0; + $endorsement_source_found = isset($source_endorsement_lookup[$policy_no][$endorsement_no]) ? 1 : 0; + + // Policy number mismatch + if ($policy_source_found == 0) { + $error_messages[$row_number]['policy_no_mismatch'] = + "Policy number ({$policy_no}) not in NHance."; + } + + // Endorsement number mismatch + if ($policy_source_found == 1 && $endorsement_source_found == 0) { + $error_messages[$row_number]['endorsement_no_mismatch'] = + "Endorsement number ({$endorsement_no}) not in NHance for policy ({$policy_no})."; + } + + // Duplicate check + if ($duplicate_found == 1) { + $error_messages[$row_number]['duplicate'] = + "Duplicate entry found. This policy and endorsement ({$policy_no} | {$endorsement_no}) combination has already been matched."; + } + } + + } + // print_rr($error_messages);die; + + if (!empty($error_messages)) { + $error_data['error_code'] = 2; + $error_data['error_data'] = $error_messages; + $status = 'failed'; + $ret_status = false; + } + + //update in DB + $this->insurerStatements->where('id', $file_id)->set(['line_items' => $line_items, 'file_status' => $status, 'reason' => json_encode($error_data)])->update(); + return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']); + } catch (\Throwable $th) { + + $errorData = [ + 'message' => $th->getMessage(), + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'code' => $th->getCode(), + 'trace' => $th->getTraceAsString(), + 'trace_array' => $th->getTrace(), // full array version (optional) + 'function' => $th->getTrace()[0]['function'] ?? null, + 'class' => $th->getTrace()[0]['class'] ?? null, + ]; + + $this->myLogger->logme("error", "POLICY-TRANSACTION-CONTROLLER - validateInsurerStatement: Exception: " . json_encode($errorData ?? [])); + $this->insurerStatements->where('id', $file_id)->set(['line_items' => 0, 'file_status' => 'failed', 'reason' => json_encode($errorData)])->update(); + return array('status' => false, 'error_code' => [], 'error_data' => $errorData); + } + } + + private function sanitizeStatementLookupValue($value): string + { + $value = trim((string)($value ?? '')); + $value = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $value); + return preg_replace('/[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{00A0}\x{200E}\x{200F}\x{202A}-\x{202E}]/u', '', $value); + } + + public function updateInsurerStatement($params) + { + /* + * Changes made: 12-06-2024 + * - Added safe file_id handling and DB file record validation before using file details. + * - Fixed physical file missing response and error message. + * - Skips empty Excel rows and collects unique policy numbers from the uploaded statement. + * - Fetches NHance source records only for those uploaded policy numbers. + * - Uses the same sanitized policy number + endorsement number matching as validation. + * - Inserts statement details only for matched rows and skips empty or unmatched Excel rows. + * - Added safe default handling for amount and reward columns before calculation/insert. + */ + helper('excel_util_helper'); + $file_id = $params['file_id'] ?? 0; + + $error_data = ['error_code' => '', 'error_data' => []]; + $status = 'success'; + $ret_status = true; + + //get file info + $file = $this->insurerStatements->find((int)$file_id); + // dd($file); + + if (empty($file)) { + //file not found in DB + return array('status' => false, 'error_code' => 0, 'error_data' => 'statement file not found in DB'); + } + $file_name_with_path = WRITEPATH . "/uploads/statements/" . $file['file_name']; + + //check physical file + if (!file_exists($file_name_with_path)) { + //file not found update status and reason + $message = "Physical file not found"; + // echo $message; + $this->myLogger->logme('error', ($message . ' for statement file id ' . $file_id)); + $this->insurerStatements->where('id', $file_id)->set(['file_status' => 'failed', 'reason' => json_encode(['error_code' => 0, 'error_data' => $message])])->update(); + return array('status' => false, 'error_code' => 0, 'error_data' => $message); //0 - Physcial file not found + } + + //get excel data to php array + $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path); + $sheet = $spreadsheet->getActiveSheet(); + + $highestRowAndColumn = $sheet->getHighestRowAndColumn(); + // dd($highestRowAndColumn); + $excel_data = $sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); + unset($excel_data[0]); + $excel_data = ExcelSanitizeHelper::sanitizeArrayData($excel_data); + // dd($excel_data); + + $policyNos = []; + + foreach ($excel_data as $row) { + if (check_row_is_empty_or_null($row)) { + continue; + } + + $policyNo = $this->sanitizeStatementLookupValue($row[1] ?? ''); + + if ($policyNo !== '') { + $policyNos[] = $policyNo; + } + } + $policyNos = array_values(array_unique($policyNos)); + + // get uploaded month transactions data + // $source_data = $this->PTCOShareDetailsModel->getNonReconcileredPolicyTransactions(insurer_id: $file['insurer_id'], insurer_branch_id: $file['branch_id'], month: $file['month']); + $source_data = !empty($policyNos) ? $this->PTCOShareDetailsModel->getNonReconcileredPolicyTransactionByPolicyAndEndorsement(insurer_id: $file['insurer_id'], insurer_branch_id: $file['branch_id'], policy_no: $policyNos) : []; + // Kint::dump($source_data);//die; + // Kint::dump($excel_data); + // die; + $source_lookup = []; + + foreach ($source_data as $source_row) { + $source_policy_no = $this->sanitizeStatementLookupValue($source_row['policy_no'] ?? ''); + $source_endorsement_no = $this->sanitizeStatementLookupValue($source_row['endorsement_no'] ?? ''); + $source_lookup[$source_policy_no . '|' . $source_endorsement_no][] = $source_row; + } + + // check policy no,insurer and etc in DB for this month + // if all good return true, otherwise return false with messssage + $data_to_update = []; + try { + foreach ($excel_data as $excel_row) { + if (check_row_is_empty_or_null($excel_row)) { + continue; + } + + // $policy_start_date = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $excel_row[4]); //policy_start_date from excel + // $policy_end_date = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $excel_row[5]); //policy_end_date from excel + $policy_no = $this->sanitizeStatementLookupValue($excel_row[1] ?? ''); //policy_number from excel + // $client_name = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $excel_row[3]); //clientname from excel + $endorsement_no = $this->sanitizeStatementLookupValue($excel_row[2] ?? ''); //endorsement number from excel + $entry_key = $policy_no . '|' . $endorsement_no; + + if (empty($source_lookup[$entry_key])) { + continue; + } + + $source_row = array_shift($source_lookup[$entry_key]); + + //calculate percentage first + $total_amt = 0; + + // $actual_bp_per = trim($excel_row[9]); //commented becoz this filed removed tfrom excel file + $actual_bp_per = 0; //set default value 0 for maintaining existing code flow + $actual_bp_brokerage = (int) trim((string)($excel_row[5] ?? 0)); + $actual_bp_amt = (int) trim((string)($excel_row[3] ?? 0)); + + if (($actual_bp_brokerage && $actual_bp_brokerage != 0 && $actual_bp_brokerage != "")) { + $total_amt += $actual_bp_brokerage; + //percentage reverse calculation + if (($actual_bp_per == 0 || $actual_bp_per == 0) && !empty($actual_bp_amt)) { + $actual_bp_per = (int) round(($actual_bp_brokerage / $actual_bp_amt) * 100, 2); + } + } else { + $actual_bp_brokerage = $actual_bp_amt * ($actual_bp_per / 100); + $total_amt += $actual_bp_brokerage; + } + + // $actual_tp_per = trim($excel_row[10]);//commented becoz this filed removed tfrom excel file + $actual_tp_per = 0;//set default value 0 for maintaining existing code flow + $actual_tp_brokerage = (int) trim((string)($excel_row[6] ?? 0)); + $actual_tp_amt = (int) trim((string)($excel_row[4] ?? 0)); + + if ($actual_tp_brokerage && $actual_tp_brokerage != 0 && $actual_tp_brokerage != "") { + $total_amt += $actual_tp_brokerage; + //percentage reverse calculation + if (($actual_tp_per == 0 || $actual_tp_per == "") && !empty($actual_tp_amt)) { + $actual_tp_per = (int) round(($actual_tp_brokerage / $actual_tp_amt) * 100, 2); + } + } else { + $actual_tp_brokerage = $actual_tp_amt * ($actual_tp_per / 100); + $total_amt += $actual_tp_brokerage; + } + + // $actual_tep_per = trim($excel_row[11]); + // $actual_tep_brokerage = trim($excel_row[14]); + // $actual_tep_amt = trim($excel_row[8]); + + $actual_tep_per = 0; + $actual_tep_brokerage = 0; + $actual_tep_amt = 0; + + if ($actual_tep_brokerage && $actual_tep_brokerage != 0 && $actual_tep_brokerage != "") { + $total_amt += $actual_tep_brokerage; + //percentage reverse calculation + if (($actual_tep_per == 0 || $actual_tep_per == "") && !empty($actual_tep_amt)) { + $actual_tep_per = ($actual_tep_brokerage / $actual_tep_amt) * 100; + } + } else { + $actual_tep_brokerage = $actual_tep_amt * ($actual_tep_per / 100); + $total_amt += $actual_tep_brokerage; + } + + //find variance + $variance_amt = $source_row['exp_amt'] - $total_amt; + + $data_to_update[] = ['co_share_id' => $source_row['id'], 'actual_bp_amt' => $actual_bp_amt, 'actual_tp_amt' => $actual_tp_amt, 'actual_tep_amt' => $actual_tep_amt, 'actual_bp_per' => $actual_bp_per, 'actual_tp_per' => $actual_tp_per, 'actual_tep_per' => $actual_tep_per, 'variance' => $variance_amt, 'actual_tep_brokerage_amt' => $actual_tep_brokerage, 'actual_tp_brokerage_amt' => $actual_tp_brokerage, 'actual_bp_brokerage_amt' => $actual_bp_brokerage, 'reward' => trim((string)($excel_row[7] ?? '')), 'statement_id' => $file_id]; + } + } catch (\Throwable $th) { + + $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - updateInsurerStatement: Exception: " . $th->getMessage() . " --- Line: " . $th->getLine() . " --- Trace: " . $th->getTraceAsString()); + $errorData = [ + 'message' => $th->getMessage(), + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'code' => $th->getCode(), + 'trace' => $th->getTraceAsString(), + 'trace_array' => $th->getTrace(), // full array version (optional) + 'function' => $th->getTrace()[0]['function'] ?? null, + 'class' => $th->getTrace()[0]['class'] ?? null, + ]; + return ['status' => 'failed', 'code' => 500, 'message' => $th->getMessage(), 'error_data' => $errorData]; + } + // dd($data_to_update); + $this->coShareStmtDetailsModel->insertBatch($data_to_update, 'id'); + // dd($data_to_update); + // if($error_data['error_code']) + // { + // $status = 'failed'; + // $ret_status = false; + // } + //update in DB + $this->insurerStatements->where('id', $file_id)->set(['file_status' => $status, 'reason' => json_encode($error_data), 'invoice_status' => 'pending'])->update(); + return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']); + } + public function getInvoicePaymentDetails() { $statement_id = $this->request->getUri()->getSegment(4); diff --git a/app/Models/PTCOShareDetailsModel.php b/app/Models/PTCOShareDetailsModel.php index 59d53c9c..e619c69d 100644 --- a/app/Models/PTCOShareDetailsModel.php +++ b/app/Models/PTCOShareDetailsModel.php @@ -121,5 +121,41 @@ class PTCOShareDetailsModel extends Model ->get() ->getResultArray(); } + + public function getNonReconcileredPolicyTransactionByPolicyAndEndorsement(string $insurer_id, string $insurer_branch_id, array $policy_no) + { + $builder = $this->db->table('pt_co_share_details pt_co') + ->select(" + pt_co.id, + pt_co.pt_id, + pt_co.exp_amt, + pt.id AS policy_transaction_id, + pt.endorsement_no, + c.client_name, + pt.created_at, + pt_co.insurer_id, + pt_co.insurer_branch_id, + pt.policy_no, + pt.policy_issue_date, + pt.policy_start_date, + pt.policy_end_date, + pt.status, + pt_co.bp_amt, + pt_co.tp_amt, + pt_co.tep_amt, + pt_co.exp_amt, + pt_co.statement_id + ") + ->join('policy_transaction pt', 'pt_co.pt_id = pt.id') + ->join('clients c', 'pt.client_id = c.id') + ->where('pt_co.is_active', 1) + ->where('pt.is_active', 1) + ->where('pt_co.insurer_id', $insurer_id) + ->where('pt_co.insurer_branch_id', $insurer_branch_id) + ->whereIn('pt.policy_no', $policy_no); + + + return $builder->get()->getResultArray(); + } } diff --git a/app/Views/insurer_statement_list.php b/app/Views/insurer_statement_list.php index e6d03e1a..5834ac98 100644 --- a/app/Views/insurer_statement_list.php +++ b/app/Views/insurer_statement_list.php @@ -302,7 +302,15 @@ $isl_col_width_px = nhance_dt_column_widths_px($isl_header_labels, $isl_col_max_ - + + + + + + + - + + Date: Tue, 12 May 2026 15:08:35 +0530 Subject: [PATCH 2/7] CHANGE_EB_CLAIMS_HISTORY_BASED_ON_THE_TOGGLE --- app/Controllers/LeadsController.php | 74 ++++++++++++++++++++++-- app/Views/leads_form.php | 87 ++++++++++++++++++++++++----- app/Views/leads_form_handler.php | 10 +++- app/Views/rfq/gpa.php | 33 +++++++---- app/Views/view_rfq.php | 23 ++++++-- 5 files changed, 194 insertions(+), 33 deletions(-) diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index ce2dc8f0..9eb1da12 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -571,6 +571,70 @@ class LeadsController extends BaseController } + // $leadFormType = (int) ($postData['lead_form_type'] ?? 1); + // $postedPolicyTypeIds = array_map('intval', (array) ($postData['policy_type_id'] ?? [])); + // $isEbRenewalGpaClaimHistory = $leadFormType === 1 + // && (int) ($postData['lead_type'] ?? 0) === 2 + // && in_array(1, $postedPolicyTypeIds, true) + // && (int) ($postData['claim_history'] ?? 0) === 1; + + // if ($isEbRenewalGpaClaimHistory) { + // $rules['first_year.*'] = [ + // 'rules' => 'required|regex_match[/^\d{4}-\d{4}$/]', + // 'errors' => [ + // 'required' => 'Claim Year is required for all entries.', + // 'regex_match' => 'Year must be in format YYYY-YYYY.', + // ], + // ]; + // $rules['emp_id.*'] = [ + // 'rules' => 'required', + // 'errors' => ['required' => 'Employee ID is required in Claim History.'], + // ]; + // $rules['emp_name.*'] = [ + // 'rules' => 'required', + // 'errors' => ['required' => 'Employee Name is required in Claim History.'], + // ]; + // $rules['gender.*'] = [ + // 'rules' => 'required|in_list[Female,Male]', + // 'errors' => [ + // 'required' => 'Gender is required in Claim History.', + // 'in_list' => 'Gender must be Female or Male.', + // ], + // ]; + // $rules['designation.*'] = [ + // 'rules' => 'required', + // 'errors' => ['required' => 'Designation is required in Claim History.'], + // ]; + // $rules['sum_insured.*'] = [ + // 'rules' => 'required|numeric', + // 'errors' => [ + // 'required' => 'Sum Insured is required in Claim History.', + // 'numeric' => 'Sum Insured must be a number.', + // ], + // ]; + // $rules['first_death_date.*'] = [ + // 'rules' => 'required|regex_match[/^[0-9]{2}-([0-9]{2}|[A-Za-z]{3})-[0-9]{4}$/]', + // 'errors' => [ + // 'required' => 'Date of Death is required.', + // 'regex_match' => 'Date of Death must be in DD-MM-YYYY or DD-MMM-YYYY format.', + // ], + // ]; + // $rules['first_cause_of_death.*'] = [ + // 'rules' => 'required|in_list[natural_death,suicide,accident,cardiac_arrest,septic_shock,heart_attack]', + // 'errors' => [ + // 'required' => 'Nature/Cause Of Death is required.', + // 'in_list' => 'Nature/Cause Of Death is invalid.', + // ], + // ]; + // $rules['first_claim_amount.*'] = [ + // 'rules' => 'required|numeric', + // 'errors' => [ + // 'required' => 'Claim/Settled Amount is required.', + // 'numeric' => 'Claim/Settled Amount must be a number.', + // ], + // ]; + // } + // 1. MANUALLY VALIDATE FILES BEFORE PROCESSING // $allFiles = $this->request->getFiles(); // foreach ($allFiles as $inputName => $files) { @@ -2140,6 +2204,8 @@ class LeadsController extends BaseController { helper('excel_util_helper'); $rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type); + $claim_history = $rfq_data['claim_history'] ?? 0; + $is_placement = false; $length = 0; @@ -2176,7 +2242,7 @@ class LeadsController extends BaseController 'No of Dependents' => $rfq_data['incept_dept_count'], 'Total Lives' => $rfq_data['incept_no_of_lives'], - 'Period of Insurance ' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided", + // 'Period of Insurance ' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided", // 'Insurer' => $rfq_data['insurer_name'] ?? " - ", // 'TPA' => $rfq_data['tpa_name'] ?? " - ", // 'Policy Run Days' => $rfq_data['policy_run_days'], @@ -2186,7 +2252,7 @@ class LeadsController extends BaseController 'Insured' => $rfq_data['client_name'], 'No of Employees at Inception' => $rfq_data['incept_emp_count'], // 'Total Sum Insured at Inception ' => $rfq_data['total_si_at_incept'], - 'Policy Period' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided", + // 'Policy Period' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided", // 'Insurer' => $rfq_data['insurer_name'] ?? " - ", // 'TPA' => $rfq_data['tpa_name'] ?? " - ", 'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "", @@ -2279,7 +2345,7 @@ class LeadsController extends BaseController 'Existing Insurer' => $rfq_data['insurer_name'], 'No of Employees at Renewal' => $rfq_data['renewal_emp_count'], 'Total Sum Insured at Renewal' => $rfq_data['total_si_at_renewal'], - 'Claims Experience for last 3 years' => "Mentioned in Claims sheet", + 'Claims Experience for last 3 years' => $claim_history == "1" ? "Mentioned in Claims sheet" : "Nil", // 'Insurer' => $rfq_data['insurer_name'] ?? " - ", // 'TPA' => $rfq_data['tpa_name'] ?? " - ", // 'Total Lives at Inception' => $rfq_data['total_lives_at_incept'], @@ -2796,7 +2862,7 @@ class LeadsController extends BaseController } //claim history new sheet; - if (! empty($rfq_data['fin_years_claims'])) { + if ($claim_history == 1 && !empty($rfq_data['fin_years_claims'])) { $claim_details = json_decode($rfq_data['fin_years_claims'], true) ?? []; diff --git a/app/Views/leads_form.php b/app/Views/leads_form.php index 939d4b57..ddd850a6 100644 --- a/app/Views/leads_form.php +++ b/app/Views/leads_form.php @@ -744,7 +744,7 @@ } - leadTypeBsedHideAndShow(lead_type) + leadTypeBsedHideAndShow(lead_type, false, policy_type_id) if (lead_type == 1) { @@ -851,6 +851,7 @@ .contact_person_email); $('#policy_type_id_1').val(res.data.policy_type_id) .select2(); + togglePolicyDateFields(lead_type, res.data.policy_type_id); $('.loader').fadeOut(); $('.loader-mask').delay(350).fadeOut('slow'); }, 1000) @@ -1093,6 +1094,7 @@ var lead_type = $('#lead_type').val(); console.log("lead_type", lead_type); + togglePolicyDateFields(lead_type); let url = ''; @@ -1281,7 +1283,7 @@ -
+
@@ -1290,7 +1292,7 @@
-
+
@@ -1776,6 +1778,8 @@ const finyearJsonString = gatherClaimExperienceData(policy_type_ids); console.log('finyearJsonString', finyearJsonString); formData.append('finyear', finyearJsonString); + let claimHistoryStatus = $("#claim_history").length > 0 && $("#claim_history").prop("checked") ? 1 : 0; + formData.set('claim_history', claimHistoryStatus); $.ajax({ data: formData, @@ -1898,6 +1902,7 @@ $('#policy_start_date').val(""); $('#policy_end_date').val(""); $("#appendArea_1").empty(); + togglePolicyDateFields(value); // $('#source_policy_start_date').val(""); // $('#source_policy_end_date').val(""); @@ -2019,6 +2024,12 @@ let claimData = []; + if ($("#claim_history").length > 0 && !$("#claim_history").prop("checked")) { + return JSON.stringify({ + "finyear": claimData + }); + } + $(".claim-row").each(function() { let year = $(this).find("[name='first_year[]']").val(); @@ -2156,15 +2167,23 @@ let policy_type_id = $('#policy_type_id_' + count).val() console.log('policy_type_id', policy_type_id); + let lead_type = $('#lead_type').val(); + let showClaimHistorySwitch = lead_type == 2 && policy_type_id == 1; + console.log('claimIndex from parent', claimIndex); let increment = claimIndex; - let claimsFields = ` + let claimsFields = `${showClaimHistorySwitch && !document.querySelector('#claim_history') ? `
+ + +

` : ``}`; + + claimsFields += `
- $year"; @@ -2174,18 +2193,18 @@
- +
- +
- @@ -2194,25 +2213,25 @@
- +
- +
- +
- $death_value) { echo ""; @@ -2222,7 +2241,7 @@
- +
-
+
-
- -
@@ -1261,8 +1294,9 @@ async function viewDetail(id) { function renderCard(opps) { const opp_cont = document.getElementById('opportunitiesContainer'); - opp_cont.innerHTML = opps.length ? opps.map(o => { - let lead_type = o.lead_type == 1 ? 'EB' : 'Non-EB'; + opp_cont.innerHTML = opps.length ? opps.map(o => { + const opportunityFormType = Number(o.lead_form_type || 1); + let lead_form_type = opportunityFormType === 1 ? 'EB' : 'Non-EB'; let status = o.status?.toLowerCase(); let statusClass = { won: 'status-text-won', @@ -1287,7 +1321,7 @@ function renderCard(opps) {
-
${lead_type}
+
${lead_form_type}
@@ -2191,12 +2225,12 @@ function submitToRedirectwithactualLeadIDUrl(){ let actual_lead_id = document.getElementById('opp_lead_id').value; - let lead_type = $('input[name="lead_form_type"]:checked').val(); + let lead_form_type = $('input[name="lead_form_type"]:checked').val(); // URL: /type / actual_lead_id - let url = '' + lead_type + '/' + actual_lead_id ; + let url = '' + lead_form_type + '/' + actual_lead_id ; - console.log('lead_type ', lead_type); + console.log('lead_form_type ', lead_form_type); console.log('opp_lead_id ', actual_lead_id); console.log('url ', url); @@ -2245,7 +2279,8 @@ document.getElementById('do_follow').addEventListener('change', function () { }); -async function openEditLeadModal(id) { +async function openEditLeadModal(id, preservedFields = {}) { + preservedFields = preservedFields || {}; // 1. Reset UI State document.getElementById('hidden_lead_id').value = id; @@ -2290,9 +2325,12 @@ async function openEditLeadModal(id) { form.querySelector('[name="company_name"]').value = lead.company_name || ''; form.querySelector('[name="email"]').value = lead.email || ''; form.querySelector('[name="phone"]').value = lead.phone || ''; - form.querySelector('[name="address"]').value = lead.address || ''; - form.querySelector('[name="website"]').value = lead.website || ''; - form.querySelector('[name="gst_number"]').value = lead.gst_number || ''; + form.querySelector('[name="address"]').value = + Object.prototype.hasOwnProperty.call(preservedFields, 'address') ? preservedFields.address : (lead.address || ''); + form.querySelector('[name="website"]').value = + Object.prototype.hasOwnProperty.call(preservedFields, 'website') ? preservedFields.website : (lead.website || ''); + form.querySelector('[name="gst_number"]').value = + Object.prototype.hasOwnProperty.call(preservedFields, 'gst_number') ? preservedFields.gst_number : (lead.gst_number || ''); // form.querySelector('[name="status"]').value = lead.status || 'New'; form.querySelector('[name="assigned_to"]').value = lead.assigned_to || ''; form.querySelector('[name="assigned_to"]').value = lead.assigned_to || ''; @@ -2384,15 +2422,27 @@ document.getElementById('contactPersonsList').onclick = async (e) => { if (!confirm('Are you sure you want to remove this contact?')) return; try { + const preservedFields = getEditLeadPreservedFields(); const res = await fetch(`${API}/contacts/${contactId}`, { method: 'DELETE' }); if (res.ok) { toastr.success('Removed successfully'); - openEditLeadModal(document.getElementById('hidden_lead_id').value); // Refresh + openEditLeadModal(document.getElementById('hidden_lead_id').value, preservedFields); // Refresh } } catch (err) { console.error(err); } } }; +function getEditLeadPreservedFields() { + const form = document.getElementById('editLeadForm'); + if (!form) return {}; + + return { + address: form.querySelector('[name="address"]')?.value || '', + website: form.querySelector('[name="website"]')?.value || '', + gst_number: form.querySelector('[name="gst_number"]')?.value || '' + }; +} + // --- 2. SAVE / UPDATE LOGIC --- const btnSaveContact = document.getElementById('btnSaveContact'); if (btnSaveContact) { @@ -2417,6 +2467,7 @@ if (btnSaveContact) { const method = editingId ? 'PUT' : 'POST'; try { + const preservedFields = getEditLeadPreservedFields(); const res = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, @@ -2430,7 +2481,7 @@ if (btnSaveContact) { resetContactForm(); // Refresh List - openEditLeadModal(leadId); + openEditLeadModal(leadId, preservedFields); } else { const err = await res.json(); if (err.messages?.name) setContactFieldError('name', err.messages.name); @@ -2537,35 +2588,95 @@ function downloadCsv(filename, rows) { URL.revokeObjectURL(link.href); } -async function exportLeadsCsv() { +function getExportFilename(response, fallbackFilename) { + const disposition = response.headers.get('content-disposition') || ''; + const match = disposition.match(/filename="?([^"]+)"?/i); + return match ? match[1] : fallbackFilename; +} + +async function downloadExportFile(url, fallbackFilename) { + const response = await fetch(url); + const contentType = response.headers.get('content-type') || ''; + + if (!response.ok || contentType.includes('application/json')) { + let message = 'Export failed. Please try again.'; + try { + const json = await response.json(); + message = json.message || json.messages?.error || message; + } catch (err) { + // Keep the generic message when the response cannot be parsed. + } + + return response.status === 404 ? toastr.warning(message) : toastr.error(message); + } + + const blob = await response.blob(); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = getExportFilename(response, fallbackFilename); + link.click(); + URL.revokeObjectURL(link.href); +} + +function initLeadExportDateRangePicker() { + const $button = $('#leadExportExcelBtn'); + if (!$button.length) return; + + if (typeof moment === 'undefined' || typeof $.fn.daterangepicker === 'undefined') { + $button.on('click', () => toastr.error('Date range picker is not available')); + return; + } + + $button.daterangepicker({ + autoUpdateInput: false, + startDate: moment().startOf('month'), + endDate: moment(), + maxDate: moment(), + opens: 'left', + drops: 'down', + locale: { + format: 'DD-MM-YYYY', + applyLabel: 'Generate Excel', + cancelLabel: 'Cancel' + }, + ranges: { + 'Today': [moment(), moment()], + 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], + 'Last 7 Days': [moment().subtract(6, 'days'), moment()], + 'Last 30 Days': [moment().subtract(29, 'days'), moment()], + 'This Month': [moment().startOf('month'), moment()], + 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] + } + }); + + $button.on('apply.daterangepicker', function (ev, picker) { + exportLeadsCsv( + picker.startDate.format('YYYY-MM-DD'), + picker.endDate.format('YYYY-MM-DD') + ); + }); +} + +async function exportLeadsCsv(fromDate, toDate) { const selectedMemberIds = getSelectedMemberIds(salesManagerHeadIds); if (selectedMemberIds.length === 0) return toastr.warning('No members available to export'); + if (!fromDate || !toDate) return toastr.warning('Please select a date range'); const params = new URLSearchParams({ status: filter === 'all' ? '' : filter, search: document.getElementById('mainSearch')?.value || '', - limit: 10000, - offset: 0, assigned_to: selectedMemberIds.join(','), - financial_year: getSelectedFinancialYear() + from_date: fromDate, + to_date: toDate }); - const res = await fetch(`${API}/leads?${params.toString()}`); - const json = await res.json(); - const rows = [ - ['Company', 'Email', 'Phone', 'Status', 'Assigned To', 'Created At'], - ...(json.data || []).map(l => [ - l.company_name || '', - l.email || '', - l.phone || '', - capitalizeStatus(l.status), - l.assigned_to_name || '', - formatIndianDate(l.created_at) - ]) - ]; - downloadCsv(`sales-leads-${getSelectedFinancialYear()}.csv`, rows); + await downloadExportFile( + `${API}/leads/export?${params.toString()}`, + `sales-leads-${fromDate}-to-${toDate}.csv` + ); } +window.addEventListener('load', initLeadExportDateRangePicker); initSalesToolbarFilters(); fetchLeads().then(() => { const leadId = new URLSearchParams(window.location.search).get('lead_id'); From 3b56b0d1f83575d505b5a1358481ed64aa926c9b Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Tue, 12 May 2026 17:17:01 +0530 Subject: [PATCH 6/7] FIX_Opportunities_TOGGLE_ISSUE --- app/Controllers/LeadsController.php | 20 ++++++++++---------- app/Views/leads_form.php | 10 +++++++--- app/Views/leads_form_handler.php | 15 ++++++++++++--- app/Views/leads_non_eb.php | 16 ++++++++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 9eb1da12..66337c36 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -1014,10 +1014,10 @@ class LeadsController extends BaseController $insertCount[] = $insert; $this->insertLeadStatus($insert, $value['status'], 3); - if (in_array((int) $value['lead_type'], [1, 3], true) && $value['status'] == 'won') { - $leadDataForClient = $this->leadsModel->find($insert); - $this->createClientWithLeadData($leadDataForClient); - } + // if (in_array((int) $value['lead_type'], [1, 3], true) && $value['status'] == 'won') { + // $leadDataForClient = $this->leadsModel->find($insert); + // $this->createClientWithLeadData($leadDataForClient); + // } if ($value['lead_form_type'] == 1) { //for this push the job to the calculateMembersDemography() function @@ -1045,12 +1045,12 @@ class LeadsController extends BaseController $this->insertMultiFilesData($data[0]['multi_file_data'], $id, $data[0]['lead_form_type']); $this->insertLeadStatus($id, $data[0]['status'], 3); - if (in_array((int) $data[0]['lead_type'], [1, 3], true) && $data[0]['status'] == 'won') { - $leadDataForClient = $this->leadsModel->find($id); - if (empty($leadDataForClient['is_client_created'])) { - $this->createClientWithLeadData($leadDataForClient); - } - } + // if (in_array((int) $data[0]['lead_type'], [1, 3], true) && $data[0]['status'] == 'won') { + // $leadDataForClient = $this->leadsModel->find($id); + // if (empty($leadDataForClient['is_client_created'])) { + // $this->createClientWithLeadData($leadDataForClient); + // } + // } return $this->respond(['status' => true, 'lead_id' => $id, 'message' => "Opportunity updated successfully", 'data' => $data], 200); } diff --git a/app/Views/leads_form.php b/app/Views/leads_form.php index e2242c4f..836ec3b8 100644 --- a/app/Views/leads_form.php +++ b/app/Views/leads_form.php @@ -822,11 +822,15 @@ $('#lost_reason').val(res.data.lost_reason || ''); $('#notes').val(res.data.notes); - let existingClientId = res.data.client_id || res.data.is_client_created || 0; + const isExistingClient = res.data.client_id !== undefined + && res.data.client_id !== null + && String(res.data.client_id) !== '' + && String(res.data.client_id) !== '0'; + let existingClientId = isExistingClient ? res.data.client_id : 0; console.log('existingClientId', existingClientId); - if (lead_type == 3) { - if (existingClientId && existingClientId != 0) { + if (lead_type == 1 || lead_type == 3) { + if (isExistingClient) { $('#exixting_client').prop('checked', true).trigger('change'); } else { $('#exixting_client').prop('checked', false).trigger('change'); diff --git a/app/Views/leads_form_handler.php b/app/Views/leads_form_handler.php index 96987f29..426f43fe 100644 --- a/app/Views/leads_form_handler.php +++ b/app/Views/leads_form_handler.php @@ -831,6 +831,10 @@ if (isset($selected_lead_type)) { let policy_type_id = data.policy_type_id || null; let lead_type = data.lead_type || null; + const isExistingClient = data.client_id !== undefined + && data.client_id !== null + && String(data.client_id) !== '' + && String(data.client_id) !== '0'; if ([1, 6, 7].includes(policy_type_id)) { let newId = 'appendAreaForClaim_' + dataIncrement; @@ -849,7 +853,7 @@ if (isset($selected_lead_type)) { if (lead_type == 1 || lead_type == 3) { $('.claim-row').hide(); - if(data.is_client_created == null || data.is_client_created == 0 || data.is_client_created == ""){ + if(!isExistingClient){ $('#exixting_client').prop('checked', false); $('#exixting_client').prop('disabled', true); @@ -1117,7 +1121,12 @@ if (isset($selected_lead_type)) { leadTypeBsedHideAndShow(lead_type); - if(data.client_id == 0 || data.client_id == null){ + const isExistingClient = data.client_id !== undefined + && data.client_id !== null + && String(data.client_id) !== '' + && String(data.client_id) !== '0'; + + if(!isExistingClient){ temp_client_id = 0; }else{ temp_client_id = data.client_id @@ -1125,7 +1134,7 @@ if (isset($selected_lead_type)) { if(lead_type == 1 || lead_type == 3){ - if(data.is_client_created == null || data.is_client_created == 0 || data.is_client_created == ""){ + if(!isExistingClient){ $('#exixting_client').prop('checked', false); $('#exixting_client').prop('disabled', true); diff --git a/app/Views/leads_non_eb.php b/app/Views/leads_non_eb.php index a0d9946e..0bd1f5ab 100644 --- a/app/Views/leads_non_eb.php +++ b/app/Views/leads_non_eb.php @@ -561,6 +561,22 @@ var pageBackButton = '