Completed
Pull Request — master (#11889)
by Mike
12:12
created

WC_Coupon::get_discount_amount()   C

Complexity

Conditions 13
Paths 72

Size

Total Lines 51
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 28
nc 72
nop 3
dl 0
loc 51
rs 5.6547
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A WC_Coupon::set_code() 0 3 1
A WC_Coupon::set_description() 0 3 1
A WC_Coupon::set_discount_type() 0 6 2
A WC_Coupon::set_amount() 0 3 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
include_once( WC_ABSPATH . 'includes/legacy/class-wc-legacy-coupon.php' );
7
8
/**
9
 * WooCommerce coupons.
10
 *
11
 * The WooCommerce coupons class gets coupon data from storage and checks coupon validity.
12
 *
13
 * @class 		WC_Coupon
14
 * @version		2.7.0
15
 * @package		WooCommerce/Classes
16
 * @category	Class
17
 * @author		WooThemes
18
 */
19
class WC_Coupon extends WC_Legacy_Coupon {
20
21
	/**
22
	 * Data array, with defaults.
23
	 * @since 2.7.0
24
	 * @var array
25
	 */
26
	protected $data = array(
27
		'code'                        => '',
28
		'amount'                      => 0,
29
		'date_created'                => '',
30
		'date_modified'               => '',
31
		'discount_type'               => 'fixed_cart',
32
		'description'                 => '',
33
		'date_expires'                => '',
34
		'usage_count'                 => 0,
35
		'individual_use'              => false,
36
		'product_ids'                 => array(),
37
		'excluded_product_ids'        => array(),
38
		'usage_limit'                 => 0,
39
		'usage_limit_per_user'        => 0,
40
		'limit_usage_to_x_items'      => 0,
41
		'free_shipping'               => false,
42
		'product_categories'          => array(),
43
		'excluded_product_categories' => array(),
44
		'exclude_sale_items'          => false,
45
		'minimum_amount'              => '',
46
		'maximum_amount'              => '',
47
		'email_restrictions'          => array(),
48
		'used_by'                     => '',
49
	);
50
51
	// Coupon message codes
52
	const E_WC_COUPON_INVALID_FILTERED               = 100;
53
	const E_WC_COUPON_INVALID_REMOVED                = 101;
54
	const E_WC_COUPON_NOT_YOURS_REMOVED              = 102;
55
	const E_WC_COUPON_ALREADY_APPLIED                = 103;
56
	const E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY = 104;
57
	const E_WC_COUPON_NOT_EXIST                      = 105;
58
	const E_WC_COUPON_USAGE_LIMIT_REACHED            = 106;
59
	const E_WC_COUPON_EXPIRED                        = 107;
60
	const E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET        = 108;
61
	const E_WC_COUPON_NOT_APPLICABLE                 = 109;
62
	const E_WC_COUPON_NOT_VALID_SALE_ITEMS           = 110;
63
	const E_WC_COUPON_PLEASE_ENTER                   = 111;
64
	const E_WC_COUPON_MAX_SPEND_LIMIT_MET 			 = 112;
65
	const E_WC_COUPON_EXCLUDED_PRODUCTS              = 113;
66
	const E_WC_COUPON_EXCLUDED_CATEGORIES            = 114;
67
	const WC_COUPON_SUCCESS                          = 200;
68
	const WC_COUPON_REMOVED                          = 201;
69
70
	/**
71
	 * Internal meta type used to store coupon data.
72
	 * @since 2.7.0
73
	 * @var string
74
	 */
75
	protected $meta_type = 'post';
76
77
	/**
78
	 * Data stored in meta keys, but not considered "meta" for a coupon.
79
	 * @since 2.7.0
80
	 * @var array
81
	 */
82
	protected $internal_meta_keys = array(
83
		'discount_type',
84
		'coupon_amount',
85
		'expiry_date',
86
		'usage_count',
87
		'individual_use',
88
		'product_ids',
89
		'exclude_product_ids',
90
		'usage_limit',
91
		'usage_limit_per_user',
92
		'limit_usage_to_x_items',
93
		'free_shipping',
94
		'product_categories',
95
		'exclude_product_categories',
96
		'exclude_sale_items',
97
		'minimum_amount',
98
		'maximum_amount',
99
		'customer_email',
100
		'_used_by',
101
		'_edit_lock',
102
		'_edit_last',
103
	);
104
105
	/**
106
	 * Coupon constructor. Loads coupon data.
107
	 * @param mixed $data Coupon data, object, ID or code.
108
	 */
109
	public function __construct( $data = '' ) {
110
		parent::__construct( $data );
111
112
		if ( $data instanceof WC_Coupon ) {
113
			$this->read( absint( $data->get_id() ) );
114
		} elseif ( $coupon = apply_filters( 'woocommerce_get_shop_coupon_data', false, $data ) ) {
115
			_doing_it_wrong( 'woocommerce_get_shop_coupon_data', 'Reading a manual coupon via woocommerce_get_shop_coupon_data has been deprecated. Please sent an instance of WC_Coupon instead.', '2.7' );
116
			$this->read_manual_coupon( $data, $coupon );
117
		} elseif ( is_numeric( $data ) && 'shop_coupon' === get_post_type( $data ) ) {
118
			$this->read( $data );
119
		} elseif ( ! empty( $data ) ) {
120
			$this->read( wc_get_coupon_id_by_code( $data ) );
121
			$this->set_code( $data );
122
		}
123
	}
124
125
	/**
126
	 * Checks the coupon type.
127
	 * @param  string $type Array or string of types
128
	 * @return bool
129
	 */
130
	public function is_type( $type ) {
131
		return ( $this->get_discount_type() == $type || ( is_array( $type ) && in_array( $this->get_discount_type(), $type ) ) );
132
	}
133
134
	/*
135
	|--------------------------------------------------------------------------
136
	| Getters
137
	|--------------------------------------------------------------------------
138
	|
139
	| Methods for getting data from the coupon object.
140
	|
141
	*/
142
143
	/**
144
	 * Get coupon code.
145
	 * @since  2.7.0
146
	 * @return string
147
	 */
148
	public function get_code() {
149
		return $this->data['code'];
150
	}
151
152
	/**
153
	 * Get coupon description.
154
	 * @since  2.7.0
155
	 * @return string
156
	 */
157
	public function get_description() {
158
		return $this->data['description'];
159
	}
160
161
	/**
162
	 * Get discount type.
163
	 * @since  2.7.0
164
	 * @return string
165
	 */
166
	public function get_discount_type() {
167
		return $this->data['discount_type'];
168
	}
169
170
	/**
171
	 * Get coupon code.
172
	 * @since  2.7.0
173
	 * @return float
174
	 */
175
	public function get_amount() {
176
		return wc_format_decimal( $this->data['amount'] );
177
	}
178
179
	/**
180
	 * Get coupon expiration date.
181
	 * @since  2.7.0
182
	 * @return int
183
	 */
184
	public function get_date_expires() {
185
		return $this->data['date_expires'];
186
	}
187
188
	/**
189
	 * Get date_created
190
	 * @since 2.7.0
191
	 * @return int
192
	 */
193
	public function get_date_created() {
194
		return $this->data['date_created'];
195
	}
196
197
	/**
198
	 * Get date_modified
199
	 * @since 2.7.0
200
	 * @return int
201
	 */
202
	public function get_date_modified() {
203
		return $this->data['date_modified'];
204
	}
205
206
	/**
207
	 * Get coupon usage count.
208
	 * @since  2.7.0
209
	 * @return integer
210
	 */
211
	public function get_usage_count() {
212
		return $this->data['usage_count'];
213
	}
214
215
	/**
216
	 * Get the "indvidual use" checkbox status.
217
	 * @since  2.7.0
218
	 * @return bool
219
	 */
220
	public function get_individual_use() {
221
		return $this->data['individual_use'];
222
	}
223
224
	/**
225
	 * Get product IDs this coupon can apply to.
226
	 * @since  2.7.0
227
	 * @return array
228
	 */
229
	public function get_product_ids() {
230
		return $this->data['product_ids'];
231
	}
232
233
	/**
234
	 * Get product IDs that this coupon should not apply to.
235
	 * @since  2.7.0
236
	 * @return array
237
	 */
238
	public function get_excluded_product_ids() {
239
		return $this->data['excluded_product_ids'];
240
	}
241
242
	/**
243
	 * Get coupon usage limit.
244
	 * @since  2.7.0
245
	 * @return integer
246
	 */
247
	public function get_usage_limit() {
248
		return $this->data['usage_limit'];
249
	}
250
251
	/**
252
	 * Get coupon usage limit per customer (for a single customer)
253
	 * @since  2.7.0
254
	 * @return integer
255
	 */
256
	public function get_usage_limit_per_user() {
257
		return $this->data['usage_limit_per_user'];
258
	}
259
260
	/**
261
	 * Usage limited to certain amount of items
262
	 * @since  2.7.0
263
	 * @return integer
264
	 */
265
	public function get_limit_usage_to_x_items() {
266
		return $this->data['limit_usage_to_x_items'];
267
	}
268
269
	/**
270
	 * If this coupon grants free shipping or not.
271
	 * @since  2.7.0
272
	 * @return bool
273
	 */
274
	public function get_free_shipping() {
275
		return $this->data['free_shipping'];
276
	}
277
278
	/**
279
	 * Get product categories this coupon can apply to.
280
	 * @since  2.7.0
281
	 * @return array
282
	 */
283
	public function get_product_categories() {
284
		return $this->data['product_categories'];
285
	}
286
287
	/**
288
	 * Get product categories this coupon cannot not apply to.
289
	 * @since  2.7.0
290
	 * @return array
291
	 */
292
	public function get_excluded_product_categories() {
293
		return $this->data['excluded_product_categories'];
294
	}
295
296
	/**
297
	 * If this coupon should exclude items on sale.
298
	 * @since  2.7.0
299
	 * @return bool
300
	 */
301
	public function get_exclude_sale_items() {
302
		return $this->data['exclude_sale_items'];
303
	}
304
305
	/**
306
	 * Get minium spend amount.
307
	 * @since  2.7.0
308
	 * @return float
309
	 */
310
	public function get_minimum_amount() {
311
		return wc_format_decimal( $this->data['minimum_amount'] );
312
	}
313
	/**
314
	 * Get maximum spend amount.
315
	 * @since  2.7.0
316
	 * @return float
317
	 */
318
	public function get_maximum_amount() {
319
		return wc_format_decimal( $this->data['maximum_amount'] );
320
	}
321
322
	/**
323
	 * Get emails to check customer usage restrictions.
324
	 * @since  2.7.0
325
	 * @return array
326
	 */
327
	public function get_email_restrictions() {
328
		return $this->data['email_restrictions'];
329
	}
330
331
	/**
332
	 * Get records of all users who have used the current coupon.
333
	 *
334
	 * @return array
335
	 */
336
	public function get_used_by() {
337
		return $this->data['used_by'];
338
	}
339
340
	/*
341
	|--------------------------------------------------------------------------
342
	| Setters
343
	|--------------------------------------------------------------------------
344
	|
345
	| Functions for setting coupon data. These should not update anything in the
346
	| database itself and should only change what is stored in the class
347
	| object.
348
	|
349
	*/
350
351
	/**
352
	 * Set coupon code.
353
	 * @since  2.7.0
354
	 * @param  string $code
355
	 * @throws WC_Data_Exception
356
	 */
357
	public function set_code( $code ) {
358
		$this->data['code'] = wc_format_coupon_code( $code );
359
	}
360
361
	/**
362
	 * Set coupon description.
363
	 * @since  2.7.0
364
	 * @param  string $description
365
	 * @throws WC_Data_Exception
366
	 */
367
	public function set_description( $description ) {
368
		$this->data['description'] = $description;
369
	}
370
371
	/**
372
	 * Set discount type.
373
	 * @since  2.7.0
374
	 * @param  string $discount_type
375
	 * @throws WC_Data_Exception
376
	 */
377
	public function set_discount_type( $discount_type ) {
378
		if ( ! in_array( $discount_type, array_keys( wc_get_coupon_types() ) ) ) {
379
			$this->error( 'coupon_invalid_discount_type', __( 'Invalid discount type', 'woocommerce' ) );
380
		}
381
		$this->data['discount_type'] = $discount_type;
382
	}
383
384
	/**
385
	 * Set amount.
386
	 * @since  2.7.0
387
	 * @param  float $amount
388
	 * @throws WC_Data_Exception
389
	 */
390
	public function set_amount( $amount ) {
391
		$this->data['amount'] = wc_format_decimal( $amount );
392
	}
393
394
	/**
395
	 * Set expiration date.
396
	 * @since  2.7.0
397
	 * @param string $timestamp Timestamp
398
	 * @throws WC_Data_Exception
399
	 */
400
	public function set_date_expires( $timestamp ) {
401
		$this->data['date_expires'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
402
	}
403
404
	/**
405
	 * Set date_created
406
	 * @since  2.7.0
407
	 * @param string $timestamp Timestamp
408
	 * @throws WC_Data_Exception
409
	 */
410
	public function set_date_created( $timestamp ) {
411
		$this->data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
412
	}
413
414
	/**
415
	 * Set date_modified
416
	 * @since  2.7.0
417
	 * @param string $timestamp
418
	 * @throws WC_Data_Exception
419
	 */
420
	public function set_date_modified( $timestamp ) {
421
		$this->data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
422
	}
423
424
	/**
425
	 * Set how many times this coupon has been used.
426
	 * @since  2.7.0
427
	 * @param  int $usage_count
428
	 * @throws WC_Data_Exception
429
	 */
430
	public function set_usage_count( $usage_count ) {
431
		$this->data['usage_count'] = absint( $usage_count );
432
	}
433
434
	/**
435
	 * Set if this coupon can only be used once.
436
	 * @since  2.7.0
437
	 * @param  bool $is_individual_use
438
	 * @throws WC_Data_Exception
439
	 */
440
	public function set_individual_use( $is_individual_use ) {
441
		$this->data['individual_use'] = (bool) $is_individual_use;
442
	}
443
444
	/**
445
	 * Set the product IDs this coupon can be used with.
446
	 * @since  2.7.0
447
	 * @param  array $product_ids
448
	 * @throws WC_Data_Exception
449
	 */
450
	public function set_product_ids( $product_ids ) {
451
		$this->data['product_ids'] = (array) $product_ids;
452
	}
453
454
	/**
455
	 * Set the product IDs this coupon cannot be used with.
456
	 * @since  2.7.0
457
	 * @param  array $excluded_product_ids
458
	 * @throws WC_Data_Exception
459
	 */
460
	public function set_excluded_product_ids( $excluded_product_ids ) {
461
		$this->data['excluded_product_ids'] = (array) $excluded_product_ids;
462
	}
463
464
	/**
465
	 * Set the amount of times this coupon can be used.
466
	 * @since  2.7.0
467
	 * @param  int $usage_limit
468
	 * @throws WC_Data_Exception
469
	 */
470
	public function set_usage_limit( $usage_limit ) {
471
		$this->data['usage_limit'] = absint( $usage_limit );
472
	}
473
474
	/**
475
	 * Set the amount of times this coupon can be used per user.
476
	 * @since  2.7.0
477
	 * @param  int $usage_limit
478
	 * @throws WC_Data_Exception
479
	 */
480
	public function set_usage_limit_per_user( $usage_limit ) {
481
		$this->data['usage_limit_per_user'] = absint( $usage_limit );
482
	}
483
484
	/**
485
	 * Set usage limit to x number of items.
486
	 * @since  2.7.0
487
	 * @param  int $limit_usage_to_x_items
488
	 * @throws WC_Data_Exception
489
	 */
490
	public function set_limit_usage_to_x_items( $limit_usage_to_x_items ) {
491
		$this->data['limit_usage_to_x_items'] = $limit_usage_to_x_items;
492
	}
493
494
	/**
495
	 * Set if this coupon enables free shipping or not.
496
	 * @since  2.7.0
497
	 * @param  bool $free_shipping
498
	 * @throws WC_Data_Exception
499
	 */
500
	public function set_free_shipping( $free_shipping ) {
501
		$this->data['free_shipping'] = (bool) $free_shipping;
502
	}
503
504
	/**
505
	 * Set the product category IDs this coupon can be used with.
506
	 * @since  2.7.0
507
	 * @param  array $product_categories
508
	 * @throws WC_Data_Exception
509
	 */
510
	public function set_product_categories( $product_categories ) {
511
		$this->data['product_categories'] = (array) $product_categories;
512
	}
513
514
	/**
515
	 * Set the product category IDs this coupon cannot be used with.
516
	 * @since  2.7.0
517
	 * @param  array $excluded_product_categories
518
	 * @throws WC_Data_Exception
519
	 */
520
	public function set_excluded_product_categories( $excluded_product_categories ) {
521
		$this->data['excluded_product_categories'] = (array) $excluded_product_categories;
522
	}
523
524
	/**
525
	 * Set if this coupon should excluded sale items or not.
526
	 * @since  2.7.0
527
	 * @param  bool $exclude_sale_items
528
	 * @throws WC_Data_Exception
529
	 */
530
	public function set_exclude_sale_items( $exclude_sale_items ) {
531
		$this->data['exclude_sale_items'] = (bool) $exclude_sale_items;
532
	}
533
534
	/**
535
	 * Set the minimum spend amount.
536
	 * @since  2.7.0
537
	 * @param  float $amount
538
	 * @throws WC_Data_Exception
539
	 */
540
	public function set_minimum_amount( $amount ) {
541
		$this->data['minimum_amount'] = wc_format_decimal( $amount );
542
	}
543
544
	/**
545
	 * Set the maximum spend amount.
546
	 * @since  2.7.0
547
	 * @param  float $amount
548
	 * @throws WC_Data_Exception
549
	 */
550
	public function set_maximum_amount( $amount ) {
551
		$this->data['maximum_amount'] = wc_format_decimal( $amount );
552
	}
553
554
	/**
555
	 * Set email restrictions.
556
	 * @since  2.7.0
557
	 * @param  array $emails
558
	 * @throws WC_Data_Exception
559
	 */
560
	public function set_email_restrictions( $emails = array() ) {
561
		$emails = array_filter( array_map( 'sanitize_email', (array) $emails ) );
562
		foreach ( $emails as $email ) {
563
			if ( ! is_email( $email ) ) {
564
				$this->error( 'coupon_invalid_email_address', __( 'Invalid email address restriction', 'woocommerce' ) );
565
			}
566
		}
567
		$this->data['email_restrictions'] = $emails;
568
	}
569
570
	/**
571
	 * Set which users have used this coupon.
572
	 * @since 2.7.0
573
	 * @param array $used_by
574
	 * @throws WC_Data_Exception
575
	 */
576
	public function set_used_by( $used_by ) {
577
		$this->data['used_by'] = array_filter( $used_by );
578
	}
579
580
	/*
581
	|--------------------------------------------------------------------------
582
	| CRUD methods
583
	|--------------------------------------------------------------------------
584
	|
585
	| Methods which create, read, update and delete coupons from the database.
586
	|
587
	| A save method is included for convenience (chooses update or create based
588
	| on if the order exists yet).
589
	|
590
	*/
591
592
	/**
593
	 * Reads an coupon from the database and sets its data to the class.
594
	 * @since 2.7.0
595
	 * @param  int $coupon_id
596
	 */
597
	public function read( $coupon_id ) {
598
		$this->set_defaults();
599
600
		if ( ! $coupon_id || ! ( $post_object = get_post( $coupon_id ) ) ) {
601
			return;
602
		}
603
604
		$this->set_id( $coupon_id );
605
		$this->set_props( array(
606
			'code'                        => $post_object->post_title,
607
			'description'                 => $post_object->post_excerpt,
608
			'date_created'                => $post_object->post_date,
609
			'date_modified'               => $post_object->post_modified,
610
			'date_expires'                => get_post_meta( $coupon_id, 'expiry_date', true ),
611
			'discount_type'               => get_post_meta( $coupon_id, 'discount_type', true ),
612
			'amount'                      => get_post_meta( $coupon_id, 'coupon_amount', true ),
613
			'usage_count'                 => get_post_meta( $coupon_id, 'usage_count', true ),
614
			'individual_use'              => 'yes' === get_post_meta( $coupon_id, 'individual_use', true ),
615
			'product_ids'                 => array_filter( (array) explode( ',', get_post_meta( $coupon_id, 'product_ids', true ) ) ),
616
			'excluded_product_ids'        => array_filter( (array) explode( ',', get_post_meta( $coupon_id, 'exclude_product_ids', true ) ) ),
617
			'usage_limit'                 => get_post_meta( $coupon_id, 'usage_limit', true ),
618
			'usage_limit_per_user'        => get_post_meta( $coupon_id, 'usage_limit_per_user', true ),
619
			'limit_usage_to_x_items'      => get_post_meta( $coupon_id, 'limit_usage_to_x_items', true ),
620
			'free_shipping'               => 'yes' === get_post_meta( $coupon_id, 'free_shipping', true ),
621
			'product_categories'          => array_filter( (array) get_post_meta( $coupon_id, 'product_categories', true ) ),
622
			'excluded_product_categories' => array_filter( (array) get_post_meta( $coupon_id, 'exclude_product_categories', true ) ),
623
			'exclude_sale_items'          => 'yes' === get_post_meta( $coupon_id, 'exclude_sale_items', true ),
624
			'minimum_amount'              => get_post_meta( $coupon_id, 'minimum_amount', true ),
625
			'maximum_amount'              => get_post_meta( $coupon_id, 'maximum_amount', true ),
626
			'email_restrictions'          => array_filter( (array) get_post_meta( $coupon_id, 'customer_email', true ) ),
627
			'used_by'                     => array_filter( (array) get_post_meta( $coupon_id, '_used_by' ) ),
628
		) );
629
		$this->read_meta_data();
630
631
		do_action( 'woocommerce_coupon_loaded', $this );
632
	}
633
634
	/**
635
	 * Create a new coupon.
636
	 * @since 2.7.0
637
	 */
638
	public function create() {
639
		$this->set_date_created( current_time( 'timestamp' ) );
640
641
		$coupon_id = wp_insert_post( apply_filters( 'woocommerce_new_coupon_data', array(
642
			'post_type'     => 'shop_coupon',
643
			'post_status'   => 'publish',
644
			'post_author'   => get_current_user_id(),
645
			'post_title'    => $this->get_code(),
646
			'post_content'  => '',
647
			'post_excerpt'  => $this->get_description(),
648
			'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
649
			'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
650
		) ), true );
651
652
		if ( $coupon_id ) {
653
			$this->set_id( $coupon_id );
654
			$this->update_post_meta( $coupon_id );
655
			$this->save_meta_data();
656
			do_action( 'woocommerce_new_coupon', $coupon_id );
657
		}
658
	}
659
660
	/**
661
	 * Updates an existing coupon.
662
	 * @since 2.7.0
663
	 */
664
	public function update() {
665
		$coupon_id = $this->get_id();
666
667
		$post_data = array(
668
			'ID'           => $coupon_id,
669
			'post_title'   => $this->get_code(),
670
			'post_excerpt' => $this->get_description(),
671
		);
672
673
		wp_update_post( $post_data );
674
		$this->update_post_meta( $coupon_id );
675
		$this->save_meta_data();
676
		do_action( 'woocommerce_update_coupon', $coupon_id );
677
	}
678
679
	/**
680
	 * Save data (either create or update depending on if we are working on an existing coupon)
681
	 * @since 2.7.0
682
	 */
683
	public function save() {
684
		if ( $this->get_id() ) {
685
			$this->update();
686
		} else {
687
			$this->create();
688
		}
689
	}
690
691
	/**
692
	 * Delete coupon from the database.
693
	 * @since 2.7.0
694
	 */
695
	public function delete() {
696
		wp_delete_post( $this->get_id() );
697
		do_action( 'woocommerce_delete_coupon', $this->get_id() );
698
		$this->set_id( 0 );
699
	}
700
701
	/**
702
	 * Helper method that updates all the post meta for a coupon based on it's settings in the WC_Coupon class.
703
	 * @since 2.7.0
704
	 * @param int $coupon_id
705
	 */
706
	private function update_post_meta( $coupon_id ) {
707
		update_post_meta( $coupon_id, 'discount_type', $this->get_discount_type() );
708
		update_post_meta( $coupon_id, 'coupon_amount', $this->get_amount() );
709
		update_post_meta( $coupon_id, 'individual_use', ( true === $this->get_individual_use() ) ? 'yes' : 'no' );
710
		update_post_meta( $coupon_id, 'product_ids', implode( ',', array_filter( array_map( 'intval', $this->get_product_ids() ) ) ) );
711
		update_post_meta( $coupon_id, 'exclude_product_ids', implode( ',', array_filter( array_map( 'intval', $this->get_excluded_product_ids() ) ) ) );
712
		update_post_meta( $coupon_id, 'usage_limit', $this->get_usage_limit() );
713
		update_post_meta( $coupon_id, 'usage_limit_per_user', $this->get_usage_limit_per_user() );
714
		update_post_meta( $coupon_id, 'limit_usage_to_x_items', $this->get_limit_usage_to_x_items() );
715
		update_post_meta( $coupon_id, 'usage_count', $this->get_usage_count() );
716
		update_post_meta( $coupon_id, 'expiry_date', $this->get_date_expires() );
717
		update_post_meta( $coupon_id, 'free_shipping', ( true === $this->get_free_shipping() ) ? 'yes' : 'no' );
718
		update_post_meta( $coupon_id, 'product_categories', array_filter( array_map( 'intval', $this->get_product_categories() ) ) );
719
		update_post_meta( $coupon_id, 'exclude_product_categories', array_filter( array_map( 'intval', $this->get_excluded_product_categories() ) ) );
720
		update_post_meta( $coupon_id, 'exclude_sale_items', ( true === $this->get_exclude_sale_items() ) ? 'yes' : 'no' );
721
		update_post_meta( $coupon_id, 'minimum_amount', $this->get_minimum_amount() );
722
		update_post_meta( $coupon_id, 'maximum_amount', $this->get_maximum_amount() );
723
		update_post_meta( $coupon_id, 'customer_email', array_filter( array_map( 'sanitize_email', $this->get_email_restrictions() ) ) );
724
	}
725
726
	/**
727
	 * Developers can programically return coupons. This function will read those values into our WC_Coupon class.
728
	 * @since  2.7.0
729
	 * @param  string $code  Coupon code
730
	 * @param  array $coupon Array of coupon properties
731
	 */
732
	public function read_manual_coupon( $code, $coupon ) {
733
		foreach ( $coupon as $key => $value ) {
734
			switch ( $key ) {
735
				case 'excluded_product_ids' :
736
				case 'exclude_product_ids' :
737
					if ( ! is_array( $coupon[ $key ] ) ) {
738
						_doing_it_wrong( $key, $key . ' should be an array instead of a string.', '2.7' );
739
						$coupon['excluded_product_ids'] = wc_string_to_array( $value );
740
					}
741
					break;
742
				case 'exclude_product_categories' :
743 View Code Duplication
				case 'excluded_product_categories' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
744
					if ( ! is_array( $coupon[ $key ] ) ) {
745
						_doing_it_wrong( $key, $key . ' should be an array instead of a string.', '2.7' );
746
						$coupon['excluded_product_categories'] = wc_string_to_array( $value );
747
					}
748
					break;
749 View Code Duplication
				case 'product_ids' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
750
					if ( ! is_array( $coupon[ $key ] ) ) {
751
						_doing_it_wrong( $key, $key . ' should be an array instead of a string.', '2.7' );
752
						$coupon[ $key ] = wc_string_to_array( $value );
753
					}
754
					break;
755
				case 'individual_use' :
756
				case 'free_shipping' :
757
				case 'exclude_sale_items' :
758
					if ( ! is_bool( $coupon[ $key ] ) ) {
759
						_doing_it_wrong( $key, $key . ' should be true or false instead of yes or no.', '2.7' );
760
						$coupon[ $key ] = wc_string_to_bool( $value );
761
					}
762
					break;
763
				case 'expiry_date' :
764
					$coupon['date_expires'] = $value;
765
					break;
766
			}
767
		}
768
		$this->set_code( $code );
769
		$this->set_props( $coupon );
770
	}
771
772
	/*
773
    |--------------------------------------------------------------------------
774
    | Other Actions
775
    |--------------------------------------------------------------------------
776
    */
777
778
	/**
779
	 * Increase usage count for current coupon.
780
	 *
781
	 * @param string $used_by Either user ID or billing email
782
	 */
783
	public function inc_usage_count( $used_by = '' ) {
784
		if ( $this->get_id() ) {
785
			$this->data['usage_count']++;
786
			update_post_meta( $this->get_id(), 'usage_count', $this->get_usage_count() );
787
			if ( $used_by ) {
788
				add_post_meta( $this->get_id(), '_used_by', strtolower( $used_by ) );
789
				$this->set_used_by( (array) get_post_meta( $this->get_id(), '_used_by' ) );
790
			}
791
		}
792
	}
793
794
	/**
795
	 * Decrease usage count for current coupon.
796
	 *
797
	 * @param string $used_by Either user ID or billing email
798
	 */
799
	public function dcr_usage_count( $used_by = '' ) {
800
		if ( $this->get_id() && $this->get_usage_count() > 0 ) {
801
			global $wpdb;
802
			$this->data['usage_count']--;
803
			update_post_meta( $this->get_id(), 'usage_count', $this->get_usage_count() );
804
			if ( $used_by ) {
805
				/**
806
				 * We're doing this the long way because `delete_post_meta( $id, $key, $value )` deletes.
807
				 * all instances where the key and value match, and we only want to delete one.
808
				 */
809
				$meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;", $used_by, $this->get_id() ) );
810
				if ( $meta_id ) {
811
					delete_metadata_by_mid( 'post', $meta_id );
812
					$this->set_used_by( (array) get_post_meta( $this->get_id(), '_used_by' ) );
813
				}
814
			}
815
		}
816
	}
817
818
	/*
819
    |--------------------------------------------------------------------------
820
    | Validation & Error Handling
821
    |--------------------------------------------------------------------------
822
    */
823
824
	/**
825
	 * Returns the error_message string.
826
	 *
827
	 * @access public
828
	 * @return string
829
	 */
830
	public function get_error_message() {
831
		return $this->error_message;
832
	}
833
834
	/**
835
	 * Ensure coupon exists or throw exception.
836
	 *
837
	 * @throws Exception
838
	 */
839
	private function validate_exists() {
840
		if ( ! $this->get_id() ) {
841
			throw new Exception( self::E_WC_COUPON_NOT_EXIST );
842
		}
843
	}
844
845
	/**
846
	 * Ensure coupon usage limit is valid or throw exception.
847
	 *
848
	 * @throws Exception
849
	 */
850
	private function validate_usage_limit() {
851
		if ( $this->get_usage_limit() > 0 && $this->get_usage_count() >= $this->get_usage_limit() ) {
852
			throw new Exception( self::E_WC_COUPON_USAGE_LIMIT_REACHED );
853
		}
854
	}
855
856
	/**
857
	 * Ensure coupon user usage limit is valid or throw exception.
858
	 *
859
	 * Per user usage limit - check here if user is logged in (against user IDs).
860
	 * Checked again for emails later on in WC_Cart::check_customer_coupons().
861
	 *
862
	 * @param  int  $user_id
863
	 * @throws Exception
864
	 */
865
	private function validate_user_usage_limit( $user_id = 0 ) {
866
		if ( empty( $user_id ) ) {
867
			$user_id = get_current_user_id();
868
		}
869
		if ( $this->get_usage_limit_per_user() > 0 && is_user_logged_in() && $this->get_id() ) {
870
			global $wpdb;
871
			$usage_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;", $this->get_id(), $user_id ) );
872
873
			if ( $usage_count >= $this->get_usage_limit_per_user() ) {
874
				throw new Exception( self::E_WC_COUPON_USAGE_LIMIT_REACHED );
875
			}
876
		}
877
	}
878
879
	/**
880
	 * Ensure coupon date is valid or throw exception.
881
	 *
882
	 * @throws Exception
883
	 */
884
	private function validate_expiry_date() {
885
		if ( $this->get_date_expires() && current_time( 'timestamp' ) > $this->get_date_expires() ) {
886
			throw new Exception( $error_code = self::E_WC_COUPON_EXPIRED );
887
		}
888
	}
889
890
	/**
891
	 * Ensure coupon amount is valid or throw exception.
892
	 *
893
	 * @throws Exception
894
	 */
895
	private function validate_minimum_amount() {
896 View Code Duplication
		if ( $this->get_minimum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_minimum_amount', $this->get_minimum_amount() > WC()->cart->get_displayed_subtotal(), $this ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Deprecated Code introduced by
The method WC_Legacy_Cart::get_displayed_subtotal() has been deprecated with message: 2.7.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
897
			throw new Exception( self::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET );
898
		}
899
	}
900
901
	/**
902
	 * Ensure coupon amount is valid or throw exception.
903
	 *
904
	 * @throws Exception
905
	 */
906
	private function validate_maximum_amount() {
907 View Code Duplication
		if ( $this->get_maximum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_maximum_amount', $this->get_maximum_amount() < WC()->cart->get_displayed_subtotal(), $this ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Deprecated Code introduced by
The method WC_Legacy_Cart::get_displayed_subtotal() has been deprecated with message: 2.7.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
908
			throw new Exception( self::E_WC_COUPON_MAX_SPEND_LIMIT_MET );
909
		}
910
	}
911
912
	/**
913
	 * Ensure coupon is valid for products in the cart is valid or throw exception.
914
	 *
915
	 * @throws Exception
916
	 */
917 View Code Duplication
	private function validate_product_ids() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
918
		if ( sizeof( $this->get_product_ids() ) > 0 ) {
919
			$valid_for_cart = false;
920
			if ( ! WC()->cart->is_empty() ) {
921
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
922
					if ( in_array( $cart_item['product_id'], $this->get_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_product_ids() ) ) {
923
						$valid_for_cart = true;
924
					}
925
				}
926
			}
927
			if ( ! $valid_for_cart ) {
928
				throw new Exception( self::E_WC_COUPON_NOT_APPLICABLE );
929
			}
930
		}
931
	}
932
933
	/**
934
	 * Ensure coupon is valid for product categories in the cart is valid or throw exception.
935
	 *
936
	 * @throws Exception
937
	 */
938 View Code Duplication
	private function validate_product_categories() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
939
		if ( sizeof( $this->get_product_categories() ) > 0 ) {
940
			$valid_for_cart = false;
941
			if ( ! WC()->cart->is_empty() ) {
942
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
943
					$product_cats = wc_get_product_cat_ids( $cart_item['product_id'] );
944
945
					// If we find an item with a cat in our allowed cat list, the coupon is valid
946
					if ( sizeof( array_intersect( $product_cats, $this->get_product_categories() ) ) > 0 ) {
947
						$valid_for_cart = true;
948
					}
949
				}
950
			}
951
			if ( ! $valid_for_cart ) {
952
				throw new Exception( self::E_WC_COUPON_NOT_APPLICABLE );
953
			}
954
		}
955
	}
956
957
	/**
958
	 * Ensure coupon is valid for sale items in the cart is valid or throw exception.
959
	 *
960
	 * @throws Exception
961
	 */
962 View Code Duplication
	private function validate_sale_items() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
963
		if ( $this->get_exclude_sale_items() && $this->is_type( wc_get_product_coupon_types() ) ) {
0 ignored issues
show
Documentation introduced by
wc_get_product_coupon_types() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
964
			$valid_for_cart      = false;
965
			$product_ids_on_sale = wc_get_product_ids_on_sale();
966
967
			if ( ! WC()->cart->is_empty() ) {
968
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
969
					if ( ! empty( $cart_item['variation_id'] ) ) {
970
						if ( ! in_array( $cart_item['variation_id'], $product_ids_on_sale, true ) ) {
971
							$valid_for_cart = true;
972
						}
973
					} elseif ( ! in_array( $cart_item['product_id'], $product_ids_on_sale, true ) ) {
974
						$valid_for_cart = true;
975
					}
976
				}
977
			}
978
			if ( ! $valid_for_cart ) {
979
				throw new Exception( self::E_WC_COUPON_NOT_VALID_SALE_ITEMS );
980
			}
981
		}
982
	}
983
984
	/**
985
	 * All exclusion rules must pass at the same time for a product coupon to be valid.
986
	 */
987
	private function validate_excluded_items() {
988
		if ( ! WC()->cart->is_empty() && $this->is_type( wc_get_product_coupon_types() ) ) {
0 ignored issues
show
Documentation introduced by
wc_get_product_coupon_types() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
989
			$valid = false;
990
991
			foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
992
				if ( $this->is_valid_for_product( $cart_item['data'], $cart_item ) ) {
0 ignored issues
show
Documentation introduced by
$cart_item is of type object<WC_Item_Product>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
993
					$valid = true;
994
					break;
995
				}
996
			}
997
998
			if ( ! $valid ) {
999
				throw new Exception( self::E_WC_COUPON_NOT_APPLICABLE );
1000
			}
1001
		}
1002
	}
1003
1004
	/**
1005
	 * Cart discounts cannot be added if non-eligble product is found in cart.
1006
	 */
1007
	private function validate_cart_excluded_items() {
1008
		if ( ! $this->is_type( wc_get_product_coupon_types() ) ) {
0 ignored issues
show
Documentation introduced by
wc_get_product_coupon_types() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1009
			$this->validate_cart_excluded_product_ids();
1010
			$this->validate_cart_excluded_product_categories();
1011
			$this->validate_cart_excluded_sale_items();
1012
		}
1013
	}
1014
1015
	/**
1016
	 * Exclude products from cart.
1017
	 *
1018
	 * @throws Exception
1019
	 */
1020 View Code Duplication
	private function validate_cart_excluded_product_ids() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1021
		// Exclude Products
1022
		if ( sizeof( $this->get_excluded_product_ids() ) > 0 ) {
1023
			$valid_for_cart = true;
1024
			if ( ! WC()->cart->is_empty() ) {
1025
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1026
					if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_excluded_product_ids() ) ) {
1027
						$valid_for_cart = false;
1028
					}
1029
				}
1030
			}
1031
			if ( ! $valid_for_cart ) {
1032
				throw new Exception( self::E_WC_COUPON_EXCLUDED_PRODUCTS );
1033
			}
1034
		}
1035
	}
1036
1037
	/**
1038
	 * Exclude categories from cart.
1039
	 *
1040
	 * @throws Exception
1041
	 */
1042 View Code Duplication
	private function validate_cart_excluded_product_categories() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1043
		if ( sizeof( $this->get_excluded_product_categories() ) > 0 ) {
1044
			$valid_for_cart = true;
1045
			if ( ! WC()->cart->is_empty() ) {
1046
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1047
					$product_cats = wc_get_product_cat_ids( $cart_item['product_id'] );
1048
					if ( sizeof( array_intersect( $product_cats, $this->get_excluded_product_categories() ) ) > 0 ) {
1049
						$valid_for_cart = false;
1050
					}
1051
				}
1052
			}
1053
			if ( ! $valid_for_cart ) {
1054
				throw new Exception( self::E_WC_COUPON_EXCLUDED_CATEGORIES );
1055
			}
1056
		}
1057
	}
1058
1059
	/**
1060
	 * Exclude sale items from cart.
1061
	 *
1062
	 * @throws Exception
1063
	 */
1064 View Code Duplication
	private function validate_cart_excluded_sale_items() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1065
		if ( $this->get_exclude_sale_items() ) {
1066
			$valid_for_cart = true;
1067
			$product_ids_on_sale = wc_get_product_ids_on_sale();
1068
			if ( ! WC()->cart->is_empty() ) {
1069
				foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1070
					if ( ! empty( $cart_item['variation_id'] ) ) {
1071
						if ( in_array( $cart_item['variation_id'], $product_ids_on_sale, true ) ) {
1072
							$valid_for_cart = false;
1073
						}
1074
					} elseif ( in_array( $cart_item['product_id'], $product_ids_on_sale, true ) ) {
1075
						$valid_for_cart = false;
1076
					}
1077
				}
1078
			}
1079
			if ( ! $valid_for_cart ) {
1080
				throw new Exception( self::E_WC_COUPON_NOT_VALID_SALE_ITEMS );
1081
			}
1082
		}
1083
	}
1084
1085
	/**
1086
	 * Check if a coupon is valid.
1087
	 *
1088
	 * @return boolean validity
1089
	 * @throws Exception
1090
	 */
1091
	public function is_valid() {
1092
		try {
1093
			$this->validate_exists();
1094
			$this->validate_usage_limit();
1095
			$this->validate_user_usage_limit();
1096
			$this->validate_expiry_date();
1097
			$this->validate_minimum_amount();
1098
			$this->validate_maximum_amount();
1099
			$this->validate_product_ids();
1100
			$this->validate_product_categories();
1101
			$this->validate_sale_items();
1102
			$this->validate_excluded_items();
1103
			$this->validate_cart_excluded_items();
1104
1105
			if ( ! apply_filters( 'woocommerce_coupon_is_valid', true, $this ) ) {
1106
				throw new Exception( self::E_WC_COUPON_INVALID_FILTERED );
1107
			}
1108
		} catch ( Exception $e ) {
1109
			$this->error_message = $this->get_coupon_error( $e->getMessage() );
1110
			return false;
1111
		}
1112
1113
		return true;
1114
	}
1115
1116
	/**
1117
	 * Check if a coupon is valid.
1118
	 *
1119
	 * @return bool
1120
	 */
1121
	public function is_valid_for_cart() {
1122
		return apply_filters( 'woocommerce_coupon_is_valid_for_cart', $this->is_type( wc_get_cart_coupon_types() ), $this );
0 ignored issues
show
Documentation introduced by
wc_get_cart_coupon_types() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1123
	}
1124
1125
	/**
1126
	 * Check if a coupon is valid for a product.
1127
	 *
1128
	 * @param  WC_Product  $product
1129
	 * @return boolean
1130
	 */
1131
	public function is_valid_for_product( $product, $values = array() ) {
1132
		if ( ! $this->is_type( wc_get_product_coupon_types() ) ) {
0 ignored issues
show
Documentation introduced by
wc_get_product_coupon_types() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1133
			return apply_filters( 'woocommerce_coupon_is_valid_for_product', false, $product, $this, $values );
1134
		}
1135
1136
		$valid        = false;
1137
		$product_cats = wc_get_product_cat_ids( $product->id );
1138
		$product_ids  = array( $product->id, ( isset( $product->variation_id ) ? $product->variation_id : 0 ), $product->get_parent() );
1139
1140
		// Specific products get the discount
1141
		if ( sizeof( $this->get_product_ids() ) && sizeof( array_intersect( $product_ids, $this->get_product_ids() ) ) ) {
1142
			$valid = true;
1143
		}
1144
1145
		// Category discounts
1146
		if ( sizeof( $this->get_product_categories() ) && sizeof( array_intersect( $product_cats, $this->get_product_categories() ) ) ) {
1147
			$valid = true;
1148
		}
1149
1150
		// No product ids - all items discounted
1151
		if ( ! sizeof( $this->get_product_ids() ) && ! sizeof( $this->get_product_categories() ) ) {
1152
			$valid = true;
1153
		}
1154
1155
		// Specific product ID's excluded from the discount
1156
		if ( sizeof( $this->get_excluded_product_ids() ) && sizeof( array_intersect( $product_ids, $this->get_excluded_product_ids() ) ) ) {
1157
			$valid = false;
1158
		}
1159
1160
		// Specific categories excluded from the discount
1161
		if ( sizeof( $this->get_excluded_product_categories() ) && sizeof( array_intersect( $product_cats, $this->get_excluded_product_categories() ) ) ) {
1162
			$valid = false;
1163
		}
1164
1165
		// Sale Items excluded from discount
1166
		if ( $this->get_exclude_sale_items() ) {
1167
			$product_ids_on_sale = wc_get_product_ids_on_sale();
1168
1169
			if ( isset( $product->variation_id ) ) {
1170
				if ( in_array( $product->variation_id, $product_ids_on_sale, true ) ) {
1171
					$valid = false;
1172
				}
1173
			} elseif ( in_array( $product->id, $product_ids_on_sale, true ) ) {
1174
				$valid = false;
1175
			}
1176
		}
1177
1178
		return apply_filters( 'woocommerce_coupon_is_valid_for_product', $valid, $product, $this, $values );
1179
	}
1180
1181
	/**
1182
	 * Converts one of the WC_Coupon message/error codes to a message string and.
1183
	 * displays the message/error.
1184
	 *
1185
	 * @param int $msg_code Message/error code.
1186
	 */
1187
	public function add_coupon_message( $msg_code ) {
1188
		$msg = $msg_code < 200 ? $this->get_coupon_error( $msg_code ) : $this->get_coupon_message( $msg_code );
1189
1190
		if ( ! $msg ) {
1191
			return;
1192
		}
1193
1194
		if ( $msg_code < 200 ) {
1195
			wc_add_notice( $msg, 'error' );
1196
		} else {
1197
			wc_add_notice( $msg );
1198
		}
1199
	}
1200
1201
	/**
1202
	 * Map one of the WC_Coupon message codes to a message string.
1203
	 *
1204
	 * @param integer $msg_code
1205
	 * @return string| Message/error string
1206
	 */
1207 View Code Duplication
	public function get_coupon_message( $msg_code ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1208
		switch ( $msg_code ) {
1209
			case self::WC_COUPON_SUCCESS :
1210
				$msg = __( 'Coupon code applied successfully.', 'woocommerce' );
1211
			break;
1212
			case self::WC_COUPON_REMOVED :
1213
				$msg = __( 'Coupon code removed successfully.', 'woocommerce' );
1214
			break;
1215
			default:
1216
				$msg = '';
1217
			break;
1218
		}
1219
		return apply_filters( 'woocommerce_coupon_message', $msg, $msg_code, $this );
1220
	}
1221
1222
	/**
1223
	 * Map one of the WC_Coupon error codes to a message string.
1224
	 *
1225
	 * @param int $err_code Message/error code.
1226
	 * @return string| Message/error string
1227
	 */
1228
	public function get_coupon_error( $err_code ) {
1229
		switch ( $err_code ) {
1230
			case self::E_WC_COUPON_INVALID_FILTERED:
1231
				$err = __( 'Coupon is not valid.', 'woocommerce' );
1232
			break;
1233
			case self::E_WC_COUPON_NOT_EXIST:
1234
				$err = sprintf( __( 'Coupon "%s" does not exist!', 'woocommerce' ), $this->get_code() );
1235
			break;
1236
			case self::E_WC_COUPON_INVALID_REMOVED:
1237
				$err = sprintf( __( 'Sorry, it seems the coupon "%s" is invalid - it has now been removed from your order.', 'woocommerce' ), $this->get_code() );
1238
			break;
1239
			case self::E_WC_COUPON_NOT_YOURS_REMOVED:
1240
				$err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours - it has now been removed from your order.', 'woocommerce' ), $this->get_code() );
1241
			break;
1242
			case self::E_WC_COUPON_ALREADY_APPLIED:
1243
				$err = __( 'Coupon code already applied!', 'woocommerce' );
1244
			break;
1245
			case self::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY:
1246
				$err = sprintf( __( 'Sorry, coupon "%s" has already been applied and cannot be used in conjunction with other coupons.', 'woocommerce' ), $this->get_code() );
1247
			break;
1248
			case self::E_WC_COUPON_USAGE_LIMIT_REACHED:
1249
				$err = __( 'Coupon usage limit has been reached.', 'woocommerce' );
1250
			break;
1251
			case self::E_WC_COUPON_EXPIRED:
1252
				$err = __( 'This coupon has expired.', 'woocommerce' );
1253
			break;
1254
			case self::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET:
1255
				$err = sprintf( __( 'The minimum spend for this coupon is %s.', 'woocommerce' ), wc_price( $this->get_minimum_amount() ) );
0 ignored issues
show
Documentation introduced by
$this->get_minimum_amount() is of type string|array, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1256
			break;
1257
			case self::E_WC_COUPON_MAX_SPEND_LIMIT_MET:
1258
				$err = sprintf( __( 'The maximum spend for this coupon is %s.', 'woocommerce' ), wc_price( $this->get_maximum_amount() ) );
0 ignored issues
show
Documentation introduced by
$this->get_maximum_amount() is of type string|array, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1259
			break;
1260
			case self::E_WC_COUPON_NOT_APPLICABLE:
1261
				$err = __( 'Sorry, this coupon is not applicable to your cart contents.', 'woocommerce' );
1262
			break;
1263
			case self::E_WC_COUPON_EXCLUDED_PRODUCTS:
1264
				// Store excluded products that are in cart in $products
1265
				$products = array();
1266
				if ( ! WC()->cart->is_empty() ) {
1267
					foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1268
						if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_excluded_product_ids() ) ) {
1269
							$products[] = $cart_item['data']->get_title();
1270
						}
1271
					}
1272
				}
1273
1274
				$err = sprintf( __( 'Sorry, this coupon is not applicable to the products: %s.', 'woocommerce' ), implode( ', ', $products ) );
1275
				break;
1276
			case self::E_WC_COUPON_EXCLUDED_CATEGORIES:
1277
				// Store excluded categories that are in cart in $categories
1278
				$categories = array();
1279
				if ( ! WC()->cart->is_empty() ) {
1280
					foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1281
						$product_cats = wc_get_product_cat_ids( $cart_item['product_id'] );
1282
1283
						if ( sizeof( $intersect = array_intersect( $product_cats, $this->get_excluded_product_categories() ) ) > 0 ) {
1284
1285
							foreach ( $intersect as $cat_id ) {
1286
								$cat = get_term( $cat_id, 'product_cat' );
1287
								$categories[] = $cat->name;
1288
							}
1289
						}
1290
					}
1291
				}
1292
1293
				$err = sprintf( __( 'Sorry, this coupon is not applicable to the categories: %s.', 'woocommerce' ), implode( ', ', array_unique( $categories ) ) );
1294
				break;
1295
			case self::E_WC_COUPON_NOT_VALID_SALE_ITEMS:
1296
				$err = __( 'Sorry, this coupon is not valid for sale items.', 'woocommerce' );
1297
			break;
1298
			default:
1299
				$err = '';
1300
			break;
1301
		}
1302
		return apply_filters( 'woocommerce_coupon_error', $err, $err_code, $this );
1303
	}
1304
1305
	/**
1306
	 * Map one of the WC_Coupon error codes to an error string.
1307
	 * No coupon instance will be available where a coupon does not exist,
1308
	 * so this static method exists.
1309
	 *
1310
	 * @param int $err_code Error code
1311
	 * @return string| Error string
1312
	 */
1313 View Code Duplication
	public static function get_generic_coupon_error( $err_code ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1314
		switch ( $err_code ) {
1315
			case self::E_WC_COUPON_NOT_EXIST:
1316
				$err = __( 'Coupon does not exist!', 'woocommerce' );
1317
			break;
1318
			case self::E_WC_COUPON_PLEASE_ENTER:
1319
				$err = __( 'Please enter a coupon code.', 'woocommerce' );
1320
			break;
1321
			default:
1322
				$err = '';
1323
			break;
1324
		}
1325
		// When using this static method, there is no $this to pass to filter
1326
		return apply_filters( 'woocommerce_coupon_error', $err, $err_code, null );
1327
	}
1328
}
1329