FEAT_ENROLLMENT_COLSED_ENROLLED_EMPLOYEE_LIST_EXCEL_MAIL
This commit is contained in:
parent
130de0bad6
commit
494885c5ab
@ -22,6 +22,10 @@ class Acl
|
||||
'#^/claim-form-download#' => ['public' => true],
|
||||
'#^/claims-feedback-form#' => ['public' => true],
|
||||
'#^/autobookstackLogin#' => ['public' => true],
|
||||
'#^/sendCroneRemainderMail#' => ['public' => true],
|
||||
'#^/sendEnrollmentClosedHrMail#' => ['public' => true],
|
||||
'#^/testSendEnrollmentClosedHrMail#' => ['public' => true],
|
||||
'#^/download_file#' => ['public' => true],
|
||||
|
||||
// ===================== DASHBOARD =====================
|
||||
'#^/dashboard#' => [
|
||||
|
||||
@ -32,7 +32,10 @@ $routes->get("testMailAttachments", "ClientController::testMailAttachments");
|
||||
$routes->get("updatePolicyTermsKey", "ClientController::updatePolicyTermsKey");
|
||||
$routes->get("updateRemainderDate", "ClientController::updateRemainderDate");
|
||||
$routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||
$routes->get("sendEnrollmentClosedHrMail", "EmployeeController::sendEnrollmentClosedHrMail");
|
||||
$routes->get("testSendEnrollmentClosedHrMail", "EmployeeController::testSendEnrollmentClosedHrMail");
|
||||
$routes->get("sendextraparam", "ClientController::sendextraparam");
|
||||
$routes->get('download_file','EmployeeController::download_file');
|
||||
// $routes->post("iAgreeForAddOn", "EmployeeRestController::iAgreeForAddOn");
|
||||
// $routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||
// $routes->post("employeeUpload", "EmployeeRestController::employeeUpload");
|
||||
@ -512,7 +515,7 @@ $routes->group("employeeRest", ['filter' => [ 'GlobalPostFileUploadGuard', 'appS
|
||||
|
||||
$routes->get("getReminderMailConfig", "EmployeeRestController::getReminderMailConfig");
|
||||
$routes->post("saveReminderMailConfig", "EmployeeRestController::saveReminderMailConfig");
|
||||
|
||||
$routes->post('download_inception','EmployeeController::download_inception');
|
||||
});
|
||||
|
||||
$routes->group("employeeRest", ['filter' => ['appSignature','AuthApiRateLimitFilter'] ], function ($routes) {
|
||||
@ -563,6 +566,9 @@ $routes->get("getBackToEnrolledDetails", "EmployeeRestController::getBackToEnrol
|
||||
$routes->cli('cli/reset-token-timeout', 'RestAuthenticationController::resetTokenTimeOut');
|
||||
$routes->cli('cli/enrollOpendAndClose', 'DashboardController::updatePolicyEnrollmentStatus');
|
||||
$routes->cli("cli/sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||
$routes->cli("cli/sendEnrollmentClosedHrMail", "EmployeeController::sendEnrollmentClosedHrMail");
|
||||
$routes->cli('cli/testSendEnrollmentClosedHrMail/(:num)/(:segment)', 'EmployeeController::testSendEnrollmentClosedHrMail/$1/$2');
|
||||
$routes->cli('cli/testSendEnrollmentClosedHrMail/(:num)', 'EmployeeController::testSendEnrollmentClosedHrMail/$1');
|
||||
$routes->cli('cli/update-emp-policy-status', 'ClientController::updateEmpAndPolicyStatus');
|
||||
$routes->get('enrollOpendAndClose', 'DashboardController::updatePolicyEnrollmentStatus');
|
||||
$routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||
|
||||
@ -37,6 +37,8 @@ use App\Controllers\Jobs\SubJob;
|
||||
use App\Controllers\EmployeeServiceController;
|
||||
use App\Controllers\EmpDataServiceController;
|
||||
|
||||
use App\Helpers\MailHelper;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
@ -824,7 +826,7 @@ class EmployeeController extends AdminController
|
||||
session()->setFlashdata('success', 'File Uploaded Successfully File is being validated.');
|
||||
return redirect()->to(base_url('employee/upload'));
|
||||
} else {
|
||||
session()->setFlashdata($return['status'], $return['message']);
|
||||
session()->setFlashdata($return['status'] ?? 'error', $return['message'] ?? 'File upload failed.');
|
||||
return redirect()->to(base_url('employee/upload'));
|
||||
}
|
||||
} else if ($event_type == 'correction') {
|
||||
@ -2938,104 +2940,526 @@ class EmployeeController extends AdminController
|
||||
|
||||
public function download_inception()
|
||||
{
|
||||
$client = $this->request->getPost('client');
|
||||
$branch = $this->request->getPost('branch');
|
||||
$policies = $this->request->getPost('policies');
|
||||
$status = $this->request->getPost('status');
|
||||
$empCode = $this->request->getPost('empCode');
|
||||
$empName = $this->request->getPost('empName');
|
||||
$contentType = strtolower($this->request->getHeaderLine('Content-Type'));
|
||||
|
||||
$result = $this->employeePolicyModel->download_inception(
|
||||
$client, $branch, $policies, $status, $empCode, $empName
|
||||
);
|
||||
|
||||
if (empty($result)) {
|
||||
return $this->response->setStatusCode(204)->setBody('No data found');
|
||||
if (str_contains($contentType, 'application/json')) {
|
||||
try {
|
||||
$payload = $this->request->getJSON(true);
|
||||
} catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) {
|
||||
log_message('error', 'download_inception: Invalid JSON payload - ' . $e->getMessage());
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Invalid JSON request body.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
$payload = is_array($payload) ? $payload : [];
|
||||
} else {
|
||||
$payload = $this->request->getPost() ?? [];
|
||||
}
|
||||
|
||||
$formatted = [];
|
||||
foreach ($result as $index => $row) {
|
||||
$rowWithSerial = ['S.NO' => $index + 1] + $row;
|
||||
$formatted[] = $rowWithSerial;
|
||||
}
|
||||
$result = $formatted;
|
||||
$client = $payload['client'] ?? null;
|
||||
$branch = $payload['branch'] ?? null;
|
||||
$policies = $payload['policies'] ?? null;
|
||||
$status = $payload['status'] ?? null;
|
||||
$empCode = $payload['empCode'] ?? null;
|
||||
$empName = $payload['empName'] ?? null;
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
log_message('error', 'download_inception: Request received - ' . json_encode([
|
||||
'client' => $client,
|
||||
'branch' => $branch,
|
||||
'policies' => $policies,
|
||||
'status' => $status,
|
||||
'empCode' => $empCode,
|
||||
'empName' => $empName,
|
||||
]));
|
||||
|
||||
$headers = array_keys($result[0]);
|
||||
$columnWidth = 20; // standard column width
|
||||
try {
|
||||
$result = $this->employeePolicyModel->download_inception(
|
||||
$client, $branch, $policies, $status, $empCode, $empName
|
||||
);
|
||||
|
||||
$colIndex = 1;
|
||||
foreach ($headers as $header) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||
$cellCoordinate = $columnLetter . '1';
|
||||
if (!is_array($result)) {
|
||||
log_message('error', 'download_inception: Unexpected result type from model');
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Failed to fetch inception data.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$sheet->setCellValue($cellCoordinate, ucfirst(str_replace('_', ' ', $header)));
|
||||
if (empty($result)) {
|
||||
log_message('error', 'download_inception: No data found for given filters');
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'No data found for given filters.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$sheet->getColumnDimension($columnLetter)->setWidth($columnWidth);
|
||||
$formatted = [];
|
||||
foreach ($result as $index => $row) {
|
||||
$rowWithSerial = ['S.NO' => $index + 1] + $row;
|
||||
$formatted[] = $rowWithSerial;
|
||||
}
|
||||
$result = $formatted;
|
||||
|
||||
$style = $sheet->getStyle($cellCoordinate);
|
||||
$style->getFont()->setBold(true);
|
||||
$style->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
$style->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$colIndex++;
|
||||
}
|
||||
$headers = array_keys($result[0]);
|
||||
$columnWidth = 20; // standard column width
|
||||
|
||||
$rowNum = 2;
|
||||
foreach ($result as $row) {
|
||||
$colIndex = 1;
|
||||
foreach ($row as $cell) {
|
||||
foreach ($headers as $header) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||
$cellCoordinate = $columnLetter . $rowNum;
|
||||
$sheet->setCellValue($cellCoordinate, $cell);
|
||||
$cellCoordinate = $columnLetter . '1';
|
||||
|
||||
$sheet->setCellValue($cellCoordinate, ucfirst(str_replace('_', ' ', $header)));
|
||||
|
||||
$sheet->getColumnDimension($columnLetter)->setWidth($columnWidth);
|
||||
|
||||
$style = $sheet->getStyle($cellCoordinate);
|
||||
$style->getFont()->setBold(true);
|
||||
$style->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
$style->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
|
||||
|
||||
$colIndex++;
|
||||
}
|
||||
$rowNum++;
|
||||
|
||||
$rowNum = 2;
|
||||
foreach ($result as $row) {
|
||||
$colIndex = 1;
|
||||
foreach ($row as $cell) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||
$cellCoordinate = $columnLetter . $rowNum;
|
||||
$sheet->setCellValue($cellCoordinate, $cell);
|
||||
$colIndex++;
|
||||
}
|
||||
$rowNum++;
|
||||
}
|
||||
|
||||
$exportDir = WRITEPATH . 'exports';
|
||||
if (!is_dir($exportDir) && !mkdir($exportDir, 0777, true) && !is_dir($exportDir)) {
|
||||
log_message('error', 'download_inception: Failed to create export directory - ' . $exportDir);
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Failed to prepare export directory.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$filename = 'inception_export_' . date('Ymd_His') . '.xlsx';
|
||||
$filepath = $exportDir . '/' . $filename;
|
||||
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($filepath);
|
||||
|
||||
if (!file_exists($filepath)) {
|
||||
log_message('error', 'download_inception: Export file was not created - ' . $filepath);
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Failed to generate export file.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$downloadUrl = base_url('download_file?file=' . urlencode($filename));
|
||||
|
||||
log_message('error', 'download_inception: Export successful. Rows: ' . count($result) . ', File: ' . $filename);
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'Inception export generated successfully.',
|
||||
'data' => [
|
||||
'downloadUrl' => $downloadUrl,
|
||||
'filename' => $filename,
|
||||
'rowCount' => count($result),
|
||||
],
|
||||
], 200);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'download_inception: Exception - ' . $e->getMessage());
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Failed to generate inception export.',
|
||||
'data' => [],
|
||||
], 500);
|
||||
}
|
||||
|
||||
|
||||
$exportDir = WRITEPATH . 'exports';
|
||||
if (!is_dir($exportDir)) {
|
||||
mkdir($exportDir, 0777, true);
|
||||
} else {
|
||||
// chmod($exportDir, 0777);
|
||||
}
|
||||
|
||||
$filename = 'inception_export_' . date('Ymd_His') . '.xlsx';
|
||||
$filepath = $exportDir . '/' . $filename;
|
||||
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($filepath);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 'success',
|
||||
'downloadUrl' => base_url('employee/download_file?file=' . urlencode($filename))
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function download_file()
|
||||
{
|
||||
$filename = $this->request->getGet('file');
|
||||
$filepath = WRITEPATH . 'exports/' . $filename;
|
||||
|
||||
if (!file_exists($filepath)) {
|
||||
return $this->response->setStatusCode(404)->setBody('File not found.');
|
||||
log_message('error', 'download_file: Request received - file=' . ($filename ?? ''));
|
||||
|
||||
try {
|
||||
if (empty($filename)) {
|
||||
log_message('error', 'download_file: Missing file parameter');
|
||||
return $this->response->setStatusCode(400)->setBody('File name is required.');
|
||||
}
|
||||
|
||||
$filename = basename($filename);
|
||||
$filepath = WRITEPATH . 'exports/' . $filename;
|
||||
|
||||
if (!is_file($filepath)) {
|
||||
log_message('error', 'download_file: File not found - ' . $filepath);
|
||||
return $this->response->setStatusCode(404)->setBody('File not found.');
|
||||
}
|
||||
|
||||
log_message('error', 'download_file: Serving file - ' . $filename);
|
||||
|
||||
return $this->response->download($filepath, null)->setFileName($filename);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'download_file: Exception - ' . $e->getMessage());
|
||||
return $this->response->setStatusCode(500)->setBody('Failed to download file.');
|
||||
}
|
||||
|
||||
return $this->response->download($filepath, null)->setFileName($filename);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cron/manual job: after enrollment close date, email the respective HR with enrolled employee summary.
|
||||
*/
|
||||
public function sendEnrollmentClosedHrMail()
|
||||
{
|
||||
$this->myLogger->logme('error', 'sendEnrollmentClosedHrMail started at ' . date('d-m-Y H:i:s a'));
|
||||
|
||||
$closedFiles = $this->employeePolicyModel->getEnrollmentClosedFilesForHrMail();
|
||||
|
||||
if (empty($closedFiles)) {
|
||||
$this->myLogger->logme('error', 'sendEnrollmentClosedHrMail: no files with enrollment closed yesterday');
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'No enrollment closed files found for processing',
|
||||
'sent_count' => 0,
|
||||
'files_processed' => 0,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$sentCount = 0;
|
||||
$skippedCount = 0;
|
||||
|
||||
foreach ($closedFiles as $file) {
|
||||
|
||||
$sentForFile = $this->dispatchEnrollmentClosedHrMail($file);
|
||||
if ($sentForFile > 0) {
|
||||
$sentCount += $sentForFile;
|
||||
} else {
|
||||
$skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', 'sendEnrollmentClosedHrMail completed at ' . date('d-m-Y H:i:s a'));
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'message' => 'Enrollment closed HR mail process completed',
|
||||
'sent_count' => $sentCount,
|
||||
'skipped_count' => $skippedCount,
|
||||
'files_processed' => count($closedFiles),
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test endpoint: send enrollment-closed HR mail for a specific file (ignores yesterday filter).
|
||||
* GET /testSendEnrollmentClosedHrMail?file_id=1258&email=test@example.com
|
||||
*/
|
||||
public function testSendEnrollmentClosedHrMail($fileIdArg = null, $emailArg = null)
|
||||
{
|
||||
if (is_cli()) {
|
||||
$segments = service('uri')->getSegments();
|
||||
$fileId = (int) ($fileIdArg ?? $segments[2] ?? 0);
|
||||
$testEmail = trim($emailArg ?? $segments[3] ?? '');
|
||||
} else {
|
||||
$fileId = (int) ($this->request->getGet('file_id') ?? $fileIdArg ?? 0);
|
||||
$testEmail = trim($this->request->getGet('email') ?? $emailArg ?? '');
|
||||
}
|
||||
|
||||
if ($fileId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'file_id is required. Example: /testSendEnrollmentClosedHrMail?file_id=1258&email=you@example.com',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$file = $this->employeePolicyModel->getEnrollmentClosedFileForHrMailById($fileId);
|
||||
|
||||
if (empty($file)) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'File not found or not eligible for enrollment closed HR mail.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
if ($testEmail !== '') {
|
||||
$file['test_hr_email'] = $testEmail;
|
||||
}
|
||||
|
||||
$sentCount = $this->dispatchEnrollmentClosedHrMail($file);
|
||||
$recipients = $testEmail !== ''
|
||||
? [$testEmail]
|
||||
: array_column(
|
||||
$this->employeePolicyModel->getClientHrContactsForMail((int) $file['client_id']),
|
||||
'email'
|
||||
);
|
||||
|
||||
return $this->respond([
|
||||
'status' => $sentCount > 0,
|
||||
'message' => $sentCount > 0
|
||||
? 'Test enrollment closed HR mail sent successfully.'
|
||||
: 'Failed to send test mail. Check application logs for details.',
|
||||
'file_id' => $fileId,
|
||||
'sent_count' => $sentCount,
|
||||
'mail_to' => $recipients,
|
||||
], 200);
|
||||
}
|
||||
|
||||
private function getHrRecipientsForEnrollmentClosedMail(array $file): array
|
||||
{
|
||||
if (!empty($file['test_hr_email'])) {
|
||||
return [[
|
||||
'email' => trim($file['test_hr_email']),
|
||||
'name' => 'HR Team',
|
||||
]];
|
||||
}
|
||||
|
||||
return $this->employeePolicyModel->getClientHrContactsForMail((int) $file['client_id']);
|
||||
}
|
||||
|
||||
private function dispatchEnrollmentClosedHrMail(array $file): int
|
||||
{
|
||||
$fileClientId = (int) $file['client_id'];
|
||||
$filePolicyId = (int) $file['policy_id'];
|
||||
|
||||
$hrRecipients = $this->getHrRecipientsForEnrollmentClosedMail($file);
|
||||
|
||||
$clientData = $this->clientModel->where('id', $fileClientId)->first();
|
||||
|
||||
if (empty($clientData)) {
|
||||
$this->myLogger->logme('error', 'Client not found for file ' . $file['file_id']);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (empty($hrRecipients)) {
|
||||
$this->myLogger->logme('error', 'No HR contacts configured for client ' . $fileClientId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$excelAttachment = $this->buildEnrolledEmployeeExcelAttachment(
|
||||
$fileClientId,
|
||||
(int) $file['client_branch_id'],
|
||||
$filePolicyId
|
||||
);
|
||||
|
||||
if ($excelAttachment === null) {
|
||||
$this->myLogger->logme('error', 'No enrolled employees or failed Excel for policy ' . $filePolicyId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sentCount = 0;
|
||||
try {
|
||||
$clientPolicy = $this->clientPolicyModel->find($filePolicyId);
|
||||
$clientLogo = base_url() . 'public/uploads/logo/' . ($clientData['client_logo'] ?? '');
|
||||
|
||||
$enrollmentOpenDate = !empty($file['enrollment_open_date'])
|
||||
? change_date_format($file['enrollment_open_date'], 'Y-m-d', 'F d, Y')
|
||||
: 'N/A';
|
||||
$enrollmentCloseDate = !empty($file['enrollment_close_date'])
|
||||
? change_date_format($file['enrollment_close_date'], 'Y-m-d', 'F d, Y')
|
||||
: 'N/A';
|
||||
|
||||
$subject = 'Enrollment Closed - Enrolled Employee List | ' . ($clientData['client_name'] ?? '');
|
||||
|
||||
foreach ($hrRecipients as $hrRecipient) {
|
||||
$hrEmail = trim($hrRecipient['email'] ?? '');
|
||||
$hrName = trim($hrRecipient['name'] ?? '') ?: 'HR Team';
|
||||
|
||||
if ($hrEmail === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mailContent = $this->buildEnrollmentClosedHrMailContent([
|
||||
'hr_name' => $hrName,
|
||||
'client_name' => $clientData['client_name'] ?? '',
|
||||
'policy_no' => $clientPolicy['policy_no'] ?? 'N/A',
|
||||
'enrollment_open_date' => $enrollmentOpenDate,
|
||||
'enrollment_close_date' => $enrollmentCloseDate,
|
||||
'employee_count' => $excelAttachment['rowCount'] ?? 0,
|
||||
]);
|
||||
|
||||
$html = view('mail_template', [
|
||||
'mail_content' => $mailContent,
|
||||
'client_logo' => $clientLogo,
|
||||
'params' => [
|
||||
'notification' => [
|
||||
'mail_content_json' => json_encode([
|
||||
'body' => ['values' => ['contentWidth' => 600]],
|
||||
]),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$mailResult = MailHelper::send_email([
|
||||
'mail' => $hrEmail,
|
||||
'subject' => $subject,
|
||||
'message' => $html,
|
||||
'attachments' => [$excelAttachment],
|
||||
'common' => [
|
||||
'client_id' => $fileClientId,
|
||||
'client_branch_id' => $file['client_branch_id'],
|
||||
'client_policy_id' => $filePolicyId,
|
||||
'employee_policy_id' => null,
|
||||
'employee_id' => null,
|
||||
'mail_type' => 'enrollment_closed_hr_mail',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->myLogger->logme('info', $mailResult);
|
||||
|
||||
$decoded = json_decode($mailResult, true);
|
||||
if (is_array($decoded) && ($decoded['status'] ?? '') === 'success') {
|
||||
$sentCount++;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'dispatchEnrollmentClosedHrMail: ' . $e->getMessage());
|
||||
} finally {
|
||||
$this->deleteGeneratedExportFile($excelAttachment['filePath'] ?? null);
|
||||
}
|
||||
|
||||
return $sentCount;
|
||||
}
|
||||
|
||||
private function deleteGeneratedExportFile(?string $filePath): void
|
||||
{
|
||||
if (empty($filePath) || !is_file($filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!@unlink($filePath)) {
|
||||
log_message('error', 'dispatchEnrollmentClosedHrMail: failed to delete export file - ' . $filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildEnrollmentClosedHrMailContent(array $data): string
|
||||
{
|
||||
$hrName = htmlspecialchars($data['hr_name'] ?? 'HR Team', ENT_QUOTES, 'UTF-8');
|
||||
$clientName = htmlspecialchars($data['client_name'] ?? '', ENT_QUOTES, 'UTF-8');
|
||||
$policyNo = htmlspecialchars($data['policy_no'] ?? 'N/A', ENT_QUOTES, 'UTF-8');
|
||||
$openDate = htmlspecialchars($data['enrollment_open_date'] ?? 'N/A', ENT_QUOTES, 'UTF-8');
|
||||
$closeDate = htmlspecialchars($data['enrollment_close_date'] ?? 'N/A', ENT_QUOTES, 'UTF-8');
|
||||
$employeeCount = (int) ($data['employee_count'] ?? 0);
|
||||
|
||||
return '
|
||||
<div align="center">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="max-width:600px;">
|
||||
<tr>
|
||||
<td align="left" style="padding:15px; font-family:arial, sans-serif; font-size:14px; line-height:22px; color:#333333;">
|
||||
<p>Dear ' . $hrName . ',</p>
|
||||
<p>The enrollment window for <strong>' . $clientName . '</strong> has closed.</p>
|
||||
<p>
|
||||
<strong>Policy No:</strong> ' . $policyNo . '<br>
|
||||
<strong>Enrollment Open Date:</strong> ' . $openDate . '<br>
|
||||
<strong>Enrollment Close Date:</strong> ' . $closeDate . '
|
||||
</p>
|
||||
<p>
|
||||
Please find the attached Excel file with the enrolled employee list
|
||||
(' . $employeeCount . ' record' . ($employeeCount === 1 ? '' : 's') . ').
|
||||
</p>
|
||||
<p>Regards,<br>Nhance Team</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an Excel attachment of enrolled employees using download_inception query.
|
||||
*
|
||||
* @return array{filePath: string, fileName: string, rowCount: int}|null
|
||||
*/
|
||||
private function buildEnrolledEmployeeExcelAttachment(int $client_id, int $branch_id, int $policy_id): ?array
|
||||
{
|
||||
try {
|
||||
$result = $this->employeePolicyModel->download_inception(
|
||||
$client_id,
|
||||
$branch_id,
|
||||
$policy_id,
|
||||
[],
|
||||
'',
|
||||
''
|
||||
);
|
||||
|
||||
if (empty($result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$formatted = [];
|
||||
foreach ($result as $index => $row) {
|
||||
$formatted[] = ['S.NO' => $index + 1] + $row;
|
||||
}
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$headers = array_keys($formatted[0]);
|
||||
$columnWidth = 20;
|
||||
|
||||
$colIndex = 1;
|
||||
foreach ($headers as $header) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||
$cellCoordinate = $columnLetter . '1';
|
||||
$sheet->setCellValue($cellCoordinate, ucfirst(str_replace('_', ' ', $header)));
|
||||
$sheet->getColumnDimension($columnLetter)->setWidth($columnWidth);
|
||||
$style = $sheet->getStyle($cellCoordinate);
|
||||
$style->getFont()->setBold(true);
|
||||
$style->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
$style->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
|
||||
$colIndex++;
|
||||
}
|
||||
|
||||
$rowNum = 2;
|
||||
foreach ($formatted as $row) {
|
||||
$colIndex = 1;
|
||||
foreach ($row as $cell) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||
$sheet->setCellValue($columnLetter . $rowNum, $cell);
|
||||
$colIndex++;
|
||||
}
|
||||
$rowNum++;
|
||||
}
|
||||
|
||||
$exportDir = WRITEPATH . 'exports';
|
||||
if (!is_dir($exportDir) && !mkdir($exportDir, 0777, true) && !is_dir($exportDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filename = 'enrolled_employees_policy_' . $policy_id . '_' . date('Ymd_His') . '.xlsx';
|
||||
$filepath = $exportDir . '/' . $filename;
|
||||
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($filepath);
|
||||
|
||||
if (!is_file($filepath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'filePath' => $filepath,
|
||||
'fileName' => $filename,
|
||||
'rowCount' => count($formatted),
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'buildEnrolledEmployeeExcelAttachment: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -88,12 +88,12 @@ class EmployeePolicyModel extends Model
|
||||
|
||||
$status_query = "employee_polices.status"; // Default fallback
|
||||
|
||||
// if ($status_type == "hr") {
|
||||
// $status_query = "CASE
|
||||
// WHEN employee_polices.status = 'enrolled' THEN 'under process'
|
||||
// ELSE employee_polices.status
|
||||
// END as status";
|
||||
// }
|
||||
if ($status_type == "hr") {
|
||||
$status_query = "CASE
|
||||
WHEN employee_polices.status = 'enrolled' THEN 'submitted'
|
||||
ELSE employee_polices.status
|
||||
END as status";
|
||||
}
|
||||
|
||||
$selectColumns = [
|
||||
'employee_polices.*',
|
||||
@ -2035,5 +2035,94 @@ class EmployeePolicyModel extends Model
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inception files whose enrollment window closed yesterday (send HR summary the next day).
|
||||
*/
|
||||
public function getEnrollmentClosedFilesForHrMail()
|
||||
{
|
||||
$subQuery = '(SELECT MAX(id) as id
|
||||
FROM files
|
||||
WHERE status = "success"
|
||||
AND enrollment_close_date IS NOT NULL
|
||||
AND DATE(enrollment_close_date) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
|
||||
GROUP BY policy_id) closed_files';
|
||||
|
||||
$builder = $this->db->table('files')
|
||||
->select([
|
||||
'files.id as file_id',
|
||||
'files.client_id',
|
||||
'files.client_branch_id',
|
||||
'files.policy_id',
|
||||
'files.enrollment_open_date',
|
||||
'files.enrollment_close_date',
|
||||
'cp.policy_type_id',
|
||||
'cp.is_addon',
|
||||
])
|
||||
->join($subQuery, 'closed_files.id = files.id')
|
||||
->join('client_policy cp', 'cp.id = files.policy_id')
|
||||
->where('cp.is_active', 1)
|
||||
->where('cp.policy_status', 1);
|
||||
|
||||
return $builder->get()->getResultArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* All active HR contacts for a client (across branches).
|
||||
*/
|
||||
public function getClientHrContactsForMail(int $client_id): array
|
||||
{
|
||||
$rows = $this->db->table('level_contacts lc')
|
||||
->select('lc.id, lc.name, lc.email')
|
||||
->join('client_branch cb', 'lc.ref_id = cb.id')
|
||||
->where('lc.contact_type', 'client')
|
||||
->where('lc.is_active', 1)
|
||||
->where('cb.client_id', $client_id)
|
||||
->where('cb.is_active', 1)
|
||||
->where('lc.email IS NOT NULL', null, false)
|
||||
->where("lc.email != ''", null, false)
|
||||
->orderBy('lc.name', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$unique = [];
|
||||
foreach ($rows as $row) {
|
||||
$email = strtolower(trim($row['email'] ?? ''));
|
||||
if ($email === '' || isset($unique[$email])) {
|
||||
continue;
|
||||
}
|
||||
$unique[$email] = [
|
||||
'id' => $row['id'],
|
||||
'name' => trim($row['name'] ?? '') ?: 'HR Team',
|
||||
'email' => trim($row['email']),
|
||||
];
|
||||
}
|
||||
|
||||
return array_values($unique);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single inception file for HR enrollment-closed mail (test/manual use).
|
||||
*/
|
||||
public function getEnrollmentClosedFileForHrMailById(int $file_id)
|
||||
{
|
||||
return $this->db->table('files')
|
||||
->select([
|
||||
'files.id as file_id',
|
||||
'files.client_id',
|
||||
'files.client_branch_id',
|
||||
'files.policy_id',
|
||||
'files.enrollment_open_date',
|
||||
'files.enrollment_close_date',
|
||||
'cp.policy_type_id',
|
||||
'cp.is_addon',
|
||||
])
|
||||
->join('client_policy cp', 'cp.id = files.policy_id')
|
||||
->where('files.id', $file_id)
|
||||
->where('files.status', 'success')
|
||||
->where('cp.is_active', 1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -858,6 +858,7 @@ function downloadInception(){
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "<?php echo base_url('employee/download_inception') ?>",
|
||||
dataType: 'json',
|
||||
data: {
|
||||
client: client,
|
||||
branch: branch,
|
||||
@ -867,18 +868,19 @@ function downloadInception(){
|
||||
empName: empName
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
if (response.status === true && response.data && response.data.downloadUrl) {
|
||||
$('<a>', {
|
||||
href: response.downloadUrl,
|
||||
download: '',
|
||||
style: 'display:none'
|
||||
}).appendTo('body')[0].click();
|
||||
href: response.data.downloadUrl,
|
||||
download: '',
|
||||
style: 'display:none'
|
||||
}).appendTo('body')[0].click();
|
||||
} else {
|
||||
alert(response.message || 'Download failed');
|
||||
toastr.error(response.message || 'Download failed', 'Error');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error:', error);
|
||||
error: function(xhr) {
|
||||
const response = xhr.responseJSON;
|
||||
toastr.error(response?.message || 'An error occurred while generating the export.', 'Error');
|
||||
},
|
||||
complete: function() {
|
||||
$('.loader').fadeOut();
|
||||
|
||||
@ -92,7 +92,7 @@ p{
|
||||
<div style="
|
||||
width:160px;
|
||||
height:80px;
|
||||
background-image:url('<?= $client_logo ?>');
|
||||
background-image:url('<?= $client_logo ?? "" ?>');
|
||||
background-size:contain;
|
||||
background-repeat:no-repeat;
|
||||
background-position: center;
|
||||
@ -113,7 +113,7 @@ p{
|
||||
<!-- mail content -->
|
||||
|
||||
<?php
|
||||
echo $mail_content;
|
||||
echo $mail_content ?? "";
|
||||
?>
|
||||
|
||||
|
||||
|
||||
187
docs/download-inception-api.md
Normal file
187
docs/download-inception-api.md
Normal file
@ -0,0 +1,187 @@
|
||||
# Download Inception API
|
||||
|
||||
Base path: `/employeeRest`
|
||||
|
||||
Generates an Excel (`.xlsx`) export of **enrolled** employees for the selected client, branch, and policy. The API returns a download URL; the file is fetched in a second request.
|
||||
|
||||
**Authentication:** JWT + app signature (same filters as other `employeeRest` routes)
|
||||
|
||||
**HTTP status:** Responses use HTTP `200` (or `500` on unhandled exceptions). Check JSON `status` and `code` for success or failure.
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Route | Method | Description |
|
||||
|-------|--------|-------------|
|
||||
| `/employeeRest/download_inception` | `POST` | Generate Excel export |
|
||||
| `/employeeRest/download_file?file={filename}` | `GET` | Download generated file |
|
||||
|
||||
---
|
||||
|
||||
## 1. Generate inception export
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Method** | `POST` |
|
||||
| **URL** | `/employeeRest/download_inception` |
|
||||
| **Content-Type** | `application/json` **or** `application/x-www-form-urlencoded` |
|
||||
|
||||
### Request body
|
||||
|
||||
| Field | Required | Type | Description |
|
||||
|-------|----------|------|-------------|
|
||||
| `client` | No | integer / string | Client ID (or MD5 client hash) |
|
||||
| `branch` | No | integer | Client branch ID |
|
||||
| `policies` | No | integer | Client policy ID |
|
||||
| `status` | No | array / string | Accepted in payload; export currently uses **enrolled** status only |
|
||||
| `empCode` | No | string | Filter by employee code |
|
||||
| `empName` | No | string | Filter by employee name (partial match) |
|
||||
|
||||
### Sample request (JSON)
|
||||
|
||||
```http
|
||||
POST /employeeRest/download_inception
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {token}
|
||||
|
||||
{
|
||||
"client": 12,
|
||||
"branch": 1,
|
||||
"policies": 12,
|
||||
"status": ["enrolled"],
|
||||
"empCode": "",
|
||||
"empName": ""
|
||||
}
|
||||
```
|
||||
|
||||
### Sample request (form)
|
||||
|
||||
```http
|
||||
POST /employeeRest/download_inception
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Bearer {token}
|
||||
|
||||
client=12&branch=1&policies=12&empCode=&empName=
|
||||
```
|
||||
|
||||
### Success response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": true,
|
||||
"code": 200,
|
||||
"message": "Inception export generated successfully.",
|
||||
"data": {
|
||||
"downloadUrl": "https://example.com/employeeRest/download_file?file=inception_export_20260625_121435.xlsx",
|
||||
"filename": "inception_export_20260625_121435.xlsx",
|
||||
"rowCount": 9
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error responses
|
||||
|
||||
**Invalid JSON**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": false,
|
||||
"code": 400,
|
||||
"message": "Invalid JSON request body.",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
**No data**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": false,
|
||||
"code": 404,
|
||||
"message": "No data found for given filters.",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
**Export / server error**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": false,
|
||||
"code": 500,
|
||||
"message": "Failed to generate inception export.",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Download file
|
||||
|
||||
After a successful export, download the file using `data.downloadUrl`.
|
||||
|
||||
```http
|
||||
GET /employeeRest/download_file?file=inception_export_20260625_121435.xlsx
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
| Query param | Required | Description |
|
||||
|-------------|----------|-------------|
|
||||
| `file` | Yes | Filename from the generate response (`data.filename`) |
|
||||
|
||||
Returns the `.xlsx` file as a download. Only the basename is allowed (path traversal is blocked).
|
||||
|
||||
---
|
||||
|
||||
## Excel columns
|
||||
|
||||
Each row includes (with `S.NO` as the first column):
|
||||
|
||||
| Column | Source |
|
||||
|--------|--------|
|
||||
| S.NO | Row index |
|
||||
| Emp ID | Employee code |
|
||||
| Name of Emp/Dep | Employee / dependent name |
|
||||
| DOB | Date of birth |
|
||||
| Gender | Gender |
|
||||
| Relationship | Relationship |
|
||||
| Basic cover SI | Sum insured |
|
||||
| Date of Coverage | Coverage date |
|
||||
| DOJ | Date of joining |
|
||||
| Basic Pay | Basic pay |
|
||||
| Band/Grade | Band / grade |
|
||||
| Designation | Designation |
|
||||
| Phone | Mobile |
|
||||
| Email | Corporate email |
|
||||
| PRE EXISTING AILMENTS | Pre-existing flag |
|
||||
| change_event | Change event |
|
||||
| date_of_exit | Exit date |
|
||||
| reason_for_exit | Exit reason |
|
||||
| unit | Unit |
|
||||
|
||||
Only **active** employees with **enrolled** policy status are included.
|
||||
|
||||
---
|
||||
|
||||
## Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant API as employeeRest/download_inception
|
||||
participant Storage as writable/exports
|
||||
|
||||
Client->>API: POST filters (client, branch, policies, ...)
|
||||
API->>Storage: Save inception_export_*.xlsx
|
||||
API-->>Client: JSON with downloadUrl + filename
|
||||
Client->>API: GET employeeRest/download_file?file=...
|
||||
API-->>Client: Excel file download
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Files are stored under `writable/exports/` and named `inception_export_{Ymd_His}.xlsx`.
|
||||
- Use the returned `downloadUrl` promptly; files may be removed by other processes.
|
||||
Loading…
Reference in New Issue
Block a user