FEAT_TEST_MEMBERS_LIST_ADDED,FIX_EXCELMERGEHELPER

This commit is contained in:
Srinivas-Saravanan 2025-01-02 15:13:58 +05:30
parent 11af72eed5
commit 5303873102
11 changed files with 1419 additions and 242 deletions

View File

@ -99,5 +99,5 @@ class Autoload extends AutoloadConfig
* @var string[] * @var string[]
* @phpstan-var list<string> * @phpstan-var list<string>
*/ */
public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload', 'excel_import_export', 'file', 'drive']; public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload', 'excel_import_export', 'file', 'drive','ExcelSanitizeHelper'];
} }

View File

@ -155,6 +155,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
$routes->get("truncate/(:any)", "EmployeeController::truncateFileData/$1"); $routes->get("truncate/(:any)", "EmployeeController::truncateFileData/$1");
$routes->get("test-rack-rate", "EmployeeController::testRackRate"); $routes->get("test-rack-rate", "EmployeeController::testRackRate");
$routes->post("test-rack-rate", "EmployeeController::testRackRate"); $routes->post("test-rack-rate", "EmployeeController::testRackRate");
$routes->get('test_members_list', 'EmployeeController::test_members_list');
}); });
$routes->group("/master", ["filter" => "authMVC"], function ($routes) { $routes->group("/master", ["filter" => "authMVC"], function ($routes) {
@ -330,6 +331,9 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
$routes->get('getCoShareStatementDetails/(:any)', 'PolicyTransactionController::getCoShareStatementDetails/$1'); $routes->get('getCoShareStatementDetails/(:any)', 'PolicyTransactionController::getCoShareStatementDetails/$1');
$routes->get('getClientPolicyDataBasedOnClientAndInsuer', 'PolicyTransactionController::getClientPolicyDataBasedOnClientAndInsuer'); $routes->get('getClientPolicyDataBasedOnClientAndInsuer', 'PolicyTransactionController::getClientPolicyDataBasedOnClientAndInsuer');
$routes->get('checkCDAmountForBasePremium', 'PolicyTransactionController::checkCDAmountForBasePremium'); $routes->get('checkCDAmountForBasePremium', 'PolicyTransactionController::checkCDAmountForBasePremium');
$routes->post('map_employees', 'EmployeeController::mapEmployees');
$routes->post('get_data_for_mapping', 'EmployeeController::getDataForMapping');
$routes->post('unmap_employees/(:num)', 'EmployeeController::unmapEmployees/$1');
}); });
$routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) { $routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) {

View File

@ -24,6 +24,7 @@ use App\Models\TPAModel;
use App\Models\InsurerExcelExportTemplateModel; use App\Models\InsurerExcelExportTemplateModel;
use App\Models\InsurerModel; use App\Models\InsurerModel;
use App\Models\ClientDepositModel; use App\Models\ClientDepositModel;
use App\Models\PolicyPremium2Model;
use App\Controllers\Jobs; use App\Controllers\Jobs;
@ -62,6 +63,7 @@ class EmployeeController extends AdminController
protected $excelExportTemplateModel; protected $excelExportTemplateModel;
protected $insurerModel; protected $insurerModel;
protected $cashDepositModel; protected $cashDepositModel;
protected $PolicyPremium2Model;
public function __construct() public function __construct()
{ {
@ -81,6 +83,7 @@ class EmployeeController extends AdminController
$this->policiesModel = new PolicesModel(); $this->policiesModel = new PolicesModel();
$this->insurerModel = new InsurerModel(); $this->insurerModel = new InsurerModel();
$this->cashDepositModel = new ClientDepositModel(); $this->cashDepositModel = new ClientDepositModel();
$this->PolicyPremium2Model = new PolicyPremium2Model();
} }
public function list() public function list()
@ -1762,7 +1765,7 @@ class EmployeeController extends AdminController
public function update_emp_data() public function update_emp_data()
{ {
$data = $this->request->getPost(); $data = $this->request->getPost();
// print_rr($data); // print_rr($data);die();
// $data['dob'] = date('Y-m-d', strtotime($data['dob'])); // $data['dob'] = date('Y-m-d', strtotime($data['dob']));
$data['dob'] = change_date_format($data['dob'], 'd/m/Y', 'Y-m-d'); $data['dob'] = change_date_format($data['dob'], 'd/m/Y', 'Y-m-d');
// print_rr($data); die; // print_rr($data); die;
@ -2021,4 +2024,107 @@ class EmployeeController extends AdminController
exit; exit;
} }
public function test_members_list(){
// $model = new EmployeeModel();
// $list = [
// ['relationship' => 'spouse','emp_code' => 'TEST002', 'name' => 'Lakshmi','email_personal' => 'lakshmi@gmail.com','mobile'=>'6382156701','gender'=>'female','dob'=>'1996-05-19','doj'=>'2022-02-01','basic_pay'=>'100000','emp_status'=>'enrolled'],
// ['relationship' => 'Daughter','emp_code' => 'TEST002', 'name' => 'Jayalakshmi','email_personal' => 'jayalakshmi@gmail.com','mobile'=>'6382156701','gender'=>'female','dob'=>'2018-05-19','doj'=>'2022-02-01','basic_pay'=>'100000','emp_status'=>'enrolled'],
// ['relationship' => 'Son','emp_code' => 'TEST002', 'name' => 'Jayam Ravi','email_personal' => 'jayamravi@gmail.com','mobile'=>'6382156701','gender'=>'male','dob'=>'1994-05-19','doj'=>'2019-02-01','basic_pay'=>'100000','emp_status'=>'enrolled'],
// // ['relationship' => 'self','emp_code' => 'TEST003', 'name' => 'Ravi Shankar','email_personal' => 'srinivassaravanan2002@gmail.com','email_corporate'=>'srinivas.saravanan@venbainfotech.com','mobile'=>'6382156701','gender'=>'male','dob'=>'1994-05-19','doj'=>'2022-02-01','basic_pay'=>'100000','emp_status'=>'enrolled']
// ['relationship' => 'spouse','emp_code' => 'TEST001', 'name' => 'Lakshmi','email_personal' => 'lakshmi@gmail.com','mobile'=>'6382156701','gender'=>'female','dob'=>'1996-05-19','doj'=>'2022-02-01','basic_pay'=>'100000','emp_status'=>'enrolled'],
// ];
// foreach($list as $listitem){
// $model->insert($listitem);
// }
// die();
$employee_data = $this->employeeModel->getTestEmployeeData();
$data['employees'] = $employee_data;
$data['clients'] = $this->clientModel->findAll();
// dd($data);
return $this->loadLayout('test_members_list',$data);
}
public function mapEmployees(){
$client_id = $this->request->getPost('client_id');
$branch_id = $this->request->getPost('branch_id');
$policy_id = $this->request->getPost('client_policy_id');
$selected_employees = (array)$this->request->getPost('selected');
$si_amt = $this->request->getPost('si_amt');
$policy_start_date_unformatted = $this->request->getPost('policy_start_date');
$policy_start_date = change_date_format($policy_start_date_unformatted, 'd/M/Y', 'Y-m-d');
$data1 = [
'client_id' => $client_id,
'client_branch_id' => $branch_id,
];
$data2 = [
'client_policy_id' => $policy_id,
'status' => 'draft',
'date_coverage' => $policy_start_date,
'basic_cover_si' => $si_amt,
];
for($i = 0; $i < count($selected_employees); $i++){
$data2['employee_id'] = $selected_employees[$i];
$result1 = $this->employeeModel->set($data1)->where('id',$selected_employees[$i])->update();
$result2 = $this->employeePolicyModel->insert($data2);
}
if ($result1 && $result2) {
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Employees mapped successfully'], 200);
} else {
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to map employees.'], 404);
}
}
public function getDataForMapping(){
$policy_id = $this->request->getPost('policy_id');log_message('error',$policy_id);
$data['policy_start_date'] = $this->clientPolicyModel->select('policy_start_date')->where('id',$policy_id)->first()['policy_start_date'];
$data['si_amt'] = $this->PolicyPremium2Model->select('si')->where('client_policy_id', $policy_id)->groupBy('si')->findAll();
log_message('error',json_encode($data).'policy id '.$policy_id);
return $this->respond(['status' => true, 'code' => 200, 'data' => $data], 200);
}
public function unmapEmployees($actionType){
$selected_employees = (array)$this->request->getPost('selected');
if($actionType == 0){
for($i = 0;$i<count($selected_employees);$i++){log_message('error',$selected_employees[$i]);
$result1 = $this->employeeModel->set(['client_id' => null, 'client_branch_id' => null])->where('id',$selected_employees[$i])->update();log_message('error',$result1);
$result2 = $this->employeePolicyModel->where('employee_id',$selected_employees[$i])->delete();log_message('error',$result2);
}
}
else if ($actionType == 1){
for($i = 0;$i<count($selected_employees);$i++){
log_message('error','Emp id is '.$selected_employees[$i]);
$result1 = $this->employeeModel->set(['client_id' => null, 'client_branch_id' => null])->where('id',$selected_employees[$i])->update();log_message('error','result 1 is '.$result1);
$result2 = $this->employeePolicyModel->where('employee_id',$selected_employees[$i])->delete();log_message('error', 'result 2 is '.$result2);
$emp_code = $this->employeeModel->select('emp_code')->where('id',$selected_employees[$i])->first()['emp_code'];log_message('error','emp_code is '.$emp_code);
$result3 = $this->employeeModel->where('emp_code',$emp_code)->whereNotIn('relationship',['self'])->delete();log_message('error','result 3 is '.$result3);
}
}
else{
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Invalid action type.'], 404);
}
if($actionType == 1){
if($result1 && $result2 && $result3){
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Employees unmapped successfully and Dependencies are Deleted'], 200);
}
else{
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to unmap employees.'], 404);
}
}else{
if($result1 && $result2 ){
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Employees unmapped successfully'], 200);
}
else{
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to unmap employees.'], 404);
}
}
}
} }

View File

@ -29,6 +29,7 @@ use App\Models\InsurerModel;
use App\Helpers\MailHelper; use App\Helpers\MailHelper;
use App\Helpers\ExcelMergeHelper; use App\Helpers\ExcelMergeHelper;
use App\Helpers\ExcelSanitizeHelper;
use Google\Service\CloudSearch\PushItem; use Google\Service\CloudSearch\PushItem;
use Kint; use Kint;
@ -99,7 +100,7 @@ class LeadsController extends BaseController
// $d = $this->constructExcelToSaveTemp(24, 1, $propsal_and_insurer = null); // $d = $this->constructExcelToSaveTemp(24, 1, $propsal_and_insurer = null);
$job_details = new Jobs(); $job_details = new Jobs();
// $r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => ['lead_id' => 24]]); $r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => ['lead_id' => 24]]);
// $d = $this->calculateMembersDemography(['lead_id' => 24]); // $d = $this->calculateMembersDemography(['lead_id' => 24]);
//$this->mergeQuoteExcelFileWithMembersListExcelFile(24, 1, $propsal_and_insurer = null); //$this->mergeQuoteExcelFileWithMembersListExcelFile(24, 1, $propsal_and_insurer = null);
// dd($d); // dd($d);
@ -740,8 +741,12 @@ class LeadsController extends BaseController
{ {
$lead_id = $params['lead_id']; $lead_id = $params['lead_id'];
$lead_data = $this->leadsModel->find($lead_id); $lead_data = $this->leadsModel->find($lead_id);
if (!$lead_data) {
return ['status' => 'failed', 'message' => 'Lead data not found'];
}
if ($lead_data['file_name']) { if ($lead_data['file_name']) {
$file_name_with_path = WRITEPATH . "/uploads/lead_files/" . $lead_data['file_name']; $file_name_with_path = WRITEPATH . "/uploads/lead_files/" . $lead_data['file_name'];
// $file_name_with_path = WRITEPATH . "/uploads/lead_files/NonPrintableCharacters.xlsx";
//check physical file //check physical file
if (!file_exists($file_name_with_path)) { if (!file_exists($file_name_with_path)) {
@ -758,9 +763,8 @@ class LeadsController extends BaseController
$members_sheet = $spreadsheet->getSheet(0); $members_sheet = $spreadsheet->getSheet(0);
$highestRowAndColumn = $members_sheet->getHighestRowAndColumn(); $highestRowAndColumn = $members_sheet->getHighestRowAndColumn();
// dd($highestRowAndColumn); // dd($highestRowAndColumn);
$members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); $uncleaned_members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']);
// dd($members[6]); $members = ExcelSanitizeHelper::sanitizeArrayData($uncleaned_members);
//get age band data //get age band data
$age_band_sheet = $spreadsheet->getSheet(1); $age_band_sheet = $spreadsheet->getSheet(1);
$highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn(); $highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn();
@ -792,19 +796,24 @@ class LeadsController extends BaseController
} }
try { try {
$result = $this->generateClassifierSpreadsheet($classifiers, 'writable/uploads/lead_files'); $result = $this->generateClassifierSpreadsheet($classifiers, WRITEPATH . 'uploads/lead_files/');
if ($result['success']) { if ($result['success']) {
$this->myLogger->logme('error', "Spreadsheet generated successfully!"); $this->myLogger->logme('error', "Spreadsheet generated successfully!");
echo "Location: " . $result['fullpath'] . "\n"; echo "Location: " . $result['fullpath'] . "\n";
echo "Filename: " . $result['filename'] . "\n"; echo "Filename: " . $result['filename'] . "\n";
$filePaths = [ $filePaths = [
['file_path' => WRITEPATH . 'uploads/lead_files/' . $lead_data['file_name'], 'sheets' => [0, 1]], ['file_path' => WRITEPATH . 'uploads/lead_files/' . $lead_data['file_name'], 'sheets' => [0, 1]],
['file_path' => 'writable/uploads/lead_files/' . $result['filename'], 'sheets' => []], ['file_path' => WRITEPATH.'/uploads/lead_files/' . $result['filename'], 'sheets' => []],
]; ];
$outputPath = WRITEPATH . 'uploads/lead_files/Member_Data.xlsx'; $outputPath = WRITEPATH . 'uploads/lead_files/Member_Data.xlsx';
$result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); $result_merge = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath);
if ($result) { if ($result_merge) {
// Call the delete function after the file is successfully created
$deleteResponse = $this->deleteGeneratedFile($result['fullpath']);
// Add delete message to response
$response['deleteMessage'] = $deleteResponse['message'];
return ['status' => 'success', 'message' => 'Member_Data Merged Suceesfully']; return ['status' => 'success', 'message' => 'Member_Data Merged Suceesfully'];
} }
} else { } else {
@ -931,23 +940,22 @@ class LeadsController extends BaseController
public function generateClassifierSpreadsheet($classifiers, $outputDir = 'exports') public function generateClassifierSpreadsheet($classifiers, $outputDir = 'exports')
{ {
if (!file_exists($outputDir)) { if (!file_exists($outputDir)) {
if (!mkdir($outputDir, 0755, true)) { if (!mkdir($outputDir, 0755, true)) {
throw new Exception("Failed to create directory: $outputDir"); throw new Exception("Failed to create directory: $outputDir");
} }
} }
// Check if directory is writable // Check if directory is writable
if (!is_writable($outputDir)) { if (!is_writable($outputDir)) {
throw new Exception("Directory is not writable: $outputDir"); throw new Exception("Directory is not writable: $outputDir");
} }
// Generate unique filename // Generate unique filename
$timestamp = date('Y-m-d_His'); $timestamp = date('Y-m-d_His');
$filename = "member_classification_{$timestamp}.xlsx"; $filename = "member_classification_{$timestamp}.xlsx";
$filepath = $outputDir . DIRECTORY_SEPARATOR . $filename; $filepath = $outputDir . DIRECTORY_SEPARATOR . $filename;
// Check if file already exists (shouldn't happen with timestamp, but just in case) // Check if file already exists (shouldn't happen with timestamp, but just in case)
if (file_exists($filepath)) { if (file_exists($filepath)) {
$counter = 1; $counter = 1;
@ -957,28 +965,39 @@ class LeadsController extends BaseController
$filename = "member_classification_{$timestamp}_{$counter}.xlsx"; $filename = "member_classification_{$timestamp}_{$counter}.xlsx";
$filepath = $outputDir . DIRECTORY_SEPARATOR . $filename; $filepath = $outputDir . DIRECTORY_SEPARATOR . $filename;
} }
$spreadsheet = new Spreadsheet(); $spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet(); $sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Demography_Data');
// Get all age bands // Get all age bands
$age_bands = array_keys(reset($classifiers['general'])); $age_bands = array_keys(reset($classifiers['general']));
array_pop($age_bands); // Remove 'Grand Total' array_pop($age_bands); // Remove 'Grand Total'
$age_bands[] = 'Grand Total'; // Add it back at the end $age_bands[] = 'Grand Total'; // Add it back at the end
$currentRow = 5; // Start from row 5 to match the example $currentRow = 5; // Start from row 5 to match the example
// Function to write section data // Function to write section data
$writeSectionData = function ($data, $sheet, &$currentRow) use ($age_bands) { $writeSectionData = function ($data, $sheet, &$currentRow, $si_type) use ($age_bands) {
// Set headers // Add section header for the SI type
$sheet->setCellValue('B' . $currentRow, strtoupper($si_type));
// Style section header
$sheet->getStyle('B' . $currentRow)->applyFromArray([
'font' => ['bold' => true, 'size' => 14],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
]);
$currentRow++; // Move to the next row after the section header
// Set headers for the data table
$sheet->setCellValue('B' . $currentRow, 'Relationship'); $sheet->setCellValue('B' . $currentRow, 'Relationship');
$col = 'C'; $col = 'C';
foreach ($age_bands as $band) { foreach ($age_bands as $band) {
$sheet->setCellValue($col . $currentRow, $band); $sheet->setCellValue($col . $currentRow, $band);
$col++; $col++;
} }
// Style headers // Style headers
$lastCol = chr(ord('B') + count($age_bands)); $lastCol = chr(ord('B') + count($age_bands));
$headerRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; $headerRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
@ -994,9 +1013,9 @@ class LeadsController extends BaseController
'horizontal' => Alignment::HORIZONTAL_CENTER, 'horizontal' => Alignment::HORIZONTAL_CENTER,
], ],
]); ]);
$currentRow++; $currentRow++;
// Write data rows // Write data rows
foreach ($data as $relation => $values) { foreach ($data as $relation => $values) {
if ($relation !== 'Grand Total') { if ($relation !== 'Grand Total') {
@ -1007,7 +1026,7 @@ class LeadsController extends BaseController
$sheet->setCellValue($col . $currentRow, $value); $sheet->setCellValue($col . $currentRow, $value);
$col++; $col++;
} }
// Style data row // Style data row
$dataRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; $dataRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
$sheet->getStyle($dataRange)->applyFromArray([ $sheet->getStyle($dataRange)->applyFromArray([
@ -1021,11 +1040,11 @@ class LeadsController extends BaseController
'horizontal' => Alignment::HORIZONTAL_CENTER, 'horizontal' => Alignment::HORIZONTAL_CENTER,
], ],
]); ]);
$currentRow++; $currentRow++;
} }
} }
// Add Grand Total row // Add Grand Total row
$sheet->setCellValue('B' . $currentRow, 'Grand Total'); $sheet->setCellValue('B' . $currentRow, 'Grand Total');
$col = 'C'; $col = 'C';
@ -1033,7 +1052,7 @@ class LeadsController extends BaseController
$sheet->setCellValue($col . $currentRow, $data['Grand Total'][$band]); $sheet->setCellValue($col . $currentRow, $data['Grand Total'][$band]);
$col++; $col++;
} }
// Style Grand Total row // Style Grand Total row
$totalRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; $totalRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
$sheet->getStyle($totalRange)->applyFromArray([ $sheet->getStyle($totalRange)->applyFromArray([
@ -1052,37 +1071,41 @@ class LeadsController extends BaseController
'horizontal' => Alignment::HORIZONTAL_CENTER, 'horizontal' => Alignment::HORIZONTAL_CENTER,
], ],
]); ]);
$currentRow += 3; // Add gap after each section $currentRow += 3; // Add gap after each section
}; };
// Write each SI section with gaps // Write each SI section with gaps
foreach ($classifiers as $si_type => $data) { foreach ($classifiers as $si_type => $data) {
$writeSectionData($data, $sheet, $currentRow); $writeSectionData($data, $sheet, $currentRow, $si_type);
} }
// Auto-size columns // Auto-size columns
foreach (range('B', chr(ord('B') + count($age_bands))) as $col) { foreach (range('B', chr(ord('B') + count($age_bands))) as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true); $sheet->getColumnDimension($col)->setAutoSize(true);
} }
// Create Excel file // Create Excel file
try { try {
// Create Excel file // Create Excel file
$writer = new Xlsx($spreadsheet); $writer = new Xlsx($spreadsheet);
$writer->save($filepath); $writer->save($filepath);
// Verify file was created successfully // Verify file was created successfully
if (!file_exists($filepath)) { if (!file_exists($filepath)) {
throw new Exception("Failed to create file: $filepath"); throw new Exception("Failed to create file: $filepath");
} }
return [ // Return the file info after creation
$response = [
'success' => true, 'success' => true,
'filepath' => $filepath, 'filepath' => $filepath,
'filename' => $filename, 'filename' => $filename,
'fullpath' => realpath($filepath) 'fullpath' => realpath($filepath)
]; ];
return $response;
} catch (Exception $e) { } catch (Exception $e) {
return [ return [
'success' => false, 'success' => false,
@ -1090,7 +1113,31 @@ class LeadsController extends BaseController
]; ];
} }
} }
// Function to delete generated file
public function deleteGeneratedFile($filePath)
{
try {
if (file_exists($filePath)) {
unlink($filePath); // Delete the file
return [
'success' => true,
'message' => "File deleted successfully"
];
} else {
return [
'success' => false,
'message' => "File does not exist"
];
}
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
public function getAge($available_col, $member, $col_index) public function getAge($available_col, $member, $col_index)
{ {
if ($available_col == 'age') { if ($available_col == 'age') {

View File

@ -1,218 +1,150 @@
<?php <?php
namespace App\Helpers; namespace App\Helpers;
use Exception;
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use Exception;
class ExcelMergeHelper class ExcelMergeHelper {
{ /**
* Main merge function with auto-retry
protected static $logger; *
* @param array $filePaths Array of file paths with optional sheets to merge.
public static function initialize() * @param string $outputPath Path to save the merged Excel file.
{ * @return string|null Returns output path on success, null on failure.
self::$logger = \Config\Services::mylogger(); */
} public static function mergeExcelFiles(array $filePaths, string $outputPath): ?string
public static function mergeExcelFiles(array $filesToMerge, string $outputPath)
{ {
try { try {
self::initialize(); log_message('debug', 'Attempting merge with original file order');
if (empty($filesToMerge)) { return self::processFiles($filePaths, $outputPath);
throw new Exception("No files provided for merging.");
}
// 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']);
}
}
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;
} catch (Exception $e) { } catch (Exception $e) {
self::$logger->logme('error',"Excel Merge Error: " . $e->getMessage()); log_message('error', 'First attempt failed: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); log_message('debug', 'Retrying with reversed file order');
throw $e;
// Reverse the file order and try again
$reversedFiles = array_reverse($filePaths);
try {
return self::processFiles($reversedFiles, $outputPath);
} catch (Exception $e2) {
log_message('error', 'Both attempts failed. Last error: ' . $e2->getMessage() . ' in ' . $e2->getFile() . ' on line ' . $e2->getLine());
return null;
}
} }
} }
private static function copyWorksheetContent($sourceSheet, $targetSheet) /**
* Process files to merge spreadsheets
*
* @param array $filePaths
* @param string $outputPath
* @return string
* @throws Exception
*/
private static function processFiles(array $filePaths, string $outputPath): string
{ {
try { log_message('debug', 'Starting Excel merge process');
self::$logger->logme('error',"Starting copyWorksheetContent"); log_message('debug', 'Files to process: ' . json_encode($filePaths));
// Get the highest row and column indexes if (empty($filePaths)) {
$highestRow = $sourceSheet->getHighestRow(); throw new Exception("No files provided to merge");
$highestColumn = $sourceSheet->getHighestColumn(); }
self::$logger->logme('error',"Copying data from range A1:{$highestColumn}{$highestRow}");
// Copy cell values only first for better performance // Initialize an empty merged spreadsheet
foreach ($sourceSheet->getRowIterator() as $row) { $mergedSpreadsheet = new Spreadsheet();
$cellIterator = $row->getCellIterator(); $mergedSpreadsheet->removeSheetByIndex(0); // Remove the default empty sheet
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $cell) { // Process the base file
$column = $cell->getColumn(); $firstFile = array_shift($filePaths);
$rowIndex = $cell->getRow(); self::processSingleFile($firstFile, $mergedSpreadsheet);
$targetSheet->setCellValue($column . $rowIndex, $cell->getValue());
// Process remaining files
foreach ($filePaths as $index => $fileInfo) {
self::processSingleFile($fileInfo, $mergedSpreadsheet, $index);
}
// Save the merged file
log_message('debug', "Saving merged file to: {$outputPath}");
$writer = IOFactory::createWriter($mergedSpreadsheet, 'Xlsx');
$writer->setPreCalculateFormulas(false);
$writer->save($outputPath);
// Clean up
$mergedSpreadsheet->disconnectWorksheets();
unset($mergedSpreadsheet);
gc_collect_cycles();
log_message('debug', 'Excel merge process completed successfully');
return $outputPath;
}
/**
* Process a single file to merge its sheets into the merged spreadsheet
*
* @param array $fileInfo
* @param Spreadsheet $mergedSpreadsheet
* @param int|null $index
* @throws Exception
*/
private static function processSingleFile(array $fileInfo, Spreadsheet $mergedSpreadsheet, int $index = null)
{
if (!isset($fileInfo['file_path']) || !file_exists($fileInfo['file_path'])) {
$path = $fileInfo['file_path'] ?? 'undefined';
log_message('error', "File " . ($index ?? 'base') . ": Invalid or missing file path: {$path}");
return;
}
log_message('debug', "Processing file " . ($index ?? 'base') . ": " . $fileInfo['file_path']);
try {
// Load the source spreadsheet
$sourceSpreadsheet = IOFactory::load($fileInfo['file_path']);
// Get all worksheets
$worksheets = $sourceSpreadsheet->getAllSheets();
$totalSheets = count($worksheets);
log_message('debug', "Total sheets in file: " . $totalSheets);
$sheetsToMerge = $fileInfo['sheets'] ?? [];
// Process each worksheet
foreach ($worksheets as $sheetIndex => $worksheet) {
if (empty($sheetsToMerge) || in_array($sheetIndex, $sheetsToMerge)) {
try {
$sheetName = $worksheet->getTitle();
log_message('debug', "Processing sheet: {$sheetName}");
// Generate unique sheet name before cloning
$newName = $sheetName;
$counter = 1;
while ($mergedSpreadsheet->sheetNameExists($newName)) {
$newName = $sheetName . '_' . $counter++;
log_message('debug', "Sheet name already exists. Generating new name: {$newName}");
}
// Clone the worksheet and set the new name
$clonedSheet = clone $worksheet;
$clonedSheet->setTitle($newName);
// Add as external sheet
$mergedSpreadsheet->addExternalSheet($clonedSheet);
log_message('debug', "Successfully added sheet: {$newName}");
} catch (Exception $e) {
log_message('error', "Error processing sheet {$sheetName} as new name {$newName}: " . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
}
} }
} }
self::$logger->logme('error',"Basic cell values copied");
// Clean up source spreadsheet
// Copy column dimensions $sourceSpreadsheet->disconnectWorksheets();
foreach ($sourceSheet->getColumnDimensions() as $columnId => $sourceDim) { unset($sourceSpreadsheet);
$targetSheet->getColumnDimension($columnId) gc_collect_cycles();
->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) { } catch (Exception $e) {
self::$logger->logme('error',"Error in copyWorksheetContent: " . $e->getMessage()); log_message('error', "Error processing file {$fileInfo['file_path']}: " . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString());
throw $e;
} }
} }
} }

View File

@ -0,0 +1,51 @@
<?php
namespace App\Helpers;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class ExcelSanitizeHelper
{
/**
* Array of non-printable characters to be removed from strings.
*
* You can modify this array to include any characters you want to remove.
*/
private static $nonPrintableChars = [
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09",
"\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", "\x10", "\x11", "\x12", "\x13",
"\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D",
"\x1E", "\x1F", "\x7F", "_x000D_", "_x000A_", "_x0009_", "_x0008_", "_x0007_",
"_x0006_", "_x0005_", "_x0004_", "_x0003_", "_x0002_", "_x0001_"
];
/**
* Sanitizes array data by removing non-printable characters and trimming whitespace from strings.
*
* @param array $data Input array containing data to sanitize
* @return array Sanitized array with non-printable characters removed and leading/trailing whitespace trimmed
*/
public static function sanitizeArrayData(array $data): array
{
try {
$cleanData = [];
foreach ($data as $key => $value) {
if (is_array($value)) {
$cleanData[$key] = self::sanitizeArrayData($value); // Recursive call for nested arrays
} elseif (is_string($value)) {
// Remove non-printable characters and trim whitespace from strings
$cleanData[$key] = trim(str_replace(self::$nonPrintableChars, '', $value));
} else {
$cleanData[$key] = $value; // Keep non-string/non-array data as is
}
}
return $cleanData;
} catch (\Exception $e) {
// Log the error and the problematic data for debugging
log_message('error', 'Error sanitizing array data: ' . $e->getMessage());
log_message('error', 'Problematic data: ' . json_encode($data));
return $data; // Return the original data in case of error
}
}
}

View File

@ -210,4 +210,32 @@ class EmployeeModel extends Model
return $results; return $results;
} }
public function getTestEmployeeData(){
$query = "
SELECT
e.*,
(
SELECT
COUNT(ep.client_policy_id)
FROM
employee_polices ep
WHERE
ep.employee_id = e.id
) AS policy_count
FROM
employees e
WHERE
e.emp_code LIKE 'TEST%'
ORDER BY
e.emp_code,
CASE
WHEN e.relationship = 'self' THEN 1
WHEN e.relationship = 'spouse' THEN 2
ELSE 3
END;
";
$results = $this->db->query($query)->getResultArray();
return $results;
}
} }

View File

@ -581,6 +581,9 @@
<li> <li>
<a href="<?= base_url('/employee/enrollment-list') ?>">View Enrolment</a> <a href="<?= base_url('/employee/enrollment-list') ?>">View Enrolment</a>
</li> </li>
<li>
<a href="<?= base_url('/employee/test_members_list') ?>">Test Members List</a>
</li>
</ul> </ul>
</div> </div>
</li> </li>

File diff suppressed because it is too large Load Diff

View File

@ -9,20 +9,21 @@ class ExcelMergeHelperTest extends TestCase
public function testMergeExcelFiles_Success() public function testMergeExcelFiles_Success()
{ {
// Use the paths you provided // Use the paths you provided
$filePaths = [ $filePaths = [
// ['file_path' =>WRITEPATH.'uploads/lead_files/Member_Data.xlsx','sheets' => []], ['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' =>WRITEPATH.'/tmp/RFQ_ABC_GMC_20241220140640.xlsx','sheets' => []],
['file_path' =>'/home/venba/Downloads/Financial_Sample.xlsx','sheets' => []],
// ['file_path' =>'/home/venba/Downloads/Employee_data.xlsx','sheets' => []],
]; ];
$outputPath = '/home/venba/Documents/merged_file.xlsx'; // Output path for the merged file $outputPath = '/home/venba/Documents/merged_file.xlsx'; // Output path for the merged file
// Call the mergeExcelFiles function // Call the mergeExcelFiles function
$result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); $result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath);
var_dump($result); echo('result is -> '.$result);
// Check if the result is true (indicating success) // Check if the result is true (indicating success)
$this->assertTrue($result, 'Expected true, but got false'); $this->assertFileExists($result);
$this->assertTrue(filesize($result) > 0);
} }
} }