From 53038731025794830adfc36ab05490f247ed9a9f Mon Sep 17 00:00:00 2001 From: Srinivas-Saravanan Date: Thu, 2 Jan 2025 15:13:58 +0530 Subject: [PATCH 1/2] FEAT_TEST_MEMBERS_LIST_ADDED,FIX_EXCELMERGEHELPER --- app/Config/Autoload.php | 2 +- app/Config/Routes.php | 4 + app/Controllers/EmployeeController.php | 108 +- app/Controllers/LeadsController.php | 119 +- app/Helpers/ExcelMergeHelper.php | 326 +++--- app/Helpers/ExcelSanitizeHelper.php | 51 + app/Models/EmployeeModel.php | 28 + app/Views/layout/header.php | 3 + app/Views/test_members_list.php | 1005 +++++++++++++++++ ...mber_classification_2024-12-25_125459.xlsx | Bin 0 -> 8343 bytes tests/unit/ExcelMergeHelperTest.php | 15 +- 11 files changed, 1419 insertions(+), 242 deletions(-) create mode 100644 app/Helpers/ExcelSanitizeHelper.php create mode 100644 app/Views/test_members_list.php create mode 100644 public/writable/uploads/lead_files/member_classification_2024-12-25_125459.xlsx diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 6cdb37c7..c80f6c1c 100755 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -99,5 +99,5 @@ class Autoload extends AutoloadConfig * @var string[] * @phpstan-var list */ - 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']; } diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 655da7e1..aef814cc 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -155,6 +155,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->get("truncate/(:any)", "EmployeeController::truncateFileData/$1"); $routes->get("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) { @@ -330,6 +331,9 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->get('getCoShareStatementDetails/(:any)', 'PolicyTransactionController::getCoShareStatementDetails/$1'); $routes->get('getClientPolicyDataBasedOnClientAndInsuer', 'PolicyTransactionController::getClientPolicyDataBasedOnClientAndInsuer'); $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) { diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 7721b203..11f6bd53 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -24,6 +24,7 @@ use App\Models\TPAModel; use App\Models\InsurerExcelExportTemplateModel; use App\Models\InsurerModel; use App\Models\ClientDepositModel; +use App\Models\PolicyPremium2Model; use App\Controllers\Jobs; @@ -62,6 +63,7 @@ class EmployeeController extends AdminController protected $excelExportTemplateModel; protected $insurerModel; protected $cashDepositModel; + protected $PolicyPremium2Model; public function __construct() { @@ -81,6 +83,7 @@ class EmployeeController extends AdminController $this->policiesModel = new PolicesModel(); $this->insurerModel = new InsurerModel(); $this->cashDepositModel = new ClientDepositModel(); + $this->PolicyPremium2Model = new PolicyPremium2Model(); } public function list() @@ -1762,7 +1765,7 @@ class EmployeeController extends AdminController public function update_emp_data() { $data = $this->request->getPost(); - // print_rr($data); + // print_rr($data);die(); // $data['dob'] = date('Y-m-d', strtotime($data['dob'])); $data['dob'] = change_date_format($data['dob'], 'd/m/Y', 'Y-m-d'); // print_rr($data); die; @@ -2021,4 +2024,107 @@ class EmployeeController extends AdminController 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;$iemployeeModel->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;$iemployeeModel->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); + } + } + + } + } diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 2ad7a1e8..44f6967f 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -29,6 +29,7 @@ use App\Models\InsurerModel; use App\Helpers\MailHelper; use App\Helpers\ExcelMergeHelper; +use App\Helpers\ExcelSanitizeHelper; use Google\Service\CloudSearch\PushItem; use Kint; @@ -99,7 +100,7 @@ class LeadsController extends BaseController // $d = $this->constructExcelToSaveTemp(24, 1, $propsal_and_insurer = null); $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]); //$this->mergeQuoteExcelFileWithMembersListExcelFile(24, 1, $propsal_and_insurer = null); // dd($d); @@ -740,8 +741,12 @@ class LeadsController extends BaseController { $lead_id = $params['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']) { $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 if (!file_exists($file_name_with_path)) { @@ -758,9 +763,8 @@ class LeadsController extends BaseController $members_sheet = $spreadsheet->getSheet(0); $highestRowAndColumn = $members_sheet->getHighestRowAndColumn(); // dd($highestRowAndColumn); - $members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); - // dd($members[6]); - + $uncleaned_members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']); + $members = ExcelSanitizeHelper::sanitizeArrayData($uncleaned_members); //get age band data $age_band_sheet = $spreadsheet->getSheet(1); $highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn(); @@ -792,19 +796,24 @@ class LeadsController extends BaseController } try { - $result = $this->generateClassifierSpreadsheet($classifiers, 'writable/uploads/lead_files'); - + $result = $this->generateClassifierSpreadsheet($classifiers, WRITEPATH . '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' => []], + ['file_path' => WRITEPATH.'/uploads/lead_files/' . $result['filename'], 'sheets' => []], ]; $outputPath = WRITEPATH . 'uploads/lead_files/Member_Data.xlsx'; - $result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); - if ($result) { + $result_merge = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath); + 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']; } } else { @@ -931,23 +940,22 @@ class LeadsController extends BaseController 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; @@ -957,28 +965,39 @@ class LeadsController extends BaseController $filename = "member_classification_{$timestamp}_{$counter}.xlsx"; $filepath = $outputDir . DIRECTORY_SEPARATOR . $filename; } - - + $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); - + $sheet->setTitle('Demography_Data'); + // 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 + $writeSectionData = function ($data, $sheet, &$currentRow, $si_type) use ($age_bands) { + // 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'); $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; @@ -994,9 +1013,9 @@ class LeadsController extends BaseController 'horizontal' => Alignment::HORIZONTAL_CENTER, ], ]); - + $currentRow++; - + // Write data rows foreach ($data as $relation => $values) { if ($relation !== 'Grand Total') { @@ -1007,7 +1026,7 @@ class LeadsController extends BaseController $sheet->setCellValue($col . $currentRow, $value); $col++; } - + // Style data row $dataRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; $sheet->getStyle($dataRange)->applyFromArray([ @@ -1021,11 +1040,11 @@ class LeadsController extends BaseController 'horizontal' => Alignment::HORIZONTAL_CENTER, ], ]); - + $currentRow++; } } - + // Add Grand Total row $sheet->setCellValue('B' . $currentRow, 'Grand Total'); $col = 'C'; @@ -1033,7 +1052,7 @@ class LeadsController extends BaseController $sheet->setCellValue($col . $currentRow, $data['Grand Total'][$band]); $col++; } - + // Style Grand Total row $totalRange = 'B' . $currentRow . ':' . $lastCol . $currentRow; $sheet->getStyle($totalRange)->applyFromArray([ @@ -1052,37 +1071,41 @@ class LeadsController extends BaseController '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); + $writeSectionData($data, $sheet, $currentRow, $si_type); } - + // 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 [ + + // Return the file info after creation + $response = [ 'success' => true, 'filepath' => $filepath, 'filename' => $filename, 'fullpath' => realpath($filepath) ]; + + return $response; + } catch (Exception $e) { return [ '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) { if ($available_col == 'age') { diff --git a/app/Helpers/ExcelMergeHelper.php b/app/Helpers/ExcelMergeHelper.php index b026296e..fb593ec5 100644 --- a/app/Helpers/ExcelMergeHelper.php +++ b/app/Helpers/ExcelMergeHelper.php @@ -1,218 +1,150 @@ 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; - + log_message('debug', 'Attempting merge with original file order'); + return self::processFiles($filePaths, $outputPath); } catch (Exception $e) { - self::$logger->logme('error',"Excel Merge Error: " . $e->getMessage()); - self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); - throw $e; + log_message('error', 'First attempt failed: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine()); + log_message('debug', 'Retrying with reversed file order'); + + // 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 { - 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}"); + log_message('debug', 'Starting Excel merge process'); + log_message('debug', 'Files to process: ' . json_encode($filePaths)); + + if (empty($filePaths)) { + throw new Exception("No files provided to merge"); + } - // 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()); + // Initialize an empty merged spreadsheet + $mergedSpreadsheet = new Spreadsheet(); + $mergedSpreadsheet->removeSheetByIndex(0); // Remove the default empty sheet + + // Process the base file + $firstFile = array_shift($filePaths); + self::processSingleFile($firstFile, $mergedSpreadsheet); + + // 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"); - - // 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"); + + // Clean up source spreadsheet + $sourceSpreadsheet->disconnectWorksheets(); + unset($sourceSpreadsheet); + gc_collect_cycles(); } catch (Exception $e) { - self::$logger->logme('error',"Error in copyWorksheetContent: " . $e->getMessage()); - self::$logger->logme('error',"Error trace: " . $e->getTraceAsString()); - throw $e; + log_message('error', "Error processing file {$fileInfo['file_path']}: " . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine()); } } } \ No newline at end of file diff --git a/app/Helpers/ExcelSanitizeHelper.php b/app/Helpers/ExcelSanitizeHelper.php new file mode 100644 index 00000000..b4950b34 --- /dev/null +++ b/app/Helpers/ExcelSanitizeHelper.php @@ -0,0 +1,51 @@ + $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 + } + } +} diff --git a/app/Models/EmployeeModel.php b/app/Models/EmployeeModel.php index 06c39c7f..b8ad55a9 100755 --- a/app/Models/EmployeeModel.php +++ b/app/Models/EmployeeModel.php @@ -210,4 +210,32 @@ class EmployeeModel extends Model 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; + } + } diff --git a/app/Views/layout/header.php b/app/Views/layout/header.php index ee423b08..8d7bd1f9 100755 --- a/app/Views/layout/header.php +++ b/app/Views/layout/header.php @@ -581,6 +581,9 @@
  • View Enrolment
  • +
  • + Test Members List +
  • diff --git a/app/Views/test_members_list.php b/app/Views/test_members_list.php new file mode 100644 index 00000000..db5ce46e --- /dev/null +++ b/app/Views/test_members_list.php @@ -0,0 +1,1005 @@ + + + + +
    +
    +
    +
    +
    +
    +

    Employees

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + $employee) { ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SNOClient/BranchBranchNameEMP CodeRelationshipGenderDate of BirthExisting Policy Count Action
    + +
    +
    +
    +
    +
    + + + + + + + + + + diff --git a/public/writable/uploads/lead_files/member_classification_2024-12-25_125459.xlsx b/public/writable/uploads/lead_files/member_classification_2024-12-25_125459.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bd45a663890734692a1839670926810227c35d33 GIT binary patch literal 8343 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 RFx30^=>5d`ME3L7{{sLRqXhr} literal 0 HcmV?d00001 diff --git a/tests/unit/ExcelMergeHelperTest.php b/tests/unit/ExcelMergeHelperTest.php index 342c17c6..f543d43a 100755 --- a/tests/unit/ExcelMergeHelperTest.php +++ b/tests/unit/ExcelMergeHelperTest.php @@ -9,20 +9,21 @@ class ExcelMergeHelperTest extends TestCase public function testMergeExcelFiles_Success() { // Use the paths you provided - $filePaths = [ - // ['file_path' =>WRITEPATH.'uploads/lead_files/Member_Data.xlsx','sheets' => []], - // ['file_path' =>WRITEPATH.'/tmp/RFQ_ABC_GMC_20241220140640.xlsx','sheets' => []], + $filePaths = [ + ['file_path' =>WRITEPATH.'uploads/lead_files/Member_Data.xlsx','sheets' => []], -['file_path' =>'/home/venba/Downloads/enrollment (1).xlsx','sheets' => []], - ['file_path' =>'/home/venba/Downloads/Financial_Sample.xlsx','sheets' => []], + ['file_path' =>WRITEPATH.'/tmp/RFQ_ABC_GMC_20241220140640.xlsx','sheets' => []], + + // ['file_path' =>'/home/venba/Downloads/Employee_data.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); + echo('result is -> '.$result); // Check if the result is true (indicating success) - $this->assertTrue($result, 'Expected true, but got false'); + $this->assertFileExists($result); + $this->assertTrue(filesize($result) > 0); } } From 208364d806cffb7d21fb3ccc585625bde3d86561 Mon Sep 17 00:00:00 2001 From: Srinivas-Saravanan Date: Fri, 3 Jan 2025 12:05:21 +0530 Subject: [PATCH 2/2] FEAT_ZEPTO_MAIL_INTEGRATION --- app/Config/Routes.php | 2 + app/Controllers/MasterController.php | 30 +- app/Helpers/ExcelMergeHelper.php | 6 +- app/Helpers/GmailResponseHandler.php | 24 +- app/Helpers/MailHelper.php | 465 ++++++++++++++++++++------- composer.json | 6 +- 6 files changed, 404 insertions(+), 129 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 1b73489f..ba8c0f25 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -400,6 +400,7 @@ $routes->get("driveListFiles", "GoogleDriveController::listFiles"); $routes->post('dmsSearch', 'PolicyTransactionController::dmsSearch', ['filter' => 'authMVC']); $routes->get('dmsSearch', 'PolicyTransactionController::dmsSearch', ['filter' => 'authMVC']); $routes->get('downloadGdriveFile', 'GoogleDriveController::downloadGdriveFile', ['filter' => 'authMVC']); +$routes->get('cli/sendZeptoMail', 'MasterController::testZeptoSMTP'); $routes->cli('cli/processjob', 'JobWorker::processJob'); $routes->cli('cli/processjobs', 'JobWorker::processJobs'); @@ -407,6 +408,7 @@ $routes->cli('cli/processjobs', 'JobWorker::processJobs'); $routes->get("processjob", "JobWorker::processJob"); $routes->cli('cli/new_gmail_token', 'MasterController::generateNewGmailAPIToken'); $routes->cli('cli/send_mail_cli', 'MasterController::testGmailAPIViaCLI'); +$routes->cli('cli/sendZeptoMail', 'MasterController::testZeptoSMTP'); $routes->cli('cli/check_bounce_mail_cli', 'MasterController::testCheckBounceMails'); $routes->cli('cli/app_check_list', 'MasterController::appCheckList'); $routes->cli('cli/new_gdrive_token', 'GoogleDriveController::generateNewGoogleDriveAccessToken'); diff --git a/app/Controllers/MasterController.php b/app/Controllers/MasterController.php index 6a81ebf5..d643952b 100755 --- a/app/Controllers/MasterController.php +++ b/app/Controllers/MasterController.php @@ -1641,7 +1641,35 @@ class MasterController extends AdminController print_r($res); } - + + public function testZeptoSMTP() { + $message = ''; + $attachments = [ + ["filePath" => ROOTPATH."public/sample_excel/sample_addition.xls","fileName" => "sample_addition.xls"], + ["filePath" => ROOTPATH."public/sample_excel/sample_inception.xls","fileName" => "sample_inception.xls"], + ["filePath" => ROOTPATH."public/assets/images/login_bg.jpg","fileName" => "login_bg.jpg"] + ]; + $email_id = 'srinivas.saravanan@venbainfotech.com'; + $common = ['mail_type'=>'test_mail_cli']; + // Send email and get response + $result = MailHelper::send_email(['mail' => $email_id, 'subject' => 'Mail Via CLI', 'message' => $message,'attachments' => $attachments,'common'=>$common]); + + + log_message('error',json_encode($result)); + // Format the response for display/debugging + $response = [ + 'success' => $result['success'] ?? false, + 'smtp_data' => [ + 'last_reply' => $result['smtp_data']['last_reply'] ?? '', + 'error_info' => $result['smtp_data']['error_info'] ?? '' + ], + 'message_id' => $result['message_id'] ?? '', + 'timestamp' => $result['timestamp'] ?? '' + ]; + + // Return JSON response + return $this->response->setJSON($result); + } public function testCheckBounceMails() { $gmailapi = \Config\Services::gmailapi(); diff --git a/app/Helpers/ExcelMergeHelper.php b/app/Helpers/ExcelMergeHelper.php index fb593ec5..36306692 100644 --- a/app/Helpers/ExcelMergeHelper.php +++ b/app/Helpers/ExcelMergeHelper.php @@ -119,9 +119,9 @@ class ExcelMergeHelper { $newName = $sheetName; $counter = 1; - while ($mergedSpreadsheet->sheetNameExists($newName)) { - $newName = $sheetName . '_' . $counter++; - log_message('debug', "Sheet name already exists. Generating new name: {$newName}"); + while (in_array($newName, $mergedSpreadsheet->getSheetNames())) { + $newName = $sheetName . "_" . $counter++; + log_message('debug', "Sheet name already exists. Trying new name: {$newName}"); } // Clone the worksheet and set the new name diff --git a/app/Helpers/GmailResponseHandler.php b/app/Helpers/GmailResponseHandler.php index f2cb07f7..4a45f65c 100644 --- a/app/Helpers/GmailResponseHandler.php +++ b/app/Helpers/GmailResponseHandler.php @@ -77,17 +77,16 @@ class GmailResponseHandler{ } } } - if(isset($params['gmail_api']['status']) && $params['gmail_api']['status'] == 1 ){ - $data['gmail_api_status'] = isset($params['gmail_api']['status']) ? $params['gmail_api']['status'] : null; - $data['gmail_api_id'] = isset($params['gmail_api']['data']['id']) ? $params['gmail_api']['data']['id'] : null; - $data['received_message'] = isset($params['gmail_api']['data']['labelIds']) ? json_encode($params['gmail_api']['data']['labelIds']) : null; + if(isset($params['zepto_api']['data']) && $params['zepto_api']['message'] == 'OK' ){ + $data['gmail_api_status'] = isset($params['zepto_api']['data']) ? $params['zepto_api']['data'][0]['code'] : null; + $data['gmail_api_id'] = isset($params['zepto_api']['request_id']) ? $params['zepto_api']['request_id'] : null; + $data['received_message'] = isset($params['zepto_api']['data'][0]['message']) ? json_encode($params['zepto_api']['data'][0]['message']) : null; } - elseif(isset($params['gmail_api']['status']) && $params['gmail_api']['status'] == false ){ - $data['gmail_api_status'] = $params['gmail_api']['status']; - if(isset($params['gmail_api']['data'])){ - $data['received_message'] = json_encode($params['gmail_api']['data']); - } + elseif(isset($params['zepto_api']['error'])){ + $data['gmail_api_status'] = isset($params['zepto_api']['error']['code'])?$params['zepto_api']['error']['code']:null; + $data['received_message'] = json_encode($params['zepto_api']); + $data['gmail_api_id'] = isset($params['zepto_api']['error']['request_id']) ? $params['zepto_api']['error']['request_id'] : null; } $gmailModel = new GmailSentHistoryModel(); @@ -96,7 +95,9 @@ class GmailResponseHandler{ public function gmail_response_logger($params) { - $this->myLogger->logme('error',json_encode($params)); + if(isset($params['zepto_api']['error'])){ + $this->myLogger->logme('error',json_encode($params)); + } } public function client_id_flag_setter($params) @@ -119,7 +120,6 @@ class GmailResponseHandler{ $error = $e->getMessage(); $this->myLogger->logme('error','An Error Occured - '.$error); } - } - +} } \ No newline at end of file diff --git a/app/Helpers/MailHelper.php b/app/Helpers/MailHelper.php index 085c510f..91e955b0 100755 --- a/app/Helpers/MailHelper.php +++ b/app/Helpers/MailHelper.php @@ -1,177 +1,422 @@ setMailType('html'); +// $email->setFrom('bbone@venbait.in', 'Nhance'); + +// $email->setTo($emaill); + +// if (!empty($bcc)) { +// // Convert the comma-separated string into an array +// $bccList = explode(',', $bcc); +// // Trim whitespace from each email ID +// $bccList = array_map('trim', $bccList); +// // Set BCC recipients +// $email->setBCC($bccList); +// } + +// $email->setSubject($subject); + +// $email->setMessage($message); + +// if ($email->send()) { + +// return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...','data' => $emaill,],200); +// } else { +// $myLogger->logme('error', "mail sent failed - $emaill"); +// return json_encode(['status' => 'failed','code' => 404,'message'=>'Email Sent Failed...','data' => $emaill ],404); +// } + +// } catch (Exception $e) { +// $msg = $e->getMessage(); +// $myLogger->logme('error', "mail sent failed - $msg"); +// return json_encode(['status' => 'failed','code' => 500,'data' => $emaill],500); +// } +// } + +// public static function bulk_mail_smtp(array $mails = []) +// { +// // print_r();die; +// $model = new JobModel(); +// $start = microtime(true); +// $runtime= 0; +// try { +// foreach ( $mails as $index=>$mail) { +// $runtime = microtime(true) - $start; +// $send_mail = self::send_email($mail); +// } + +// return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...'],200); + +// } catch (\Throwable $th) { +// return json_encode(['status' => 'failed','code' => 500],500); +// } +// } + +// //using GmailAPI +// public static function send_email($params) +// { + +// // print_r($params);die; +// $myLogger = \Config\Services::mylogger(); +// $gmailapi = \Config\Services::gmailapi(); + +// $emaill = $params['mail']; +// $subject = $params['subject']; +// $message = $params['message']; +// $attachments = isset($params['attachments']) && count($params['attachments']) ? $params['attachments'] : []; +// $common = isset($params['common'])?$params['common']:''; +// //check BCC mail +// if (isset($params['bcc'])) { +// $bcc = $params['bcc']; +// if(!is_array($bcc)){ +// $bcc = explode(',', $bcc); +// } +// }else{ +// $bcc = []; +// } + +// //check CC mail +// if (isset($params['cc'])) { +// $cc = $params['cc']; +// if(!is_array($cc)){ +// $cc = explode(',', $cc); +// } +// }else{ +// $cc = []; +// } + +// //check REPLY TO mail +// $reply_to = ""; +// if(isset($params['reply_to']) && !empty($params['reply_to'])){ +// $reply_to = $params['reply_to']; +// } +// $MailDataHelper = new GmailResponseHandler(); + +// $res['params'] = $params; +// try { + +// $res['gmail_api'] = $gmailapi->sendMessage($emaill, $subject, $message, 'no-reply@nhanceindia.in', $reply_to, $cc, $bcc,$attachments); +// if($res['gmail_api']['status']) +// { +// $response = $res; +// $MailDataHelper->receive_and_distribute_param_to_functions($response); +// return json_encode(['status' => 'success','code' => 200, 'message'=>('Email Sent Successfully...'.$emaill),'data' => $res,],200); +// } +// else +// { +// $myLogger->logme('error', "mail sent failed - $emaill"); +// $response = $res; +// $MailDataHelper->receive_and_distribute_param_to_functions($response); +// return json_encode(['status' => 'failed','code' => 404,'message'=>( 'Email Sent Failed...' . $emaill ),'data' => $res ],404); +// } + +// } catch (Exception $e) { +// $msg['error'] = $e->getMessage(); +// $msg['params'] = $params; +// $myLogger->logme('error', "mail sent failed - $msg"); +// $response = $msg; +// $MailDataHelper->receive_and_distribute_param_to_functions($response); +// return json_encode(['status' => 'failed','code' => 500,'data' => $msg],500); +// } + + +// } + + +// public static function bulk_mail(array $mails = []) +// { +// // print_r($mails);die; +// $model = new JobModel(); +// $start = microtime(true); +// $runtime= 0; +// try { +// foreach ( $mails as $index=>$mail) { +// $runtime = microtime(true) - $start; +// $send_mail = self::send_email($mail); +// } + +// return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...'],200); + +// } catch (\Throwable $th) { +// $error = $th->getMessage(); +// log_message('error',$error); +// return json_encode(['status' => 'failed','code' => 500,'error'=>$error],500); +// } +// } + +// } + + namespace App\Helpers; use Psr\Log\LoggerInterface; - use App\Controllers\BaseController; - -use PHPMailer\PHPMailer\PHPMailer; -use PHPMailer\PHPMailer\SMTP; -use PHPMailer\PHPMailer\Exception; - use App\Models\JobModel; class MailHelper { public static function send_email_smtp($params) { - - // print_r($params);die; $myLogger = \Config\Services::mylogger(); $emaill = $params['mail']; $subject = $params['subject']; $message = $params['message']; if (isset($params['bcc'])) { $bcc = $params['bcc']; - }else{ - $bcc= ''; + } else { + $bcc = ''; } + $from_address = getenv('email.fromEmail'); + + try { - - $email = \Config\Services::email(); - $email->setMailType('html'); - $email->setFrom('bbone@venbait.in', 'Nhance'); - - $email->setTo($emaill); + $curl = curl_init(); + $postData = [ + 'from' => [ + 'address' => $from_address + ], + 'to' => [ + [ + 'email_address' => [ + 'address' => $emaill + ] + ] + ], + 'subject' => $subject, + 'htmlbody' => $message + ]; + // Add BCC if present if (!empty($bcc)) { - // Convert the comma-separated string into an array $bccList = explode(',', $bcc); - // Trim whitespace from each email ID $bccList = array_map('trim', $bccList); - // Set BCC recipients - $email->setBCC($bccList); + $postData['bcc'] = array_map(function($email) { + return [ + 'email_address' => [ + 'address' => $email + ] + ]; + }, $bccList); } - $email->setSubject($subject); + curl_setopt_array($curl, [ + CURLOPT_URL => "https://api.zeptomail.in/v1.1/email", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 30, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => json_encode($postData), + CURLOPT_HTTPHEADER => [ + "accept: application/json", + "authorization: Zoho-enczapikey " . getenv('ZEPTO_API_KEY'), + "cache-control: no-cache", + "content-type: application/json", + ], + ]); - $email->setMessage($message); + $response = curl_exec($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $err = curl_error($curl); - if ($email->send()) { + curl_close($curl); + if ($err) { + $myLogger->logme('error', "mail sent failed - $emaill"); + return json_encode(['status' => 'failed', 'code' => 404, 'message' => 'Email Sent Failed...', 'data' => $emaill], 404); + } - return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...','data' => $emaill,],200); + $apiResponse = json_decode($response, true); + + if ($httpCode === 200) { + return json_encode(['status' => 'success', 'code' => 200, 'message' => 'Email Sent Successfully...', 'data' => $emaill], 200); } else { $myLogger->logme('error', "mail sent failed - $emaill"); - return json_encode(['status' => 'failed','code' => 404,'message'=>'Email Sent Failed...','data' => $emaill ],404); + return json_encode(['status' => 'failed', 'code' => 404, 'message' => 'Email Sent Failed...', 'data' => $emaill], 404); } - - } catch (Exception $e) { + + } catch (\Exception $e) { $msg = $e->getMessage(); $myLogger->logme('error', "mail sent failed - $msg"); - return json_encode(['status' => 'failed','code' => 500,'data' => $emaill],500); - } + return json_encode(['status' => 'failed', 'code' => 500, 'data' => $emaill], 500); + } } public static function bulk_mail_smtp(array $mails = []) { - // print_r();die; $model = new JobModel(); $start = microtime(true); - $runtime= 0; - try { - foreach ( $mails as $index=>$mail) { + $runtime = 0; + try { + foreach ($mails as $index => $mail) { $runtime = microtime(true) - $start; $send_mail = self::send_email($mail); - } - - return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...'],200); + } + + return json_encode(['status' => 'success', 'code' => 200, 'message' => 'Email Sent Successfully...'], 200); - } catch (\Throwable $th) { - return json_encode(['status' => 'failed','code' => 500],500); - } + } catch (\Throwable $th) { + return json_encode(['status' => 'failed', 'code' => 500], 500); + } } - //using GmailAPI - public static function send_email($params) + public static function send_email($params) { - - // print_r($params);die; $myLogger = \Config\Services::mylogger(); - $gmailapi = \Config\Services::gmailapi(); - + $emaill = $params['mail']; $subject = $params['subject']; $message = $params['message']; - $attachments = isset($params['attachments']) && count($params['attachments']) ? $params['attachments'] : []; - $common = isset($params['common'])?$params['common']:''; - //check BCC mail - if (isset($params['bcc'])) { - $bcc = $params['bcc']; - if(!is_array($bcc)){ - $bcc = explode(',', $bcc); - } - }else{ - $bcc = []; - } + $attachments = isset($params['attachments']) ? $params['attachments'] : []; + $common = isset($params['common']) ? $params['common'] : ''; - //check CC mail - if (isset($params['cc'])) { - $cc = $params['cc']; - if(!is_array($cc)){ - $cc = explode(',', $cc); - } - }else{ - $cc = []; - } - - //check REPLY TO mail - $reply_to = ""; - if(isset($params['reply_to']) && !empty($params['reply_to'])){ - $reply_to = $params['reply_to']; - } - $MailDataHelper = new GmailResponseHandler(); - - $res['params'] = $params; + $from_address = getenv('email.fromEmail'); try { - - $res['gmail_api'] = $gmailapi->sendMessage($emaill, $subject, $message, 'no-reply@nhanceindia.in', $reply_to, $cc, $bcc,$attachments); - if($res['gmail_api']['status']) - { - $response = $res; - $MailDataHelper->receive_and_distribute_param_to_functions($response); - return json_encode(['status' => 'success','code' => 200, 'message'=>('Email Sent Successfully...'.$emaill),'data' => $res,],200); - } - else - { - $myLogger->logme('error', "mail sent failed - $emaill"); - $response = $res; - $MailDataHelper->receive_and_distribute_param_to_functions($response); - return json_encode(['status' => 'failed','code' => 404,'message'=>( 'Email Sent Failed...' . $emaill ),'data' => $res ],404); - } - - } catch (Exception $e) { + $curl = curl_init(); + + $postData = [ + 'from' => [ + 'address' => $from_address + ], + 'to' => [ + [ + 'email_address' => [ + 'address' => $emaill + ] + ] + ], + 'subject' => $subject, + 'htmlbody' => $message + ]; + + // Handle attachments + if (!empty($attachments)) { + $postData['attachments'] = []; + foreach ($attachments as $attachment) { + if (isset($attachment['filePath']) && file_exists($attachment['filePath'])) { + // Determine MIME type dynamically + $fileMimeType = mime_content_type($attachment['filePath']); + + $postData['attachments'][] = [ + 'content' => base64_encode(file_get_contents($attachment['filePath'])), + 'name' => $attachment['fileName'], + 'mime_type' => $fileMimeType, // Add mime_type field + ]; + } + } + } + + + curl_setopt_array($curl, [ + CURLOPT_URL => "https://api.zeptomail.in/v1.1/email", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 30, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => json_encode($postData), + CURLOPT_HTTPHEADER => [ + "accept: application/json", + "authorization: Zoho-enczapikey " . getenv('ZEPTO_API_KEY'), + "cache-control: no-cache", + "content-type: application/json", + ], + ]); + + $mail_result = curl_exec($curl); + $err = curl_error($curl); + curl_close($curl); + // log_message('error','inside mail helper'); + // log_message('error', $response.' error->'.$err);die(); + $res['params'] = $params; + $MailDataHelper = new GmailResponseHandler(); + $apiResponse = json_decode($mail_result, true); + $res['zepto_api'] = $apiResponse; + if (isset($apiResponse['error'])) { + $response = $res; + $MailDataHelper->receive_and_distribute_param_to_functions($response); + return json_encode([ + 'status' => 'failed', + 'code' => 404, + 'message' => 'Email Sent Failed...' . $emaill, + 'data' => $res + ], 404); + } + + // $apiResponse = json_decode($response, true); + + // Check if request_id exists in response (indicates success) + if (isset($apiResponse['data'])) { + $response = $res; + $MailDataHelper->receive_and_distribute_param_to_functions($response); + return json_encode([ + 'status' => 'success', + 'code' => 200, + 'message' => 'Email Sent Successfully...' . $emaill, + 'data' => $res + ], 200); + } + + } catch (\Exception $e) { $msg['error'] = $e->getMessage(); $msg['params'] = $params; - $myLogger->logme('error', "mail sent failed - $msg"); - $response = $msg; + $response = $res; $MailDataHelper->receive_and_distribute_param_to_functions($response); - return json_encode(['status' => 'failed','code' => 500,'data' => $msg],500); + return json_encode([ + 'status' => 'failed', + 'code' => 500, + 'data' => $msg + ], 500); } - - } - public static function bulk_mail(array $mails = []) { - // print_r($mails);die; $model = new JobModel(); $start = microtime(true); - $runtime= 0; - try { - foreach ( $mails as $index=>$mail) { + $runtime = 0; + try { + foreach ($mails as $index => $mail) { $runtime = microtime(true) - $start; $send_mail = self::send_email($mail); + } + + return json_encode(['status' => 'success', 'code' => 200, 'message' => 'Email Sent Successfully...'], 200); + + } catch (\Throwable $th) { + $error = $th->getMessage(); + log_message('error', $error); + return json_encode(['status' => 'failed', 'code' => 500, 'error' => $error], 500); } - - return json_encode(['status' => 'success','code' => 200, 'message'=>'Email Sent Successfully...'],200); - - } catch (\Throwable $th) { - $error = $th->getMessage(); - log_message('error',$error); - return json_encode(['status' => 'failed','code' => 500,'error'=>$error],500); - } } - } ?> \ No newline at end of file diff --git a/composer.json b/composer.json index 73453d9c..c456241c 100755 --- a/composer.json +++ b/composer.json @@ -17,15 +17,15 @@ "dompdf/dompdf": "^2.0", "firebase/php-jwt": "^6.10", "google/apiclient": "^2.15.0", + "kreait/firebase-php": "^7.0", "laminas/laminas-escaper": "^2.9", "php-amqplib/php-amqplib": "^2.8", "phpmailer/phpmailer": "^6.9", "phpoffice/phpspreadsheet": "^2.1", + "psr/http-message": "^1.0", "psr/log": "^1.1", "slim/slim": "^4.13", - "zircote/swagger-php": "^4.8", - "psr/http-message": "^1.0", - "kreait/firebase-php":"^7.0" + "zircote/swagger-php": "^4.8" }, "require-dev": { "codeigniter/coding-standard": "^1.7",