env('pt.renewalReminder.enabled', 'true'), 'daily' => env('pt.renewalReminder.daily', 'true'), 'days' => env('pt.renewalReminder.days', 'mon,tue,wed,thu,fri'), 'clientTypes' => env('pt.renewalReminder.clientTypes', '2'), 'dateField' => env('pt.renewalReminder.dateField', 'renewal_date'), 'daysBefore' => env('pt.renewalReminder.daysBefore', 30), 'includeClientEmail' => env('pt.renewalReminder.includeClientEmail', 'true'), 'includeOverdue' => env('pt.renewalReminder.includeOverdue', 'false'), 'toEmails' => env('pt.renewalReminder.toEmails', ''), 'ccEmails' => env('pt.renewalReminder.ccEmails', ''), 'bccEmails' => env('pt.renewalReminder.bccEmails', ''), 'fromMail' => env('pt.renewalReminder.fromMail', ''), ]; $dbConfig = $this->loadConfigFromNotification(); $merged = array_merge($defaults, $dbConfig); $this->applyMergedConfig($merged); } /** * @return array */ private function loadConfigFromNotification(): array { try { $db = Database::connect(); $row = $db->table('notifications') ->where('template_name', self::TEMPLATE_NAME) ->groupStart() ->where('client_id', null) ->orWhere('client_id', 0) ->orWhere('client_id', '') ->groupEnd() ->orderBy('id', 'DESC') ->get() ->getRowArray(); if (empty($row)) { log_message('error', '[PtRenewalReminderConfig] No notifications row for retail_reminder_mail; using env defaults'); return []; } $this->notificationId = isset($row['id']) ? (int) $row['id'] : null; $this->mailSubject = trim((string) ($row['subject'] ?? '')); $this->mailContent = (string) ($row['mail_content'] ?? ''); $json = trim((string) ($row['config_json'] ?? '')); if ($json === '') { log_message('error', '[PtRenewalReminderConfig] config_json empty for notification_id=' . ($this->notificationId ?? 'n/a')); return []; } $decoded = json_decode($json, true); if (! is_array($decoded)) { log_message('error', '[PtRenewalReminderConfig] Invalid config_json for notification_id=' . ($this->notificationId ?? 'n/a')); return []; } return $decoded; } catch (\Throwable $e) { log_message( 'error', '[PtRenewalReminderConfig] Failed loading notification config: ' . $e->getMessage() ); return []; } } /** * @param array $merged */ private function applyMergedConfig(array $merged): void { $this->enabled = $this->toBool($merged['enabled'] ?? false); $this->daily = $this->toBool($merged['daily'] ?? false); $daysRaw = $merged['days'] ?? 'mon,tue,wed,thu,fri'; if (is_array($daysRaw)) { $daysRaw = implode(',', $daysRaw); } $this->days = array_values(array_filter(array_map( static fn (string $day): string => strtolower(substr(trim($day), 0, 3)), explode(',', (string) $daysRaw) ))); $clientTypesRaw = $merged['clientTypes'] ?? '2'; if (is_array($clientTypesRaw)) { $this->clientTypes = array_values(array_unique(array_filter(array_map( static fn ($type): int => (int) $type, $clientTypesRaw ), static fn (int $type): bool => $type > 0))); } else { $this->clientTypes = array_values(array_unique(array_filter(array_map( static fn (string $type): int => (int) trim($type), explode(',', (string) $clientTypesRaw) ), static fn (int $type): bool => $type > 0))); } $dateField = strtolower(trim((string) ($merged['dateField'] ?? 'renewal_date'))); if (! in_array($dateField, self::ALLOWED_DATE_FIELDS, true)) { log_message( 'error', '[PtRenewalReminderConfig] Invalid dateField "' . $dateField . '", falling back to renewal_date' ); $dateField = 'renewal_date'; } $this->dateField = $dateField; $this->daysBefore = max(0, (int) ($merged['daysBefore'] ?? 30)); $this->includeClientEmail = $this->toBool($merged['includeClientEmail'] ?? true); $this->includeOverdue = $this->toBool($merged['includeOverdue'] ?? false); $this->toEmails = $this->parseEmailList($merged['toEmails'] ?? ''); $this->ccEmails = $this->parseEmailList($merged['ccEmails'] ?? ''); $this->bccEmails = $this->parseEmailList($merged['bccEmails'] ?? ''); $this->fromMail = trim((string) ($merged['fromMail'] ?? '')); } public function shouldRunToday(): bool { if (! $this->enabled) { return false; } if ($this->daily) { return true; } $today = strtolower(date('D')); return in_array($today, $this->days, true); } public function getDateField(): string { return $this->dateField; } public function getDaysBefore(): int { return $this->daysBefore; } /** * @return int[] */ public function getClientTypes(): array { return $this->clientTypes; } /** * @return string[] */ public function getToEmails(): array { return $this->toEmails; } /** * @return string[] */ public function getCcEmails(): array { return $this->ccEmails; } /** * @return string[] */ public function getBccEmails(): array { return $this->bccEmails; } public function getFromMail(): string { return $this->fromMail; } public function includeClientEmail(): bool { return $this->includeClientEmail; } public function includeOverdue(): bool { return $this->includeOverdue; } public function getMailSubject(): string { return $this->mailSubject; } public function getMailContent(): string { return $this->mailContent; } /** * Flatten config for UI / API response. * * @return array */ public function toArray(): array { return [ 'enabled' => $this->enabled, 'daily' => $this->daily, 'days' => implode(',', $this->days), 'clientTypes' => $this->clientTypes, 'dateField' => $this->dateField, 'daysBefore' => $this->daysBefore, 'includeClientEmail' => $this->includeClientEmail, 'includeOverdue' => $this->includeOverdue, 'toEmails' => implode(',', $this->toEmails), 'ccEmails' => implode(',', $this->ccEmails), 'bccEmails' => implode(',', $this->bccEmails), 'fromMail' => $this->fromMail, ]; } /** * @return string[] */ private function parseEmailList(mixed $value): array { if ($value === null || $value === '') { return []; } if (is_array($value)) { $emails = array_map('trim', $value); } else { $emails = array_map('trim', explode(',', (string) $value)); } $unique = []; foreach ($emails as $email) { if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { continue; } $key = strtolower($email); if (! isset($unique[$key])) { $unique[$key] = $email; } } return array_values($unique); } private function toBool(mixed $value): bool { if (is_bool($value)) { return $value; } return in_array(strtolower((string) $value), ['1', 'true', 'yes', 'on'], true); } }