loadSettings(); $this->loadTemplates(); $this->initHooks(); } /** * Initialize hooks */ private function initHooks(): void { // Register AJAX handlers add_action('wp_ajax_easy_invoice_send_invoice_email', [$this, 'handleSendInvoiceEmail']); add_action('wp_ajax_nopriv_easy_invoice_send_invoice_email', [$this, 'handleSendInvoiceEmail']); add_action('wp_ajax_easy_invoice_send_quote_email', [$this, 'handleSendQuoteEmail']); add_action('wp_ajax_nopriv_easy_invoice_send_quote_email', [$this, 'handleSendQuoteEmail']); // Add email settings to admin add_action('admin_init', [$this, 'registerEmailSettings']); add_filter('easy_invoice_settings_sections', [$this, 'addEmailSettingsSection']); // Refresh settings when they're updated add_action('update_option_easy_invoice_email_from_name', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_from_address', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_reply_to', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_reply_to_name', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_enable_email_styling', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_logo', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_footer_text', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_bcc_admin', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_admin_email', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_subject', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_email_body', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_quote_subject', [$this, 'refreshSettings']); add_action('update_option_easy_invoice_quote_body', [$this, 'refreshSettings']); // Add email logs add_action('easy_invoice_email_sent', [$this, 'logEmailSent'], 10, 3); add_action('easy_invoice_email_failed', [$this, 'logEmailFailed'], 10, 3); } /** * Load email settings */ private function loadSettings(): void { $this->settings = [ 'from_name' => get_option('easy_invoice_email_from_name', get_bloginfo('name')), 'from_email' => get_option('easy_invoice_email_from_address', get_bloginfo('admin_email')), 'reply_to_email' => get_option('easy_invoice_email_reply_to', ''), 'reply_to_name' => get_option('easy_invoice_email_reply_to_name', ''), 'enable_html' => get_option('easy_invoice_enable_email_styling', 'yes'), 'email_logo' => get_option('easy_invoice_email_logo', ''), 'footer_text' => get_option('easy_invoice_email_footer_text', ''), 'bcc_admin' => get_option('easy_invoice_bcc_admin', 'no'), 'admin_email' => get_option('easy_invoice_admin_email', get_option('admin_email')), ]; } /** * Load email templates */ private function loadTemplates(): void { $this->templates = [ 'invoice_new' => [ 'enabled' => get_option('easy_invoice_invoice_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_invoice_email_subject', __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice')), 'body' => get_option('easy_invoice_invoice_email_body', $this->getDefaultInvoiceTemplate()), 'type' => 'invoice' ], 'invoice_reminder' => [ 'enabled' => get_option('easy_invoice_invoice_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_reminder_subject', 'Payment Reminder - Invoice #{{invoice_number}}'), 'body' => get_option('easy_invoice_reminder_body', $this->getDefaultReminderTemplate()), 'type' => 'invoice' ], 'invoice_paid' => [ 'enabled' => get_option('easy_invoice_payment_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_payment_email_subject', __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice')), 'body' => get_option('easy_invoice_payment_email_body', $this->getDefaultPaymentTemplate()), 'type' => 'invoice' ], 'quote_new' => [ 'enabled' => get_option('easy_invoice_quote_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_quote_email_subject', __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice')), 'body' => get_option('easy_invoice_quote_email_body', $this->getDefaultQuoteTemplate()), 'type' => 'quote' ], 'quote_accepted' => [ 'enabled' => get_option('easy_invoice_quote_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_quote_accepted_subject', 'Quote Accepted - #{{quote_number}}'), 'body' => get_option('easy_invoice_quote_accepted_body', $this->getDefaultQuoteAcceptedTemplate()), 'type' => 'quote' ], 'quote_declined' => [ 'enabled' => get_option('easy_invoice_quote_email_enabled', 'yes') === 'yes', 'subject' => get_option('easy_invoice_quote_declined_subject', 'Quote Declined - #{{quote_number}}'), 'body' => get_option('easy_invoice_quote_declined_body', $this->getDefaultQuoteDeclinedTemplate()), 'type' => 'quote' ] ]; } /** * Send invoice email * * @param Invoice $invoice The invoice * @param string $template_type Template type (new, reminder, paid) * @param array $additional_data Additional data for template * @return array Result array with success status and message */ public function sendInvoiceEmail(Invoice $invoice, string $template_type = 'new', array $additional_data = []): array { try { // Validate invoice if (!$invoice || !$invoice->getId()) { return ['success' => false, 'message' => __('Invalid invoice', 'easy-invoice')]; } // Get client email $client_email = $invoice->getCustomerEmail(); if (empty($client_email)) { return ['success' => false, 'message' => __('Client email is missing', 'easy-invoice')]; } // Get template $template_key = 'invoice_' . $template_type; if (!isset($this->templates[$template_key])) { return ['success' => false, 'message' => __('Email template not found', 'easy-invoice')]; } $template = $this->templates[$template_key]; // Check if email is enabled if (!$template['enabled']) { return ['success' => false, 'message' => __('Invoice available email is disabled', 'easy-invoice')]; } // Prepare email data $email_data = $this->prepareInvoiceEmailData($invoice, $template, $additional_data); // Send email $sent = $this->sendEmail( $email_data['to'], $email_data['subject'], $email_data['message'], $email_data['headers'] ); if ($sent) { // Log success do_action('easy_invoice_email_sent', $invoice, $client_email, $template_type); return [ 'success' => true, 'message' => __('Email sent successfully', 'easy-invoice'), 'email_data' => $email_data ]; } else { // Log failure do_action('easy_invoice_email_failed', $invoice, $client_email, $template_type); return ['success' => false, 'message' => __('Failed to send email', 'easy-invoice')]; } } catch (\Exception $e) { $this->log('Email sending error: ' . $e->getMessage(), 'error'); return ['success' => false, 'message' => __('Error sending email: ', 'easy-invoice') . $e->getMessage()]; } } /** * Send quote email * * @param Quote $quote The quote * @param string $template_type Template type (new, accepted, declined) * @param array $additional_data Additional data for template * @return array Result array with success status and message */ public function sendQuoteEmail(Quote $quote, string $template_type = 'new', array $additional_data = []): array { try { // Validate quote if (!$quote || !$quote->getId()) { return ['success' => false, 'message' => __('Invalid quote', 'easy-invoice')]; } // Get client email $client_email = $quote->getCustomerEmail(); if (empty($client_email)) { return ['success' => false, 'message' => __('Client email is missing', 'easy-invoice')]; } // Get template $template_key = 'quote_' . $template_type; if (!isset($this->templates[$template_key])) { return ['success' => false, 'message' => __('Email template not found', 'easy-invoice')]; } $template = $this->templates[$template_key]; // Check if email is enabled if (!$template['enabled']) { return ['success' => false, 'message' => __('Quote available email is disabled', 'easy-invoice')]; } // Prepare email data $email_data = $this->prepareQuoteEmailData($quote, $template, $additional_data); // Send email $sent = $this->sendEmail( $email_data['to'], $email_data['subject'], $email_data['message'], $email_data['headers'] ); if ($sent) { // Log success do_action('easy_invoice_quote_email_sent', $quote, $client_email, $template_type); return [ 'success' => true, 'message' => __('Quote email sent successfully', 'easy-invoice'), 'email_data' => $email_data ]; } else { // Log failure do_action('easy_invoice_quote_email_failed', $quote, $client_email, $template_type); return ['success' => false, 'message' => __('Failed to send quote email', 'easy-invoice')]; } } catch (\Exception $e) { $this->log('Quote email sending error: ' . $e->getMessage(), 'error'); return ['success' => false, 'message' => __('Error sending quote email: ', 'easy-invoice') . $e->getMessage()]; } } /** * Prepare invoice email data * * @param Invoice $invoice The invoice * @param array $template The email template * @param array $additional_data Additional data * @return array Email data */ private function prepareInvoiceEmailData(Invoice $invoice, array $template, array $additional_data = []): array { // Get replacements $replacements = $this->getInvoiceReplacements($invoice, $additional_data); // Process template $subject = $this->processTemplate($template['subject'], $replacements); $message = $this->processTemplate($template['body'], $replacements); $message = do_shortcode($message); // Render shortcodes like [easy_invoice_url ...] // Add HTML wrapper if enabled if ($this->settings['enable_html'] === 'yes') { $message = $this->wrapInHtmlTemplate($message); } // Prepare headers $headers = $this->prepareEmailHeaders(); // Add BCC to admin if enabled if ($this->settings['bcc_admin'] === 'yes' && !empty($this->settings['admin_email'])) { $headers[] = 'Bcc: ' . $this->settings['admin_email']; } return [ 'to' => $invoice->getCustomerEmail(), 'subject' => $subject, 'message' => $message, 'headers' => $headers ]; } /** * Prepare quote email data * * @param Quote $quote The quote * @param array $template The email template * @param array $additional_data Additional data * @return array Email data */ private function prepareQuoteEmailData(Quote $quote, array $template, array $additional_data = []): array { // Get replacements $replacements = $this->getQuoteReplacements($quote, $additional_data); // Process template $subject = $this->processTemplate($template['subject'], $replacements); $message = $this->processTemplate($template['body'], $replacements); // Add HTML wrapper if enabled if ($this->settings['enable_html'] === 'yes') { $message = $this->wrapInHtmlTemplate($message); } // Prepare headers $headers = $this->prepareEmailHeaders(); // Add BCC to admin if enabled if ($this->settings['bcc_admin'] === 'yes' && !empty($this->settings['admin_email'])) { $headers[] = 'Bcc: ' . $this->settings['admin_email']; } return [ 'to' => $quote->getCustomerEmail(), 'subject' => $subject, 'message' => $message, 'headers' => $headers ]; } /** * Prepare payment email data * * @param Invoice $invoice The invoice * @param array $template The email template * @param array $payment_data Payment data * @return array Email data */ private function preparePaymentEmailData(Invoice $invoice, array $template, array $payment_data = []): array { // Get replacements $replacements = $this->getPaymentReplacements($invoice, $payment_data); // Process template $subject = $this->processTemplate($template['subject'], $replacements); $message = $this->processTemplate($template['body'], $replacements); // Add HTML wrapper if enabled if ($this->settings['enable_html'] === 'yes') { $message = $this->wrapInHtmlTemplate($message); } // Prepare headers $headers = $this->prepareEmailHeaders(); // Add BCC to admin if enabled if ($this->settings['bcc_admin'] === 'yes' && !empty($this->settings['admin_email'])) { $headers[] = 'Bcc: ' . $this->settings['admin_email']; } return [ 'to' => $invoice->getCustomerEmail(), 'subject' => $subject, 'message' => $message, 'headers' => $headers ]; } /** * Get invoice replacements * * @param Invoice $invoice The invoice * @param array $additional_data Additional data * @return array Replacements */ private function getInvoiceReplacements(Invoice $invoice, array $additional_data = []): array { $currency_symbol = get_option('easy_invoice_currency_symbol', '$'); // Secure link support $invoice_url = get_permalink($invoice->getId()); $secure_links_enabled = get_option('easy_invoice_pro_enable_secure_links', 'no') === 'yes'; if ($secure_links_enabled && class_exists('\EasyInvoicePro\Controllers\PermalinkController')) { $secure_url = \EasyInvoicePro\Controllers\PermalinkController::getInvoiceSecureLinkUrl($invoice->getId()); if ($secure_url) { $invoice_url = $secure_url; } } return array_merge([ '{{invoice_number}}' => $invoice->getNumber(), '{{invoice_title}}' => $invoice->getTitle(), '{{client_name}}' => $invoice->getCustomerName(), '{{client_email}}' => $invoice->getCustomerEmail(), '{{client_address}}' => $invoice->getCustomerAddress(), '{{company_name}}' => get_bloginfo('name'), '{{company_email}}' => $this->settings['from_email'], '{{company_phone}}' => get_option('easy_invoice_company_phone', ''), '{{company_address}}' => get_option('easy_invoice_company_address', ''), '{{company_website}}' => get_option('easy_invoice_company_website', ''), '{{total_amount}}' => (new \EasyInvoice\Helpers\InvoiceFormatter($invoice))->format($invoice->getTotal()), '{{subtotal}}' => (new \EasyInvoice\Helpers\InvoiceFormatter($invoice))->format($invoice->getSubtotal()), '{{tax_amount}}' => (new \EasyInvoice\Helpers\InvoiceFormatter($invoice))->format($invoice->getTaxAmount()), '{{discount_amount}}' => (new \EasyInvoice\Helpers\InvoiceFormatter($invoice))->format($invoice->getDiscountAmount()), '{{due_date}}' => date('F j, Y', strtotime($invoice->getDueDate())), '{{issue_date}}' => date('F j, Y', strtotime($invoice->getIssueDate())), '{{invoice_url}}' => $invoice_url, // Use correct (possibly secure) link '{{payment_url}}' => add_query_arg('payment', '1', get_permalink($invoice->getId())), '{{site_url}}' => get_site_url(), '{{admin_url}}' => admin_url(), '{{payment_terms}}' => get_option('easy_invoice_payment_terms', __('Due on receipt', 'easy-invoice')), ], $additional_data); } /** * Get quote replacements * * @param Quote $quote The quote * @param array $additional_data Additional data * @return array Replacements */ private function getQuoteReplacements(Quote $quote, array $additional_data = []): array { $currency_symbol = get_option('easy_invoice_currency_symbol', '$'); $quote_url = get_permalink($quote->getId()); $secure_links_enabled = get_option('easy_invoice_pro_enable_secure_links', 'no') === 'yes'; if ($secure_links_enabled && class_exists('\EasyInvoicePro\Controllers\PermalinkController')) { $secure_url = \EasyInvoicePro\Controllers\PermalinkController::getQuoteSecureLinkUrl($quote->getId()); if ($secure_url) { $quote_url = $secure_url; } } return array_merge([ '{{quote_number}}' => $quote->getNumber(), '{{quote_title}}' => $quote->getTitle(), '{{client_name}}' => $quote->getCustomerName(), '{{client_email}}' => $quote->getCustomerEmail(), '{{client_address}}' => $quote->getCustomerAddress(), '{{company_name}}' => get_bloginfo('name'), '{{company_email}}' => $this->settings['from_email'], '{{company_phone}}' => get_option('easy_invoice_company_phone', ''), '{{company_address}}' => get_option('easy_invoice_company_address', ''), '{{company_website}}' => get_option('easy_invoice_company_website', ''), '{{total_amount}}' => (new \EasyInvoice\Helpers\QuoteFormatter($quote))->format($quote->getTotal()), '{{subtotal}}' => (new \EasyInvoice\Helpers\QuoteFormatter($quote))->format($quote->getSubtotal()), '{{tax_amount}}' => (new \EasyInvoice\Helpers\QuoteFormatter($quote))->format($quote->getTaxAmount()), '{{discount_amount}}' => (new \EasyInvoice\Helpers\QuoteFormatter($quote))->format($quote->getDiscountAmount()), '{{expiry_date}}' => date('F j, Y', strtotime($quote->getExpiryDate())), '{{issue_date}}' => date('F j, Y', strtotime($quote->getIssueDate())), '{{quote_url}}' => $quote_url, '{{site_url}}' => get_site_url(), '{{admin_url}}' => admin_url(), '{{payment_terms}}' => get_option('easy_invoice_payment_terms', __('Due on receipt', 'easy-invoice')), ], $additional_data); } /** * Get payment replacements * * @param Invoice $invoice The invoice * @param array $payment_data Payment data * @return array Replacements */ private function getPaymentReplacements(Invoice $invoice, array $payment_data = []): array { $replacements = $this->getInvoiceReplacements($invoice, $payment_data); // Add payment-specific replacements $replacements['{{payment_amount}}'] = isset($payment_data['amount']) ? $this->formatCurrency($payment_data['amount']) : $this->formatCurrency($invoice->getTotal()); $replacements['{{payment_date}}'] = isset($payment_data['date']) ? $payment_data['date'] : current_time('Y-m-d'); $replacements['{{payment_method}}'] = isset($payment_data['method']) ? $payment_data['method'] : __('Online Payment', 'easy-invoice'); $replacements['{{transaction_id}}'] = isset($payment_data['transaction_id']) ? $payment_data['transaction_id'] : __('N/A', 'easy-invoice'); $replacements['{{acceptance_date}}'] = isset($payment_data['acceptance_date']) ? $payment_data['acceptance_date'] : current_time('Y-m-d'); $replacements['{{response_date}}'] = isset($payment_data['response_date']) ? $payment_data['response_date'] : current_time('Y-m-d'); $replacements['{{decline_reason}}'] = isset($payment_data['decline_reason']) ? $payment_data['decline_reason'] : __('No specific reason provided', 'easy-invoice'); return $replacements; } /** * Process template with replacements * * @param string $template The template * @param array $replacements The replacements * @return string Processed template */ private function processTemplate(string $template, array $replacements): string { return easy_invoice_str_replace(array_keys($replacements), array_values($replacements), $template); } /** * Prepare email headers * * @return array Headers */ private function prepareEmailHeaders(): array { $headers = [ 'Content-Type: text/html; charset=UTF-8', 'From: ' . $this->settings['from_name'] . ' <' . $this->settings['from_email'] . '>', ]; // Add Reply-To if set if (!empty($this->settings['reply_to_email'])) { $reply_to_name = !empty($this->settings['reply_to_name']) ? $this->settings['reply_to_name'] : $this->settings['from_name']; $headers[] = 'Reply-To: ' . $reply_to_name . ' <' . $this->settings['reply_to_email'] . '>'; } return $headers; } /** * Wrap message in HTML template * * @param string $message The message * @return string HTML wrapped message */ private function wrapInHtmlTemplate(string $message): string { $logo_html = ''; if (!empty($this->settings['email_logo'])) { $logo_html = '
' . esc_attr($this->settings['from_name']) . '
'; } $footer_html = ''; if (!empty($this->settings['footer_text'])) { $footer_html = '
' . wpautop($this->settings['footer_text']) . '
'; } return ' ' . esc_html($this->settings['from_name']) . '
' . $logo_html . '
' . wpautop($message) . '
' . $footer_html . '
'; } /** * Handle AJAX send invoice email */ public function handleSendInvoiceEmail(): void { // Verify nonce if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_send_invoice_email')) { wp_send_json_error(__('Security check failed', 'easy-invoice')); } // Get invoice ID $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0; if (!$invoice_id) { wp_send_json_error(__('Invalid invoice ID', 'easy-invoice')); } // Get invoice $repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository(); $invoice = $repository->find($invoice_id); if (!$invoice) { wp_send_json_error(__('Invoice not found', 'easy-invoice')); } // Send email $result = $this->sendInvoiceEmail($invoice, 'new'); if ($result['success']) { wp_send_json_success($result['message']); } else { wp_send_json_error($result['message']); } } /** * Handle AJAX send quote email */ public function handleSendQuoteEmail(): void { // Verify nonce if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_send_quote_email')) { wp_send_json_error(__('Security check failed', 'easy-invoice')); } // Get quote ID $quote_id = isset($_POST['quote_id']) ? intval($_POST['quote_id']) : 0; if (!$quote_id) { wp_send_json_error(__('Invalid quote ID', 'easy-invoice')); } // Get quote $repository = \EasyInvoice\Providers\QuoteServiceProvider::getQuoteRepository(); $quote = $repository->find($quote_id); if (!$quote) { wp_send_json_error(__('Quote not found', 'easy-invoice')); } // Send email $result = $this->sendQuoteEmail($quote, 'new'); if ($result['success']) { // Log the quote email sent $quote_log_service = new \EasyInvoice\Services\QuoteLogService(); $quote_log_service->logSent($quote_id, $quote->getCustomerEmail()); wp_send_json_success($result['message']); } else { wp_send_json_error($result['message']); } } /** * Register email settings */ public function registerEmailSettings(): void { // Email settings section add_settings_section( 'easy_invoice_email_settings', __('Email Configuration', 'easy-invoice'), [$this, 'emailSettingsSectionCallback'], 'easy_invoice_settings' ); // Register settings register_setting('easy_invoice_settings', 'easy_invoice_email_from_name'); register_setting('easy_invoice_settings', 'easy_invoice_email_from_address'); register_setting('easy_invoice_settings', 'easy_invoice_email_reply_to'); register_setting('easy_invoice_settings', 'easy_invoice_email_reply_to_name'); register_setting('easy_invoice_settings', 'easy_invoice_enable_email_styling'); register_setting('easy_invoice_settings', 'easy_invoice_email_logo'); register_setting('easy_invoice_settings', 'easy_invoice_email_footer_text'); register_setting('easy_invoice_settings', 'easy_invoice_bcc_admin'); register_setting('easy_invoice_settings', 'easy_invoice_admin_email'); // Add settings fields add_settings_field( 'easy_invoice_email_from_name', __('From Name', 'easy-invoice'), [$this, 'textFieldCallback'], 'easy_invoice_settings', 'easy_invoice_email_settings', ['label_for' => 'easy_invoice_email_from_name'] ); add_settings_field( 'easy_invoice_email_from_address', __('From Email Address', 'easy-invoice'), [$this, 'emailFieldCallback'], 'easy_invoice_settings', 'easy_invoice_email_settings', ['label_for' => 'easy_invoice_email_from_address'] ); add_settings_field( 'easy_invoice_email_reply_to', __('Reply-To Email', 'easy-invoice'), [$this, 'emailFieldCallback'], 'easy_invoice_settings', 'easy_invoice_email_settings', ['label_for' => 'easy_invoice_email_reply_to'] ); add_settings_field( 'easy_invoice_enable_email_styling', __('Enable HTML Emails', 'easy-invoice'), [$this, 'checkboxFieldCallback'], 'easy_invoice_settings', 'easy_invoice_email_settings', ['label_for' => 'easy_invoice_enable_email_styling'] ); add_settings_field( 'easy_invoice_bcc_admin', __('BCC Admin on All Emails', 'easy-invoice'), [$this, 'checkboxFieldCallback'], 'easy_invoice_settings', 'easy_invoice_email_settings', ['label_for' => 'easy_invoice_bcc_admin'] ); } /** * Add email settings section * * @param array $sections Settings sections * @return array Modified sections */ public function addEmailSettingsSection(array $sections): array { $sections['email'] = [ 'title' => __('Email Settings', 'easy-invoice'), 'description' => __('Configure email sending options and templates', 'easy-invoice'), 'icon' => 'fas fa-envelope', 'fields' => [ 'easy_invoice_email_from_name' => [ 'label' => __('From Name', 'easy-invoice'), 'type' => 'text', 'default' => get_bloginfo('name'), 'col_span' => 'sm:col-span-3' ], 'easy_invoice_email_from_address' => [ 'label' => __('From Email Address', 'easy-invoice'), 'type' => 'email', 'default' => get_bloginfo('admin_email'), 'col_span' => 'sm:col-span-3' ], 'easy_invoice_email_reply_to' => [ 'label' => __('Reply-To Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-3' ], 'easy_invoice_enable_email_styling' => [ 'label' => __('Enable HTML Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-3' ], 'easy_invoice_bcc_admin' => [ 'label' => __('BCC Admin on All Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-3' ], 'easy_invoice_email_logo' => [ 'label' => __('Email Logo URL', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6' ], 'easy_invoice_email_footer_text' => [ 'label' => __('Email Footer Text', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6' ], ] ]; return $sections; } /** * Email settings section callback */ public function emailSettingsSectionCallback(): void { echo '

' . __('Configure how emails are sent from Easy Invoice.', 'easy-invoice') . '

'; } /** * Text field callback * * @param array $args Field arguments */ public function textFieldCallback(array $args): void { $field_id = $args['label_for']; $value = get_option($field_id, ''); echo ''; } /** * Email field callback * * @param array $args Field arguments */ public function emailFieldCallback(array $args): void { $field_id = $args['label_for']; $value = get_option($field_id, ''); echo ''; } /** * Checkbox field callback * * @param array $args Field arguments */ public function checkboxFieldCallback(array $args): void { $field_id = $args['label_for']; $value = get_option($field_id, ''); echo ''; echo '' . __('Enable this option', 'easy-invoice') . ''; } /** * Log email sent * * @param Invoice $invoice The invoice * @param string $email The email address * @param string $type The email type */ public function logEmailSent($invoice, string $email, string $type): void { $this->log(sprintf('Email sent to %s for invoice #%s (%s)', $email, $invoice->getNumber(), $type), 'info'); } /** * Log email failed * * @param Invoice $invoice The invoice * @param string $email The email address * @param string $type The email type */ public function logEmailFailed($invoice, string $email, string $type): void { $this->log(sprintf('Email failed to %s for invoice #%s (%s)', $email, $invoice->getNumber(), $type), 'error'); } /** * Get default invoice template * * @return string Template */ private function getDefaultInvoiceTemplate(): string { return '

๐Ÿ“„ Your Invoice is Ready

Dear {{client_name}},

Invoice #{{invoice_number}}
{{total_amount}}
Due Date: {{due_date}}

Your invoice has been prepared and is ready for payment. You can view and download the complete invoice from the attachment or visit the link below.

๐Ÿ“‹ Payment Details:
โ€ข Invoice Number: {{invoice_number}}
โ€ข Total Amount: {{total_amount}}
โ€ข Due Date: {{due_date}}
โ€ข Payment Terms: {{payment_terms}}

๐Ÿ”— View Invoice Online:
{{invoice_url}}

Shortcode: [easy_invoice_url number="{{invoice_number}}" text="View Invoice"]

โš ๏ธ Important: Please ensure payment is received by the due date to avoid any late fees or service interruptions.

If you have any questions about this invoice, please do not hesitate to contact us.

Thank you for your business!

Best regards,
{{company_name}}
{{company_email}}

'; } private function getDefaultReminderTemplate(): string { return '

โฐ Payment Reminder

Dear {{client_name}},

Invoice #{{invoice_number}}
{{total_amount}}
Due Date: {{due_date}}

This is a friendly reminder that payment for the above invoice is now due. If you have already made the payment, please disregard this message.

๐Ÿ’ณ Payment Options:
โ€ข Online payment through our secure portal
โ€ข Bank transfer to the details provided
โ€ข Check or money order

๐Ÿ”— View Invoice Online:
{{invoice_url}}

Shortcode: [easy_invoice_url number="{{invoice_number}}" text="View Invoice"]

๐Ÿ“ž Need Help? If you have any questions or need to discuss payment arrangements, please contact us immediately.

Thank you for your prompt attention to this matter.

Best regards,
{{company_name}}
{{company_email}}

'; } private function getDefaultPaymentTemplate(): string { return '

โœ… Payment Received - Thank You!

Dear {{client_name}},

Payment Confirmation
Invoice #{{invoice_number}}
{{payment_amount}}
Payment Date: {{payment_date}}
Payment Method: {{payment_method}}

We have successfully received your payment. Thank you for your prompt payment!

๐Ÿ“Š Payment Details:
โ€ข Invoice Number: {{invoice_number}}
โ€ข Amount Paid: {{payment_amount}}
โ€ข Payment Date: {{payment_date}}
โ€ข Payment Method: {{payment_method}}
โ€ข Transaction ID: {{transaction_id}}

๐ŸŽ‰ Status: PAID
Your payment has been processed and your account is now up to date. We appreciate your business!

If you have any questions about this payment or need a receipt, please don\'t hesitate to contact us.

Thank you for choosing our services!

Best regards,
{{company_name}}
{{company_email}}

'; } private function getDefaultQuoteTemplate(): string { return '

๐Ÿ“‹ Your Quote is Ready

Dear {{client_name}},

Quote #{{quote_number}}
{{total_amount}}
Valid Until: {{expiry_date}}

We have prepared a detailed quote for your project. You can view and download the complete quote from the attachment or visit the link below.

๐Ÿ“‹ Quote Summary:
โ€ข Quote Number: {{quote_number}}
โ€ข Total Amount: {{total_amount}}
โ€ข Valid Until: {{expiry_date}}
โ€ข Terms: {{payment_terms}}

๐Ÿ”— View Quote Online:
{{quote_url}}

Shortcode: [easy_quote_url number="{{quote_number}}" text="View Quote"]

โฐ Time Sensitive: This quote is valid until {{expiry_date}}. Please review and respond within this timeframe.

If you have any questions or would like to discuss any aspects of this quote, please contact us.

We look forward to working with you!

Best regards,
{{company_name}}
{{company_email}}

'; } private function getDefaultQuoteAcceptedTemplate(): string { return '

๐ŸŽ‰ Quote Accepted - Project Confirmed!

Dear {{client_name}},

Quote #{{quote_number}} - ACCEPTED
{{total_amount}}
Acceptance Date: {{acceptance_date}}

Thank you for accepting our quote! We\'re excited to begin working on your project.

๐Ÿš€ Next Steps:
โ€ข We will create an invoice for the accepted quote
โ€ข You will receive payment instructions
โ€ข Project work will begin as scheduled

๐Ÿ“ž What\'s Next? Our team will be in touch shortly with the next steps and any additional information you may need.

Thank you for choosing our services!

Best regards,
{{company_name}}
{{company_email}}

'; } private function getDefaultQuoteDeclinedTemplate(): string { return '

๐Ÿ“ Quote Response Received

Dear {{client_name}},

Quote #{{quote_number}} - DECLINED
Response Date: {{response_date}}

We have received your response regarding our quote. We understand that this quote may not have met your current needs.

๐Ÿ“‹ Feedback:
โ€ข Reason: {{decline_reason}}
โ€ข Response Date: {{response_date}}

๐Ÿค Future Opportunities: We appreciate you taking the time to review our proposal. If your requirements change in the future, we would be happy to discuss new opportunities.

Thank you for considering our services!

Best regards,
{{company_name}}
{{company_email}}

'; } /** * Refresh settings and templates * Call this method when settings are updated */ public function refreshSettings(): void { $this->loadSettings(); $this->loadTemplates(); } /** * Get email templates * * @return array Templates */ public function getTemplates(): array { return $this->templates; } /** * Get email settings * * @return array Settings */ public function getSettings(): array { return $this->settings; } /** * Test email functionality * * @param string $to_email Email to send test to * @return array Result */ public function testEmail(string $to_email): array { $subject = 'Easy Invoice - Email Configuration Test'; $message = '

๐Ÿงช Email Configuration Test

Hello!

โœ… Test Email Successfully Sent
Date: ' . current_time('Y-m-d H:i:s') . '
To: ' . esc_html($to_email) . '

This is a test email to verify that your Easy Invoice email configuration is working correctly.

โš™๏ธ Email Settings Verified:
โ€ข From Name: ' . esc_html($this->settings['from_name']) . '
โ€ข From Email: ' . esc_html($this->settings['from_email']) . '
โ€ข Reply-To: ' . esc_html($this->settings['reply_to_email'] ?: 'Not set') . '
โ€ข HTML Emails: ' . ($this->settings['enable_html'] === 'yes' ? 'Enabled' : 'Disabled') . '

๐ŸŽ‰ Congratulations! If you received this email, your email configuration is working properly and you can now send invoices, quotes, and payment confirmations to your clients.

Thank you for using Easy Invoice!

Best regards,
' . esc_html($this->settings['from_name']) . '

'; if ($this->settings['enable_html'] === 'yes') { $message = $this->wrapInHtmlTemplate($message); } $headers = $this->prepareEmailHeaders(); $sent = $this->sendEmail($to_email, $subject, $message, $headers); if ($sent) { return ['success' => true, 'message' => __('Test email sent successfully', 'easy-invoice')]; } else { return ['success' => false, 'message' => __('Failed to send test email', 'easy-invoice')]; } } /** * Send test template email with custom subject and body * * @param string $to_email Email to send test to * @param string $subject Email subject * @param string $body Email body * @return array Result */ public function sendTestTemplateEmail(string $to_email, string $subject, string $body): array { if ($this->settings['enable_html'] === 'yes') { $body = $this->wrapInHtmlTemplate($body); } $headers = $this->prepareEmailHeaders(); $sent = $this->sendEmail($to_email, $subject, $body, $headers); if ($sent) { return ['success' => true, 'message' => __('Template test email sent successfully', 'easy-invoice')]; } else { return ['success' => false, 'message' => __('Failed to send template test email', 'easy-invoice')]; } } /** * Send payment received email * * @param Invoice $invoice The invoice * @param array $payment_data Payment data * @return array Result array with success status and message */ public function sendPaymentEmail(Invoice $invoice, array $payment_data = []): array { try { // Validate invoice if (!$invoice || !$invoice->getId()) { return ['success' => false, 'message' => __('Invalid invoice', 'easy-invoice')]; } // Get client email $client_email = $invoice->getCustomerEmail(); if (empty($client_email)) { return ['success' => false, 'message' => __('Client email is missing', 'easy-invoice')]; } // Get template $template_key = 'invoice_paid'; if (!isset($this->templates[$template_key])) { return ['success' => false, 'message' => __('Payment email template not found', 'easy-invoice')]; } $template = $this->templates[$template_key]; // Check if email is enabled if (!$template['enabled']) { return ['success' => false, 'message' => __('Payment received email is disabled', 'easy-invoice')]; } // Prepare email data $email_data = $this->preparePaymentEmailData($invoice, $template, $payment_data); // Send email $sent = $this->sendEmail( $email_data['to'], $email_data['subject'], $email_data['message'], $email_data['headers'] ); if ($sent) { // Log success do_action('easy_invoice_payment_email_sent', $invoice, $client_email, $payment_data); return [ 'success' => true, 'message' => __('Payment email sent successfully', 'easy-invoice'), 'email_data' => $email_data ]; } else { // Log failure do_action('easy_invoice_payment_email_failed', $invoice, $client_email, $payment_data); return ['success' => false, 'message' => __('Failed to send payment email', 'easy-invoice')]; } } catch (\Exception $e) { $this->log('Payment email sending error: ' . $e->getMessage(), 'error'); return ['success' => false, 'message' => __('Error sending payment email: ', 'easy-invoice') . $e->getMessage()]; } } }