Passed
Push — master ( 3d88bf...f3309e )
by Brian
05:31
created

GetPaid_Invoice_Notification_Emails   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 438
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 44
eloc 146
c 2
b 0
f 0
dl 0
loc 438
rs 8.8798

20 Methods

Rating   Name   Duplication   Size   Complexity  
A init_email_type_hook() 0 19 4
A filter_email_recipients() 0 13 3
A get_invoice_merge_tags() 0 26 3
A init_hooks() 0 7 2
A invoice_merge_tags() 0 17 3
A send_email() 0 31 3
A __construct() 0 19 1
A force_send_overdue_notice() 0 3 1
A failed_invoice() 0 6 1
A cancelled_invoice() 0 6 1
A refunded_invoice() 0 6 1
A is_payment_form_invoice() 0 2 2
A user_note() 0 6 1
A completed_invoice() 0 11 3
A overdue() 0 34 5
A onhold_invoice() 0 6 1
A processing_invoice() 0 6 1
A get_date_query() 0 20 2
A user_invoice() 0 11 3
A new_invoice() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like GetPaid_Invoice_Notification_Emails often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GetPaid_Invoice_Notification_Emails, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Contains the invoice notification emails management class.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * This class handles invoice notificaiton emails.
11
 *
12
 */
13
class GetPaid_Invoice_Notification_Emails {
14
15
	/**
16
	 * The array of invoice email actions.
17
	 *
18
	 * @param array
19
	 */
20
	public $invoice_actions;
21
22
	/**
23
	 * Class constructor
24
	 *
25
	 */
26
	public function __construct() {
27
28
		$this->invoice_actions = apply_filters(
29
			'getpaid_notification_email_invoice_triggers',
30
			array(
31
				'getpaid_new_invoice'                   => array( 'new_invoice', 'user_invoice' ),
32
				'getpaid_invoice_status_wpi-cancelled'  => 'cancelled_invoice',
33
				'getpaid_invoice_status_wpi-failed'     => 'failed_invoice',
34
				'getpaid_invoice_status_wpi-onhold'     => 'onhold_invoice',
35
				'getpaid_invoice_status_wpi-processing' => 'processing_invoice',
36
				'getpaid_invoice_status_publish'        => 'completed_invoice',
37
				'getpaid_invoice_status_wpi-renewal'    => 'completed_invoice',
38
				'getpaid_invoice_status_wpi-refunded'   => 'refunded_invoice',
39
				'getpaid_new_customer_note'             => 'user_note',
40
				'getpaid_daily_maintenance'             => 'overdue',
41
			)
42
		);
43
44
		$this->init_hooks();
45
46
	}
47
48
	/**
49
	 * Registers email hooks.
50
	 */
51
	public function init_hooks() {
52
53
		add_filter( 'getpaid_get_email_merge_tags', array( $this, 'invoice_merge_tags' ), 10, 2 );
54
		add_filter( 'getpaid_invoice_email_recipients', array( $this, 'filter_email_recipients' ), 10, 2 );
55
56
		foreach ( $this->invoice_actions as $hook => $email_type ) {
57
			$this->init_email_type_hook( $hook, $email_type );
58
		}
59
	}
60
61
	/**
62
	 * Registers an email hook for an invoice action.
63
	 * 
64
	 * @param string $hook
65
	 * @param string|array $email_type
66
	 */
67
	public function init_email_type_hook( $hook, $email_type ) {
68
69
		$email_type = wpinv_parse_list( $email_type );
70
71
		foreach ( $email_type as $type ) {
72
73
			$email = new GetPaid_Notification_Email( $type );
74
75
			// Abort if it is not active.
76
			if ( ! $email->is_active() ) {
77
				continue;
78
			}
79
80
			if ( method_exists( $this, $type ) ) {
81
				add_action( $hook, array( $this, $type ), 100, 2 );
82
				continue;
83
			}
84
85
			do_action( 'getpaid_invoice_init_email_type_hook', $type, $hook );
86
		}
87
88
	}
89
90
	/**
91
	 * Filters invoice merge tags.
92
	 *
93
	 * @param array $merge_tags
94
	 * @param mixed|WPInv_Invoice|WPInv_Subscription $object
95
	 */
96
	public function invoice_merge_tags( $merge_tags, $object ) {
97
98
		if ( is_a( $object, 'WPInv_Invoice' ) ) {
99
			return array_merge(
100
				$merge_tags,
101
				$this->get_invoice_merge_tags( $object )
102
			);
103
		}
104
105
		if ( is_a( $object, 'WPInv_Subscription' ) ) {
106
			return array_merge(
107
				$merge_tags,
108
				$this->get_invoice_merge_tags( $object->get_parent_payment() )
109
			);
110
		}
111
112
		return $merge_tags;
113
114
	}
115
116
	/**
117
	 * Generates invoice merge tags.
118
	 *
119
	 * @param WPInv_Invoice $invoice
120
	 * @return array
121
	 */
122
	public function get_invoice_merge_tags( $invoice ) {
123
124
		// Abort if it does not exist.
125
		if ( ! $invoice->get_id() ) {
126
			return array();
127
		}
128
129
		return array(
130
			'{name}'                => sanitize_text_field( $invoice->get_user_full_name() ),
131
			'{full_name}'           => sanitize_text_field( $invoice->get_user_full_name() ),
132
			'{first_name}'          => sanitize_text_field( $invoice->get_first_name() ),
133
			'{last_name}'           => sanitize_text_field( $invoice->get_last_name() ),
134
			'{email}'               => sanitize_email( $invoice->get_email() ),
135
			'{invoice_number}'      => sanitize_text_field( $invoice->get_number() ),
136
			'{invoice_currency}'    => sanitize_text_field( $invoice->get_currency() ),
137
			'{invoice_total}'       => wpinv_price( wpinv_format_amount( $invoice->get_total() ) ),
0 ignored issues
show
Bug introduced by
wpinv_format_amount($invoice->get_total()) of type string is incompatible with the type double expected by parameter $amount of wpinv_price(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

137
			'{invoice_total}'       => wpinv_price( /** @scrutinizer ignore-type */ wpinv_format_amount( $invoice->get_total() ) ),
Loading history...
138
			'{invoice_link}'        => esc_url( $invoice->get_view_url() ),
139
			'{invoice_pay_link}'    => esc_url( $invoice->get_checkout_payment_url() ),
140
			'{invoice_receipt_link}'=> esc_url( $invoice->get_receipt_url() ),
141
			'{invoice_date}'        => getpaid_format_date_value( $invoice->get_date_created() ),
142
			'{invoice_due_date}'    => getpaid_format_date_value( $invoice->get_due_date(), __( 'on receipt', 'invoicing' ) ),
143
			'{invoice_quote}'       => sanitize_text_field( $invoice->get_type() ),
144
			'{invoice_label}'       => sanitize_text_field( ucfirst( $invoice->get_type() ) ),
145
			'{invoice_description}' => wp_kses_post( $invoice->get_description() ),
146
			'{subscription_name}'   => wp_kses_post( $invoice->get_subscription_name() ),
147
			'{is_was}'              => strtotime( $invoice->get_due_date() ) < current_time( 'timestamp' ) ? __( 'was', 'invoicing' ) : __( 'is', 'invoicing' ),
148
		);
149
150
	}
151
152
	/**
153
	 * Helper function to send an email.
154
	 *
155
	 * @param WPInv_Invoice $invoice
156
	 * @param GetPaid_Notification_Email $email
157
	 * @param string $type
158
	 * @param string|array $recipients
159
	 * @param array $extra_args Extra template args.
160
	 */
161
	public function send_email( $invoice, $email, $type, $recipients, $extra_args = array() ) {
162
163
		do_action( 'getpaid_before_send_invoice_notification', $type, $invoice, $email );
164
165
		$mailer     = new GetPaid_Notification_Email_Sender();
166
		$merge_tags = $email->get_merge_tags();
167
168
		$result = $mailer->send(
169
			apply_filters( 'getpaid_invoice_email_recipients', wpinv_parse_list( $recipients ), $email ),
170
			$email->add_merge_tags( $email->get_subject(), $merge_tags ),
171
			$email->get_content( $merge_tags, $extra_args ),
172
			$email->get_attachments()
173
		);
174
175
		// Maybe send a copy to the admin.
176
		if ( $email->include_admin_bcc() ) {
177
			$mailer->send(
178
				wpinv_get_admin_email(),
0 ignored issues
show
Bug introduced by
It seems like wpinv_get_admin_email() can also be of type false; however, parameter $to of GetPaid_Notification_Email_Sender::send() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
				/** @scrutinizer ignore-type */ wpinv_get_admin_email(),
Loading history...
179
				$email->add_merge_tags( $email->get_subject() . __( ' - ADMIN BCC COPY', 'invoicing' ), $merge_tags ),
180
				$email->get_content( $merge_tags ),
181
				$email->get_attachments()
182
			);
183
		}
184
185
		if ( ! $result ) {
186
			$invoice->add_note( sprintf( __( 'Failed sending %s notification email.', 'invoicing' ), sanitize_key( $type ) ), false, false, true );
187
		}
188
189
		do_action( 'getpaid_after_send_invoice_notification', $type, $invoice, $email );
190
191
		return $result;
192
	}
193
194
	/**
195
	 * Also send emails to any cc users.
196
	 *
197
	 * @param array $recipients
198
	 * @param GetPaid_Notification_Email $email
199
	 */
200
	public function filter_email_recipients( $recipients, $email ) {
201
202
		if ( ! $email->is_admin_email() ) {
203
			$cc = $email->object->get_email_cc();
0 ignored issues
show
Bug introduced by
The method get_email_cc() does not exist on WPInv_Item. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
			/** @scrutinizer ignore-call */ 
204
   $cc = $email->object->get_email_cc();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method get_email_cc() does not exist on WPInv_Subscription. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
			/** @scrutinizer ignore-call */ 
204
   $cc = $email->object->get_email_cc();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204
205
			if ( ! empty( $cc ) ) {
206
				$cc = array_map( 'sanitize_email', wpinv_parse_list( $cc ) );
207
				$recipients = array_filter( array_unique( array_merge( $recipients, $cc ) ) );
208
			}
209
210
		}
211
212
		return $recipients;
213
214
	}
215
216
	/**
217
	 * Sends a new invoice notification.
218
	 *
219
	 * @param WPInv_Invoice $invoice
220
	 */
221
	public function new_invoice( $invoice ) {
222
223
		// Only send this email for invoices created via the admin page.
224
		if ( ! $invoice->is_type( 'invoice' ) || $this->is_payment_form_invoice( $invoice->get_id() ) ) {
225
			return;
226
		}
227
228
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
229
		$recipient = wpinv_get_admin_email();
230
231
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
0 ignored issues
show
Bug introduced by
It seems like $recipient can also be of type false; however, parameter $recipients of GetPaid_Invoice_Notification_Emails::send_email() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
232
233
	}
234
235
	/**
236
	 * Sends a cancelled invoice notification.
237
	 *
238
	 * @param WPInv_Invoice $invoice
239
	 */
240
	public function cancelled_invoice( $invoice ) {
241
242
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
243
		$recipient = wpinv_get_admin_email();
244
245
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
0 ignored issues
show
Bug introduced by
It seems like $recipient can also be of type false; however, parameter $recipients of GetPaid_Invoice_Notification_Emails::send_email() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

245
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
246
247
	}
248
249
	/**
250
	 * Sends a failed invoice notification.
251
	 *
252
	 * @param WPInv_Invoice $invoice
253
	 */
254
	public function failed_invoice( $invoice ) {
255
256
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
257
		$recipient = wpinv_get_admin_email();
258
259
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
0 ignored issues
show
Bug introduced by
It seems like $recipient can also be of type false; however, parameter $recipients of GetPaid_Invoice_Notification_Emails::send_email() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
		return $this->send_email( $invoice, $email, __FUNCTION__, /** @scrutinizer ignore-type */ $recipient );
Loading history...
260
261
	}
262
263
	/**
264
	 * Sends a notification whenever an invoice is put on hold.
265
	 *
266
	 * @param WPInv_Invoice $invoice
267
	 */
268
	public function onhold_invoice( $invoice ) {
269
270
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
271
		$recipient = $invoice->get_email();
272
273
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
274
275
	}
276
277
	/**
278
	 * Sends a notification whenever an invoice is marked as processing payment.
279
	 *
280
	 * @param WPInv_Invoice $invoice
281
	 */
282
	public function processing_invoice( $invoice ) {
283
284
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
285
		$recipient = $invoice->get_email();
286
287
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
288
289
	}
290
291
	/**
292
	 * Sends a notification whenever an invoice is paid.
293
	 *
294
	 * @param WPInv_Invoice $invoice
295
	 */
296
	public function completed_invoice( $invoice ) {
297
298
		// (Maybe) abort if it is a renewal invoice.
299
		if ( $invoice->is_renewal() && ! wpinv_get_option( 'email_completed_invoice_renewal_active', false ) ) {
300
			return;
301
		}
302
303
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
304
		$recipient = $invoice->get_email();
305
306
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
307
308
	}
309
310
	/**
311
	 * Sends a notification whenever an invoice is refunded.
312
	 *
313
	 * @param WPInv_Invoice $invoice
314
	 */
315
	public function refunded_invoice( $invoice ) {
316
317
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
318
		$recipient = $invoice->get_email();
319
320
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
321
322
	}
323
324
	/**
325
	 * Notifies a user about new invoices
326
	 *
327
	 * @param WPInv_Invoice $invoice
328
	 */
329
	public function user_invoice( $invoice ) {
330
331
		// Only send this email for invoices created via the admin page.
332
		if ( ! $invoice->is_type( 'invoice' ) || $this->is_payment_form_invoice( $invoice->get_id() ) ) {
333
			return;
334
		}
335
336
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
337
		$recipient = $invoice->get_email();
338
339
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient );
340
341
	}
342
343
	/**
344
	 * Checks if an invoice is a payment form invoice.
345
	 *
346
	 * @param int $invoice
347
	 * @return bool
348
	 */
349
	public function is_payment_form_invoice( $invoice ) {
350
		return empty( $_GET['getpaid-admin-action'] ) && 'payment_form' == get_post_meta( $invoice, 'wpinv_created_via', true );
351
	}
352
353
	/**
354
	 * Notifies admin about new invoice notes
355
	 *
356
	 * @param WPInv_Invoice $invoice
357
	 * @param string $note
358
	 */
359
	public function user_note( $invoice, $note ) {
360
361
		$email     = new GetPaid_Notification_Email( __FUNCTION__, $invoice );
362
		$recipient = $invoice->get_email();
363
364
		return $this->send_email( $invoice, $email, __FUNCTION__, $recipient, array( 'customer_note' => $note ) );
365
366
	}
367
368
	/**
369
	 * (Force) Sends overdue notices.
370
	 *
371
	 * @param WPInv_Invoice $invoice
372
	 */
373
	public function force_send_overdue_notice( $invoice ) {
374
		$email = new GetPaid_Notification_Email( 'overdue', $invoice );
375
		return $this->send_email( $invoice, $email, 'overdue', $invoice->get_email() );
376
	}
377
378
	/**
379
	 * Sends overdue notices.
380
	 *
381
	 * @TODO: Create an invoices query class.
382
	 */
383
	public function overdue() {
384
		global $wpdb;
385
386
		$email = new GetPaid_Notification_Email( __FUNCTION__ );
387
388
		// Fetch reminder days.
389
		$reminder_days = array_unique( wp_parse_id_list( $email->get_option( 'days' ) ) );
390
391
		// Abort if non is set.
392
		if ( empty( $reminder_days ) ) {
393
			return;
394
		}
395
396
		// Retrieve date query.
397
		$date_query = $this->get_date_query( $reminder_days );
398
399
		// Invoices table.
400
		$table = $wpdb->prefix . 'getpaid_invoices';
401
402
		// Fetch invoices.
403
		$invoices  = $wpdb->get_col(
404
			"SELECT posts.ID FROM $wpdb->posts as posts
405
			LEFT JOIN $table as invoices ON invoices.post_id = posts.ID
406
			WHERE posts.post_type = 'wpi_invoice' AND posts.post_status = 'wpi-pending' $date_query");
407
408
		foreach ( $invoices as $invoice ) {
409
410
			// Only send this email for invoices created via the admin page.
411
			if ( ! $this->is_payment_form_invoice( $invoice ) ) {
412
				$invoice       = new WPInv_Invoice( $invoice );
413
				$email->object = $invoice;
414
415
				if ( $invoice->needs_payment() ) {
416
					$this->send_email( $invoice, $email, __FUNCTION__, $invoice->get_email() );
417
				}
418
419
			}
420
421
		}
422
423
	}
424
425
	/**
426
	 * Calculates the date query for an invoices query
427
	 *
428
	 * @param array $reminder_days
429
	 * @return string
430
	 */
431
	public function get_date_query( $reminder_days ) {
432
433
		$date_query = array(
434
			'relation'  => 'OR'
435
		);
436
437
		foreach ( $reminder_days as $days ) {
438
			$date = date_parse( date( 'Y-m-d', strtotime( "-$days days", current_time( 'timestamp' ) ) ) );
439
440
			$date_query[] = array(
441
				'year'  => $date['year'],
442
				'month' => $date['month'],
443
				'day'   => $date['day'],
444
			);
445
446
		}
447
448
		$date_query = new WP_Date_Query( $date_query, 'invoices.due_date' );
449
450
		return $date_query->get_sql();
451
452
	}
453
454
}
455