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', 'email_subject', 'email_body', '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); } $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) { 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 (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(); 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)]; } }