diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 569a22f..80256f5 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -511,7 +511,6 @@ $routes->group("employeeRest", ['filter' => [ 'GlobalPostFileUploadGuard', 'appS $routes->get("getExcelFileErrors/(:any)", "EmployeeController::getExcelFileErrors/$1"); $routes->post("sendReminderMail", "EmployeeRestController::sendReminderMail"); - $routes->get("sendReminderMail", "EmployeeRestController::sendReminderMail"); $routes->get("getReminderMailConfig", "EmployeeRestController::getReminderMailConfig"); $routes->post("saveReminderMailConfig", "EmployeeRestController::saveReminderMailConfig"); diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php index 1739b54..0a62204 100755 --- a/app/Controllers/DashboardController.php +++ b/app/Controllers/DashboardController.php @@ -350,7 +350,13 @@ class DashboardController extends AdminController ->where('template_name', 'member_reminder_mail') ->first(); - if ($notification_data && $notification_data['enabled'] == 1 && !empty($notification_data['mail_content'])) { + $reminderConfig = $this->reminderMailConfigModel->getByClientPolicyId((int) $client_policy['id']); + $notification_data = $this->reminderMailConfigModel->resolveNotificationTemplate( + $reminderConfig, + $notification_data + ); + + if ($this->reminderMailConfigModel->canSendReminderMail($reminderConfig, $notification_data)) { //manaual if (empty($send_mail_for_manual_or_crone)) { diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 3aba419..1616f34 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -3267,7 +3267,10 @@ class EmployeeController extends AdminController $sentCount = 0; try { $clientPolicy = $this->clientPolicyModel->find($filePolicyId); - $clientLogo = base_url() . 'public/uploads/logo/' . ($clientData['client_logo'] ?? ''); + $clientLogo = ''; + if (!empty($clientData['client_logo'])) { + $clientLogo = base_url() . 'public/uploads/logo/' . $clientData['client_logo']; + } $acm_Mails = $clientData['common_mails'] ?? ''; if(empty($acm_Mails)){ @@ -3282,6 +3285,11 @@ class EmployeeController extends AdminController : 'N/A'; $subject = 'Enrollment Closed - Enrolled Employee List | ' . ($clientData['client_name'] ?? ''); + $statusTilesHtml = $this->renderEnrollmentStatusTilesForMail( + $fileClientId, + (int) $file['client_branch_id'], + $filePolicyId + ); foreach ($hrRecipients as $hrRecipient) { $hrEmail = trim($hrRecipient['email'] ?? ''); @@ -3291,7 +3299,7 @@ class EmployeeController extends AdminController continue; } - $mailContent = $this->buildEnrollmentClosedHrMailContent([ + $mailContent = $statusTilesHtml . $this->buildEnrollmentClosedHrMailContent([ 'hr_name' => $hrName, 'client_name' => $clientData['client_name'] ?? '', 'policy_no' => $clientPolicy['policy_no'] ?? 'N/A', @@ -3355,6 +3363,17 @@ class EmployeeController extends AdminController } } + private function renderEnrollmentStatusTilesForMail(int $client_id, int $branch_id, int $policy_id): string + { + $counts = $this->employeePolicyModel->getEnrollmentStatusTileCounts( + $client_id, + $branch_id, + $policy_id + ); + + return view('mail/enrollment_status_tiles', ['counts' => $counts]); + } + private function buildEnrollmentClosedHrMailContent(array $data): string { $hrName = htmlspecialchars($data['hr_name'] ?? 'HR Team', ENT_QUOTES, 'UTF-8'); @@ -3368,19 +3387,19 @@ class EmployeeController extends AdminController
-
-

Dear ' . $hrName . ',

-

The enrollment window for ' . $clientName . ' has closed.

-

+

+
Dear ' . $hrName . ',
+
The enrollment window for ' . $clientName . ' has closed.
+
Policy No: ' . $policyNo . '
Enrollment Open Date: ' . $openDate . '
Enrollment Close Date: ' . $closeDate . ' -

-

+

+
Please find the attached Excel file with the enrolled employee list (' . $employeeCount . ' record' . ($employeeCount === 1 ? '' : 's') . '). -

-

Regards,
Nhance Team

+
+
Regards,
Nhance Team
diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index a4a0f85..1cc99b3 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -3187,25 +3187,26 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'failed','code' => 200,'data' => [] ], 200); } - $openEnrollmentPolicies = $this->employeePolicyModel - ->select('employee_polices.client_policy_id') - ->join('employees', 'employee_polices.employee_id = employees.id') - ->whereIn('employee_polices.client_policy_id', $allowedPolicyIds) - ->where('md5(employees.client_id)', $client_id) - ->where('employees.client_branch_id', $client_branch_id) - ->where('employee_polices.enrollment_open_date <= CURDATE()', null, false) - ->where('employee_polices.enrollment_close_date >= CURDATE()', null, false) - ->where('employee_polices.enrollment_open_date IS NOT NULL', null, false) - ->where('employee_polices.enrollment_close_date IS NOT NULL', null, false) - ->where('employees.is_active', 1) - ->where('employee_polices.is_active', 1) - ->whereIn('employee_polices.status', ['draft', 'enrolled']) - ->whereIn('employees.emp_status', ['draft', 'enrolled']) - ->groupBy('employee_polices.client_policy_id') - ->findAll(); - - $policyId = array_column($openEnrollmentPolicies, 'client_policy_id'); + // $openEnrollmentPolicies = $this->employeePolicyModel + // ->select('employee_polices.client_policy_id') + // ->join('employees', 'employee_polices.employee_id = employees.id') + // ->whereIn('employee_polices.client_policy_id', $allowedPolicyIds) + // ->where('md5(employees.client_id)', $client_id) + // ->where('employees.client_branch_id', $client_branch_id) + // ->where('employee_polices.enrollment_open_date <= CURDATE()', null, false) + // ->where('employee_polices.enrollment_close_date >= CURDATE()', null, false) + // ->where('employee_polices.enrollment_open_date IS NOT NULL', null, false) + // ->where('employee_polices.enrollment_close_date IS NOT NULL', null, false) + // ->where('employees.is_active', 1) + // ->where('employee_polices.is_active', 1) + // ->whereIn('employee_polices.status', ['draft', 'enrolled']) + // ->whereIn('employees.emp_status', ['draft', 'enrolled']) + // ->groupBy('employee_polices.client_policy_id') + // ->findAll(); + // $policyId = array_column($openEnrollmentPolicies, 'client_policy_id'); + + $policyId = $allowedPolicyIds; if(empty($policyId)){ return $this->respond(['status' => 'failed','code' => 200,'data' => [] ], 200); } @@ -4683,8 +4684,9 @@ class EmployeeRestController extends AdminController public function getReminderMailConfig() { try { - $clientPolicyId = $this->request->getGet('client_policy_id') - ?? $this->request->getPost('client_policy_id'); + + $clientPolicyId = $this->request->getGet('client_policy_id'); + $is_email_template = $this->request->getGet('is_email_template') ?? false; if (empty($clientPolicyId)) { return $this->respond([ @@ -4706,6 +4708,27 @@ class EmployeeRestController extends AdminController $config = $this->reminderMailConfigModel->getByClientPolicyId((int) $clientPolicyId); + if ($is_email_template) { + if (empty($config)) { + + $notification = $this->notificationModel->where('client_id', $clientPolicy['client_id'])->where('template_name', 'member_reminder_mail')->first(); + if (empty($notification)) { + return $this->respond([ + 'status' => false, + 'code' => 404, + 'message' => 'Member reminder mail notification not found', + ], 200); + } + + return $this->respond(['status' => true, 'code' => 200, 'data' => ['email_subject' => $notification['subject'], 'email_body' => $notification['mail_content']]], 200); + } + return $this->respond([ + 'status' => true, + 'code' => 200, + 'data' => ['email_subject' => $config['email_subject'], 'email_body' => $config['email_body']], + ], 200); + } + if (empty($config) && !empty($clientPolicy['reminder_date'])) { $config = [ 'client_policy_id' => (int) $clientPolicyId, @@ -4749,6 +4772,8 @@ class EmployeeRestController extends AdminController ?? null; $isEnabled = $requestData['is_enabled'] ?? null; $hrId = $requestData['hr_id'] ?? null; + $emailSubject = $requestData['email_subject'] ?? null; + $emailBody = $requestData['email_body'] ?? null; if (empty($clientPolicyId)) { return $this->respond([ @@ -4776,15 +4801,25 @@ class EmployeeRestController extends AdminController ], 200); } + $saveOptions = [ + 'id' => $configId !== null && $configId !== '' ? (int) $configId : null, + 'hr_id' => $hrId !== null && $hrId !== '' ? (int) $hrId : null, + ]; + + if (array_key_exists('email_subject', $requestData)) { + $saveOptions['email_subject'] = $emailSubject; + } + + if (array_key_exists('email_body', $requestData)) { + $saveOptions['email_body'] = $emailBody; + } + $result = $this->reminderMailConfigModel->saveConfig( (int) $clientPolicyId, $frequency, $reminderDays !== null ? (string) $reminderDays : null, $isEnabled === null ? 1 : (int) $isEnabled, - [ - 'id' => $configId !== null && $configId !== '' ? (int) $configId : null, - 'hr_id' => $hrId !== null && $hrId !== '' ? (int) $hrId : null, - ] + $saveOptions ); if (!$result['status']) { @@ -4825,6 +4860,8 @@ class EmployeeRestController extends AdminController 'frequency' => $this->request->getVar('frequency'), 'reminder_days' => $this->request->getVar('reminder_days'), 'working_days' => $this->request->getVar('working_days'), + 'email_subject' => $this->request->getVar('email_subject'), + 'email_body' => $this->request->getVar('email_body'), 'is_enabled' => $this->request->getVar('is_enabled'), 'hr_id' => $this->request->getVar('hr_id'), ], static fn ($value) => $value !== null && $value !== ''); @@ -4833,8 +4870,34 @@ class EmployeeRestController extends AdminController public function sendReminderMail() { try { - $clientPolicyId = $this->request->getGet('client_policy_id') - ?? $this->request->getPost('client_policy_id'); + + if (!$this->request->is('post')) { + return $this->respond([ + 'status' => false, + 'code' => 405, + 'message' => 'Only POST method is allowed', + ], 200); + } + + try { + $requestData = $this->request->getJSON(true); + } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) { + return $this->respond([ + 'status' => false, + 'code' => 400, + 'message' => 'Invalid JSON request body', + ], 200); + } + + if (!is_array($requestData) || empty($requestData)) { + return $this->respond([ + 'status' => false, + 'code' => 400, + 'message' => 'JSON request body is required', + ], 200); + } + + $clientPolicyId = $requestData['client_policy_id'] ?? null; if (empty($clientPolicyId)) { return $this->respond([ @@ -4854,9 +4917,20 @@ class EmployeeRestController extends AdminController ], 200); } - $dashboardController = new DashboardController(); + $emailSubject = $requestData['email_subject'] ?? null; + $emailBody = $requestData['email_body'] ?? null; + $hrId = $requestData['hr_id'] ?? null; - return $dashboardController->sendManualReminder( + if (array_key_exists('email_subject', $requestData) || array_key_exists('email_body', $requestData)) { + $this->reminderMailConfigModel->saveEmailTemplate( + (int) $clientPolicyId, + $emailSubject, + $emailBody, + $hrId !== null && $hrId !== '' ? (int) $hrId : null + ); + } + + return $this->dispatchManualReminder( $clientPolicy['client_id'], $clientPolicy['client_branch_id'], $clientPolicyId @@ -4870,4 +4944,15 @@ class EmployeeRestController extends AdminController } } + protected function dispatchManualReminder($clientId, $clientBranchId, $clientPolicyId) + { + $dashboardController = new DashboardController(); + + return $dashboardController->sendManualReminder( + $clientId, + $clientBranchId, + $clientPolicyId + ); + } + } \ No newline at end of file diff --git a/app/Database/reminder_mail_config.sql b/app/Database/reminder_mail_config.sql index d2267de..e38b5b2 100644 --- a/app/Database/reminder_mail_config.sql +++ b/app/Database/reminder_mail_config.sql @@ -5,6 +5,8 @@ CREATE TABLE IF NOT EXISTS `reminder_mail_config` ( `client_policy_id` INT UNSIGNED NOT NULL, `frequency` ENUM('daily', 'weekly', 'monthly', 'custom', 'working_days') NOT NULL DEFAULT 'custom', `reminder_days` VARCHAR(255) DEFAULT NULL COMMENT 'weekly: 0-6 (Sun-Sat); working_days: 1-7 (Mon-Sun) or Mon,Tue,...; monthly/custom: 1-31; daily: NULL', + `email_subject` VARCHAR(500) DEFAULT NULL COMMENT 'Custom reminder mail subject; falls back to notification template when NULL', + `email_body` TEXT DEFAULT NULL COMMENT 'Custom reminder mail body; falls back to notification template when NULL', `is_enabled` TINYINT(1) NOT NULL DEFAULT 1, `is_active` TINYINT(1) NOT NULL DEFAULT 1, `created_by` INT UNSIGNED DEFAULT NULL, @@ -16,3 +18,10 @@ CREATE TABLE IF NOT EXISTS `reminder_mail_config` ( KEY `idx_reminder_mail_config_frequency` (`frequency`), KEY `idx_reminder_mail_config_is_enabled` (`is_enabled`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Upgrade existing installations (ignore duplicate-column errors if already applied) +ALTER TABLE `reminder_mail_config` + ADD COLUMN `email_subject` VARCHAR(500) DEFAULT NULL COMMENT 'Custom reminder mail subject; falls back to notification template when NULL' AFTER `reminder_days`; + +ALTER TABLE `reminder_mail_config` + ADD COLUMN `email_body` TEXT DEFAULT NULL COMMENT 'Custom reminder mail body; falls back to notification template when NULL' AFTER `email_subject`; diff --git a/app/Models/EmployeePolicyModel.php b/app/Models/EmployeePolicyModel.php index d75197c..834f295 100755 --- a/app/Models/EmployeePolicyModel.php +++ b/app/Models/EmployeePolicyModel.php @@ -1987,7 +1987,9 @@ class EmployeePolicyModel extends Model // Conditionally add where clauses - if ($client_id != 0 && !empty(trim($client_id))) { + if (is_string($client_id) && preg_match('/^[a-f0-9]{32}$/i', $client_id)) { + $result->where('MD5(emp.client_id)', $client_id); + } elseif ($client_id != 0 && !empty($client_id)) { $result->where('emp.client_id', $client_id); } if ($branch_id != 0 && !empty(trim($branch_id))) { @@ -2124,5 +2126,65 @@ class EmployeePolicyModel extends Model ->getRowArray(); } + /** + * Enrollment status tile counts (same logic as enrollment_list.php). + */ + public function getEnrollmentStatusTileCounts(int $client_id, int $branch_id, int $client_policy_id): array + { + $authSubQuery = '(SELECT user_id FROM auth_history WHERE user_type = "employee" GROUP BY user_id) auth_history'; + + $rows = $this->db->table('employees') + ->select('employees.id, employees.relationship, employees.emp_status, auth_history.user_id as logged_in_user_id') + ->join('employee_polices', 'employees.id = employee_polices.employee_id') + ->join($authSubQuery, 'employees.id = auth_history.user_id', 'left', false) + ->where('employees.client_id', $client_id) + ->where('employees.client_branch_id', $branch_id) + ->where('employees.is_active', 1) + ->where('employees.emp_status !=', 'truncated') + ->where('employee_polices.client_policy_id', $client_policy_id) + ->where('employee_polices.is_active', 1) + ->where('employee_polices.status !=', 'truncated') + ->groupBy('employees.id, employees.relationship, employees.emp_status, auth_history.user_id') + ->get() + ->getResultArray(); + + $counts = [ + 'emp_count' => 0, + 'enrolled' => 0, + 'not_enrolled' => 0, + 'logged_in' => 0, + 'not_logged_in' => 0, + 'draft' => 0, + ]; + + foreach ($rows as $row) { + if (strtolower(trim($row['relationship'] ?? '')) !== 'self') { + continue; + } + + $counts['emp_count']++; + + $isEnrolled = ($row['emp_status'] ?? '') === 'enrolled'; + if ($isEnrolled) { + $counts['enrolled']++; + } else { + $counts['not_enrolled']++; + } + + $isLoggedIn = !empty($row['logged_in_user_id']); + if ($isLoggedIn) { + $counts['logged_in']++; + } else { + $counts['not_logged_in']++; + } + + if (!$isEnrolled && $isLoggedIn) { + $counts['draft']++; + } + } + + return $counts; + } + } \ No newline at end of file diff --git a/app/Models/ReminderMailConfigModel.php b/app/Models/ReminderMailConfigModel.php index 50132cc..c4d7933 100644 --- a/app/Models/ReminderMailConfigModel.php +++ b/app/Models/ReminderMailConfigModel.php @@ -50,6 +50,8 @@ class ReminderMailConfigModel extends Model 'client_policy_id', 'frequency', 'reminder_days', + 'email_subject', + 'email_body', 'is_enabled', 'is_active', 'created_by', @@ -209,9 +211,90 @@ class ReminderMailConfigModel extends Model $config['working_day_labels'] = $this->formatWorkingDayLabels($config['reminder_days'] ?? null); } + $config['has_custom_template'] = $this->hasCustomTemplate($config); + return $config; } + public function hasCustomTemplate(?array $config): bool + { + if (empty($config)) { + return false; + } + + return trim((string) ($config['email_subject'] ?? '')) !== '' + && trim((string) ($config['email_body'] ?? '')) !== ''; + } + + /** + * Apply reminder_mail_config template to notification data when configured. + */ + public function resolveNotificationTemplate(?array $config, ?array $notificationData): ?array + { + if (!$this->hasCustomTemplate($config)) { + return $notificationData; + } + + $notificationData = is_array($notificationData) ? $notificationData : []; + $notificationData['subject'] = $config['email_subject']; + $notificationData['mail_content'] = $config['email_body']; + $notificationData['enabled'] = 1; + + return $notificationData; + } + + public function canSendReminderMail(?array $config, ?array $notificationData): bool + { + if ($this->hasCustomTemplate($config)) { + return true; + } + + return !empty($notificationData) + && (int) ($notificationData['enabled'] ?? 0) === 1 + && trim((string) ($notificationData['mail_content'] ?? '')) !== ''; + } + + public function saveEmailTemplate( + int $clientPolicyId, + ?string $emailSubject, + ?string $emailBody, + ?int $hrId = null + ): void { + if ($emailSubject === null && $emailBody === null) { + return; + } + + $existing = $this->getByClientPolicyId($clientPolicyId); + + if ($existing) { + $payload = ['updated_by' => $hrId]; + + if ($emailSubject !== null) { + $payload['email_subject'] = $emailSubject; + } + + if ($emailBody !== null) { + $payload['email_body'] = $emailBody; + } + + $this->update((int) $existing['id'], $payload); + + return; + } + + $this->insert([ + 'client_policy_id' => $clientPolicyId, + 'frequency' => self::FREQUENCY_CUSTOM, + 'reminder_days' => null, + 'email_subject' => $emailSubject, + 'email_body' => $emailBody, + 'is_enabled' => 1, + 'is_active' => 1, + 'created_by' => $hrId, + 'updated_by' => $hrId, + ]); + } + public function normalizeReminderDays(string $frequency, ?string $reminderDays): ?string { if ($frequency === self::FREQUENCY_DAILY) { @@ -313,6 +396,14 @@ class ReminderMailConfigModel extends Model 'is_active' => 1, ]; + if (array_key_exists('email_subject', $options)) { + $payload['email_subject'] = $options['email_subject']; + } + + if (array_key_exists('email_body', $options)) { + $payload['email_body'] = $options['email_body']; + } + if ($id) { $existing = $this->where('id', $id)->where('is_active', 1)->first(); diff --git a/app/Views/mail/enrollment_status_tiles.php b/app/Views/mail/enrollment_status_tiles.php new file mode 100644 index 0000000..afe5ed4 --- /dev/null +++ b/app/Views/mail/enrollment_status_tiles.php @@ -0,0 +1,57 @@ + 'Emp Count', 'value' => (int) ($counts['emp_count'] ?? 0), 'accent' => '#00999E'], + ['label' => 'Enroled', 'value' => (int) ($counts['enrolled'] ?? 0), 'accent' => '#0ab39c'], + ['label' => 'Not Enroled', 'value' => (int) ($counts['not_enrolled'] ?? 0), 'accent' => '#f06548'], + ['label' => 'Logged-In', 'value' => (int) ($counts['logged_in'] ?? 0), 'accent' => '#299cdb'], + ['label' => 'Not Logged-In', 'value' => (int) ($counts['not_logged_in'] ?? 0), 'accent' => '#878a99'], + ['label' => 'Draft', 'value' => (int) ($counts['draft'] ?? 0), 'accent' => '#f7b84b'], +]; +$tileRows = array_chunk($tiles, 3); +?> + +
+ + + + + + + +
+ Enrollment Summary +
+ + + + + + + + + + + +
+ + + + + + + +
 
+
+ +
+
+ +
+
+
 
+
+
diff --git a/app/Views/mail_template.php b/app/Views/mail_template.php index 5650efa..0ceac34 100644 --- a/app/Views/mail_template.php +++ b/app/Views/mail_template.php @@ -89,16 +89,11 @@ p{