From e8dd72da0e465445457c35d5e35d057870fcd98b Mon Sep 17 00:00:00 2001 From: Srinivas-Saravanan Date: Mon, 23 Dec 2024 09:20:17 +0530 Subject: [PATCH] FEAT_EXCEL MERGE HELPER ADDED --- app/Controllers/JobWorker.php | 4 + app/Controllers/LeadsController.php | 956 ++++++++++++++++++++-------- app/Helpers/ExcelMergeHelper.php | 269 +++++--- tests/unit/ExcelMergeHelperTest.php | 20 +- tests/unit/LeadsControllerTest.php | 183 ++++++ 5 files changed, 1078 insertions(+), 354 deletions(-) create mode 100644 tests/unit/LeadsControllerTest.php diff --git a/app/Controllers/JobWorker.php b/app/Controllers/JobWorker.php index a0d5f694..fcaf2096 100755 --- a/app/Controllers/JobWorker.php +++ b/app/Controllers/JobWorker.php @@ -126,6 +126,10 @@ class JobWorker extends AdminController 'employeesEnrollmentInsert' => [ 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\EmployeeServiceController', + ], + 'calculateMembersDemography' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\LeadsController', ] ]; diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 856fb649..a1169767 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -4,10 +4,14 @@ namespace App\Controllers; use CodeIgniter\API\ResponseTrait; + +use Exception; + use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Style\Fill; use App\Models\UserModel; use App\Models\ClientModel; @@ -24,13 +28,15 @@ use App\Models\RFQModel; use App\Models\InsurerModel; use App\Helpers\MailHelper; +use App\Helpers\ExcelMergeHelper; +use Google\Service\CloudSearch\PushItem; use Kint; class LeadsController extends BaseController -{ +{ use ResponseTrait; - + //log message protected $myLogger; @@ -89,18 +95,20 @@ class LeadsController extends BaseController } public function viewLeadsList() - { + { // $d = $this->constructExcelToSaveTemp(24, 1, $propsal_and_insurer = null); - // $d = $this->calculateMembersDemography(['lead_id' => 34]); + $job_details = new Jobs(); + // $r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => ['lead_id' => 24]]); + // $d = $this->calculateMembersDemography(['lead_id' => 24]); //$this->mergeQuoteExcelFileWithMembersListExcelFile(24, 1, $propsal_and_insurer = null); // dd($d); $data['page_name'] = 'Leads'; // Set basic data - $data['issuer'] = $this->issuer; - $data['client_type'] = $this->clientType; - $data['lead_type'] = $this->leadType; + $data['issuer'] = $this->issuer; + $data['client_type'] = $this->clientType; + $data['lead_type'] = $this->leadType; $data['lead_status'] = $this->leadsStatus; // Fetch policy types and entity data @@ -136,45 +144,45 @@ class LeadsController extends BaseController $data = $this->prepareLeadData(); // print_r($this->request->getPost()); die; - + if (!$id) { return $this->insertNewLead($data); } else { return $this->updateOldLead($id, $data); } } - + private function prepareLeadData() { $data = $this->request->getPost(); - - if($data['lead_type'] == 2){ + + if ($data['lead_type'] == 2) { $client_data = $this->clientModel->where('id', $data['client_id'])->where('is_active', 1)->first(); $data['client_name'] = $client_data['client_name']; $data['client_short_name'] = $client_data['short_name']; $data['entity_type_id'] = $client_data['entity_type_id']; $data['client_type'] = $client_data['client_type']; - }else{ + } else { $data['client_id'] = 0; $data['client_branch_id'] = 0; $data['source_policy_id'] = 0; } $data['client_code'] = generate_client_code(); - if($data['client_code'] == 2){ + if ($data['client_code'] == 2) { $data['client_code'] = generate_client_code('IC'); } - + $data = $this->prepareMultipleLeadData($data); // print_r($data); die; return $data; } private function prepareMultipleLeadData($data) - { + { // print_r($data); die; $processedData = []; - foreach($data['policy_type_id'] as $index => $value){ + foreach ($data['policy_type_id'] as $index => $value) { // Separate the insurer and insurer branch, handle missing or invalid data @@ -191,7 +199,7 @@ class LeadsController extends BaseController $tpa_branch_id = 0; $tpa_id = 0; } - + // Separate the insurer and insurer branch, handle missing or invalid data if (isset($data['proposed_insurer'][$index]) && strpos($data['proposed_insurer'][$index], '-') !== false) { @@ -209,18 +217,18 @@ class LeadsController extends BaseController $proposed_tpa_id = 0; } - if(!empty($data['policy_start_date'][$index])){ + if (!empty($data['policy_start_date'][$index])) { $policy_start_date = change_date_format($data['policy_start_date'][$index], 'd/m/Y', 'Y-m-d'); - }else{ + } else { $policy_start_date = null; } - - if(!empty($data['policy_end_date'][$index])){ + + if (!empty($data['policy_end_date'][$index])) { $policy_end_date = change_date_format($data['policy_end_date'][$index], 'd/m/Y', 'Y-m-d'); - }else{ + } else { $policy_end_date = null; } - + $processedData[] = [ 'lead_type' => $data['lead_type'], 'issuer' => $data['issuer'], @@ -242,18 +250,18 @@ class LeadsController extends BaseController 'policy_type_id' => $value, 'salse_person_id' => $data['salse_person_id'] ?? 0, - 'insurer_id' => $insurer_id ?? 0, - 'insurer_branch_id' => $insurer_branch_id ?? 0, - 'tpa_id' => $tpa_id ?? 0, - 'tpa_branch_id' => $tpa_branch_id ?? 0, - 'policy_start_date' => $policy_start_date, - 'policy_end_date' => $policy_end_date, - 'no_of_lives' => $data['no_of_lives'][$index] ?? null, - 'claims' => $data['claims'][$index] ?? null, - 'location' => $data['location'][$index] ?? null, - 'proposed_insurer_id' => $proposed_insurer_id ?? 0, - 'proposed_insurer_branch_id' => $proposed_insurer_branch_id ?? 0, - 'proposed_tpa_id' => $proposed_tpa_id ?? 0, + 'insurer_id' => $insurer_id ?? 0, + 'insurer_branch_id' => $insurer_branch_id ?? 0, + 'tpa_id' => $tpa_id ?? 0, + 'tpa_branch_id' => $tpa_branch_id ?? 0, + 'policy_start_date' => $policy_start_date, + 'policy_end_date' => $policy_end_date, + 'no_of_lives' => $data['no_of_lives'][$index] ?? null, + 'claims' => $data['claims'][$index] ?? null, + 'location' => $data['location'][$index] ?? null, + 'proposed_insurer_id' => $proposed_insurer_id ?? 0, + 'proposed_insurer_branch_id' => $proposed_insurer_branch_id ?? 0, + 'proposed_tpa_id' => $proposed_tpa_id ?? 0, 'proposed_tpa_branch_id' => $proposed_tpa_branch_id ?? 0, 'status' => $data['status'] ?? null, @@ -263,59 +271,58 @@ class LeadsController extends BaseController return $processedData; } - + private function insertNewLead($data) - { + { $insertCount = []; - foreach($data as $value){ + foreach ($data as $value) { $insert = $this->leadsModel->insert($value); $insertCount[] = $insert; $this->insertLeadStatus($insert, $value['status'], 3); } if (count($insertCount) > 0) { - return $this->respond(['status' => true, 'lead_id' => $insert, 'message' => 'New Lead created successfully', 'data' =>$data], 200); + return $this->respond(['status' => true, 'lead_id' => $insert, 'message' => 'New Lead created successfully', 'data' => $data], 200); } - return $this->respond(['status' => false, 'lead_id' => $insert, 'message' => "Failed to create Lead", 'data' =>$data], 200); + return $this->respond(['status' => false, 'lead_id' => $insert, 'message' => "Failed to create Lead", 'data' => $data], 200); } - + private function updateOldLead($id, $data) - { + { if ($this->leadsModel->where('id', $id)->set($data[0])->update()) { $this->insertLeadStatus($id, $data[0]['status'], 3); - return $this->respond(['status' => true, 'lead_id' => $id, 'message' => "Lead updated successfully", 'data' =>$data], 200); + return $this->respond(['status' => true, 'lead_id' => $id, 'message' => "Lead updated successfully", 'data' => $data], 200); } - return $this->respond(['status' => false, 'lead_id' => $id, 'message' => "Failed to update Lead", 'data' =>$data], 200); + return $this->respond(['status' => false, 'lead_id' => $id, 'message' => "Failed to update Lead", 'data' => $data], 200); } // Get the Single Lead data for edit public function getLeadDataForEdit($id) - { + { $data = $this->leadsModel - ->where('leads.id', $id) - ->where('leads.is_active', 1) - ->first(); + ->where('leads.id', $id) + ->where('leads.is_active', 1) + ->first(); - if(!empty($data['policy_start_date'])){ + if (!empty($data['policy_start_date'])) { $data['policy_start_date'] = change_date_format($data['policy_start_date'], 'Y-m-d', 'd/m/Y'); - }else{ + } else { $data['policy_start_date'] = null; } - if(!empty($data['policy_end_date'])){ + if (!empty($data['policy_end_date'])) { $data['policy_end_date'] = change_date_format($data['policy_end_date'], 'Y-m-d', 'd/m/Y'); - }else{ + } else { $data['policy_end_date'] = null; } - - if($data){ + + if ($data) { return $this->respond(['status' => true, 'data' => $data], 200); - }else{ + } else { return $this->respond(['status' => false], 200); } - } private function insertLeadStatus($primaryKey, $status, $statusType) @@ -333,39 +340,40 @@ class LeadsController extends BaseController //--------RFQ----------------------------------------------------------------------------------------------- - public function viewRFQ($id, $type = 1){ + public function viewRFQ($id, $type = 1) + { $data['rfq_data'] = $this->RFQModel - ->where('lead_id', $id) - ->where('type', $type) - ->where('is_active', 1) - ->first(); + ->where('lead_id', $id) + ->where('type', $type) + ->where('is_active', 1) + ->first(); $data['rfq_count'] = $this->RFQModel - ->where('lead_id', $id) - ->where('type', 1) - ->where('is_active', 1) - ->countAllResults(); + ->where('lead_id', $id) + ->where('type', 1) + ->where('is_active', 1) + ->countAllResults(); $data['qcr_count'] = $this->RFQModel - ->where('lead_id', $id) - ->where('type', 2) - ->where('is_active', 1) - ->countAllResults(); + ->where('lead_id', $id) + ->where('type', 2) + ->where('is_active', 1) + ->countAllResults(); // dd(count($data['rfq_data'])); $data['lead_id'] = $id; $lead_data = $this->leadsModel - ->select('leads.*, policy_type.question_json') - ->join('policy_type', 'leads.policy_type_id = policy_type.id') - ->where('leads.id', $id) - ->where('leads.is_active', 1) - ->first(); + ->select('leads.*, policy_type.question_json') + ->join('policy_type', 'leads.policy_type_id = policy_type.id') + ->where('leads.id', $id) + ->where('leads.is_active', 1) + ->first(); // dd($lead_data); - if($lead_data['lead_type'] == 2){ + if ($lead_data['lead_type'] == 2) { $client_policy_data = $this->clientPolicyModel->where('is_active', 1)->where('id', $lead_data['source_policy_id'])->first(); $data['policy_terms'] = $client_policy_data['policy_terms']; } @@ -380,7 +388,8 @@ class LeadsController extends BaseController $this->loadLayout('view_rfq.php', $data); } - public function createRFQ(){ + public function createRFQ() + { $data = $this->request->getPost(); $lead_id = $data['lead_id']; @@ -395,14 +404,14 @@ class LeadsController extends BaseController $result = $this->RFQModel->insert($data); if ($result) { - return $this->respond(['status' => true, 'id' => $result, 'message' => 'RFQ created successfully', 'data' =>$data], 200); + return $this->respond(['status' => true, 'id' => $result, 'message' => 'RFQ created successfully', 'data' => $data], 200); } - return $this->respond(['status' => false, 'id' => $result, 'message' => "Failed to create RFQ", 'data' =>$data], 200); - + return $this->respond(['status' => false, 'id' => $result, 'message' => "Failed to create RFQ", 'data' => $data], 200); } - public function createQCR(){ + public function createQCR() + { $data = $this->request->getPost(); $lead_id = $data['lead_id']; @@ -418,10 +427,10 @@ class LeadsController extends BaseController $result = $this->RFQModel->insert($data); if ($result) { - return $this->respond(['status' => true, 'id' => $result, 'message' => 'QCR created successfully', 'data' =>$data], 200); + return $this->respond(['status' => true, 'id' => $result, 'message' => 'QCR created successfully', 'data' => $data], 200); } - return $this->respond(['status' => false, 'id' => $result, 'message' => "Failed to create QCR", 'data' =>$data], 200); + return $this->respond(['status' => false, 'id' => $result, 'message' => "Failed to create QCR", 'data' => $data], 200); } @@ -429,16 +438,17 @@ class LeadsController extends BaseController //export main route function - public function exportQCRandRFQ($lead_id, $type, $export_type){ - $this-> exportExcelForQCRandRFQ($lead_id, $type); + public function exportQCRandRFQ($lead_id, $type, $export_type) + { + $this->exportExcelForQCRandRFQ($lead_id, $type); } //FOR EXCEL public function exportExcelForQCRandRFQ($lead_id, $type) { - //$filepath = $this->constructExcelToSaveTemp($lead_id, $type); + $filepath = $this->constructExcelToSaveTemp($lead_id, $type); $filepath = $filepath['filePath']; - + if (file_exists($filepath)) { // Set headers to force download header('Content-Description: File Transfer'); @@ -446,24 +456,24 @@ class LeadsController extends BaseController header('Content-Disposition: attachment; filename="' . basename($filepath) . '"'); header('Content-Length: ' . filesize($filepath)); header('Pragma: public'); - + // Output the file content readfile($filepath); - + // Delete the file after download unlink($filepath); - + exit; } else { echo "File does not exist."; } } - + //Construct excel file and save the file to the folder and return file path public function constructExcelToSaveTemp($lead_id, $type, $propsal_and_insurer = null) - { + { $rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type); - + // dd($rfq_data, $lead_id, $type); $lead_data = [ @@ -471,20 +481,20 @@ class LeadsController extends BaseController 'Insurer' => $rfq_data['insurer_name'] . ' - ' . $rfq_data['insurer_branch_name'], 'TPA' => $rfq_data['tpa_name'] . ' - ' . $rfq_data['tpa_branch_name'], ]; - + $data = json_decode($rfq_data['json'], true); - if($type == 2){ + if ($type == 2) { $data = $this->convertJsonForQCR($data, 'stc'); - if($propsal_and_insurer !== null){ + if ($propsal_and_insurer !== null) { list($proposal_key, $insurer_key) = explode('-', $propsal_and_insurer, 2); $data = $this->transformProposelData($data, $proposal_key, $insurer_key); } } - + $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); - + // Start with lead_data at the top $rowNumber = 1; foreach ($lead_data as $key => $value) { @@ -504,26 +514,26 @@ class LeadsController extends BaseController ], ]); - + $rowNumber += 2; // Add headers and subheaders $headers = $data['table_data']['headers']; $subHeaderRow = $rowNumber + 1; $columnLetter = 'A'; - + foreach ($headers as $header) { if (in_array($header['parentHeader'], ['Item Key', 'Action'])) { continue; } - + if ($header['parentHeader'] === 'Sno') { $header['parentHeader'] = 'S.No.'; } - + $startColumn = $columnLetter; // Start of the current header range $subHeaderCount = count($header['subHeaders']); // Number of subheaders for this parent header - + // Set parent header value $sheet->setCellValue("{$startColumn}{$rowNumber}", $header['parentHeader']); $sheet->getStyle("{$startColumn}{$rowNumber}")->applyFromArray([ @@ -533,7 +543,7 @@ class LeadsController extends BaseController 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER, ], ]); - + // Merge header cells if it spans multiple subheaders if ($subHeaderCount > 1) { $endColumn = chr(ord($startColumn) + $subHeaderCount - 1); // Calculate the end column @@ -541,7 +551,7 @@ class LeadsController extends BaseController } else { $endColumn = $startColumn; // No merge needed if only one subheader } - + // Add subheaders foreach ($header['subHeaders'] as $subHeader) { $sheet->setCellValue("{$columnLetter}{$subHeaderRow}", $subHeader); @@ -555,7 +565,7 @@ class LeadsController extends BaseController $columnLetter++; // Move to the next column for subheaders } } - + // Apply border to the header range $headerRange = "A{$rowNumber}:" . chr(ord($columnLetter) - 1) . "{$subHeaderRow}"; $sheet->getStyle($headerRange)->applyFromArray([ @@ -570,7 +580,7 @@ class LeadsController extends BaseController // Increase row height for headers and subheaders $sheet->getRowDimension($rowNumber)->setRowHeight(30); // Header row height $sheet->getRowDimension($subHeaderRow)->setRowHeight(25); // Subheader row height - + $rowNumber = $subHeaderRow + 2; $column_data = $data['table_data']['data']; $serial_no = 1; @@ -604,18 +614,18 @@ class LeadsController extends BaseController ], ], ]); - - if($type == 2){ + + if ($type == 2) { $rowNumber += 2; - + // Add premium data $premiumData = $data['premium_data']['data']; $premium = ['Premium']; $gst = ['GST']; $total = ['Total']; - + foreach ($premiumData as $proposal => $insurers) { foreach ($insurers as $insurer => $values) { $premium[] = $values['Premium']; @@ -623,7 +633,7 @@ class LeadsController extends BaseController $total[] = $values['Total']; } } - + foreach ([$premium, $gst, $total] as $index => $rowData) { $columnLetter = 'B'; foreach ($rowData as $key => $value) { @@ -645,23 +655,22 @@ class LeadsController extends BaseController ], ], ]); - } - + // Auto-size columns foreach ($sheet->getColumnIterator() as $column) { $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true); } - + // Set filename $string = ($type == 2) ? 'QCR' : 'RFQ'; $filename = "{$string}_{$rfq_data['client_short_name']}_{$rfq_data['policy_type']}_" . date('YmdHis') . '.xlsx'; - + // Save to temporary location $uploadFilePath = WRITEPATH . 'tmp/' . $filename; $writer = new Xlsx($spreadsheet); $writer->save($uploadFilePath); - + return [ 'filePath' => $uploadFilePath, 'fileName' => $filename, @@ -670,60 +679,54 @@ class LeadsController extends BaseController //Merge RFQ/QCR file with lead members file public function mergeQuoteExcelFileWithMembersListExcelFile($lead_id, $type, $source_file_path) - { + { $rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type); - if($source_file_path && $rfq_data['file_name']) - { - + if ($source_file_path && $rfq_data['file_name']) { } return true; } public function calculateMembersDemography($params) - { + { $lead_id = $params['lead_id']; $lead_data = $this->leadsModel->find($lead_id); - // print_rr($lead_data); - if($lead_data['file_name']) - { - $file_name_with_path = WRITEPATH."/uploads/lead_files/".$lead_data['file_name']; - + if ($lead_data['file_name']) { + $file_name_with_path = WRITEPATH . "/uploads/lead_files/" . $lead_data['file_name']; + //check physical file - if(!file_exists($file_name_with_path)) - { + if (!file_exists($file_name_with_path)) { //file not found update status and reason - $message = "Lead Physcial file not found"; + $message = "Lead Physcial file not found"; // echo $message; - $this->myLogger->logme('error',($message . ' for file ' . $file_name_with_path)); - return ['status' => 'failed','message' => 'no physcial file']; + $this->myLogger->logme('error', ($message . ' for file ' . $file_name_with_path)); + return ['status' => 'failed', 'message' => 'no physical file']; } - $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path); + $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path); - //get members data - $members_sheet = $spreadsheet->getSheet(0); - $highestRowAndColumn = $members_sheet->getHighestRowAndColumn(); - // dd($highestRowAndColumn); - $members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); - // dd($members); + //get members data + $members_sheet = $spreadsheet->getSheet(0); + $highestRowAndColumn = $members_sheet->getHighestRowAndColumn(); + // dd($highestRowAndColumn); + $members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); + // dd($members[6]); - //get age band data - $age_band_sheet = $spreadsheet->getSheet(1); - $highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn(); - $age_band_data = $age_band_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); - print_r($age_band_data); + //get age band data + $age_band_sheet = $spreadsheet->getSheet(1); + $highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn(); + $age_band_data = $age_band_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); - //check age or dob column - $members_heading = $members[0]; - Kint::dump($members_heading); - $available_col = null; + + //check age or dob column + $members_heading = $members[0]; + $available_col = null; $col_index = null; - if (in_array('age', array_map('strtolower',$members_heading))) { + if (in_array('age', array_map('strtolower', $members_heading))) { $available_col = 'age'; - $col_index = array_search('age',array_map('strtolower',$members_heading)); + $col_index = array_search('age', array_map('strtolower', $members_heading)); } elseif (in_array('dob', array_map('strtolower', $members_heading))) { $available_col = 'dob'; $col_index = array_search('dob', array_map('strtolower', $members_heading)); @@ -731,28 +734,358 @@ class LeadsController extends BaseController if ($available_col == null) { $message = 'No DOB or Age column '; - $this->myLogger->logme('error',($message . $file_name_with_path)); - return ['status' => 'failed','message' => $message]; + $this->myLogger->logme('error', ($message . $file_name_with_path)); + return ['status' => 'failed', 'message' => $message]; + } + try { + $classifiers = $this->getDemographyData($members, $age_band_data, $members_heading, $available_col, $col_index); + } catch (Exception $e) { + return ['status' => 'fail', 'message' => $e->getMessage()]; } - //convert age slab data into array - - } - else - { - $this->myLogger->logme('error',(' no file found ' . $file_name_with_path)); - return ['status' => 'failed','message' => 'no file found']; - } + try { + $result = $this->generateClassifierSpreadsheet($classifiers, 'writable/uploads/lead_files'); + if ($result['success']) { + $this->myLogger->logme('error', "Spreadsheet generated successfully!"); + echo "Location: " . $result['fullpath'] . "\n"; + echo "Filename: " . $result['filename'] . "\n"; + $filePaths = [ + ['file_path' => WRITEPATH . 'uploads/lead_files/' . $lead_data['file_name'], 'sheets' => [0, 1]], + ['file_path' => 'writable/uploads/lead_files/' . $result['filename'], 'sheets' => []], + ]; + $outputPath = WRITEPATH . 'uploads/lead_files/Member_Data.xlsx'; + $result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); + if ($result) { + return ['status' => 'success', 'message' => 'Member_Data Merged Suceesfully']; + } + } else { + echo "Error generating spreadsheet: " . $result['error']; + } + } catch (Exception $e) { + echo "Error: " . $e->getMessage(); + return ['status' => 'fail', 'message' => $e->getMessage()]; + } + } else { + $this->myLogger->logme('error', (' no file found ' . $file_name_with_path)); + return ['status' => 'failed', 'message' => 'no file found']; + } } - + public function getDemographyData($members, $age_band_data, $members_heading, $available_col, $col_index) + { + $first_loop = 0; + $col_index_si = array_search('si enhancement', array_map('strtolower', $members_heading)); + $col_index_relationship = array_search('relationship', array_map('strtolower', $members_heading)); + $relations = []; + $si_amt = ['general']; // Initialize with 'general' as per first loop condition + + // First pass - collect unique relations and SI amounts + foreach ($members as $member) { + if ($first_loop == 0) { + $first_loop++; + continue; + } + if ($member == null) { + continue; + } + + if (!in_array($member[$col_index_relationship], $relations) && $member[$col_index_relationship] != null) { + $relations[] = $member[$col_index_relationship]; + } + + if (!in_array($member[$col_index_si], $si_amt) && $member[$col_index_si] != null) { + $si_amt[] = $member[$col_index_si]; + } + } + // Initialize classifier array + $classifiers = []; + foreach ($si_amt as $si) { + $classifiers[$si] = []; + // Initialize relation counts including Grand Total row + foreach ($relations as $relation) { + $classifiers[$si][$relation] = []; + foreach ($age_band_data as $age_bands) { + foreach ($age_bands as $age_interval) { + $classifiers[$si][$relation][$age_interval] = 0; + } + } + // Add Grand Total column for each relation echo('test passed 3.5'); + + $classifiers[$si][$relation]['Grand Total'] = 0; + } + // Initialize Grand Total row + $classifiers[$si]['Grand Total'] = []; + foreach ($age_band_data as $age_bands) { + foreach ($age_bands as $age_interval) { + $classifiers[$si]['Grand Total'][$age_interval] = 0; + } + } + // Add grand total of grand totals + $classifiers[$si]['Grand Total']['Grand Total'] = 0; + } + // Process members and count them + foreach ($members as $member) { + if ($first_loop == 0) { + $first_loop++; + continue; + } + // Get age + $age = $this->getAge($available_col, $member, $col_index); + + + $relation = $member[$col_index_relationship]; + $member_si = $member[$col_index_si]; + + // Find the correct age band (only once per member) + $found_band = false; + foreach ($age_band_data as $age_bands) { + if ($found_band) break; + + foreach ($age_bands as $age_band) { + //get Min and Max Age + list($min_age, $max_age) = $this->getAgeRange($age_band); + + // If age fits in this band + if ($age >= $min_age && $age <= $max_age) { + // Update counts for general category + if (isset($classifiers['general'][$relation][$age_band])) { + // Increment count for specific relation and age band + $classifiers['general'][$relation][$age_band]++; + // Update row total (Grand Total column) + $classifiers['general'][$relation]['Grand Total']++; + // Update column total (Grand Total row) + $classifiers['general']['Grand Total'][$age_band]++; + // Update grand total of grand totals + $classifiers['general']['Grand Total']['Grand Total']++; + } + + // Update counts for specific SI category + if (isset($classifiers[$member_si][$relation][$age_band])) { + // Increment count for specific relation and age band + $classifiers[$member_si][$relation][$age_band]++; + // Update row total (Grand Total column) + $classifiers[$member_si][$relation]['Grand Total']++; + // Update column total (Grand Total row) + $classifiers[$member_si]['Grand Total'][$age_band]++; + // Update grand total of grand totals + $classifiers[$member_si]['Grand Total']['Grand Total']++; + } + + $found_band = true; + break; + } + } + } + } + return $classifiers; + } + + public function generateClassifierSpreadsheet($classifiers, $outputDir = 'exports') + { + + if (!file_exists($outputDir)) { + if (!mkdir($outputDir, 0755, true)) { + throw new Exception("Failed to create directory: $outputDir"); + } + } + + // Check if directory is writable + if (!is_writable($outputDir)) { + throw new Exception("Directory is not writable: $outputDir"); + } + + // Generate unique filename + $timestamp = date('Y-m-d_His'); + $filename = "member_classification_{$timestamp}.xlsx"; + $filepath = $outputDir . DIRECTORY_SEPARATOR . $filename; + + // Check if file already exists (shouldn't happen with timestamp, but just in case) + if (file_exists($filepath)) { + $counter = 1; + while (file_exists($outputDir . DIRECTORY_SEPARATOR . "member_classification_{$timestamp}_{$counter}.xlsx")) { + $counter++; + } + $filename = "member_classification_{$timestamp}_{$counter}.xlsx"; + $filepath = $outputDir . DIRECTORY_SEPARATOR . $filename; + } + + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // Get all age bands + $age_bands = array_keys(reset($classifiers['general'])); + array_pop($age_bands); // Remove 'Grand Total' + $age_bands[] = 'Grand Total'; // Add it back at the end + + $currentRow = 5; // Start from row 5 to match the example + + // Function to write section data + $writeSectionData = function ($data, $sheet, &$currentRow) use ($age_bands) { + // Set headers + $sheet->setCellValue('B' . $currentRow, 'Relationship'); + $col = 'C'; + foreach ($age_bands as $band) { + $sheet->setCellValue($col . $currentRow, $band); + $col++; + } + + // Style headers + $lastCol = chr(ord('B') + count($age_bands)); + $headerRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; + $sheet->getStyle($headerRange)->applyFromArray([ + 'font' => ['bold' => true], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'], + ], + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ], + ]); + + $currentRow++; + + // Write data rows + foreach ($data as $relation => $values) { + if ($relation !== 'Grand Total') { + $sheet->setCellValue('B' . $currentRow, $relation); + $col = 'C'; + foreach ($age_bands as $band) { + $value = $values[$band] ?: ''; // Convert 0 to empty string + $sheet->setCellValue($col . $currentRow, $value); + $col++; + } + + // Style data row + $dataRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; + $sheet->getStyle($dataRange)->applyFromArray([ + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'], + ], + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ], + ]); + + $currentRow++; + } + } + + // Add Grand Total row + $sheet->setCellValue('B' . $currentRow, 'Grand Total'); + $col = 'C'; + foreach ($age_bands as $band) { + $sheet->setCellValue($col . $currentRow, $data['Grand Total'][$band]); + $col++; + } + + // Style Grand Total row + $totalRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; + $sheet->getStyle($totalRange)->applyFromArray([ + 'font' => ['bold' => true], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'], + ], + ], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => ['rgb' => 'F2F2F2'], + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ], + ]); + + $currentRow += 3; // Add gap after each section + }; + + // Write each SI section with gaps + foreach ($classifiers as $si_type => $data) { + $writeSectionData($data, $sheet, $currentRow); + } + + // Auto-size columns + foreach (range('B', chr(ord('B') + count($age_bands))) as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + + // Create Excel file + try { + // Create Excel file + $writer = new Xlsx($spreadsheet); + $writer->save($filepath); + + // Verify file was created successfully + if (!file_exists($filepath)) { + throw new Exception("Failed to create file: $filepath"); + } + + return [ + 'success' => true, + 'filepath' => $filepath, + 'filename' => $filename, + 'fullpath' => realpath($filepath) + ]; + } catch (Exception $e) { + return [ + 'success' => false, + 'error' => $e->getMessage() + ]; + } + } + + public function getAge($available_col, $member, $col_index) + { + if ($available_col == 'age') { + $age = $member[$col_index]; + } else { + $dob = $member[$col_index]; + if ($dob == 'DOB') { + return; + } + $dobDate = \DateTime::createFromFormat('d-M-Y', $dob); + $currentDate = new \DateTime(); + if ($dobDate == false) { + $this->myLogger->logme('error', $dob . 'is not valid'); + return; + } + $age = $currentDate->diff($dobDate)->y; + } + return $age; + } + + public function getAgeRange($age_band) + { + $age_band = trim($age_band); + // Parse age range + if (strpos($age_band, '-') === false) { + $min_age = (int)filter_var($age_band, FILTER_SANITIZE_NUMBER_INT); + $max_age = PHP_INT_MAX; + } else { + $parts = explode("-", $age_band); + $min_age = (int)$parts[0]; + $max_age = (int)$parts[1]; + } + + return array($min_age, $max_age); + } + + //this funciton for send mail to insurer and client with either RFQ/QCR public function sendMailWithAttachement() { helper('excel_util_helper'); helper('MailHelper'); - + helper('ExcelMergeHelper'); $params = $this->request->getGet(); // print_r($params); die; $lead_id = $params['lead_id']; @@ -777,9 +1110,9 @@ class LeadsController extends BaseController ->first(); $cc_mails = []; - + //get CC Mails - if($recipient_type == 'internal' || $recipient_type == 'placement'){ + if ($recipient_type == 'internal' || $recipient_type == 'placement') { $cc_data = isset($params['cc']) ? $params['cc'] : ""; $param_cc_mail = json_decode($cc_data, true); @@ -796,18 +1129,17 @@ class LeadsController extends BaseController $cc_mails = array_column($userData, 'email'); // print_r(json_encode($cc_mails)); die; - + // If no emails were found, return an error response if (empty($cc_mails)) { - return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'No valid CC mail addresses found!'], 200); + return $this->respond(['status' => 'failed', 'code' => 400, 'data' => '', 'message' => 'No valid CC mail addresses found!'], 200); } - } else { // Handle case where param_cc_mail is not valid - return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'CC mail not found!'], 200); + return $this->respond(['status' => 'failed', 'code' => 400, 'data' => '', 'message' => 'CC mail not found!'], 200); } } - + if ($recipient_type == 'client' && ($lead_data['contact_person_email'] == '' || $lead_data['contact_person_email'] == null)) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => '', 'messgae' => 'Recipient mail not found ! '], 200); } @@ -817,12 +1149,28 @@ class LeadsController extends BaseController //get file path to attach $file_info = $this->constructExcelToSaveTemp($lead_id, ($file_type == 'rfq' ? 1 : 2), $propsal_and_insurer); + if ($lead_data['file_name'] != null && $lead_data['file_name'] != '') { + $temp_file_path = $file_info['filePath']; + $temp_file_name = $file_info['fileName']; + $lead_file_path = WRITEPATH . 'uploads/lead_files/' . $lead_data['file_name']; + if ($lead_file_path) { + $filePaths = [ + ['file_path' => $temp_file_path, 'sheets' => []], + ['file_path' => $lead_file_path, 'sheets' => []] + ]; + $outputPath = dirname($temp_file_path) . '/' . 'merged_' . $temp_file_name; + $result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); + // print_rr($result); + } + } + // print_rr($lead_data); + // print_rr($file_info); // $file_path = WRITEPATH."uploads/excel/sample/correction.xls"; // $file_name = $file_type.'.xlsx'; // !dd($file_info); - - $file_path = $file_info['filePath']; - $file_name = $file_info['fileName']; + + $file_path = $result; + $file_name = basename($result); $attachments = [['fileName' => $file_name, 'filePath' => $file_path]]; //get recipient address @@ -837,9 +1185,9 @@ class LeadsController extends BaseController // print_r($recipient_data); die; - } else if($recipient_type == 'client') { + } else if ($recipient_type == 'client') { $recipient_data = [['name' => $lead_data['contact_person_name'], 'email' => $lead_data['contact_person_email']]]; - }else{ + } else { $recipient_data = [['name' => "Team", 'email' => $params['to']]]; } // print_r($recipient_data); die; @@ -847,7 +1195,7 @@ class LeadsController extends BaseController $subject = $file_type == 'rfq' ? 'Request for Quotation from ' . $lead_data['client_name'] . ' for ' . $lead_data['policy_type'] : 'Quotation Comparison Report for ' . $lead_data['policy_type']; $original_message = '

Request for Quotation (RFQ)

Dear {{RECIPIENT_NAME}},

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

RFQ Details

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

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

Best regards,

Nhance India Pvt Ltd

© Nhance India Pvt Ltd. All rights reserved.

'; - if($recipient_data){ + if ($recipient_data) { foreach ($recipient_data as $recipient) { $message = $original_message; @@ -859,13 +1207,13 @@ class LeadsController extends BaseController // print_rr($message);calculate_days_bw_dates - $res = MailHelper::send_email(['mail' => $recipient['email'], 'cc'=> $cc_mails, 'subject' => $subject, 'message' => $message, 'attachments' => $attachments, 'reply_to' => $reply_to]); + $res = MailHelper::send_email(['mail' => $recipient['email'], 'cc' => $cc_mails, 'subject' => $subject, 'message' => $message, 'attachments' => $attachments, 'reply_to' => $reply_to]); // !dd($res); $result_data[] = ['mail' => $recipient['email'], 'status' => $res]; } } - if($recipient_type == 'placement'){ + if ($recipient_type == 'placement') { list($proposal_key, $insurer_key) = explode('-', $propsal_and_insurer, 2); @@ -873,7 +1221,7 @@ class LeadsController extends BaseController 'proposel_name' => $proposal_key, 'insurer_name' => $insurer_key, 'insurer' => $params['insurer_and_branch'], - ] ; + ]; $data = [ 'proposel_data' => json_encode($lead_update_data), @@ -893,12 +1241,11 @@ class LeadsController extends BaseController $data = $this->levelContactModel->getContactForRFQ(); - if($data){ + if ($data) { return $this->respond(['status' => true, 'data' => $data], 200); - }else{ + } else { return $this->respond(['status' => false], 200); } - } public function getInsurerBranchContacts($insurer_and_branch_id) @@ -906,12 +1253,12 @@ class LeadsController extends BaseController if (strpos($insurer_and_branch_id, '-') === false) { return $this->respond(['status' => false, 'message' => 'Invalid ID format'], 400); } - + // Split the insurer_and_branch_id list($insurerBranchId, $insurerId) = explode('-', $insurer_and_branch_id); - + $data = $this->levelContactModel->getContactForRFQ($insurerId, $insurerBranchId); - + if (!empty($data)) { return $this->respond(['status' => true, 'data' => $data], 200); } else { @@ -919,12 +1266,13 @@ class LeadsController extends BaseController } } - function transformProposelData($data, $proposel, $insurer){ + function transformProposelData($data, $proposel, $insurer) + { // print_r($data['premium_data']['data']); die; $headerData = []; - + // Default headers: S. No and Particulars $defaultHeaders = [ [ @@ -936,10 +1284,10 @@ class LeadsController extends BaseController 'subHeaders' => ['-'] ] ]; - + // Add default headers to the result $headerData = array_merge($headerData, $defaultHeaders); - + foreach ($data['table_data']['headers'] as $header) { // Check if the parentHeader matches the target proposal if ($header['parentHeader'] === $proposel) { @@ -966,13 +1314,13 @@ class LeadsController extends BaseController $sno = $entry['SNO']; $items = $entry['items']; $dataEntry = $entry['data']; - + $result = [ "SNO" => $sno, "items" => $items, "data" => [] ]; - + foreach ($dataEntry as $item) { // Include Sno and Particulars by default @@ -984,7 +1332,7 @@ class LeadsController extends BaseController "input_value" => $item['input_value'] ]; } - + // Include Proposal with Quote Asked by default if ($item['parentth'] === $proposel && $item['subth'] === "Quote Asked") { $result['data'][] = [ @@ -994,7 +1342,7 @@ class LeadsController extends BaseController "input_value" => $item['input_value'] ]; } - + // Example of including matching specific proposals and insurers if ($item['parentth'] === $proposel && $item['subth'] === $insurer) { $result['data'][] = [ @@ -1005,13 +1353,13 @@ class LeadsController extends BaseController ]; } } - + // Add to the final result $columnData[] = $result; } - + $premiumData = []; - + foreach ($data['premium_data']['data'] as $key => $value) { if ($key === $proposel) { $premiumData[$key]['Quote Asked'] = $value['Quote Asked']; @@ -1024,15 +1372,14 @@ class LeadsController extends BaseController $data['premium_data']['data'] = $premiumData; return $data; - } - + function convertJsonForQCR($json, $type) { if ($json) { // Deep copy of JSON $first_json = json_decode(json_encode($json), true); - + // Column-wise Check: Remove headers and relevant data if qcr == 0 foreach ($json['proposal_data']['over_all_column_data'] as $proposalKey => $proposalData) { if ($proposalData['stc'] == 0 || $proposalData['stc'] === false) { @@ -1042,23 +1389,23 @@ class LeadsController extends BaseController unset($first_json['table_data']['headers'][$index]); } } - + // Remove data entries with matching parentth foreach ($first_json['table_data']['data'] as &$item) { $item['data'] = array_values(array_filter($item['data'], function ($entry) use ($proposalKey) { return $entry['parentth'] !== $proposalKey; })); } - + // Remove proposalKey from over_all_column_data unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]); - - if($type == 'stc'){ + + if ($type == 'stc') { // Remove proposalKey from premium_data unset($first_json['premium_data']['data'][$proposalKey]); } } - + // Insurer Check: Remove subHeaders and relevant data for insurers with qcr == 0 foreach ($proposalData['insurers'] as $insurerIndex => $insurer) { if ($insurer['stc'] === 0 || $insurer['stc'] === false) { @@ -1069,24 +1416,24 @@ class LeadsController extends BaseController })); } } - + foreach ($first_json['table_data']['data'] as &$item) { $item['data'] = array_values(array_filter($item['data'], function ($entry) use ($insurer) { return $entry['subth'] !== $insurer['display_name']; })); } - + // Remove insurer from proposal's insurers array unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]['insurers'][$insurerIndex]); - if($type == 'stc'){ + if ($type == 'stc') { unset($first_json['premium_data']['data'][$proposalKey][$insurer['display_name']]); } } } } - + // Row-wise Check: Remove rows if qcr == 0 for actions foreach ($first_json['table_data']['data'] as $rowKey => $rowData) { foreach ($rowData['data'] as $data) { @@ -1096,7 +1443,7 @@ class LeadsController extends BaseController } } } - + // Reindex arrays to maintain proper structure $first_json['table_data']['headers'] = array_values($first_json['table_data']['headers']); $first_json['table_data']['data'] = array_values($first_json['table_data']['data']); @@ -1104,32 +1451,32 @@ class LeadsController extends BaseController $proposal['insurers'] = array_values($proposal['insurers']); return $proposal; }, $first_json['proposal_data']['over_all_column_data']); - + return $first_json; } - + return null; } - + //----- Featch Lead data and insert Client ------------------------------------------------------------------------------------------- public function featchLeadDataAndInsertClient($lead_id) { $data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first(); - + if (!$data) { return $this->respond(['status' => false, 'message' => 'Failed to create Client', 'data' => null], 200); } - + $result = $this->createClientWithLeadData($data); - + if ($result) { $policy_data = $this->clientPolicyModel->where('client_id', $result)->where('is_active', 1)->first(); return $this->respond(['status' => true, 'message' => 'New Client created successfully', 'client_id' => $result, 'data' => $data, 'client_policy_id' => $policy_data['id']], 200); } - + return $this->respond(['status' => false, 'message' => 'Failed to create client', 'data' => $data], 200); } @@ -1145,7 +1492,7 @@ class LeadsController extends BaseController return $client_id; } - + private function prepareClientData($data) { return [ @@ -1157,27 +1504,27 @@ class LeadsController extends BaseController 'pan' => $data['pan'], ]; } - + public function createClientBranchAndContactWithLeadData($data, $client_id) { $branch_data = $this->prepareClientBranchData($data, $client_id); $branch_id = $this->clientBranchModel->insert($branch_data); - + if ($branch_id) { $this->leadsModel->where('id', $data['id'])->set('is_client_created', $client_id)->update(); $contact_data = $this->prepareContactData($data, $branch_id); $this->levelContactModel->insert($contact_data); - + $this->createClientPolicyWithLeadData($data, $client_id, $branch_id); } - + return $branch_id; } - + private function prepareClientBranchData($data, $client_id) { $default_unit = trim(($data['client_short_name'] ?? '') . '-' . ($data['branch_code'] ?? ''), '-'); - + return [ 'client_id' => $client_id, 'branch_name' => $data['branch_name'], @@ -1186,7 +1533,7 @@ class LeadsController extends BaseController 'units' => json_encode([$default_unit]), ]; } - + private function prepareContactData($data, $branch_id) { return [ @@ -1197,22 +1544,22 @@ class LeadsController extends BaseController 'ref_id' => $branch_id, ]; } - + public function createClientPolicyWithLeadData($data, $client_id, $branch_id) { $client_policy_data = $this->prepareClientPolicyData($data, $client_id, $branch_id); $client_policy_id = $this->clientPolicyModel->insert($client_policy_data); - if($client_policy_id){ + if ($client_policy_id) { $this->leadsModel->where('id', $data['id'])->set('is_client_created', $client_id)->update(); } return $client_policy_id; } - + private function prepareClientPolicyData($data, $client_id, $branch_id) - { + { $proposel_data = json_decode($data['proposel_data'], true); // print_r($proposel_data); die; list($insurer_branch_id, $insurer_id) = explode('-', $proposel_data['insurer'], 2); @@ -1231,7 +1578,7 @@ class LeadsController extends BaseController ]; $terms = $this->preparePolicyTermsFromRFQ($data); - + $policy_type_id = $data['policy_type_id']; if (in_array($policy_type_id, [1, 2, 6, 7])) { $client_policy_data['is_addon'] = 1; // Base Policy @@ -1241,61 +1588,114 @@ class LeadsController extends BaseController // if ($base_policy) { // $data['is_addon'] = 3; // Dependent Addon // } else { - $data['is_addon'] = 1; + $data['is_addon'] = 1; // } } $client_policy_data['policy_terms'] = $this->preparePolicyTermsFromRFQ($data); // print_r($client_policy_data); die; - + return $client_policy_data; } - + private function preparePolicyTermsFromRFQ($data) - { + { $proposel_data = json_decode($data['proposel_data'], true); $QCRData = $this->RFQModel->where('is_active', 1)->where('type', 2)->where('lead_id', $data['id'])->first(); - + $JSON = json_decode($QCRData['json'], true); - $converted_json = $this->transformProposelData($JSON, $proposel_data['proposel_name'], $proposel_data['insurer_name']); - + $converted_json = $this->transformProposelData($JSON, $proposel_data['proposel_name'], $proposel_data['insurer_name']); + return $this->convertQCRJsonToPolicyTerms($converted_json, $data['policy_type_id'], $proposel_data['proposel_name'], $proposel_data['insurer_name']); } private function convertQCRJsonToPolicyTerms($data, $policy_type, $proposel_name, $insurer_name) { $GMC_Keys = [ - "sum_insured", "family_floater", "family_floaters", "age_ratio", "waiverofpreexistingdiseases", - "maternitycoverage", "twindelivery", "preandpostnatal", "babyday1cover", "9monthwaitingperiodwaived", - "coverfromthedateofjoining", "waiverof1,2,3&4thyearexclusions", "waiverof30dayswaitingperiod", - "prehospitalizationcover", "congenitaldiseasesinternal", "copayzonewisecopay", - "bioabsorbablestenttoriclensmultifocallens", "roomrentlimit", "proportionatedeductionclause", - "ailmentcapping", "ambulancecharges", "airambulance", "familytransportationbenefit", - "reasonableandcustomarycharges", "ayudhtreatmentcover", "congenitaldiseasesexternal", - "optionalparentalcopay", "posthospitalizationcover", "corporatebuffer", "sublimitofcorporatebuffer", - "ayushTreatmentCoverData", "armdcovered", "suminsuredenhancement", "automaticsuminsuredreinstatement", - "additionalsicknessbenefit", "lasiksurgery", "midterminclusion", "capd", "organdonorexpenses", - "moderntreatmentsasperirdai", "Wellness", "days_of_discharge", "days_from_dod", - "special_condition_label", "special_condition_input", "multiple_sum_insured", - "cataract", "cataractData" + "sum_insured", + "family_floater", + "family_floaters", + "age_ratio", + "waiverofpreexistingdiseases", + "maternitycoverage", + "twindelivery", + "preandpostnatal", + "babyday1cover", + "9monthwaitingperiodwaived", + "coverfromthedateofjoining", + "waiverof1,2,3&4thyearexclusions", + "waiverof30dayswaitingperiod", + "prehospitalizationcover", + "congenitaldiseasesinternal", + "copayzonewisecopay", + "bioabsorbablestenttoriclensmultifocallens", + "roomrentlimit", + "proportionatedeductionclause", + "ailmentcapping", + "ambulancecharges", + "airambulance", + "familytransportationbenefit", + "reasonableandcustomarycharges", + "ayudhtreatmentcover", + "congenitaldiseasesexternal", + "optionalparentalcopay", + "posthospitalizationcover", + "corporatebuffer", + "sublimitofcorporatebuffer", + "ayushTreatmentCoverData", + "armdcovered", + "suminsuredenhancement", + "automaticsuminsuredreinstatement", + "additionalsicknessbenefit", + "lasiksurgery", + "midterminclusion", + "capd", + "organdonorexpenses", + "moderntreatmentsasperirdai", + "Wellness", + "days_of_discharge", + "days_from_dod", + "special_condition_label", + "special_condition_input", + "multiple_sum_insured", + "cataract", + "cataractData" ]; - + $GPA_Keys = [ - "sumInsured2", "totalSumInsured", "age_ratio", "accidentalDeathBenefit", "permanentTotalDisablement", - "permanentPartialDisablement", "temporaryTotalDisablementBenefit", "accidentalHospitalizationExpenses", - "childrenEducationWelfareFund", "compassionateVisitExpenses", "compassionateVisitExpensesData", - "brokenBoneExpenses", "brokenBoneExpensesData", "ambulanceCharges", "ambulanceChargesData", - "burnExpenses", "burnExpensesData", "carriageOfDeadBody", "carriageOfDeadBodyData", - "animalSnakeInsectBite", "terrorism", "worldwideCover", "gpa_special_condition_label", - "gpa_special_condition_input", "multiple_sum_insured" + "sumInsured2", + "totalSumInsured", + "age_ratio", + "accidentalDeathBenefit", + "permanentTotalDisablement", + "permanentPartialDisablement", + "temporaryTotalDisablementBenefit", + "accidentalHospitalizationExpenses", + "childrenEducationWelfareFund", + "compassionateVisitExpenses", + "compassionateVisitExpensesData", + "brokenBoneExpenses", + "brokenBoneExpensesData", + "ambulanceCharges", + "ambulanceChargesData", + "burnExpenses", + "burnExpensesData", + "carriageOfDeadBody", + "carriageOfDeadBodyData", + "animalSnakeInsectBite", + "terrorism", + "worldwideCover", + "gpa_special_condition_label", + "gpa_special_condition_input", + "multiple_sum_insured" ]; - + // Select keys based on policy type $termsKey = $policy_type == 1 ? $GPA_Keys : $GMC_Keys; - + // Initialize terms_array with default empty values $terms_array = array_fill_keys($termsKey, ""); $specialKeys = ['special_condition_label', 'special_condition_input', 'gpa_special_condition_label', 'gpa_special_condition_input', 'multiple_sum_insured']; @@ -1310,37 +1710,39 @@ class LeadsController extends BaseController "child" => ["min" => 0, "max" => "25"], "elders" => ["min" => 0, "max" => 0], ] : ["self" => ["min" => "18", "max" => "60"]]; - + foreach ($data['table_data']['data'] as $dataRow) { $item = $dataRow['items'] ?? ''; - + foreach ($dataRow['data'] as $cellData) { $parentth = $cellData['parentth'] ?? ''; $subth = $cellData['subth'] ?? ''; $input_value = $cellData['input_value'] ?? ''; $value = $cellData['value'] ?? ''; - + // Skip unwanted keys - if (in_array($parentth, ['Sno', 'Item Key', 'Particulars', 'Action']) || - in_array($subth, ['Quote Asked'])) { + if ( + in_array($parentth, ['Sno', 'Item Key', 'Particulars', 'Action']) || + in_array($subth, ['Quote Asked']) + ) { continue; } - + // Handle special conditions if (str_starts_with($item, "special_condition") && $parentth === $proposel_name && $subth === $insurer_name) { - + $parts = explode("-", $input_value); $question = $parts[0] ?? ''; $answer = $parts[1] ?? ''; $labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label'; $inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input'; - + $terms_array[$labelKey][] = $question; $terms_array[$inputKey][] = $answer; continue; } - + // Handle sum insured if (in_array($item, ['sum_insured', 'sumInsured2'])) { $si_amt = explode(",", $value); @@ -1348,42 +1750,40 @@ class LeadsController extends BaseController $terms_array['multiple_sum_insured'] = array_slice($si_amt, 1); continue; } - + // Handle family floaters if ($item === 'family_composition') { $terms_array['family_floaters'] = isJsonString($input_value) ? json_decode($input_value, true) : $input_value; continue; } - + // Decode JSON if valid $terms_array[$item] = isJsonString($input_value) ? (json_decode($input_value, true)['key'] ?? '') : $input_value; } } - + return json_encode($terms_array); } - + public function featchClientPolicyFromLead($client_id, $branch_id, $lead_id) { $data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first(); - + if (!$data) { return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => null], 200); } - + $result = $this->createClientPolicyWithLeadData($data, $client_id, $branch_id); // print_r($result); die; - + if ($result) { $policy_data = $this->clientPolicyModel->where('id', $result)->where('is_active', 1)->first(); return $this->respond(['status' => true, 'message' => 'New Policy created successfully', 'client_policy_id' => $result, 'data' => $data, 'client_id' => $policy_data['client_id']], 200); } - - return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => $data], 200); - - } -} \ No newline at end of file + return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => $data], 200); + } +} diff --git a/app/Helpers/ExcelMergeHelper.php b/app/Helpers/ExcelMergeHelper.php index 06f68c66..b026296e 100644 --- a/app/Helpers/ExcelMergeHelper.php +++ b/app/Helpers/ExcelMergeHelper.php @@ -4,90 +4,215 @@ namespace App\Helpers; use Exception; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class ExcelMergeHelper { - /** - * Merge multiple Excel files into a single spreadsheet. - * - * @param array $filesToMerge Array of files to merge. - * @param string $outputPath Path to save the merged file. - * @return bool True if merge successful, false otherwise. - */ - public static function mergeExcelFiles(array $filesToMerge, string $outputPath): bool + + protected static $logger; + + public static function initialize() + { + self::$logger = \Config\Services::mylogger(); + } + public static function mergeExcelFiles(array $filesToMerge, string $outputPath) { try { - // Validate input: If no files are provided, throw an error. + self::initialize(); if (empty($filesToMerge)) { - throw new Exception("No files provided for merging"); + throw new Exception("No files provided for merging."); } - // Initialize the base spreadsheet (starting with null, will be created with the first file) - $spreadsheet = null; - - // Loop through the files to merge - foreach ($filesToMerge as $filePath => $sheetsToMerge) { - // Load the current file - $currentSpreadsheet = IOFactory::load($filePath); - - // Debugging: Log the number of sheets in the current file - error_log("File: $filePath has " . $currentSpreadsheet->getSheetCount() . " sheets."); - - // If this is the first file, initialize the base spreadsheet - if ($spreadsheet === null) { - $spreadsheet = $currentSpreadsheet; - - // If specific sheets are provided for the first file, remove the others - if (!is_null($sheetsToMerge) && !empty($sheetsToMerge)) { - $sheetCount = $spreadsheet->getSheetCount(); - for ($i = $sheetCount - 1; $i >= 0; $i--) { - if (!in_array($i, $sheetsToMerge)) { - $spreadsheet->removeSheetByIndex($i); - } - } - } - } else { - // If we are not working with the first file, we need to merge specified sheets - if (is_null($sheetsToMerge) || empty($sheetsToMerge)) { - // If no specific sheets, merge all sheets - $sheetsToMerge = range(0, $currentSpreadsheet->getSheetCount() - 1); - } - - // Merge the specified sheets from the current file - foreach ($sheetsToMerge as $sheetIndex) { - // Check if sheet index is within bounds - if ($sheetIndex < 0 || $sheetIndex >= $currentSpreadsheet->getSheetCount()) { - throw new Exception("Invalid sheet index $sheetIndex in file $filePath"); - } - - // Retrieve the sheet to copy - $sheetToCopy = $currentSpreadsheet->getSheet($sheetIndex); - - // Handle sheet name collision by renaming the sheet with a counter - $baseName = $sheetToCopy->getTitle(); - $sheetName = $baseName; - $counter = 1; - while ($spreadsheet->sheetNameExists($sheetName)) { - $sheetName = $baseName . '_merged_' . $counter; - $counter++; - } - $sheetToCopy->setTitle($sheetName); - - // Clone and add the sheet to the base spreadsheet - $spreadsheet->addSheet(clone $sheetToCopy); - } + // Validate files exist and are readable + foreach ($filesToMerge as $fileData) { + if (!file_exists($fileData['file_path'])) { + throw new Exception("File not found: " . $fileData['file_path']); + } + if (!is_readable($fileData['file_path'])) { + throw new Exception("File not readable: " . $fileData['file_path']); } } - // Save the merged file - $writer = new Xlsx($spreadsheet); - $writer->save($outputPath); + self::$logger->logme('error',"Creating new spreadsheet..."); + $mergedSpreadsheet = new Spreadsheet(); + $firstSheet = true; + + foreach ($filesToMerge as $fileIndex => $fileData) { + $filePath = $fileData['file_path']; + $sheetsToMerge = $fileData['sheets']; + self::$logger->logme('error',"Processing file $fileIndex: $filePath"); + + try { + self::$logger->logme('error',"Attempting to load file: $filePath"); + $reader = IOFactory::createReaderForFile($filePath); + $reader->setReadDataOnly(true); // This can help with memory usage + self::$logger->logme('error',"Reader created successfully"); + + $currentSpreadsheet = $reader->load($filePath); + self::$logger->logme('error',"File loaded successfully"); + + $sheetCount = $currentSpreadsheet->getSheetCount(); + self::$logger->logme('error',"File $filePath has $sheetCount sheets."); + + if (empty($sheetsToMerge)) { + $sheetsToMerge = range(0, $sheetCount - 1); + } + + foreach ($sheetsToMerge as $sourceSheetIndex) { + self::$logger->logme('error',"Processing sheet index: $sourceSheetIndex"); + + if ($sourceSheetIndex < 0 || $sourceSheetIndex >= $sheetCount) { + self::$logger->logme('error',"Invalid sheet index $sourceSheetIndex in file $filePath"); + continue; + } + + try { + $sourceSheet = $currentSpreadsheet->getSheet($sourceSheetIndex); + self::$logger->logme('error',"Source sheet loaded"); + + $baseName = $sourceSheet->getTitle(); + self::$logger->logme('error',"Processing sheet: $baseName"); + + $newSheetName = $baseName; + $counter = 1; + + while ($mergedSpreadsheet->sheetNameExists($newSheetName)) { + $newSheetName = $baseName . '_' . $fileIndex . '_' . $counter; + $counter++; + } + self::$logger->logme('error',"New sheet name will be: $newSheetName"); + + if ($firstSheet) { + $targetSheet = $mergedSpreadsheet->getActiveSheet(); + $targetSheet->setTitle($newSheetName); + $firstSheet = false; + self::$logger->logme('error',"Using first sheet"); + } else { + $targetSheet = new Worksheet($mergedSpreadsheet, $newSheetName); + $mergedSpreadsheet->addSheet($targetSheet); + self::$logger->logme('error',"Created new sheet"); + } + + self::$logger->logme('error',"Starting content copy for sheet: $newSheetName"); + self::copyWorksheetContent($sourceSheet, $targetSheet); + self::$logger->logme('error',"Content copied successfully for sheet: $newSheetName"); + + } catch (Exception $e) { + self::$logger->logme('error',"Error processing sheet $sourceSheetIndex: " . $e->getMessage()); + self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); + continue; + } + } + + // Free up memory + $currentSpreadsheet->disconnectWorksheets(); + unset($currentSpreadsheet); + self::$logger->logme('error',"Cleaned up spreadsheet resources"); + + } catch (Exception $e) { + self::$logger->logme('error',"Error processing file $filePath: " . $e->getMessage()); + self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); + continue; + } + + self::$logger->logme('error',"Current sheet names in merged spreadsheet:"); + foreach ($mergedSpreadsheet->getSheetNames() as $name) { + self::$logger->logme('error'," - $name"); + } + } + + if ($mergedSpreadsheet->getSheetCount() === 0) { + throw new Exception("No sheets were successfully merged."); + } + + self::$logger->logme('error',"Setting active sheet to index 0"); + $mergedSpreadsheet->setActiveSheetIndex(0); + + self::$logger->logme('error',"Preparing to save merged file to: $outputPath"); + + // Ensure output directory exists + $outputDir = dirname($outputPath); + if (!is_dir($outputDir)) { + mkdir($outputDir, 0777, true); + } + + if (!is_writable($outputDir)) { + throw new Exception("Output directory is not writable: $outputDir"); + } + + if (ob_get_length()) { + ob_end_clean(); + } + + self::$logger->logme('error',"Creating Excel writer"); + $writer = new Xlsx($mergedSpreadsheet); + + self::$logger->logme('error',"Saving file..."); + $writer->save($outputPath); + + // Free up memory + $mergedSpreadsheet->disconnectWorksheets(); + unset($mergedSpreadsheet); + + self::$logger->logme('error',"Merge successful. File saved at: $outputPath"); + return $outputPath; - return true; // Return true if merge was successful } catch (Exception $e) { - // Log the error (optional) and return false on failure - error_log("Excel Merge Error: " . $e->getMessage()); - return false; + self::$logger->logme('error',"Excel Merge Error: " . $e->getMessage()); + self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); + throw $e; } } -} + + private static function copyWorksheetContent($sourceSheet, $targetSheet) + { + try { + self::$logger->logme('error',"Starting copyWorksheetContent"); + + // Get the highest row and column indexes + $highestRow = $sourceSheet->getHighestRow(); + $highestColumn = $sourceSheet->getHighestColumn(); + self::$logger->logme('error',"Copying data from range A1:{$highestColumn}{$highestRow}"); + + // Copy cell values only first for better performance + foreach ($sourceSheet->getRowIterator() as $row) { + $cellIterator = $row->getCellIterator(); + $cellIterator->setIterateOnlyExistingCells(true); + foreach ($cellIterator as $cell) { + $column = $cell->getColumn(); + $rowIndex = $cell->getRow(); + $targetSheet->setCellValue($column . $rowIndex, $cell->getValue()); + } + } + self::$logger->logme('error',"Basic cell values copied"); + + // Copy column dimensions + foreach ($sourceSheet->getColumnDimensions() as $columnId => $sourceDim) { + $targetSheet->getColumnDimension($columnId) + ->setWidth($sourceDim->getWidth()) + ->setVisible($sourceDim->getVisible()) + ->setAutoSize($sourceDim->getAutoSize()); + } + self::$logger->logme('error',"Column dimensions copied"); + + // Copy row dimensions + foreach ($sourceSheet->getRowDimensions() as $rowId => $sourceDim) { + $targetSheet->getRowDimension($rowId) + ->setRowHeight($sourceDim->getRowHeight()) + ->setVisible($sourceDim->getVisible()); + } + self::$logger->logme('error',"Row dimensions copied"); + + // Copy basic worksheet properties + $targetSheet->setShowGridlines($sourceSheet->getShowGridlines()); + $targetSheet->setShowRowColHeaders($sourceSheet->getShowRowColHeaders()); + self::$logger->logme('error',"Worksheet properties copied"); + + } catch (Exception $e) { + self::$logger->logme('error',"Error in copyWorksheetContent: " . $e->getMessage()); + self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); + throw $e; + } + } +} \ No newline at end of file diff --git a/tests/unit/ExcelMergeHelperTest.php b/tests/unit/ExcelMergeHelperTest.php index e648f0b6..342c17c6 100755 --- a/tests/unit/ExcelMergeHelperTest.php +++ b/tests/unit/ExcelMergeHelperTest.php @@ -10,16 +10,28 @@ class ExcelMergeHelperTest extends TestCase { // Use the paths you provided $filePaths = [ - '/home/venba/Downloads/Financial_Sample.xlsx' => [1,3,2], - '/home/venba/Downloads/enrollment.xlsx' => [] + // ['file_path' =>WRITEPATH.'uploads/lead_files/Member_Data.xlsx','sheets' => []], + // ['file_path' =>WRITEPATH.'/tmp/RFQ_ABC_GMC_20241220140640.xlsx','sheets' => []], + +['file_path' =>'/home/venba/Downloads/enrollment (1).xlsx','sheets' => []], + ['file_path' =>'/home/venba/Downloads/Financial_Sample.xlsx','sheets' => []], ]; $outputPath = '/home/venba/Documents/merged_file.xlsx'; // Output path for the merged file // Call the mergeExcelFiles function $result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); - var_dump($result); + var_dump($result); // Check if the result is true (indicating success) $this->assertTrue($result, 'Expected true, but got false'); } -} \ No newline at end of file +} +// $temp_file_path = WRITEPATH.'tmp/RFQ_ABC_GMC_20241219172307.xlsx'; +// $temp_file_name = 'RFQ_ABC_GMC_20241219172307.xlsx'; +// $lead_file_path = WRITEPATH.'uploads/lead_files/Member_Data.xlsx'; + +// $filePaths = [ +// ['file_path' => $temp_file_path,'sheets' =>[]], +// ['file_path' =>$lead_file_path ,'sheets' =>[]] +// ]; +// $outputPath = dirname($temp_file_path).'/'.$temp_file_name.'_merged'; diff --git a/tests/unit/LeadsControllerTest.php b/tests/unit/LeadsControllerTest.php new file mode 100644 index 00000000..7b8a3518 --- /dev/null +++ b/tests/unit/LeadsControllerTest.php @@ -0,0 +1,183 @@ +leadsController = new LeadsController(); + } + + public function testGetDemographyDataBasicScenario() + { + echo('passed 0'); + // Arrange + $members = [ + // Header row + ['Name', 'Age', 'Relationship', 'SI Enhancement'], + // Data rows + ['John', '25', 'Self', '100000'], + ['Jane', '23', 'Spouse', '100000'], + ['Kid', '5', 'Child', '50000'] + ]; + echo('passed 1'); + $age_band_data = [ + ['0-17', '18-35', '36-45', 'Above 46'] + ]; + echo('passed 2'); + $members_heading = ['Name', 'Age', 'Relationship', 'SI Enhancement']; + $available_col = 'age';print_rr($available_col); + $col_index = 1; + echo('passed 3'); + try{ + echo('Inside try'); + // Act + echo('passed 3.5'); + $result = $this->leadsController->getDemographyData($members,$age_band_data,$members_heading,$available_col,$col_index); + + echo('passed 4'); + } catch (Exception $e) { + echo "Message: " . $e->getMessage() . "\n"; + echo "File: " . $e->getFile() . "\n"; + echo "Line: " . $e->getLine() . "\n"; + // Optionally, print the full stack trace + echo "Stack trace: " . $e->getTraceAsString() . "\n"; + } + var_dump($result); + // Assert + $this->assertIsArray($result); + $this->assertArrayHasKey('general', $result); + $this->assertArrayHasKey('100000', $result); + $this->assertArrayHasKey('50000', $result); + + // Check counts for general category + $this->assertEquals(1, $result['general']['Self']['18-35']); + $this->assertEquals(1, $result['general']['Spouse']['18-35']); + $this->assertEquals(1, $result['general']['Child']['0-17']); + } + + public function testGetDemographyDataWithDOB() + { + // Arrange + $members = [ + // Header row + ['Name', 'DOB', 'Relationship', 'SI Enhancement'], + // Data rows + ['John', '01-Jan-1995', 'Self', '100000'], + ['Jane', '01-Jan-1998', 'Spouse', '100000'] + ]; + + $age_band_data = [ + ['18-35', '36-45', '46+'] + ]; + + $members_heading = ['Name', 'DOB', 'Relationship', 'SI Enhancement']; + $available_col = 'dob'; + $col_index = 1; + + // Act + $result = $this->leadsController->getDemographyData( + $members, + $age_band_data, + $members_heading, + $available_col, + $col_index + ); + + // Assert + $this->assertIsArray($result); + $this->assertArrayHasKey('general', $result); + $this->assertArrayHasKey('100000', $result); + + // Both members should be in 18-35 age band + $this->assertEquals(1, $result['general']['Self']['18-35']); + $this->assertEquals(1, $result['general']['Spouse']['18-35']); + } + + public function testGetDemographyDataWithEmptyMembers() + { + // Arrange + $members = [ + // Only header row + ['Name', 'Age', 'Relationship', 'SI Enhancement'] + ]; + + $age_band_data = [ + ['0-17', '18-35', '36-45', '46+'] + ]; + + $members_heading = ['Name', 'Age', 'Relationship', 'SI Enhancement']; + $available_col = 'age'; + $col_index = 1; + + // Act + $result = $this->leadsController->getDemographyData( + $members, + $age_band_data, + $members_heading, + $available_col, + $col_index + ); + + // Assert + $this->assertIsArray($result); + $this->assertArrayHasKey('general', $result); + + // Check that all totals are zero + $this->assertEquals(0, $result['general']['Grand Total']['Grand Total']); + } + + public function testGetDemographyDataWithMultipleSIBands() + { + // Arrange + $members = [ + // Header row + ['Name', 'Age', 'Relationship', 'SI Enhancement'], + // Data rows with different SI amounts + ['John', '25', 'Self', '100000'], + ['Jane', '23', 'Spouse', '200000'], + ['Kid1', '5', 'Child', '100000'], + ['Kid2', '7', 'Child', '200000'] + ]; + + $age_band_data = [ + ['0-17', '18-35', '36-45', '46+'] + ]; + + $members_heading = ['Name', 'Age', 'Relationship', 'SI Enhancement']; + $available_col = 'age'; + $col_index = 1; + + // Act + $result = $this->leadsController->getDemographyData( + $members, + $age_band_data, + $members_heading, + $available_col, + $col_index + ); + + // Assert + $this->assertIsArray($result); + $this->assertArrayHasKey('100000', $result); + $this->assertArrayHasKey('200000', $result); + + // Check counts for different SI bands + $this->assertEquals(1, $result['100000']['Self']['18-35']); + $this->assertEquals(1, $result['200000']['Spouse']['18-35']); + $this->assertEquals(1, $result['100000']['Child']['0-17']); + $this->assertEquals(1, $result['200000']['Child']['0-17']); + + // Check general category totals + $this->assertEquals(2, $result['general']['Child']['0-17']); + $this->assertEquals(2, $result['general']['Child']['Grand Total']); + } +} \ No newline at end of file