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 | easy-invoice.php +133 -10 2.2.02.4.0 View file →
@@ -2,9 +2,9 @@
2 2 /**
3 3 * Plugin Name: Easy Invoice
4 4 * Plugin URI: https://matrixaddons.com/plugins/easy-invoice
5 5 * Description: A beautiful, full-featured invoicing solution for WordPress. Create professional invoices, quotes, and manage payments with ease.
6 - * Version: 2.2.0
6 + * Version: 2.4.0
7 7 * Author: MatrixAddons
8 8 * Author URI: https://matrixaddons.com
9 9 * License: GPL v2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -9,12 +9,11 @@
9 9 * License: GPL v2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 11 * Text Domain: easy-invoice
12 12 * Domain Path: /languages
13 - * Requires at least: 5.6
14 - * Tested up to: 6.9
13 + * Requires at least: 6.0
14 + * Tested up to: 7.1
15 15 * Requires PHP: 7.4
16 - * Network: false
17 16 *
18 17 * @package EasyInvoice
19 18 */
20 19
@@ -24,9 +23,9 @@
24 23 }
25 24
26 25
27 26 // Define plugin constants.
28 -define( 'EASY_INVOICE_VERSION', '2.2.0' );
27 +define( 'EASY_INVOICE_VERSION', '2.4.0' );
29 28 define( 'EASY_INVOICE_PLUGIN_FILE', __FILE__ );
30 29 define( 'EASY_INVOICE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
31 30 define( 'EASY_INVOICE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
32 31
@@ -39,15 +38,33 @@
39 38 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/NotificationHelper.php';
40 39 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Admin/Helpers/Utility.php';
41 40 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/GeneralHelper.php';
42 41 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/ToastHelper.php';
42 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/CapabilityHelper.php';
43 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AuditHelper.php';
44 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AutoloadOptimizer.php';
45 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/BrandHelper.php';
43 46 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TaxHelper.php';
44 47 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/FormattingHelper.php';
45 48 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EmailHelper.php';
49 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/MoneyHelper.php';
46 50 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EnqueueHelper.php';
47 51 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/PdfHelper.php';
48 52 require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/QuoteInvoiceHelper.php';
53 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TemplateLocator.php';
49 54
55 +// Addons system — Free plugin owns the listing UI + license gate;
56 +// Pro plugin owns the executable addon code.
57 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/LicensePlanResolver.php';
58 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonRegistry.php';
59 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonManager.php';
60 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/ProAddonMigration.php';
61 +require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Controllers/AddonsController.php';
62 +
63 +// Register the Pro→addon migration. Only fires inside admin (admin_init
64 +// hook), only when Pro is active, only once per MIGRATION_VERSION bump.
65 +\EasyInvoice\Addons\ProAddonMigration::register();
66 +
50 67 /**
51 68 * Initialize the plugin.
52 69 *
53 70 * @since 1.0.0
@@ -79,12 +96,8 @@
79 96
80 97 // Initialize EmailManager
81 98 $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
82 99
83 - // Initialize form manager
84 - //$form_manager = \EasyInvoice\Forms\FormManager::getInstance();
85 - // $form_manager->setInvoiceNumberServiceProvider($invoice_number_service_provider);
86 -
87 100 // Initialize controllers.
88 101 $payment_controller = new \EasyInvoice\Controllers\PaymentController();
89 102 $payment_controller->init();
90 103
@@ -99,22 +112,129 @@
99 112
100 113 // Initialize review notice service
101 114 \EasyInvoice\Services\ReviewNoticeService::init();
102 115
116 + // One-time notice explaining that document links now require an access key.
117 + // Registered here rather than on activation because WordPress updates a plugin
118 + // by replacing files, without firing the activation hook — so an upgrading site
119 + // would otherwise get no warning that previously-emailed links stop working.
120 + \EasyInvoice\Services\DocumentAccessNoticeService::init();
121 + \EasyInvoice\Services\ProCompatibilityNotice::init();
122 + \EasyInvoice\Services\TaxTreatment::init();
123 + \EasyInvoice\Rest\RestController::init();
124 + \EasyInvoice\Services\PdfDownload::init();
125 + \EasyInvoice\Services\DocumentPage::init();
126 + \EasyInvoice\Services\DocumentViews::init();
127 + \EasyInvoice\Services\DocumentAttachments::init();
128 + \EasyInvoice\Services\InvoiceRetention::init();
129 + \EasyInvoice\Controllers\CreditNoteController::init();
130 + \EasyInvoice\Controllers\VatCheckController::init();
131 + \EasyInvoice\Controllers\StatementController::init();
132 + \EasyInvoice\Controllers\ImportController::init();
133 +
103 134 // Initialize promotion service
104 135 \EasyInvoice\Services\PromotionService::init();
105 136
137 + // Initialize Addons controller (admin page + AJAX toggle)
138 + (new \EasyInvoice\Controllers\AddonsController())->init();
139 +
106 140 do_action('easy_invoice_loaded');
107 141
108 142 }
109 143 add_action( 'init', 'easy_invoice_init' );
110 144
145 +// Any plugin option change invalidates the per-request settings cache the
146 +// money formatter reads (see SettingsController::getSettings()).
147 +foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
148 + add_action( $ei_opt_hook, static function ( $option ) {
149 + if ( is_string( $option ) && 0 === strpos( $option, 'easy_invoice_' ) ) {
150 + \EasyInvoice\Controllers\SettingsController::flushSharedCache();
151 + // The builder field set depends on settings (tax fields are only registered
152 + // while tax is on), so the models' cached definitions go with it.
153 + \EasyInvoice\Models\Invoice::flushFieldDefinitionsCache();
154 + \EasyInvoice\Models\Quote::flushFieldDefinitionsCache();
155 + }
156 + } );
157 +}
158 +unset( $ei_opt_hook );
159 +
160 +// Payment and credit-note writes invalidate the balance totals primed for
161 +// dashboard and report screens (see InvoiceBalance::prime()).
162 +add_action( 'save_post', static function ( $post_id, $post ) {
163 + if ( $post instanceof WP_Post && in_array( $post->post_type, [ 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice', 'easy_invoice_quote' ], true ) ) {
164 + \EasyInvoice\Services\InvoiceBalance::forget();
165 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
166 + }
167 +}, 10, 2 );
168 +add_action( 'updated_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
169 + if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
170 + \EasyInvoice\Services\InvoiceBalance::forget();
171 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
172 + }
173 +}, 10, 3 );
174 +add_action( 'added_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
175 + if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
176 + \EasyInvoice\Services\InvoiceBalance::forget();
177 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
178 + }
179 +}, 10, 3 );
180 +
181 +// Persisted invoice totals (InvoiceTotalsCache): written on save, backfilled by cron,
182 +// dropped when a total's inputs change.
183 +add_action( 'easy_invoice_totals_backfill', [ \EasyInvoice\Services\InvoiceTotalsCache::class, 'cronBackfill' ] );
184 +add_action( \EasyInvoice\Services\QuoteTotals::CRON_HOOK, [ \EasyInvoice\Services\QuoteTotals::class, 'cronBackfill' ] );
185 +foreach ( [ 'updated_post_meta', 'added_post_meta', 'deleted_post_meta' ] as $ei_meta_hook ) {
186 + add_action( $ei_meta_hook, static function ( $meta_id, $post_id, $meta_key ) {
187 + if ( in_array( $meta_key, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_META, true ) && 'easy_invoice' === get_post_type( $post_id ) ) {
188 + \EasyInvoice\Services\InvoiceTotalsCache::invalidate( (int) $post_id );
189 + }
190 + if ( in_array( $meta_key, \EasyInvoice\Services\QuoteTotals::DEPENDENT_META, true ) && 'easy_invoice_quote' === get_post_type( $post_id ) ) {
191 + \EasyInvoice\Services\QuoteTotals::invalidate( (int) $post_id );
192 + }
193 + }, 10, 3 );
194 +}
195 +unset( $ei_meta_hook );
196 +foreach ( [ 'trashed_post', 'untrashed_post', 'deleted_post', 'before_delete_post' ] as $ei_post_hook ) {
197 + add_action( $ei_post_hook, static function ( $post_id ) {
198 + if ( in_array( get_post_type( $post_id ), [ 'easy_invoice', 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice_quote' ], true ) ) {
199 + \EasyInvoice\Services\InvoiceBalance::forget();
200 + \EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
201 + }
202 + } );
203 +}
204 +unset( $ei_post_hook );
205 +foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
206 + add_action( $ei_opt_hook, static function ( $option ) {
207 + if ( in_array( $option, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_OPTIONS, true ) ) {
208 + \EasyInvoice\Services\InvoiceTotalsCache::invalidateAll();
209 + }
210 + } );
211 +}
212 +unset( $ei_opt_hook );
213 +add_action( 'easy_invoice_loaded', static function () {
214 + $want = \EasyInvoice\Services\InvoiceTotalsCache::CACHE_VERSION;
215 + if ( $want !== get_option( 'easy_invoice_totals_cache_version' ) ) {
216 + update_option( 'easy_invoice_totals_cache_version', $want, false );
217 + \EasyInvoice\Services\InvoiceTotalsCache::invalidateAll(); // drops stale totals and queues the backfill
218 + }
219 + if ( '1' !== get_option( 'easy_invoice_quote_totals_version' ) ) {
220 + update_option( 'easy_invoice_quote_totals_version', '1', false );
221 + \EasyInvoice\Services\QuoteTotals::scheduleBackfill();
222 + }
223 + // Versioned memo keys from the first 2.4.0 builds left one transient per write behind.
224 + if ( '1' !== get_option( 'easy_invoice_stats_memo_keys' ) ) {
225 + global $wpdb;
226 + $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_ei_stats_%' OR option_name LIKE '_transient_timeout_ei_stats_%'" );
227 + update_option( 'easy_invoice_stats_memo_keys', '1', false );
228 + }
229 +} );
230 +
111 231 /**
112 232 * Handle payment gateway logging
113 233 */
114 234 function easy_invoice_payment_gateway_log_handler($message, $level, $gateway_name) {
115 235 $log_message = sprintf('[%s] Easy Invoice %s Gateway: %s',
116 - date('Y-m-d H:i:s'),
236 + gmdate('Y-m-d H:i:s'),
117 237 ucfirst($gateway_name),
118 238 $message
119 239 );
120 240
@@ -202,8 +322,11 @@
202 322 flush_rewrite_rules();
203 323
204 324 // Clear scheduled events
205 325 \EasyInvoice\Services\QuoteExpirationService::clear_schedule();
326 + foreach ( [ 'easy_invoice_payment_reminder', 'easy_invoice_send_payment_reminders', 'easy_invoice_quote_expiration_check' ] as $hook ) {
327 + wp_clear_scheduled_hook( $hook );
328 + }
206 329 }
207 330
208 331 /**
209 332 * Toast notification helper functions