diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 908dfe62..42aab688 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/LeadsController.php b/app/Controllers/LeadsController.php index d4750154..fb1c7bf6 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/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/Helpers/merge_pdf_helper.php b/app/Helpers/merge_pdf_helper.php index 4498b9f8..a192e33e 100644 --- a/app/Helpers/merge_pdf_helper.php +++ b/app/Helpers/merge_pdf_helper.php @@ -141,14 +141,8 @@ if (! function_exists('merge_ticket_pdfs')) { $src = $sourceFile['path']; try { if ($sourceFile['mime'] === 'application/pdf') { - $pageCount = $mpdf->setSourceFile($src); - for ($p = 1; $p <= $pageCount; $p++) { - $tplId = $mpdf->importPage($p); - $mpdf->AddPage(); - // adjustPageSize=true makes the output page match the imported page. - $mpdf->useTemplate($tplId, 0, 0, null, null, true); - $totalPages++; - } + $pageCount = merge_ticket_pdf_add_pdf_pages($mpdf, $src, $tempDir); + $totalPages += $pageCount; log_message('error', "merge_ticket_pdfs | added PDF | file={$src} | pages={$pageCount}"); } elseif (in_array($sourceFile['mime'], ['image/jpeg', 'image/png'], true)) { if (merge_ticket_pdf_add_image_page($mpdf, $src)) { @@ -226,6 +220,81 @@ if (! function_exists('merge_ticket_pdfs')) { } } +if (! function_exists('merge_ticket_pdf_add_pdf_pages')) { + /** + * Import PDF pages. If FPDI cannot import the source (commonly encrypted + * PDFs), normalize through Ghostscript and retry. + */ + function merge_ticket_pdf_add_pdf_pages(Mpdf $mpdf, string $pdfPath, string $tempDir): int + { + try { + return merge_ticket_pdf_import_pdf_pages($mpdf, $pdfPath); + } catch (\Throwable $e) { + log_message('error', "merge_ticket_pdfs | direct PDF import failed | file={$pdfPath} | " . $e->getMessage()); + + $normalizedPath = merge_ticket_pdf_normalize_with_ghostscript($pdfPath, $tempDir); + if ($normalizedPath === null) { + throw $e; + } + + try { + $pageCount = merge_ticket_pdf_import_pdf_pages($mpdf, $normalizedPath); + log_message('error', "merge_ticket_pdfs | Ghostscript normalized PDF imported | original={$pdfPath} | normalized={$normalizedPath} | pages={$pageCount}"); + @unlink($normalizedPath); + return $pageCount; + } catch (\Throwable $normalizedError) { + @unlink($normalizedPath); + throw $normalizedError; + } + } + } +} + +if (! function_exists('merge_ticket_pdf_import_pdf_pages')) { + function merge_ticket_pdf_import_pdf_pages(Mpdf $mpdf, string $pdfPath): int + { + $pageCount = $mpdf->setSourceFile($pdfPath); + for ($p = 1; $p <= $pageCount; $p++) { + $tplId = $mpdf->importPage($p); + $mpdf->AddPage(); + // adjustPageSize=true makes the output page match the imported page. + $mpdf->useTemplate($tplId, 0, 0, null, null, true); + } + + return $pageCount; + } +} + +if (! function_exists('merge_ticket_pdf_normalize_with_ghostscript')) { + function merge_ticket_pdf_normalize_with_ghostscript(string $pdfPath, string $tempDir): ?string + { + $ghostscript = is_executable('/usr/bin/gs') ? '/usr/bin/gs' : trim((string) @shell_exec('command -v gs 2>/dev/null')); + if ($ghostscript === '' || ! is_executable($ghostscript)) { + log_message('error', "merge_ticket_pdfs | Ghostscript not available for PDF normalization | file={$pdfPath}"); + return null; + } + + $normalizedPath = rtrim($tempDir, '/\\') . DIRECTORY_SEPARATOR . 'normalized_' . uniqid('', true) . '.pdf'; + $cmd = escapeshellarg($ghostscript) + . ' -q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite -dCompatibilityLevel=1.4' + . ' -sOutputFile=' . escapeshellarg($normalizedPath) + . ' ' . escapeshellarg($pdfPath) + . ' 2>&1'; + + $output = []; + $exitCode = 1; + @exec($cmd, $output, $exitCode); + + if ($exitCode !== 0 || ! is_file($normalizedPath) || filesize($normalizedPath) <= 0) { + log_message('error', 'merge_ticket_pdfs | Ghostscript normalization failed | file=' . $pdfPath . ' | exit=' . $exitCode . ' | output=' . implode(' ', $output)); + @unlink($normalizedPath); + return null; + } + + return $normalizedPath; + } +} + if (! function_exists('merge_ticket_pdf_resolve_mime')) { /** * Resolve file type from stored MIME, filesystem MIME, and extension. 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_