PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | includes/Controllers/SettingsController.php +209 -335 2.1.102.4.0 View file →
@@ -25,8 +25,25 @@
25 25 */
26 26 private $settings_cache = null;
27 27
28 28 /**
29 + * Settings resolved once per request. Every money formatter used to
30 + * construct its own controller and rebuild the whole settings array (the
31 + * config tree with every translated label) — around a millisecond per
32 + * formatted amount, ten seconds on a 4,500-line statement.
33 + *
34 + * @var array|null
35 + */
36 + private static $shared_settings_cache = null;
37 +
38 + /**
39 + * Drop the per-request settings cache (any easy_invoice_* option changed).
40 + */
41 + public static function flushSharedCache(): void {
42 + self::$shared_settings_cache = null;
43 + }
44 +
45 + /**
29 46 * Settings configuration cache
30 47 *
31 48 * @var array
32 49 */
@@ -181,8 +198,12 @@
181 198 'easy_invoice_company_name' => ['label' => __('Company Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
182 199 'easy_invoice_company_email' => ['label' => __('Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-6' ],
183 200 'easy_invoice_company_phone' => ['label' => __('Phone Number', 'easy-invoice'), 'type' => 'tel', 'default' => '', 'col_span' => 'sm:col-span-6' ],
184 201 'easy_invoice_company_website' => ['label' => __('Website', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6' ],
202 + // Both are required by EN 16931 and are what makes an intra-EU
203 + // reverse charge determinable. See Services\TaxTreatment.
204 + 'easy_invoice_company_vat_number' => ['label' => __('VAT number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Your VAT identifier, including the country prefix — for example DE123456789.', 'easy-invoice') ],
205 + 'easy_invoice_company_country' => ['label' => __('Country', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Two-letter country code, for example DE, FR or GB.', 'easy-invoice') ],
185 206 'easy_invoice_company_address' => ['label' => __('Address', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6' ],
186 207 'easy_invoice_tax_number' => ['label' => __('Tax ID / VAT Number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
187 208 'easy_invoice_company_logo' => ['label' => __('Company Logo', 'easy-invoice'), 'type' => 'image', 'default' => '', 'col_span' => 'sm:col-span-6' ],
188 209 ]
@@ -282,9 +303,9 @@
282 303 ],
283 304 'easy_invoice_quote_footer_text' => [
284 305 'label' => __('Footer Text', 'easy-invoice'),
285 306 'type' => 'wp_editor',
286 - 'default' => __('Thanks for choosing Easy Invoice', 'easy-invoice'),
307 + 'default' => __('Thank you for your business.', 'easy-invoice'),
287 308 'description' => __('You can modify your quote footer text from here. HTML tags supports: a, br, em, strong, hr, p, h1 to h4', 'easy-invoice'),
288 309 'col_span' => 'sm:col-span-6'
289 310 ],
290 311 'easy_invoice_quote_accept_button' => [
@@ -422,8 +443,15 @@
422 443 'easy_invoice_email_reply_to' => ['label' => __('Reply-To Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-3'],
423 444 'easy_invoice_email_reply_to_name' => ['label' => __('Reply-To Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3'],
424 445 'easy_invoice_enable_email_styling' => ['label' => __('Enable HTML Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
425 446 'easy_invoice_bcc_admin' => ['label' => __('BCC Admin on All Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6'],
447 + // Off by default: attaching a document changes what every
448 + // customer receives and makes messages much larger, which
449 + // some SMTP relays reject. See EmailManager::shouldAttachInvoicePdf().
450 + 'easy_invoice_attach_pdf_to_email' => ['label' => __('Attach a PDF copy to invoice and quote emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Generates the document on the server and attaches it. The PDF contains real, selectable text rather than an image.', 'easy-invoice')],
451 + 'easy_invoice_pdf_paper_size' => ['label' => __('PDF paper size', 'easy-invoice'), 'type' => 'select', 'default' => 'a4', 'options' => ['a4' => 'A4', 'letter' => 'Letter', 'legal' => 'Legal'], 'col_span' => 'sm:col-span-3'],
452 + 'easy_invoice_pdf_download_method' => ['label' => __('"Download as PDF" button', 'easy-invoice'), 'type' => 'select', 'default' => 'browser', 'options' => ['browser' => __('Exactly as shown on screen (rendered in the browser)', 'easy-invoice'), 'server' => __('Generated on the server (real text, smaller file)', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('The browser capture reproduces the page pixel for pixel — every design, custom template and watermark. The server file is selectable text and a fraction of the size. Emails, the API and e-invoicing always use the server.', 'easy-invoice')],
453 + 'easy_invoice_pdf_layout' => ['label' => __('Server-generated PDF layout', 'easy-invoice'), 'type' => 'select', 'default' => 'design', 'options' => ['design' => __('Match the selected invoice / quote design', 'easy-invoice'), 'plain' => __('Plain print layout', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('Applies to PDFs the server produces: email attachments, the API, e-invoicing, and the download button when set to "server". The design is the template chosen on the document; the plain layout is a neutral table-based page.', 'easy-invoice')],
426 454 'easy_invoice_admin_email' => ['label' => __('Admin Email for BCC', 'easy-invoice'), 'type' => 'email', 'default' => get_option('admin_email', ''), 'col_span' => 'sm:col-span-6'],
427 455 'easy_invoice_email_logo' => ['label' => __('Email Logo URL', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6'],
428 456 'easy_invoice_email_footer_text' => ['label' => __('Email Footer Text', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6'],
429 457 'easy_invoice_test_email' => [
@@ -440,41 +468,9 @@
440 468 'description' => __('Configure email sent when an invoice is available for the client', 'easy-invoice'),
441 469 'fields' => [
442 470 'easy_invoice_invoice_email_enabled' => ['label' => __('Enable Invoice Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
443 471 'easy_invoice_invoice_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
444 - 'easy_invoice_invoice_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => __('<h2>📄 Your Invoice is Ready</h2>
445 -
446 -<p>Dear {{client_name}},</p>
447 -
448 -<div class="highlight-box">
449 - <p><strong>Invoice #{{invoice_number}}</strong><br>
450 - <span class="amount-highlight">{{total_amount}}</span><br>
451 - Due Date: <strong>{{due_date}}</strong></p>
452 -</div>
453 -
454 -<p>Your invoice has been prepared and is ready for payment. You can view and download the complete invoice from the attachment.</p>
455 -
456 -<div class="info-box">
457 - <p><strong>📋 Payment Details:</strong><br>
458 - • Invoice Number: {{invoice_number}}<br>
459 - • Total Amount: {{total_amount}}<br>
460 - • Due Date: {{due_date}}<br>
461 - • Payment Terms: {{payment_terms}}</p>
462 -</div>
463 -
464 -<div class="warning-box">
465 - <p><strong>⚠️ Important:</strong> Please ensure payment is received by the due date to avoid any late fees or service interruptions.</p>
466 -</div>
467 -
468 -<p>If you have any questions about this invoice, please don\'t hesitate to contact us.</p>
469 -
470 -<div class="divider"></div>
471 -
472 -<p>Thank you for your business!</p>
473 -
474 -<p>Best regards,<br>
475 -<strong>{{company_name}}</strong><br>
476 -{{company_email}}</p>', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
472 + 'easy_invoice_invoice_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'), 'col_span' => 'sm:col-span-6'],
477 473 'easy_invoice_invoice_test_template' => [
478 474 'label' => __('Test Invoice Email', 'easy-invoice'),
479 475 'type' => 'test_template_email',
480 476 'default' => '',
@@ -489,47 +485,9 @@
489 485 'description' => __('Configure email sent when a quote is available for the client', 'easy-invoice'),
490 486 'fields' => [
491 487 'easy_invoice_quote_email_enabled' => ['label' => __('Enable Quote Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
492 488 'easy_invoice_quote_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
493 - 'easy_invoice_quote_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => __('<h2>📋 Your Quote is Ready</h2>
494 -
495 -<p>Dear {{client_name}},</p>
496 -
497 -<div class="highlight-box">
498 - <p><strong>Quote #{{quote_number}}</strong><br>
499 - <span class="amount-highlight">{{total_amount}}</span><br>
500 - Valid Until: <strong>{{expiry_date}}</strong></p>
501 -</div>
502 -
503 -<p>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.</p>
504 -
505 -<div class="info-box">
506 - <p><strong>📋 Quote Summary:</strong><br>
507 - • Quote Number: {{quote_number}}<br>
508 - • Total Amount: {{total_amount}}<br>
509 - • Valid Until: {{expiry_date}}<br>
510 - • Terms: {{payment_terms}}</p>
511 -</div>
512 -
513 -<div class="highlight-box">
514 - <p><strong>🔗 View Quote Online:</strong><br>
515 - <a href="{{quote_url}}" style="color: #3b82f6; text-decoration: underline;">{{quote_url}}</a></p>
516 - <p><strong>Shortcode:</strong> <code>[easy_quote_url number="{{quote_number}}" text="View Quote"]</code></p>
517 -</div>
518 -
519 -<div class="warning-box">
520 - <p><strong>⏰ Time Sensitive:</strong> This quote is valid until {{expiry_date}}. Please review and respond within this timeframe.</p>
521 -</div>
522 -
523 -<p>If you have any questions or would like to discuss any aspects of this quote, please contact us.</p>
524 -
525 -<div class="divider"></div>
526 -
527 -<p>We look forward to working with you!</p>
528 -
529 -<p>Best regards,<br>
530 -<strong>{{company_name}}</strong><br>
531 -{{company_email}}</p>', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
489 + 'easy_invoice_quote_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('quote'), 'col_span' => 'sm:col-span-6'],
532 490 'easy_invoice_quote_test_template' => [
533 491 'label' => __('Test Quote Email', 'easy-invoice'),
534 492 'type' => 'test_template_email',
535 493 'default' => '',
@@ -544,45 +502,9 @@
544 502 'description' => __('Configure email sent when a payment is received', 'easy-invoice'),
545 503 'fields' => [
546 504 'easy_invoice_payment_email_enabled' => ['label' => __('Enable Payment Received Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
547 505 'easy_invoice_payment_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
548 - 'easy_invoice_payment_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => __('<h2>✅ Payment Received - Thank You!</h2>
549 -
550 -<p>Dear {{client_name}},</p>
551 -
552 -<div class="success-box">
553 - <p><strong>Payment Confirmation</strong><br>
554 - Invoice #{{invoice_number}}<br>
555 - <span class="amount-highlight">{{payment_amount}}</span><br>
556 - Payment Date: <strong>{{payment_date}}</strong><br>
557 - Payment Method: <strong>{{payment_method}}</strong></p>
558 -</div>
559 -
560 -<p>We have successfully received your payment. Thank you for your prompt payment!</p>
561 -
562 -<div class="info-box">
563 - <p><strong>📊 Payment Details:</strong><br>
564 - • Invoice Number: {{invoice_number}}<br>
565 - • Amount Paid: {{payment_amount}}<br>
566 - • Payment Date: {{payment_date}}<br>
567 - • Payment Method: {{payment_method}}<br>
568 - • Transaction ID: {{transaction_id}}</p>
569 -</div>
570 -
571 -<div class="highlight-box">
572 - <p><strong>🎉 Status: PAID</strong><br>
573 - Your payment has been processed and your account is now up to date. We appreciate your business!</p>
574 -</div>
575 -
576 -<p>If you have any questions about this payment or need a receipt, please don\'t hesitate to contact us.</p>
577 -
578 -<div class="divider"></div>
579 -
580 -<p>Thank you for choosing our services!</p>
581 -
582 -<p>Best regards,<br>
583 -<strong>{{company_name}}</strong><br>
584 -{{company_email}}</p>', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
506 + 'easy_invoice_payment_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('payment'), 'col_span' => 'sm:col-span-6'],
585 507 'easy_invoice_payment_test_template' => [
586 508 'label' => __('Test Payment Email', 'easy-invoice'),
587 509 'type' => 'test_template_email',
588 510 'default' => '',
@@ -618,8 +540,9 @@
618 540 'easy_invoice_text_rate_price' => ['label' => __('Rate', 'easy-invoice'), 'type' => 'text', 'default' => __('Rate', 'easy-invoice'), 'col_span' => '', 'description' => __('Rate or price label', 'easy-invoice')],
619 541 'easy_invoice_text_adjust' => ['label' => __('Adjust', 'easy-invoice'), 'type' => 'text', 'default' => __('Adjust', 'easy-invoice'), 'col_span' => '', 'description' => __('Adjust label', 'easy-invoice')],
620 542 'easy_invoice_text_sub_total' => ['label' => __('Sub Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Sub Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Sub total label', 'easy-invoice')],
621 543 'easy_invoice_text_total' => ['label' => __('Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Total label', 'easy-invoice')],
544 + 'easy_invoice_text_line_total' => ['label' => __('Line total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Heading of the line-item amount column', 'easy-invoice')],
622 545 'easy_invoice_text_tax' => ['label' => __('Tax', 'easy-invoice'), 'type' => 'text', 'default' => __('Tax', 'easy-invoice'), 'col_span' => '', 'description' => __('Tax label', 'easy-invoice')],
623 546 'easy_invoice_text_discount' => ['label' => __('Discount', 'easy-invoice'), 'type' => 'text', 'default' => __('Discount', 'easy-invoice'), 'col_span' => '', 'description' => __('Discount label', 'easy-invoice')],
624 547 'easy_invoice_text_print' => ['label' => __('Print', 'easy-invoice'), 'type' => 'text', 'default' => __('Print', 'easy-invoice'), 'col_span' => '', 'description' => __('Print button text', 'easy-invoice')],
625 548 'easy_invoice_text_download_pdf' => ['label' => __('Download as PDF', 'easy-invoice'), 'type' => 'text', 'default' => __('Download as PDF', 'easy-invoice'), 'col_span' => '', 'description' => __('Download PDF button text', 'easy-invoice')],
@@ -654,8 +577,9 @@
654 577 ],
655 578
656 579 'easy_invoice_invoice_numbering' => ['label' => __('Auto-increment invoice numbers', 'easy-invoice'), 'type' => 'checkbox', 'description' => __('Automatically increment invoice numbers for new invoices', 'easy-invoice'), 'default' => 'yes', 'col_span' => 'sm:col-span-6' ],
657 580 'easy_invoice_payment_reminder_days' => ['label' => __('Payment Reminder Days', 'easy-invoice'), 'type' => 'number', 'default' => 3, 'col_span' => 'sm:col-span-3'],
581 + 'easy_invoice_remove_data_on_uninstall' => ['label' => __('Remove all data when the plugin is deleted', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Off, deleting the plugin from the Plugins screen keeps every invoice, quote, payment, client record and setting, so reinstalling picks up where you left off. On, all of it is removed — invoices are tax records, so leave this off unless you are certain.', 'easy-invoice')],
658 582 ]
659 583 ],
660 584 ];
661 585
@@ -897,17 +821,21 @@
897 821 *
898 822 * @return void
899 823 */
900 824 public function saveSettings() {
901 - // Verify nonce
902 - if (!wp_verify_nonce($_POST['easy_invoice_settings_nonce'], 'easy_invoice_settings')) {
903 - wp_die(__('Security check failed', 'easy-invoice'));
825 + // Verify nonce. $_POST['easy_invoice_settings_nonce'] was read unguarded, so a
826 + // request without it raised "Undefined array key" before the check could run.
827 + $nonce = isset($_POST['easy_invoice_settings_nonce'])
828 + ? sanitize_text_field(wp_unslash($_POST['easy_invoice_settings_nonce']))
829 + : '';
830 + if (!wp_verify_nonce($nonce, 'easy_invoice_settings')) {
831 + wp_die(esc_html__('Security check failed', 'easy-invoice'));
904 832 }
905 833
906 834
907 835 // Check permissions
908 836 if (!current_user_can('manage_options')) {
909 - wp_die(__('You do not have permission to perform this action', 'easy-invoice'));
837 + wp_die(esc_html__('You do not have permission to perform this action', 'easy-invoice'));
910 838 }
911 839
912 840 $posted_settings = $_POST['settings'] ?? [];
913 841
@@ -949,8 +877,30 @@
949 877 }
950 878 }
951 879 }
952 880
881 + // Fields other code adds to the Tax section for display only
882 + // (`easy_invoice_tax_section_additional_fields`) are saved with the
883 + // same rules — the reverse-charge switch lived only there and could
884 + // never be turned on from the screen.
885 + $extra_tax_fields = apply_filters('easy_invoice_tax_section_additional_fields', [], []);
886 + $known = [];
887 + foreach ($settings_config as $section_data) {
888 + foreach ((array) ($section_data['fields'] ?? []) as $k => $unused) { $known[$k] = true; }
889 + foreach ((array) ($section_data['subsections'] ?? []) as $sub) { foreach ((array) ($sub['fields'] ?? []) as $k => $unused) { $known[$k] = true; } }
890 + }
891 + foreach ((array) $extra_tax_fields as $option_key => $field_config) {
892 + if (!is_string($option_key) || isset($known[$option_key]) || !is_array($field_config)) {
893 + continue;
894 + }
895 + if (isset($posted_settings[$option_key])) {
896 + $this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
897 + } elseif (($field_config['type'] ?? '') === 'checkbox') {
898 + update_option($option_key, 'no');
899 + $response['saved_options'][$option_key] = 'no';
900 + }
901 + }
902 +
953 903 // Handle special sections like payment methods
954 904 if (isset($settings_config['payment']) && isset($settings_config['payment']['is_special_section'])) {
955 905 $this->savePaymentSettings($posted_settings, $response);
956 906 }
@@ -959,8 +909,9 @@
959 909 wp_cache_flush();
960 910
961 911 // Clear settings cache
962 912 $this->settings_cache = null;
913 + self::$shared_settings_cache = null;
963 914
964 915 // Log the save action
965 916 $this->log('Settings saved successfully by user: ' . get_current_user_id());
966 917
@@ -1073,8 +1024,11 @@
1073 1024 // Return cached settings if available
1074 1025 if ($use_cache && is_array($this->settings_cache)) {
1075 1026 return $this->settings_cache;
1076 1027 }
1028 + if ($use_cache && is_array(self::$shared_settings_cache)) {
1029 + return $this->settings_cache = self::$shared_settings_cache;
1030 + }
1077 1031
1078 1032 $settings = [];
1079 1033 $config = $this->get_settings_fields_config();
1080 1034
@@ -1081,8 +1035,15 @@
1081 1035 foreach ($config as $section_id => $section_data) {
1082 1036 if ($section_id === 'payment' && isset($section_data['gateways'])) {
1083 1037 // Handle gateway fields specifically
1084 1038 foreach ($section_data['gateways'] as $gateway_id => $gateway_config) {
1039 + // The per-gateway display name is saved by savePaymentSettings()
1040 + // but was never loaded back, so the field always showed the default.
1041 + $display_key = 'easy_invoice_gateway_display_name_' . $gateway_id;
1042 + $display_val = get_option($display_key, '');
1043 + if ('' !== $display_val) {
1044 + $settings[$display_key] = $display_val;
1045 + }
1085 1046 if (isset($gateway_config['fields']) && is_array($gateway_config['fields'])) {
1086 1047 foreach ($gateway_config['fields'] as $option_key => $field_data) {
1087 1048 $default_value = $field_data['default'] ?? '';
1088 1049 $settings[$option_key] = get_option($option_key, $default_value);
@@ -1144,8 +1105,9 @@
1144 1105 $settings['easy_invoice_payment_gateway_order'] = get_option('easy_invoice_payment_gateway_order', []);
1145 1106
1146 1107 // Cache settings
1147 1108 $this->settings_cache = $settings;
1109 + self::$shared_settings_cache = $settings;
1148 1110
1149 1111 // Allow third party to add/modify settings
1150 1112 return apply_filters('easy_invoice_settings', $settings);
1151 1113 }
@@ -1163,8 +1125,34 @@
1163 1125 /**
1164 1126 * Initialize default settings
1165 1127 */
1166 1128 public function initializeSettings() {
1129 + // One-time migration (2.1.22): strip the leftover
1130 + // "<strong>Shortcode:</strong> <code>[easy_(invoice|quote)_url …]</code>"
1131 + // reference line from saved email bodies. Originally intended as admin-side
1132 + // documentation, but it lived inside the email template and got emailed to
1133 + // clients verbatim. Guarded by a flag option so each install runs at most once.
1134 + //
1135 + // The regex tolerates the optional surrounding <p>...</p> because WP's
1136 + // wp_kses_post may normalise the wrapping when the option is saved.
1137 + if (!get_option('easy_invoice_email_shortcode_hint_removed_v2')) {
1138 + foreach (['easy_invoice_invoice_email_body', 'easy_invoice_quote_email_body'] as $opt_key) {
1139 + $body = get_option($opt_key, '');
1140 + if (!is_string($body) || $body === '') {
1141 + continue;
1142 + }
1143 + $cleaned = preg_replace(
1144 + '#\s*(?:<p>\s*)?<strong>\s*Shortcode\s*:\s*</strong>\s*<code>\s*\[easy_(?:invoice|quote)_url[^\]]*\]\s*</code>(?:\s*</p>)?\s*#i',
1145 + '',
1146 + $body
1147 + );
1148 + if ($cleaned !== null && $cleaned !== $body) {
1149 + update_option($opt_key, $cleaned);
1150 + }
1151 + }
1152 + update_option('easy_invoice_email_shortcode_hint_removed_v2', 1);
1153 + }
1154 +
1167 1155 // Initialize invoice number settings if not already set
1168 1156 if (!get_option('easy_invoice_invoice_prefix')) {
1169 1157 update_option('easy_invoice_invoice_prefix', 'INV-');
1170 1158 }
@@ -1258,8 +1246,9 @@
1258 1246 'easy_invoice_text_rate_price' => __('Rate', 'easy-invoice'),
1259 1247 'easy_invoice_text_adjust' => __('Adjust', 'easy-invoice'),
1260 1248 'easy_invoice_text_sub_total' => __('Sub Total', 'easy-invoice'),
1261 1249 'easy_invoice_text_total' => __('Total', 'easy-invoice'),
1250 + 'easy_invoice_text_line_total' => __('Total', 'easy-invoice'),
1262 1251 'easy_invoice_text_tax' => __('Tax', 'easy-invoice'),
1263 1252 'easy_invoice_text_discount' => __('Discount', 'easy-invoice'),
1264 1253 'easy_invoice_text_page' => __('Page', 'easy-invoice'),
1265 1254 'easy_invoice_text_print' => __('Print', 'easy-invoice'),
@@ -1299,47 +1288,9 @@
1299 1288 update_option('easy_invoice_invoice_email_subject', __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'));
1300 1289 }
1301 1290
1302 1291 if (!get_option('easy_invoice_invoice_email_body')) {
1303 - update_option('easy_invoice_invoice_email_body', __('<h2>📄 Your Invoice is Ready</h2>
1304 -
1305 -<p>Dear {{client_name}},</p>
1306 -
1307 -<div class="highlight-box">
1308 - <p><strong>Invoice #{{invoice_number}}</strong><br>
1309 - <span class="amount-highlight">{{total_amount}}</span><br>
1310 - Due Date: <strong>{{due_date}}</strong></p>
1311 -</div>
1312 -
1313 -<p>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.</p>
1314 -
1315 -<div class="info-box">
1316 - <p><strong>📋 Payment Details:</strong><br>
1317 - • Invoice Number: {{invoice_number}}<br>
1318 - • Total Amount: {{total_amount}}<br>
1319 - • Due Date: {{due_date}}<br>
1320 - • Payment Terms: {{payment_terms}}</p>
1321 -</div>
1322 -
1323 -<div class="highlight-box">
1324 - <p><strong>🔗 View Invoice Online:</strong><br>
1325 - <a href="{{invoice_url}}" style="color: #3b82f6; text-decoration: underline;">{{invoice_url}}</a></p>
1326 - <p><strong>Shortcode:</strong> <code>[easy_invoice_url number="{{invoice_number}}" text="View Invoice"]</code></p>
1327 -</div>
1328 -
1329 -<div class="warning-box">
1330 - <p><strong>⚠️ Important:</strong> Please ensure payment is received by the due date to avoid any late fees or service interruptions.</p>
1331 -</div>
1332 -
1333 -<p>If you have any questions about this invoice, please don\'t hesitate to contact us.</p>
1334 -
1335 -<div class="divider"></div>
1336 -
1337 -<p>Thank you for your business!</p>
1338 -
1339 -<p>Best regards,<br>
1340 -<strong>{{company_name}}</strong><br>
1341 -{{company_email}}</p>', 'easy-invoice'));
1292 + update_option('easy_invoice_invoice_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'));
1342 1293 }
1343 1294
1344 1295 if (!get_option('easy_invoice_quote_email_enabled')) {
1345 1296 update_option('easy_invoice_quote_email_enabled', 'yes');
@@ -1349,41 +1300,9 @@
1349 1300 update_option('easy_invoice_quote_email_subject', __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'));
1350 1301 }
1351 1302
1352 1303 if (!get_option('easy_invoice_quote_email_body')) {
1353 - update_option('easy_invoice_quote_email_body', __('<h2>Your Quote is Ready</h2>
1354 -
1355 -<p>Dear {{client_name}},</p>
1356 -
1357 -<div class="highlight-box">
1358 - <p><strong>Quote #{{quote_number}}</strong><br>
1359 - Total Amount: <strong>{{total_amount}}</strong><br>
1360 - Valid Until: <strong>{{expiry_date}}</strong></p>
1361 -</div>
1362 -
1363 -<p>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.</p>
1364 -
1365 -<div class="info-box">
1366 - <p><strong>Quote Summary:</strong><br>
1367 - • Quote Number: {{quote_number}}<br>
1368 - • Total Amount: {{total_amount}}<br>
1369 - • Valid Until: {{expiry_date}}<br>
1370 - • Terms: {{payment_terms}}</p>
1371 -</div>
1372 -
1373 -<div class="highlight-box">
1374 - <p><strong>🔗 View Quote Online:</strong><br>
1375 - <a href="{{quote_url}}" style="color: #3b82f6; text-decoration: underline;">{{quote_url}}</a></p>
1376 - <p><strong>Shortcode:</strong> <code>[easy_quote_url number="{{quote_number}}" text="View Quote"]</code></p>
1377 -</div>
1378 -
1379 -<p>This quote is valid until {{expiry_date}}. If you have any questions or would like to discuss any aspects of this quote, please contact us.</p>
1380 -
1381 -<p>We look forward to working with you!</p>
1382 -
1383 -<p>Best regards,<br>
1384 -<strong>{{company_name}}</strong><br>
1385 -{{company_email}}</p>', 'easy-invoice'));
1304 + update_option('easy_invoice_quote_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('quote'));
1386 1305 }
1387 1306
1388 1307 if (!get_option('easy_invoice_payment_email_enabled')) {
1389 1308 update_option('easy_invoice_payment_email_enabled', 'yes');
@@ -1393,45 +1312,9 @@
1393 1312 update_option('easy_invoice_payment_email_subject', __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'));
1394 1313 }
1395 1314
1396 1315 if (!get_option('easy_invoice_payment_email_body')) {
1397 - update_option('easy_invoice_payment_email_body', __('<h2>✅ Payment Received - Thank You!</h2>
1398 -
1399 -<p>Dear {{client_name}},</p>
1400 -
1401 -<div class="success-box">
1402 - <p><strong>Payment Confirmation</strong><br>
1403 - Invoice #{{invoice_number}}<br>
1404 - <span class="amount-highlight">{{payment_amount}}</span><br>
1405 - Payment Date: <strong>{{payment_date}}</strong><br>
1406 - Payment Method: <strong>{{payment_method}}</strong></p>
1407 -</div>
1408 -
1409 -<p>We have successfully received your payment. Thank you for your prompt payment!</p>
1410 -
1411 -<div class="info-box">
1412 - <p><strong>📊 Payment Details:</strong><br>
1413 - • Invoice Number: {{invoice_number}}<br>
1414 - • Amount Paid: {{payment_amount}}<br>
1415 - • Payment Date: {{payment_date}}<br>
1416 - • Payment Method: {{payment_method}}<br>
1417 - • Transaction ID: {{transaction_id}}</p>
1418 -</div>
1419 -
1420 -<div class="highlight-box">
1421 - <p><strong>🎉 Status: PAID</strong><br>
1422 - Your payment has been processed and your account is now up to date. We appreciate your business!</p>
1423 -</div>
1424 -
1425 -<p>If you have any questions about this payment or need a receipt, please don\'t hesitate to contact us.</p>
1426 -
1427 -<div class="divider"></div>
1428 -
1429 -<p>Thank you for choosing our services!</p>
1430 -
1431 -<p>Best regards,<br>
1432 -<strong>{{company_name}}</strong><br>
1433 -{{company_email}}</p>', 'easy-invoice'));
1316 + update_option('easy_invoice_payment_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('payment'));
1434 1317 }
1435 1318 }
1436 1319
1437 1320 /**
@@ -1485,9 +1368,9 @@
1485 1368 wp_send_json_error(['message' => __('Please provide a valid email address', 'easy-invoice')]);
1486 1369 }
1487 1370
1488 1371 // Get payment reminder settings
1489 - $subject = get_option('easy_invoice_pro_payment_reminder_subject', __('A friendly reminder - Invoice #{{invoice_number}}', 'easy-invoice-pro'));
1372 + $subject = get_option('easy_invoice_pro_payment_reminder_subject', __('A friendly reminder - Invoice #{{invoice_number}}', 'easy-invoice'));
1490 1373 $body = get_option('easy_invoice_pro_payment_reminder_body', '');
1491 1374
1492 1375 if (empty($body)) {
1493 1376 wp_send_json_error(['message' => __('Payment reminder email template is empty. Please configure the email message first.', 'easy-invoice')]);
@@ -1499,9 +1382,10 @@
1499 1382 'client_name' => 'Test Client',
1500 1383 'company_name' => get_option('easy_invoice_company_name', get_bloginfo('name') ?: 'Easy Invoice'),
1501 1384 'company_email' => get_option('easy_invoice_company_email', get_option('admin_email', '')),
1502 1385 'total_amount' => '$1,000.00',
1503 - 'due_date' => date('Y-m-d', strtotime('+7 days')),
1386 + 'amount_due' => '$1,000.00',
1387 + 'due_date' => wp_date('Y-m-d', strtotime('+7 days')),
1504 1388 ];
1505 1389
1506 1390 // Replace placeholders
1507 1391 foreach ($sample_data as $placeholder => $value) {
@@ -1581,16 +1465,36 @@
1581 1465 'quote_number' => 'TEST-Q-001',
1582 1466 'client_name' => 'Test Client',
1583 1467 'company_name' => get_option('easy_invoice_company_name', get_bloginfo('name') ?: 'Easy Invoice'),
1584 1468 'company_email' => get_option('easy_invoice_company_email', get_option('admin_email', '')),
1585 - 'total_amount' => '$1,000.00',
1586 - 'payment_amount' => '$1,000.00',
1587 - 'due_date' => date('Y-m-d', strtotime('+30 days')),
1588 - 'expiry_date' => date('Y-m-d', strtotime('+30 days')),
1589 - 'payment_date' => date('Y-m-d'),
1469 + 'total_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
1470 + 'payment_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
1471 + 'due_date' => wp_date('Y-m-d', strtotime('+30 days')),
1472 + 'expiry_date' => wp_date('Y-m-d', strtotime('+30 days')),
1473 + 'payment_date' => current_time('Y-m-d'),
1590 1474 'payment_method' => 'Credit Card',
1591 1475 'transaction_id' => 'TXN-' . uniqid(),
1592 1476 'payment_terms' => get_option('easy_invoice_payment_terms', __('Due on receipt', 'easy-invoice')),
1477 + // Everything the stock templates can mention, so a test never shows a raw tag.
1478 + 'amount_due' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
1479 + 'subtotal' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
1480 + 'tax_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(0) : '$0.00',
1481 + 'discount_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(0) : '$0.00',
1482 + 'issue_date' => current_time('Y-m-d'),
1483 + 'invoice_title' => __('Sample invoice', 'easy-invoice'),
1484 + 'quote_title' => __('Sample quote', 'easy-invoice'),
1485 + 'invoice_url' => home_url('/'),
1486 + 'quote_url' => home_url('/'),
1487 + 'payment_url' => home_url('/'),
1488 + 'site_url' => home_url('/'),
1489 + 'admin_url' => admin_url(),
1490 + 'client_email' => 'client@example.com',
1491 + 'client_first_name' => 'Test',
1492 + 'client_last_name' => 'Client',
1493 + 'client_address' => '1 Example Street',
1494 + 'company_phone' => get_option('easy_invoice_company_phone', ''),
1495 + 'company_address' => get_option('easy_invoice_company_address', ''),
1496 + 'company_website' => get_option('easy_invoice_company_website', ''),
1593 1497 ];
1594 1498
1595 1499 // Ensure all sample data values are strings
1596 1500 foreach ($sample_data as $key => $value) {
@@ -1700,9 +1604,9 @@
1700 1604 *
1701 1605 * @return string Quote footer text
1702 1606 */
1703 1607 public static function getQuoteFooterText(): string {
1704 - return get_option('easy_invoice_quote_footer_text', __('Thanks for choosing Easy Invoice', 'easy-invoice'));
1608 + return get_option('easy_invoice_quote_footer_text', __('Thank you for your business.', 'easy-invoice'));
1705 1609 }
1706 1610
1707 1611 /**
1708 1612 * Check if quote accept button should be shown
@@ -1783,41 +1687,9 @@
1783 1687 *
1784 1688 * @return string Invoice email body
1785 1689 */
1786 1690 public static function getInvoiceEmailBody(): string {
1787 - return get_option('easy_invoice_invoice_email_body', __('<h2>📄 Your Invoice is Ready</h2>
1788 -
1789 -<p>Dear {{client_name}},</p>
1790 -
1791 -<div class="highlight-box">
1792 - <p><strong>Invoice #{{invoice_number}}</strong><br>
1793 - <span class="amount-highlight">{{total_amount}}</span><br>
1794 - Due Date: <strong>{{due_date}}</strong></p>
1795 -</div>
1796 -
1797 -<p>Your invoice has been prepared and is ready for payment. You can view and download the complete invoice from the attachment.</p>
1798 -
1799 -<div class="info-box">
1800 - <p><strong>📋 Payment Details:</strong><br>
1801 - • Invoice Number: {{invoice_number}}<br>
1802 - • Total Amount: {{total_amount}}<br>
1803 - • Due Date: {{due_date}}<br>
1804 - • Payment Terms: {{payment_terms}}</p>
1805 -</div>
1806 -
1807 -<div class="warning-box">
1808 - <p><strong>⚠️ Important:</strong> Please ensure payment is received by the due date to avoid any late fees or service interruptions.</p>
1809 -</div>
1810 -
1811 -<p>If you have any questions about this invoice, please do not hesitate to contact us.</p>
1812 -
1813 -<div class="divider"></div>
1814 -
1815 -<p>Thank you for your business!</p>
1816 -
1817 -<p>Best regards,<br>
1818 -<strong>{{company_name}}</strong><br>
1819 -{{company_email}}</p>', 'easy-invoice'));
1691 + return get_option('easy_invoice_invoice_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'));
1820 1692 }
1821 1693
1822 1694 /**
1823 1695 * Check if quote available email is enabled
@@ -1842,41 +1714,9 @@
1842 1714 *
1843 1715 * @return string Quote email body
1844 1716 */
1845 1717 public static function getQuoteEmailBody(): string {
1846 - return get_option('easy_invoice_quote_email_body', __('<h2>📋 Your Quote is Ready</h2>
1847 -
1848 -<p>Dear {{client_name}},</p>
1849 -
1850 -<div class="highlight-box">
1851 - <p><strong>Quote #{{quote_number}}</strong><br>
1852 - <span class="amount-highlight">{{total_amount}}</span><br>
1853 - Valid Until: <strong>{{expiry_date}}</strong></p>
1854 -</div>
1855 -
1856 -<p>We have prepared a detailed quote for your project. You can view and download the complete quote from the attachment.</p>
1857 -
1858 -<div class="info-box">
1859 - <p><strong>📋 Quote Summary:</strong><br>
1860 - • Quote Number: {{quote_number}}<br>
1861 - • Total Amount: {{total_amount}}<br>
1862 - • Valid Until: {{expiry_date}}<br>
1863 - • Terms: {{payment_terms}}</p>
1864 -</div>
1865 -
1866 -<div class="warning-box">
1867 - <p><strong>⏰ Time Sensitive:</strong> This quote is valid until {{expiry_date}}. Please review and respond within this timeframe.</p>
1868 -</div>
1869 -
1870 -<p>If you have any questions or would like to discuss any aspects of this quote, please contact us.</p>
1871 -
1872 -<div class="divider"></div>
1873 -
1874 -<p>We look forward to working with you!</p>
1875 -
1876 -<p>Best regards,<br>
1877 -<strong>{{company_name}}</strong><br>
1878 -{{company_email}}</p>', 'easy-invoice'));
1718 + return get_option('easy_invoice_quote_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('quote'));
1879 1719 }
1880 1720
1881 1721 /**
1882 1722 * Check if payment received email is enabled
@@ -1901,45 +1741,9 @@
1901 1741 *
1902 1742 * @return string Payment email body
1903 1743 */
1904 1744 public static function getPaymentEmailBody(): string {
1905 - return get_option('easy_invoice_payment_email_body', __('<h2>✅ Payment Received - Thank You!</h2>
1906 -
1907 -<p>Dear {{client_name}},</p>
1908 -
1909 -<div class="success-box">
1910 - <p><strong>Payment Confirmation</strong><br>
1911 - Invoice #{{invoice_number}}<br>
1912 - <span class="amount-highlight">{{payment_amount}}</span><br>
1913 - Payment Date: <strong>{{payment_date}}</strong><br>
1914 - Payment Method: <strong>{{payment_method}}</strong></p>
1915 -</div>
1916 -
1917 -<p>We have successfully received your payment. Thank you for your prompt payment!</p>
1918 -
1919 -<div class="info-box">
1920 - <p><strong>📊 Payment Details:</strong><br>
1921 - • Invoice Number: {{invoice_number}}<br>
1922 - • Amount Paid: {{payment_amount}}<br>
1923 - • Payment Date: {{payment_date}}<br>
1924 - • Payment Method: {{payment_method}}<br>
1925 - • Transaction ID: {{transaction_id}}</p>
1926 -</div>
1927 -
1928 -<div class="highlight-box">
1929 - <p><strong>🎉 Status: PAID</strong><br>
1930 - Your payment has been processed and your account is now up to date. We appreciate your business!</p>
1931 -</div>
1932 -
1933 -<p>If you have any questions about this payment or need a receipt, please do not hesitate to contact us.</p>
1934 -
1935 -<div class="divider"></div>
1936 -
1937 -<p>Thank you for choosing our services!</p>
1938 -
1939 -<p>Best regards,<br>
1940 -<strong>{{company_name}}</strong><br>
1941 -{{company_email}}</p>', 'easy-invoice'));
1745 + return get_option('easy_invoice_payment_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('payment'));
1942 1746 }
1943 1747
1944 1748 // ========================================
1945 1749 // TEXT SETTINGS HELPER FUNCTIONS
@@ -1961,11 +1765,74 @@
1961 1765 * @param string $key Text setting key
1962 1766 * @param string $default Default text
1963 1767 * @return string Custom text or default
1964 1768 */
1769 + /**
1770 + * Every document label the site can override, with its saved value.
1771 + *
1772 + * The getText*() methods below are what templates call; this list exists
1773 + * for anything that needs the whole set at once -- the Pro Translations
1774 + * page offers each saved label for translation.
1775 + *
1776 + * @return array<string,array{label:string,default:string,value:string}> keyed by setting key.
1777 + */
1778 + public static function getTextLabels(): array {
1779 + $labels = [
1780 + 'invoice' => __('Invoice', 'easy-invoice'),
1781 + 'invoices' => __('Invoices', 'easy-invoice'),
1782 + 'to' => __('To', 'easy-invoice'),
1783 + 'from' => __('From', 'easy-invoice'),
1784 + 'invoice_number' => __('Invoice Number', 'easy-invoice'),
1785 + 'invoice_date' => __('Invoice Date', 'easy-invoice'),
1786 + 'due_date' => __('Due Date', 'easy-invoice'),
1787 + 'total_due' => __('Total Due', 'easy-invoice'),
1788 + 'qty' => __('Qty', 'easy-invoice'),
1789 + 'service' => __('Service', 'easy-invoice'),
1790 + 'rate_price' => __('Rate', 'easy-invoice'),
1791 + 'adjust' => __('Adjust', 'easy-invoice'),
1792 + 'sub_total' => __('Sub Total', 'easy-invoice'),
1793 + 'total' => __('Total', 'easy-invoice'),
1794 + 'tax' => __('Tax', 'easy-invoice'),
1795 + 'discount' => __('Discount', 'easy-invoice'),
1796 + 'print' => __('Print', 'easy-invoice'),
1797 + 'download_pdf' => __('Download as PDF', 'easy-invoice'),
1798 + 'send_email' => __('Send Email', 'easy-invoice'),
1799 + 'pay_now' => __('Pay Now', 'easy-invoice'),
1800 + 'quote' => __('Quote', 'easy-invoice'),
1801 + 'quote_number' => __('Quote Number', 'easy-invoice'),
1802 + 'quote_date' => __('Quote Date', 'easy-invoice'),
1803 + 'valid_until' => __('Valid Until Date', 'easy-invoice'),
1804 + 'accept_quote' => __('Accept Quote', 'easy-invoice'),
1805 + 'decline_quote' => __('Decline Quote', 'easy-invoice'),
1806 + 'decline_reason' => __('Reason for declining', 'easy-invoice'),
1807 + ];
1808 + $out = [];
1809 + foreach ($labels as $key => $default) {
1810 + $out[$key] = [
1811 + 'label' => ucfirst(str_replace('_', ' ', $key)),
1812 + 'default' => $default,
1813 + 'value' => (string) get_option('easy_invoice_text_' . $key, $default),
1814 + ];
1815 + }
1816 + return $out;
1817 + }
1818 +
1965 1819 public static function getTextSetting(string $key, string $default): string {
1966 1820 $option_key = 'easy_invoice_text_' . $key;
1967 - return get_option($option_key, $default);
1821 + $value = (string) get_option($option_key, $default);
1822 +
1823 + /**
1824 + * Filter a document label after the saved override is applied.
1825 + *
1826 + * Saved labels are literal text and skip gettext, so anything that
1827 + * renders a document in another language (Pro's Client Language) uses
1828 + * this to run them through the active locale.
1829 + *
1830 + * @param string $value Label to show.
1831 + * @param string $key Setting key, e.g. 'total'.
1832 + * @param string $default Translated default.
1833 + */
1834 + return (string) apply_filters('easy_invoice_text_setting', $value, $key, $default);
1968 1835 }
1969 1836
1970 1837 // Invoice Text Settings
1971 1838 public static function getTextInvoice(): string {
@@ -2019,8 +1886,13 @@
2019 1886 public static function getTextTotal(): string {
2020 1887 return self::getTextSetting('total', __('Total', 'easy-invoice'));
2021 1888 }
2022 1889
1890 + /** Heading of the line-item amount column, separate from the grand total. */
1891 + public static function getTextLineTotal(): string {
1892 + return self::getTextSetting('line_total', __('Total', 'easy-invoice'));
1893 + }
1894 +
2023 1895 public static function getTextTax(): string {
2024 1896 return self::getTextSetting('tax', __('Tax', 'easy-invoice'));
2025 1897 }
2026 1898
@@ -2177,8 +2049,9 @@
2177 2049 // Update the next invoice number counter to continue from the last regenerated number + 1
2178 2050 update_option('easy_invoice_next_invoice_number', $current_number);
2179 2051
2180 2052 wp_send_json_success([
2053 + /* translators: %d: number regenerated. */
2181 2054 'message' => sprintf(__('Successfully regenerated %d invoice numbers', 'easy-invoice'), $regenerated_count),
2182 2055 'regenerated_count' => $regenerated_count,
2183 2056 'next_number' => $current_number
2184 2057 ]);
@@ -2236,8 +2109,9 @@
2236 2109
2237 2110 update_option('easy_invoice_next_quote_number', $current_number);
2238 2111
2239 2112 wp_send_json_success([
2113 + /* translators: %d: number regenerated. */
2240 2114 'message' => sprintf(__('Successfully regenerated %d quote numbers', 'easy-invoice'), $regenerated_count),
2241 2115 'regenerated_count' => $regenerated_count,
2242 2116 'next_number' => $current_number
2243 2117 ]);