MERGE_UAT_MAIL-CR

This commit is contained in:
Ubuntu 2026-06-24 12:41:44 +05:30
commit c8b32c8b2f
8 changed files with 1200 additions and 40 deletions

View File

@ -507,6 +507,12 @@ $routes->group("employeeRest", ['filter' => [ 'GlobalPostFileUploadGuard', 'appS
$routes->get("copyActiveEmployeeAndDependentDetails", "EmployeeRestController::copyActiveEmployeeAndDependentDetails");
$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");
});
$routes->group("employeeRest", ['filter' => ['appSignature','AuthApiRateLimitFilter'] ], function ($routes) {

View File

@ -19,6 +19,7 @@ use App\Models\JobModel;
use App\Models\ClientPolicyModel;
use App\Models\NotificationModel;
use App\Models\EmployeePolicyModel;
use App\Models\ReminderMailConfigModel;
use App\Controllers\PendingActionsContrller;
use App\Controllers\EmpDataServiceController;
@ -32,6 +33,7 @@ class DashboardController extends AdminController
protected $clientPolicyModel;
protected $notificationModel;
protected $employeePolicyModel;
protected $reminderMailConfigModel;
protected $policyTransactionModel;
protected $policyStatus;
protected $colorShades;
@ -45,8 +47,9 @@ class DashboardController extends AdminController
$this->userMessageModel = new UserMessageModel();
$this->clientPolicyModel = new ClientPolicyModel();
$this->notificationModel = new NotificationModel();
$this->employeePolicyModel = new EmployeePolicyModel();
$this->clientModel = new ClientModel();
$this->employeePolicyModel = new EmployeePolicyModel();
$this->reminderMailConfigModel = new ReminderMailConfigModel();
$this->clientModel = new ClientModel();
$this->policyTransactionModel = new PolicyTransactionModel();
$this->myLogger = \Config\Services::mylogger();
@ -438,14 +441,12 @@ class DashboardController extends AdminController
} else { // crone
if (!empty($client_policy['reminder_date'])) {
$shouldSendToday = $this->reminderMailConfigModel->shouldSendTodayForPolicy(
(int) $client_policy['id'],
$client_policy['reminder_date'] ?? null
);
$currentDate = date('d');
$remainder_dates = explode(",", $client_policy['reminder_date']);
foreach ($remainder_dates as $date) {
if ($currentDate == $date) {
if ($shouldSendToday) {
$this->myLogger->logme('error', 'Notification should be sent for client policy ID: ' . $client_policy['id']);
@ -526,11 +527,8 @@ class DashboardController extends AdminController
$reminder_whole_mail[] = $mail_result;
}
}
}
} else {
$this->myLogger->logme('error', "send Remainder crone --- Remainder date is empty()");
$this->myLogger->logme('error', 'send Remainder crone --- reminder not scheduled for today');
}
}

View File

@ -33,6 +33,7 @@ use App\Models\AuditHistoryModel;
use App\Models\SIMappingModel;
use App\Models\InsurerModel;
use App\Models\HrFileUploadModel;
use App\Models\ReminderMailConfigModel;
use App\Controllers\Jobs ;
use App\Controllers\JobWorker ;
@ -80,6 +81,7 @@ class EmployeeRestController extends AdminController
protected $employeeHelper;
protected $insurerModel;
protected $hrFileUploadModel;
protected ReminderMailConfigModel $reminderMailConfigModel;
public function __construct()
@ -108,6 +110,7 @@ class EmployeeRestController extends AdminController
$this->employeeHelper = new EmployeeHelper();
$this->insurerModel = new InsurerModel();
$this->hrFileUploadModel = new HrFileUploadModel();
$this->reminderMailConfigModel = new ReminderMailConfigModel();
}
@ -3162,8 +3165,9 @@ class EmployeeRestController extends AdminController
public function getPolicyLevelEmployeeSummaryData()
{
$hr_id = $this->request->getGet('hr_id');
$client_id = $this->request->getGet('client_id');
$client_branch_id = $this->request->getGet('client_branch_id');
$restAuthController = new RestAuthenticationController;
//call and get allowed policy data from post enrollment
$queryParams = [
@ -3174,35 +3178,78 @@ class EmployeeRestController extends AdminController
$HRAccessData = json_decode($HRAccessRes,true);
if(isset($HRAccessData['data']['allowed_pre_policies']))
{
$policyId = json_decode($HRAccessData['data']['allowed_pre_policies'],true);
$allowedPolicyIds = json_decode($HRAccessData['data']['allowed_pre_policies'],true);
}else{
$policyId = [];
$allowedPolicyIds = [];
}
if(count($policyId) == 0){
return $this->respond(['status' => 'failed','code' => (count($policyId) ? 200 : 200),'data' => [] ], 200);
if(empty($allowedPolicyIds)){
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');
if(empty($policyId)){
return $this->respond(['status' => 'failed','code' => 200,'data' => [] ], 200);
}
$ClientPolicyData = $this->clientPolicyModel->select('client_policy.id as client_policy_id , client_policy.client_id as client_id, client_policy.policy_type_id as policy_type_id, client_policy.is_addon as is_addon , client_policy.open_for_enrollment as OpenForEnrollment , client_policy.inception_type as inception_type, client_policy.policy_no as policy_no, client_policy.insurer_id as insurer_id, DATE_FORMAT(client_policy.policy_end_date, "%d-%m-%Y") AS policy_expiry_date ')
->where('md5(client_policy.client_id)', $this->request->getGet('client_id') )
->where('client_policy.client_branch_id', $this->request->getGet('client_branch_id') )
->where('md5(client_policy.client_id)', $client_id )
->where('client_policy.client_branch_id', $client_branch_id )
->where('client_policy.is_active', 1 )
->where('client_policy.policy_status', 1)
->whereIn('client_policy.id', $policyId)
->findAll();
$loggedInCounts = [];
$clientPolicyIds = array_column($ClientPolicyData, 'client_policy_id');
if (!empty($clientPolicyIds)) {
$loggedInRows = $this->employeeModel
->select('employee_polices.client_policy_id, COUNT(DISTINCT employees.id) as logged_in_count')
->join('employee_polices', 'employees.id = employee_polices.employee_id')
->join('auth_history', 'employees.id = auth_history.user_id AND auth_history.user_type = "employee"', 'inner')
->whereIn('employee_polices.client_policy_id', $clientPolicyIds)
->where('md5(employees.client_id)', $client_id)
->where('employees.client_branch_id', $client_branch_id)
->where('employees.relationship', 'Self')
->where('employees.is_active', 1)
->where('employee_polices.is_active', 1)
->groupBy('employee_polices.client_policy_id')
->findAll();
foreach ($loggedInRows as $loggedInRow) {
$loggedInCounts[$loggedInRow['client_policy_id']] = (int) $loggedInRow['logged_in_count'];
}
}
$result = [];
// dd( $ClientPolicyData);
foreach ($ClientPolicyData as $key => $value)
{
$policyTypeData = $this->policyTypeModel->where('id',$value['policy_type_id'])->get()->getRow();
$insurerData = $this->insurerModel->where('id',$value['insurer_id'])->get()->getRow();
$value['type'] = $policyTypeData->policy_type;
$value['policy_name'] = $policyTypeData->long_name;
$value['type'] = $policyTypeData->policy_type ?? null;
$value['policy_name'] = $policyTypeData->long_name ?? null;
$value['insurer_name'] = $insurerData->name ?? null;
$value['insurer_short_name'] = $insurerData->short_name ?? null;
$employeeDetails = $this->employeePolicyModel->getEmployeePolicy( client_id:$value['client_id'],policy_id: $value['client_policy_id'],status:0,branch_id:$this->request->getGet('client_branch_id'));
$employeeDetails = $this->employeePolicyModel->getEmployeePolicy( client_id:$value['client_id'],policy_id: $value['client_policy_id'],status:0,branch_id:$client_branch_id);
$enrolledCount = 0;
$draftCount = 0;
if(count($employeeDetails))
@ -3219,18 +3266,17 @@ class EmployeeRestController extends AdminController
$value['totalMembersCount'] = count($employeeDetails);
$value['membersCountOfEnrolled'] = $enrolledCount;
$value['membersCountOfDraft'] = $draftCount;
$value['membersCountOfLoggedIn'] = $loggedInCounts[$value['client_policy_id']] ?? 0;
array_push($result,$value);
}
if($result)
{
return $this->respond(['status' => 'success','code' => (count($result) ? 200 : 200),'data' => $result ], 200);
return $this->respond(['status' => 'success','code' => 200,'data' => $result ], 200);
}else{
return $this->respond(['status' => 'failed','code' => (count($result) ? 200 : 200),'data' => [] ], 200);
return $this->respond(['status' => 'failed','code' => 200,'data' => [] ], 200);
}
}
@ -4634,4 +4680,194 @@ class EmployeeRestController extends AdminController
return $policyIds;
}
public function getReminderMailConfig()
{
try {
$clientPolicyId = $this->request->getGet('client_policy_id')
?? $this->request->getPost('client_policy_id');
if (empty($clientPolicyId)) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'client_policy_id is required',
], 200);
}
$clientPolicy = $this->clientPolicyModel->find($clientPolicyId);
if (empty($clientPolicy)) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'Client policy not found',
], 200);
}
$config = $this->reminderMailConfigModel->getByClientPolicyId((int) $clientPolicyId);
if (empty($config) && !empty($clientPolicy['reminder_date'])) {
$config = [
'client_policy_id' => (int) $clientPolicyId,
'frequency' => ReminderMailConfigModel::FREQUENCY_CUSTOM,
'reminder_days' => $clientPolicy['reminder_date'],
'is_enabled' => 1,
'is_active' => 1,
'source' => 'legacy_client_policy',
];
}
if (!empty($config)) {
$config = $this->reminderMailConfigModel->enrichConfig($config);
}
return $this->respond([
'status' => true,
'code' => 200,
'data' => $config,
'working_day_options' => $this->reminderMailConfigModel->getWorkingDayOptions(),
], 200);
} catch (\Exception $e) {
return $this->respond([
'status' => false,
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
public function saveReminderMailConfig()
{
try {
$requestData = $this->getReminderMailConfigRequestData();
$clientPolicyId = $requestData['client_policy_id'] ?? null;
$configId = $requestData['id'] ?? null;
$frequency = strtolower(trim((string) ($requestData['frequency'] ?? '')));
$reminderDays = $requestData['reminder_days']
?? $requestData['working_days']
?? null;
$isEnabled = $requestData['is_enabled'] ?? null;
$hrId = $requestData['hr_id'] ?? null;
if (empty($clientPolicyId)) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'client_policy_id is required',
], 200);
}
if (empty($frequency)) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'frequency is required',
], 200);
}
$clientPolicy = $this->clientPolicyModel->find($clientPolicyId);
if (empty($clientPolicy)) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'Client policy not found',
], 200);
}
$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,
]
);
if (!$result['status']) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => $result['message'],
], 200);
}
return $this->respond([
'status' => true,
'code' => 200,
'action' => $result['action'] ?? 'saved',
'message' => 'Reminder mail configuration saved successfully',
'data' => $result['data'],
], 200);
} catch (\Exception $e) {
return $this->respond([
'status' => false,
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
private function getReminderMailConfigRequestData(): array
{
$json = $this->request->getJSON(true);
if (is_array($json) && !empty($json)) {
return $json;
}
return array_filter([
'id' => $this->request->getVar('id'),
'client_policy_id' => $this->request->getVar('client_policy_id'),
'frequency' => $this->request->getVar('frequency'),
'reminder_days' => $this->request->getVar('reminder_days'),
'working_days' => $this->request->getVar('working_days'),
'is_enabled' => $this->request->getVar('is_enabled'),
'hr_id' => $this->request->getVar('hr_id'),
], static fn ($value) => $value !== null && $value !== '');
}
public function sendReminderMail()
{
try {
$clientPolicyId = $this->request->getGet('client_policy_id')
?? $this->request->getPost('client_policy_id');
if (empty($clientPolicyId)) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'client_policy_id is required',
], 200);
}
$clientPolicy = $this->clientPolicyModel->find($clientPolicyId);
if (empty($clientPolicy)) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'Client policy not found',
], 200);
}
$dashboardController = new DashboardController();
return $dashboardController->sendManualReminder(
$clientPolicy['client_id'],
$clientPolicy['client_branch_id'],
$clientPolicyId
);
} catch (\Exception $e) {
return $this->respond([
'status' => false,
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
}

View File

@ -0,0 +1,18 @@
-- Reminder mail configuration table (fresh install + upgrade + legacy data migration)
CREATE TABLE IF NOT EXISTS `reminder_mail_config` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`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',
`is_enabled` TINYINT(1) NOT NULL DEFAULT 1,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_by` INT UNSIGNED DEFAULT NULL,
`updated_by` INT UNSIGNED DEFAULT NULL,
`created_at` DATETIME DEFAULT NULL,
`updated_at` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_reminder_mail_config_client_policy_id` (`client_policy_id`),
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;

View File

@ -88,14 +88,14 @@ 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 'under process'
// ELSE employee_polices.status
// END as status";
// }
$result = $this->select([
$selectColumns = [
'employee_polices.*',
'policy_type.policy_type as policy_name',
'im.short_name as insurer_short_name',
@ -214,9 +214,19 @@ class EmployeePolicyModel extends Model
ELSE NULL
END
)) AS newly_added_updated_at",
];
$status_query
], false)
if ($status_type === 'hr') {
$selectColumns[] = "(CASE
WHEN emp.relationship = 'Self'
THEN IF(auth_history.user_id IS NOT NULL, 'Yes', 'No')
ELSE NULL
END) AS logged_in";
}
$selectColumns[] = $status_query;
$result = $this->select($selectColumns, false)
->join('employees emp', 'employee_polices.employee_id = emp.id')
->join('client_policy cp', 'employee_polices.client_policy_id = cp.id') //cp - client policy
->join('policies pm', 'cp.policy_id = pm.id', 'left') //pm - policy master
@ -226,8 +236,18 @@ class EmployeePolicyModel extends Model
->join('tpa tpam', 'cp.tpa_id = tpam.id', 'left') //tpam - tpa master
->join('tpa_branch tpab', 'cp.tpa_branch_id = tpab.id', 'left') //tpab - tpa branch
->join('clients cm', 'cp.client_id = cm.id') //cm - client master
->join('client_branch', 'emp.client_branch_id = client_branch.id') //cm - client master
->orderBy('emp.emp_code', 'ASC')
->join('client_branch', 'emp.client_branch_id = client_branch.id'); //cm - client master
if ($status_type === 'hr') {
$result->join(
"(SELECT user_id FROM auth_history WHERE user_type = 'employee' GROUP BY user_id) auth_history",
'emp.id = auth_history.user_id',
'left',
false
);
}
$result->orderBy('emp.emp_code', 'ASC')
->orderBy('employee_polices.employee_id', 'ASC');

View File

@ -0,0 +1,342 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class ReminderMailConfigModel extends Model
{
public const FREQUENCY_DAILY = 'daily';
public const FREQUENCY_WEEKLY = 'weekly';
public const FREQUENCY_MONTHLY = 'monthly';
public const FREQUENCY_CUSTOM = 'custom';
public const FREQUENCY_WORKING_DAYS = 'working_days';
private const WORKING_DAY_NAMES = [
'mon' => 1,
'monday' => 1,
'tue' => 2,
'tuesday' => 2,
'wed' => 3,
'wednesday' => 3,
'thu' => 4,
'thursday' => 4,
'fri' => 5,
'friday' => 5,
'sat' => 6,
'saturday' => 6,
'sun' => 7,
'sunday' => 7,
];
private const WORKING_DAY_LABELS = [
1 => 'Mon',
2 => 'Tue',
3 => 'Wed',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat',
7 => 'Sun',
];
protected $table = 'reminder_mail_config';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $allowedFields = [
'client_policy_id',
'frequency',
'reminder_days',
'is_enabled',
'is_active',
'created_by',
'updated_by',
];
protected $allowCallbacks = true;
public function getByClientPolicyId(int $clientPolicyId): ?array
{
return $this->where('client_policy_id', $clientPolicyId)
->where('is_active', 1)
->first();
}
public function shouldSendTodayForPolicy(int $clientPolicyId, ?string $legacyReminderDate = null): bool
{
$config = $this->getByClientPolicyId($clientPolicyId);
if ($config) {
if (empty($config['is_enabled'])) {
return false;
}
return $this->shouldSendToday($config);
}
if (empty($legacyReminderDate)) {
return false;
}
$currentDate = date('d');
foreach (explode(',', $legacyReminderDate) as $date) {
if ($currentDate == trim($date)) {
return true;
}
}
return false;
}
public function shouldSendToday(array $config): bool
{
switch ($config['frequency']) {
case self::FREQUENCY_DAILY:
return true;
case self::FREQUENCY_WEEKLY:
return in_array((int) date('w'), $this->parseDays($config['reminder_days'] ?? ''), true);
case self::FREQUENCY_MONTHLY:
case self::FREQUENCY_CUSTOM:
return in_array((int) date('j'), $this->parseDays($config['reminder_days'] ?? ''), true);
case self::FREQUENCY_WORKING_DAYS:
return in_array((int) date('N'), $this->parseWorkingDays($config['reminder_days'] ?? ''), true);
default:
return false;
}
}
public function parseDays(?string $days): array
{
if ($days === null || trim($days) === '') {
return [];
}
return array_values(array_unique(array_map(
'intval',
array_filter(array_map('trim', explode(',', $days)), static fn ($day) => $day !== '')
)));
}
public function getWorkingDayOptions(): array
{
$options = [];
foreach (self::WORKING_DAY_LABELS as $value => $label) {
$options[] = [
'value' => $value,
'label' => $label,
'key' => strtolower($label),
];
}
return $options;
}
public function parseWorkingDays(?string $days): array
{
if ($days === null || trim($days) === '') {
return [];
}
$parsed = [];
foreach (explode(',', $days) as $day) {
$day = trim($day);
if ($day === '') {
continue;
}
if (is_numeric($day)) {
$num = (int) $day;
// Accept 0 as Sunday (same as 7 in ISO-8601 date('N'))
if ($num === 0) {
$num = 7;
}
$parsed[] = $num;
continue;
}
$key = strtolower($day);
if (isset(self::WORKING_DAY_NAMES[$key])) {
$parsed[] = self::WORKING_DAY_NAMES[$key];
}
}
return array_values(array_unique($parsed));
}
public function normalizeWorkingDays(?string $days): ?string
{
$parsed = $this->parseWorkingDays($days);
if (empty($parsed)) {
return null;
}
sort($parsed);
return implode(',', $parsed);
}
public function formatWorkingDayLabels(?string $days): array
{
$labels = [];
foreach ($this->parseWorkingDays($days) as $day) {
if (isset(self::WORKING_DAY_LABELS[$day])) {
$labels[] = self::WORKING_DAY_LABELS[$day];
}
}
return $labels;
}
public function enrichConfig(array $config): array
{
if (($config['frequency'] ?? '') === self::FREQUENCY_WORKING_DAYS) {
$config['working_day_labels'] = $this->formatWorkingDayLabels($config['reminder_days'] ?? null);
}
return $config;
}
public function normalizeReminderDays(string $frequency, ?string $reminderDays): ?string
{
if ($frequency === self::FREQUENCY_DAILY) {
return null;
}
if ($frequency === self::FREQUENCY_WORKING_DAYS) {
return $this->normalizeWorkingDays($reminderDays);
}
return $reminderDays !== null ? trim((string) $reminderDays) : null;
}
public function validateConfig(string $frequency, ?string $reminderDays): ?string
{
$allowedFrequencies = [
self::FREQUENCY_DAILY,
self::FREQUENCY_WEEKLY,
self::FREQUENCY_MONTHLY,
self::FREQUENCY_CUSTOM,
self::FREQUENCY_WORKING_DAYS,
];
if (!in_array($frequency, $allowedFrequencies, true)) {
return 'Invalid frequency. Allowed values: daily, weekly, monthly, custom, working_days.';
}
if ($frequency === self::FREQUENCY_DAILY) {
return null;
}
if ($frequency === self::FREQUENCY_WORKING_DAYS) {
$days = $this->parseWorkingDays($reminderDays);
if (empty($days)) {
return 'reminder_days is required. Use Mon-Sun or 1-7 (0 also accepted for Sunday).';
}
foreach ($days as $day) {
if ($day < 1 || $day > 7) {
return 'Working day reminder_days must be between 1 (Mon) and 7 (Sun).';
}
}
return null;
}
$days = $this->parseDays($reminderDays);
if (empty($days)) {
return 'reminder_days is required for the selected frequency.';
}
if ($frequency === self::FREQUENCY_WEEKLY) {
foreach ($days as $day) {
if ($day < 0 || $day > 6) {
return 'Weekly reminder_days must be between 0 (Sunday) and 6 (Saturday).';
}
}
return null;
}
foreach ($days as $day) {
if ($day < 1 || $day > 31) {
return 'Monthly/custom reminder_days must be between 1 and 31.';
}
}
return null;
}
/**
* @param array{id?: int|null, hr_id?: int|null} $options
*/
public function saveConfig(
int $clientPolicyId,
string $frequency,
?string $reminderDays,
int $isEnabled = 1,
array $options = []
): array {
$id = isset($options['id']) && $options['id'] !== '' ? (int) $options['id'] : null;
$hrId = isset($options['hr_id']) && $options['hr_id'] !== ''
? (int) $options['hr_id']
: null;
$validationError = $this->validateConfig($frequency, $reminderDays);
if ($validationError !== null) {
return ['status' => false, 'message' => $validationError];
}
$payload = [
'client_policy_id' => $clientPolicyId,
'frequency' => $frequency,
'reminder_days' => $this->normalizeReminderDays($frequency, $reminderDays),
'is_enabled' => $isEnabled,
'is_active' => 1,
];
if ($id) {
$existing = $this->where('id', $id)->where('is_active', 1)->first();
if (empty($existing)) {
return ['status' => false, 'message' => 'Reminder mail configuration not found'];
}
if ((int) $existing['client_policy_id'] !== $clientPolicyId) {
return ['status' => false, 'message' => 'client_policy_id does not match the configuration record'];
}
$payload['updated_by'] = $hrId;
$this->update($id, $payload);
$config = $this->find($id);
return ['status' => true, 'action' => 'updated', 'data' => $this->enrichConfig($config)];
}
$payload['created_by'] = $hrId;
$payload['updated_by'] = $hrId;
$this->insert($payload);
$config = $this->find($this->getInsertID());
return ['status' => true, 'action' => 'created', 'data' => $this->enrichConfig($config)];
}
}

540
docs/reminder-mail-api.md Normal file
View File

@ -0,0 +1,540 @@
# Reminder Mail API Documentation
Base path for all endpoints: `/employeeRest`
**Authentication:** JWT + app signature (same filters as other `employeeRest` routes)
**Content types:** JSON body or form-urlencoded (where noted)
**HTTP status:** All responses return HTTP `200`. Check the JSON `status` and `code` fields for success or failure.
---
## Table of Contents
1. [Get Configuration](#1-get-configuration)
2. [Save / Update Configuration](#2-save--update-configuration)
3. [Send Reminder Mail (Manual)](#3-send-reminder-mail-manual)
4. [Frequency Reference](#frequency-reference)
5. [Working Days Reference](#working-days-reference)
6. [Quick Reference](#quick-reference)
---
## 1. Get Configuration
Fetch reminder mail configuration for a client policy.
| | |
|---|---|
| **Method** | `GET` |
| **URL** | `/employeeRest/getReminderMailConfig` |
### Request Parameters
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `client_policy_id` | Yes | integer | Client policy ID |
### Sample Request
```http
GET /employeeRest/getReminderMailConfig?client_policy_id=123
```
### Success Response — Config Exists
```json
{
"status": true,
"code": 200,
"data": {
"id": 10,
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "1,3,5",
"working_day_labels": ["Mon", "Wed", "Fri"],
"is_enabled": 1,
"is_active": 1,
"created_by": 45,
"updated_by": 45,
"created_at": "2026-06-23 10:00:00",
"updated_at": "2026-06-23 11:30:00"
},
"working_day_options": [
{ "value": 1, "label": "Mon", "key": "mon" },
{ "value": 2, "label": "Tue", "key": "tue" },
{ "value": 3, "label": "Wed", "key": "wed" },
{ "value": 4, "label": "Thu", "key": "thu" },
{ "value": 5, "label": "Fri", "key": "fri" },
{ "value": 6, "label": "Sat", "key": "sat" },
{ "value": 7, "label": "Sun", "key": "sun" }
]
}
```
### Success Response — Legacy Fallback (no config row, legacy `reminder_date` on policy)
```json
{
"status": true,
"code": 200,
"data": {
"client_policy_id": 123,
"frequency": "custom",
"reminder_days": "1,15,28",
"is_enabled": 1,
"is_active": 1,
"source": "legacy_client_policy"
},
"working_day_options": [
{ "value": 1, "label": "Mon", "key": "mon" },
{ "value": 2, "label": "Tue", "key": "tue" },
{ "value": 3, "label": "Wed", "key": "wed" },
{ "value": 4, "label": "Thu", "key": "thu" },
{ "value": 5, "label": "Fri", "key": "fri" },
{ "value": 6, "label": "Sat", "key": "sat" },
{ "value": 7, "label": "Sun", "key": "sun" }
]
}
```
### Success Response — No Config
```json
{
"status": true,
"code": 200,
"data": null,
"working_day_options": [
{ "value": 1, "label": "Mon", "key": "mon" },
{ "value": 2, "label": "Tue", "key": "tue" },
{ "value": 3, "label": "Wed", "key": "wed" },
{ "value": 4, "label": "Thu", "key": "thu" },
{ "value": 5, "label": "Fri", "key": "fri" },
{ "value": 6, "label": "Sat", "key": "sat" },
{ "value": 7, "label": "Sun", "key": "sun" }
]
}
```
### Error Responses
```json
{
"status": false,
"code": 400,
"message": "client_policy_id is required"
}
```
```json
{
"status": false,
"code": 404,
"message": "Client policy not found"
}
```
---
## 2. Save / Update Configuration
Create or update reminder mail configuration.
| | |
|---|---|
| **Method** | `POST` |
| **URL** | `/employeeRest/saveReminderMailConfig` |
### Insert vs Update
| Condition | Action |
|-----------|--------|
| `id` **not** sent | **Insert** new record |
| `id` **sent** | **Update** existing record by primary key |
### Request Parameters
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `client_policy_id` | Yes | integer | Client policy ID |
| `frequency` | Yes | string | `daily`, `weekly`, `monthly`, `custom`, `working_days` |
| `reminder_days` | Depends | string | Comma-separated days (see [Frequency Reference](#frequency-reference)) |
| `working_days` | No | string | Alias for `reminder_days` when `frequency` is `working_days` |
| `is_enabled` | No | integer | `1` (enabled) or `0` (disabled). Default: `1` |
| `hr_id` | No | integer | Stored as `created_by` / `updated_by`. Null if omitted |
| `id` | No | integer | Primary key — required for update |
### Sample Payload — Insert (Daily)
```json
{
"client_policy_id": 123,
"frequency": "daily",
"is_enabled": 1,
"hr_id": 45
}
```
### Sample Payload — Insert (Working Days — weekdays only)
```json
{
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "Mon,Wed,Fri",
"is_enabled": 1,
"hr_id": 45
}
```
### Sample Payload — Insert (Working Days — including Sat/Sun)
```json
{
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "Mon,Wed,Fri,Sat,Sun",
"is_enabled": 1,
"hr_id": 45
}
```
Or numeric: `"1,3,5,6,7"` (Sat=6, Sun=7; `0` is also accepted for Sunday).
Alternative using `working_days` alias:
```json
{
"client_policy_id": 123,
"frequency": "working_days",
"working_days": "Mon,Tue,Thu",
"hr_id": 45
}
```
### Sample Payload — Insert (Weekly)
```json
{
"client_policy_id": 123,
"frequency": "weekly",
"reminder_days": "1,3,5",
"hr_id": 45
}
```
### Sample Payload — Insert (Monthly)
```json
{
"client_policy_id": 123,
"frequency": "monthly",
"reminder_days": "1,15",
"hr_id": 45
}
```
### Sample Payload — Insert (Custom)
```json
{
"client_policy_id": 123,
"frequency": "custom",
"reminder_days": "5,10,20",
"hr_id": 45
}
```
### Sample Payload — Update
```json
{
"id": 10,
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "Mon,Tue,Wed,Thu,Fri",
"is_enabled": 1,
"hr_id": 45
}
```
### Success Response — Created
```json
{
"status": true,
"code": 200,
"action": "created",
"message": "Reminder mail configuration saved successfully",
"data": {
"id": 10,
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "1,3,5",
"working_day_labels": ["Mon", "Wed", "Fri"],
"is_enabled": 1,
"is_active": 1,
"created_by": 45,
"updated_by": 45,
"created_at": "2026-06-23 10:00:00",
"updated_at": "2026-06-23 10:00:00"
}
}
```
### Success Response — Updated
```json
{
"status": true,
"code": 200,
"action": "updated",
"message": "Reminder mail configuration saved successfully",
"data": {
"id": 10,
"client_policy_id": 123,
"frequency": "working_days",
"reminder_days": "1,2,3,4,5",
"working_day_labels": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"is_enabled": 1,
"is_active": 1,
"created_by": 45,
"updated_by": 45,
"created_at": "2026-06-23 10:00:00",
"updated_at": "2026-06-23 12:00:00"
}
}
```
### Error Responses
```json
{
"status": false,
"code": 400,
"message": "client_policy_id is required"
}
```
```json
{
"status": false,
"code": 400,
"message": "frequency is required"
}
```
```json
{
"status": false,
"code": 404,
"message": "Client policy not found"
}
```
```json
{
"status": false,
"code": 400,
"message": "Reminder mail configuration not found"
}
```
```json
{
"status": false,
"code": 400,
"message": "client_policy_id does not match the configuration record"
}
```
```json
{
"status": false,
"code": 400,
"message": "Invalid frequency. Allowed values: daily, weekly, monthly, custom, working_days."
}
```
```json
{
"status": false,
"code": 400,
"message": "reminder_days is required. Use Mon-Sun or 1-7 (0 also accepted for Sunday)."
}
```
```json
{
"status": false,
"code": 400,
"message": "Working day reminder_days must be between 1 (Mon) and 7 (Sun)."
}
```
### Audit Fields (`hr_id`)
| Action | `created_by` | `updated_by` |
|--------|--------------|--------------|
| Insert + `hr_id` sent | `hr_id` | `hr_id` |
| Insert + `hr_id` omitted | `null` | `null` |
| Update + `hr_id` sent | unchanged | `hr_id` |
| Update + `hr_id` omitted | unchanged | `null` |
---
## 3. Send Reminder Mail (Manual)
Manually trigger reminder mail for a policy. Sends immediately regardless of schedule configuration.
| | |
|---|---|
| **Method** | `GET` or `POST` |
| **URL** | `/employeeRest/sendReminderMail` |
### Request Parameters
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `client_policy_id` | Yes | integer | Client policy ID |
### Sample Request (POST)
```http
POST /employeeRest/sendReminderMail
Content-Type: application/json
{
"client_policy_id": 123
}
```
### Sample Request (GET)
```http
GET /employeeRest/sendReminderMail?client_policy_id=123
```
### Success Response
```json
{
"status": true,
"code": 200,
"message": "Mail sent successfully"
}
```
### Error Responses
```json
{
"status": false,
"code": 400,
"message": "client_policy_id is required"
}
```
```json
{
"status": false,
"code": 404,
"message": "Client policy not found"
}
```
```json
{
"status": false,
"code": 200,
"message": "There is no data to send",
"message2": "Failed"
}
```
```json
{
"status": false,
"code": 200,
"message": "There is no data to send",
"message2": "No policy data found to send"
}
```
---
## Frequency Reference
| `frequency` | `reminder_days` required? | Format | Example |
|-------------|---------------------------|--------|---------|
| `daily` | No | — | — |
| `weekly` | Yes | Weekday `06` (SunSat) | `"1,3,5"` |
| `monthly` | Yes | Day of month `131` | `"1,15,28"` |
| `custom` | Yes | Day of month `131` | `"5,10,20"` |
| `working_days` | Yes | Weekday `17` (MonSun) or day names | `"Mon,Wed,Fri,Sat"` or `"1,3,5,6"` |
---
## Working Days Reference
Used when `frequency = working_days`. Select any combination of weekdays including Saturday and Sunday.
| Label | Numeric Value |
|-------|---------------|
| Mon | 1 |
| Tue | 2 |
| Wed | 3 |
| Thu | 4 |
| Fri | 5 |
| Sat | 6 |
| Sun | 7 |
Accepted input formats:
- Day names: `Mon`, `Tue`, `Wed`, `Thu`, `Fri`, `Sat`, `Sun` (case-insensitive)
- Numeric: `1``7` (ISO-8601, matches PHP `date('N')`)
- Sunday alias: `0` is normalized to `7`
- Comma-separated combinations: `"Mon,Wed,Sat"` or `"1,3,6"`
Stored in DB as normalized numeric string (e.g. `"1,3,6,7"`).
---
## Quick Reference
| API | Method | Purpose |
|-----|--------|---------|
| `/employeeRest/getReminderMailConfig` | GET | Fetch config by `client_policy_id` |
| `/employeeRest/saveReminderMailConfig` | POST | Create (no `id`) or update (with `id`) |
| `/employeeRest/sendReminderMail` | GET / POST | Manually send reminder mail |
---
## Notes
1. **Scheduled sends (cron)** use the saved configuration from the `reminder_mail_config` table.
2. **Manual send** via `/sendReminderMail` ignores the schedule and sends immediately.
3. **`working_day_options`** is returned on every get-config response for UI dropdown/checkbox rendering.
4. **Legacy policies** without a config row may still expose `client_policy.reminder_date` as `frequency: custom` with `source: legacy_client_policy`.
---
## Database
Configuration is stored in the `reminder_mail_config` table.
| Column | Description |
|--------|-------------|
| `id` | Primary key |
| `client_policy_id` | Unique per policy |
| `frequency` | Schedule type |
| `reminder_days` | Comma-separated day values |
| `is_enabled` | Enable/disable reminders |
| `created_by` / `updated_by` | Set from `hr_id` in API payload |
| `created_at` / `updated_at` | Timestamps |
SQL migration:
- `app/Database/reminder_mail_config.sql`

Binary file not shown.