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

WC_Order::set_cart_hash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit;
5
}
6
7
/**
8
 * Order Class.
9
 *
10
 * These are regular WooCommerce orders, which extend the abstract order class.
11
 *
12
 * @class    WC_Order
13
 * @version  2.2.0
14
 * @package  WooCommerce/Classes
15
 * @category Class
16
 * @author   WooThemes
17
 */
18
class WC_Order extends WC_Abstract_Order {
19
20
	/**
21
	 * Data stored in meta keys, but not considered "meta" for an order.
22
	 * @since 2.7.0
23
	 * @var array
24
	 */
25
	protected $_internal_meta_keys = array(
26
		'_customer_user', '_order_key', '_order_currency', '_billing_first_name',
27
		'_billing_last_name', '_billing_company', '_billing_address_1', '_billing_address_2',
28
		'_billing_city', '_billing_state', '_billing_postcode', '_billing_country',
29
		'_billing_email', '_billing_phone', '_shipping_first_name', '_shipping_last_name',
30
		'_shipping_company', '_shipping_address_1', '_shipping_address_2', '_shipping_city',
31
		'_shipping_state', '_shipping_postcode', '_shipping_country', '_completed_date',
32
		'_paid_date', '_edit_lock', '_edit_last', '_cart_discount', '_cart_discount_tax',
33
		'_order_shipping', '_order_shipping_tax', '_order_tax', '_order_total', '_order_total',
34
		'_payment_method', '_payment_method_title', '_transaction_id', '_customer_ip_address',
35
		'_customer_user_agent', '_created_via', '_order_version', '_prices_include_tax',
36
		'_customer_note', '_date_completed', '_date_paid', '_payment_tokens',
37
	);
38
39
	/**
40
	 * Stores data about status changes so relevant hooks can be fired.
41
	 * @var bool|array
42
	 */
43
	protected $_status_transition = false;
44
45
	/**
46
	 * Extend the abstract _data properties and then read the order object.
47
	 *
48
	 * @param  int|object|WC_Order $order Order to init.
49
	 */
50
	public function __construct( $order = 0 ) {
51
		$this->_data = array_merge( $this->_data, array(
52
			'billing'              => array(
53
				'first_name'       => '',
54
				'last_name'        => '',
55
				'company'          => '',
56
				'address_1'        => '',
57
				'address_2'        => '',
58
				'city'             => '',
59
				'state'            => '',
60
				'postcode'         => '',
61
				'country'          => '',
62
				'email'            => '',
63
				'phone'            => '',
64
			),
65
			'shipping'             => array(
66
				'first_name'       => '',
67
				'last_name'        => '',
68
				'company'          => '',
69
				'address_1'        => '',
70
				'address_2'        => '',
71
				'city'             => '',
72
				'state'            => '',
73
				'postcode'         => '',
74
				'country'          => '',
75
			),
76
			'payment_method'       => '',
77
			'payment_method_title' => '',
78
			'transaction_id'       => '',
79
			'customer_ip_address'  => '',
80
			'customer_user_agent'  => '',
81
			'created_via'          => '',
82
			'customer_note'        => '',
83
			'date_completed'       => '',
84
			'date_paid'            => '',
85
			'cart_hash'            => '',
86
		) );
87
		parent::__construct( $order );
88
	}
89
90
	/**
91
	 * When a payment is complete this function is called.
92
	 *
93
	 * Most of the time this should mark an order as 'processing' so that admin can process/post the items.
94
	 * If the cart contains only downloadable items then the order is 'completed' since the admin needs to take no action.
95
	 * Stock levels are reduced at this point.
96
	 * Sales are also recorded for products.
97
	 * Finally, record the date of payment.
98
	 *
99
	 * Order must exist.
100
	 *
101
	 * @param string $transaction_id Optional transaction id to store in post meta.
102
	 * @return bool success
103
	 */
104
	public function payment_complete( $transaction_id = '' ) {
105
		if ( ! $this->get_id() ) {
106
			return false;
107
		}
108
109
		do_action( 'woocommerce_pre_payment_complete', $this->get_id() );
110
111
		if ( ! empty( WC()->session ) ) {
112
			WC()->session->set( 'order_awaiting_payment', false );
113
		}
114
115
		if ( $this->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_payment_complete', array( 'on-hold', 'pending', 'failed', 'cancelled' ), $this ) ) ) {
116
			$order_needs_processing = false;
117
118
			if ( sizeof( $this->get_items() ) > 0 ) {
119
				foreach ( $this->get_items() as $item ) {
120
					if ( $item->is_type( 'line_item' ) && ( $product = $item->get_product() ) ) {
121
						$virtual_downloadable_item = $product->is_downloadable() && $product->is_virtual();
122
123
						if ( apply_filters( 'woocommerce_order_item_needs_processing', ! $virtual_downloadable_item, $product, $this->get_id() ) ) {
124
							$order_needs_processing = true;
125
							break;
126
						}
127
					}
128
				}
129
			}
130
131
			if ( ! empty( $transaction_id ) ) {
132
				$this->set_transaction_id( $transaction_id );
133
			}
134
135
			$this->set_status( apply_filters( 'woocommerce_payment_complete_order_status', $order_needs_processing ? 'processing' : 'completed', $this->get_id() ) );
136
			$this->set_date_paid( current_time( 'timestamp' ) );
137
			$this->save();
138
139
			do_action( 'woocommerce_payment_complete', $this->get_id() );
140
		} else {
141
			do_action( 'woocommerce_payment_complete_order_status_' . $this->get_status(), $this->get_id() );
142
		}
143
144
		return true;
145
	}
146
147
	/**
148
	 * Gets order total - formatted for display.
149
	 * @return string
150
	 */
151
	public function get_formatted_order_total( $tax_display = '', $display_refunded = true ) {
152
		$formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_currency() ) );
153
		$order_total    = $this->get_total();
154
		$total_refunded = $this->get_total_refunded();
155
		$tax_string     = '';
156
157
		// Tax for inclusive prices
158
		if ( wc_tax_enabled() && 'incl' == $tax_display ) {
159
			$tax_string_array = array();
160
161
			if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
162
				foreach ( $this->get_tax_totals() as $code => $tax ) {
163
					$tax_amount         = ( $total_refunded && $display_refunded ) ? wc_price( WC_Tax::round( $tax->amount - $this->get_total_tax_refunded_by_rate_id( $tax->rate_id ) ), array( 'currency' => $this->get_currency() ) ) : $tax->formatted_amount;
164
					$tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
165
				}
166
			} else {
167
				$tax_amount         = ( $total_refunded && $display_refunded ) ? $this->get_total_tax() - $this->get_total_tax_refunded() : $this->get_total_tax();
168
				$tax_string_array[] = sprintf( '%s %s', wc_price( $tax_amount, array( 'currency' => $this->get_currency() ) ), WC()->countries->tax_or_vat() );
169
			}
170
			if ( ! empty( $tax_string_array ) ) {
171
				$tax_string = ' ' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
172
			}
173
		}
174
175
		if ( $total_refunded && $display_refunded ) {
176
			$formatted_total = '<del>' . strip_tags( $formatted_total ) . '</del> <ins>' . wc_price( $order_total - $total_refunded, array( 'currency' => $this->get_currency() ) ) . $tax_string . '</ins>';
177
		} else {
178
			$formatted_total .= $tax_string;
179
		}
180
181
		return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
182
	}
183
184
	/*
185
	|--------------------------------------------------------------------------
186
	| CRUD methods
187
	|--------------------------------------------------------------------------
188
	|
189
	| Methods which create, read, update and delete orders from the database.
190
	| Written in abstract fashion so that the way orders are stored can be
191
	| changed more easily in the future.
192
	|
193
	| A save method is included for convenience (chooses update or create based
194
	| on if the order exists yet).
195
	|
196
	*/
197
198
	/**
199
	 * Insert data into the database.
200
	 * @since 2.7.0
201
	 */
202
	public function create() {
203
		parent::create();
204
205
		// Store additonal order data
206
		if ( $this->get_id() ) {
207
			$this->update_post_meta( '_billing_first_name', $this->get_billing_first_name() );
208
			$this->update_post_meta( '_billing_last_name', $this->get_billing_last_name() );
209
			$this->update_post_meta( '_billing_company', $this->get_billing_company() );
210
			$this->update_post_meta( '_billing_address_1', $this->get_billing_address_1() );
211
			$this->update_post_meta( '_billing_address_2', $this->get_billing_address_2() );
212
			$this->update_post_meta( '_billing_city', $this->get_billing_city() );
213
			$this->update_post_meta( '_billing_state', $this->get_billing_state() );
214
			$this->update_post_meta( '_billing_postcode', $this->get_billing_postcode() );
215
			$this->update_post_meta( '_billing_country', $this->get_billing_country() );
216
			$this->update_post_meta( '_billing_email', $this->get_billing_email() );
217
			$this->update_post_meta( '_billing_phone', $this->get_billing_phone() );
218
			$this->update_post_meta( '_shipping_first_name', $this->get_shipping_first_name() );
219
			$this->update_post_meta( '_shipping_last_name', $this->get_shipping_last_name() );
220
			$this->update_post_meta( '_shipping_company', $this->get_shipping_company() );
221
			$this->update_post_meta( '_shipping_address_1', $this->get_shipping_address_1() );
222
			$this->update_post_meta( '_shipping_address_2', $this->get_shipping_address_2() );
223
			$this->update_post_meta( '_shipping_city', $this->get_shipping_city() );
224
			$this->update_post_meta( '_shipping_state', $this->get_shipping_state() );
225
			$this->update_post_meta( '_shipping_postcode', $this->get_shipping_postcode() );
226
			$this->update_post_meta( '_shipping_country', $this->get_shipping_country() );
227
			$this->update_post_meta( '_payment_method', $this->get_payment_method() );
228
			$this->update_post_meta( '_payment_method_title', $this->get_payment_method_title() );
229
			$this->update_post_meta( '_transaction_id', $this->get_transaction_id() );
230
			$this->update_post_meta( '_customer_ip_address', $this->get_customer_ip_address() );
231
			$this->update_post_meta( '_customer_user_agent', $this->get_customer_user_agent() );
232
			$this->update_post_meta( '_created_via', $this->get_created_via() );
233
			$this->update_post_meta( '_customer_note', $this->get_customer_note() );
234
			$this->update_post_meta( '_date_completed', $this->get_date_completed() );
235
			$this->update_post_meta( '_date_paid', $this->get_date_paid() );
236
			$this->update_post_meta( '_cart_hash', $this->get_cart_hash() );
237
			do_action( 'woocommerce_new_order', $this->get_id() );
238
		}
239
	}
240
241
	/**
242
	 * Read from the database.
243
	 * @since 2.7.0
244
	 * @param int $id ID of object to read.
245
	 */
246
	public function read( $id ) {
247
		parent::read( $id );
248
249
		// Read additonal order data
250
		if ( $order_id = $this->get_id() ) {
251
			$post_object = get_post( $this->get_id() );
252
			$this->set_billing_first_name( get_post_meta( $order_id, '_billing_first_name', true ) );
253
			$this->set_billing_last_name( get_post_meta( $order_id, '_billing_last_name', true ) );
254
			$this->set_billing_company( get_post_meta( $order_id, '_billing_company', true ) );
255
			$this->set_billing_address_1( get_post_meta( $order_id, '_billing_address_1', true ) );
256
			$this->set_billing_address_2( get_post_meta( $order_id, '_billing_address_2', true ) );
257
			$this->set_billing_city( get_post_meta( $order_id, '_billing_city', true ) );
258
			$this->set_billing_state( get_post_meta( $order_id, '_billing_state', true ) );
259
			$this->set_billing_postcode( get_post_meta( $order_id, '_billing_postcode', true ) );
260
			$this->set_billing_country( get_post_meta( $order_id, '_billing_country', true ) );
261
			$this->set_billing_email( get_post_meta( $order_id, '_billing_email', true ) );
262
			$this->set_billing_phone( get_post_meta( $order_id, '_billing_phone', true ) );
263
			$this->set_shipping_first_name( get_post_meta( $order_id, '_shipping_first_name', true ) );
264
			$this->set_shipping_last_name( get_post_meta( $order_id, '_shipping_last_name', true ) );
265
			$this->set_shipping_company( get_post_meta( $order_id, '_shipping_company', true ) );
266
			$this->set_shipping_address_1( get_post_meta( $order_id, '_shipping_address_1', true ) );
267
			$this->set_shipping_address_2( get_post_meta( $order_id, '_shipping_address_2', true ) );
268
			$this->set_shipping_city( get_post_meta( $order_id, '_shipping_city', true ) );
269
			$this->set_shipping_state( get_post_meta( $order_id, '_shipping_state', true ) );
270
			$this->set_shipping_postcode( get_post_meta( $order_id, '_shipping_postcode', true ) );
271
			$this->set_shipping_country( get_post_meta( $order_id, '_shipping_country', true ) );
272
			$this->set_payment_method( get_post_meta( $order_id, '_payment_method', true ) );
273
			$this->set_payment_method_title( get_post_meta( $order_id, '_payment_method_title', true ) );
274
			$this->set_transaction_id( get_post_meta( $order_id, '_transaction_id', true ) );
275
			$this->set_customer_ip_address( get_post_meta( $order_id, '_customer_ip_address', true ) );
276
			$this->set_customer_user_agent( get_post_meta( $order_id, '_customer_user_agent', true ) );
277
			$this->set_created_via( get_post_meta( $order_id, '_created_via', true ) );
278
			$this->set_customer_note( get_post_meta( $order_id, '_customer_note', true ) );
279
			$this->set_date_completed( get_post_meta( $order_id, '_completed_date', true ) );
280
			$this->set_date_paid( get_post_meta( $order_id, '_paid_date', true ) );
281
			$this->set_cart_hash( get_post_meta( $order_id, '_cart_hash', true ) );
282
			$this->set_customer_note( $post_object->post_excerpt );
283
284
			// Map user data
285
			if ( ! $this->get_billing_email() && ( $user = $this->get_user() ) ) {
286
				$this->set_billing_email( $user->user_email );
287
			}
288
		}
289
	}
290
291
	/**
292
	 * Update data in the database.
293
	 * @since 2.7.0
294
	 */
295
	public function update() {
296
		// Store additonal order data
297
		$this->update_post_meta( '_billing_first_name', $this->get_billing_first_name() );
298
		$this->update_post_meta( '_billing_last_name', $this->get_billing_last_name() );
299
		$this->update_post_meta( '_billing_company', $this->get_billing_company() );
300
		$this->update_post_meta( '_billing_address_1', $this->get_billing_address_1() );
301
		$this->update_post_meta( '_billing_address_2', $this->get_billing_address_2() );
302
		$this->update_post_meta( '_billing_city', $this->get_billing_city() );
303
		$this->update_post_meta( '_billing_state', $this->get_billing_state() );
304
		$this->update_post_meta( '_billing_postcode', $this->get_billing_postcode() );
305
		$this->update_post_meta( '_billing_country', $this->get_billing_country() );
306
		$this->update_post_meta( '_billing_email', $this->get_billing_email() );
307
		$this->update_post_meta( '_billing_phone', $this->get_billing_phone() );
308
		$this->update_post_meta( '_shipping_first_name', $this->get_shipping_first_name() );
309
		$this->update_post_meta( '_shipping_last_name', $this->get_shipping_last_name() );
310
		$this->update_post_meta( '_shipping_company', $this->get_shipping_company() );
311
		$this->update_post_meta( '_shipping_address_1', $this->get_shipping_address_1() );
312
		$this->update_post_meta( '_shipping_address_2', $this->get_shipping_address_2() );
313
		$this->update_post_meta( '_shipping_city', $this->get_shipping_city() );
314
		$this->update_post_meta( '_shipping_state', $this->get_shipping_state() );
315
		$this->update_post_meta( '_shipping_postcode', $this->get_shipping_postcode() );
316
		$this->update_post_meta( '_shipping_country', $this->get_shipping_country() );
317
		$this->update_post_meta( '_payment_method', $this->get_payment_method() );
318
		$this->update_post_meta( '_payment_method_title', $this->get_payment_method_title() );
319
		$this->update_post_meta( '_transaction_id', $this->get_transaction_id() );
320
		$this->update_post_meta( '_customer_ip_address', $this->get_customer_ip_address() );
321
		$this->update_post_meta( '_customer_user_agent', $this->get_customer_user_agent() );
322
		$this->update_post_meta( '_created_via', $this->get_created_via() );
323
		$this->update_post_meta( '_customer_note', $this->get_customer_note() );
324
		$this->update_post_meta( '_date_completed', $this->get_date_completed() );
325
		$this->update_post_meta( '_date_paid', $this->get_date_paid() );
326
		$this->update_post_meta( '_cart_hash', $this->get_cart_hash() );
327
328
		$customer_changed = $this->update_post_meta( '_customer_user', $this->get_customer_id() );
329
330
		// Update parent
331
		parent::update();
332
333
		// If customer changed, update any downloadable permissions
334
		if ( $customer_changed ) {
335
			$wpdb->update( $wpdb->prefix . "woocommerce_downloadable_product_permissions",
336
				array(
337
					'user_id'    => $this->get_customer_id(),
338
					'user_email' => $this->get_billing_email(),
339
				),
340
				array(
341
					'order_id'   => $this->get_id(),
342
				),
343
				array(
344
					'%d',
345
					'%s',
346
				),
347
				array(
348
					'%d',
349
				)
350
			);
351
		}
352
353
		// Handle status change
354
		$this->status_transition();
355
	}
356
357
	/**
358
	 * Set order status.
359
	 * @since 2.7.0
360
	 * @param string $new_status Status to change the order to. No internal wc- prefix is required.
361
	 * @param string $note (default: '') Optional note to add.
362
	 * @param bool $manual_update is this a manual order status change?
363
	 * @param array details of change
364
	 */
365
	public function set_status( $new_status, $note = '', $manual_update = false ) {
366
		$result = parent::set_status( $new_status );
367
368
		if ( ! empty( $result['from'] ) && $result['from'] !== $result['to'] ) {
369
			$this->_status_transition = array(
370
				'from'   => ! empty( $this->_status_transition['from'] ) ? $this->_status_transition['from'] : $result['from'],
371
				'to'     => $result['to'],
372
				'note'   => $note,
373
				'manual' => (bool) $manual_update,
374
			);
375
376
			if ( 'pending' === $result['from'] && ! $manual_update ) {
377
				$this->set_date_paid( current_time( 'timestamp' ) );
378
			}
379
380
			if ( 'completed' === $result['to'] ) {
381
				$this->set_date_completed( current_time( 'timestamp' ) );
382
			}
383
		}
384
385
		return $result;
386
	}
387
388
	/**
389
	 * Updates status of order immediately. Order must exist.
390
	 * @uses WC_Order::set_status()
391
	 */
392
	public function update_status( $new_status, $note = '', $manual = false ) {
393
		if ( ! $this->get_id() ) {
394
			return false;
395
		}
396
		$this->set_status( $new_status, $note, $manual );
397
		$this->save();
398
		return true;
399
	}
400
401
	/**
402
	 * Handle the status transition.
403
	 */
404
	protected function status_transition() {
405
		if ( $this->_status_transition ) {
406
			if ( ! empty( $this->_status_transition['from'] ) ) {
407
				$transition_note = sprintf( __( 'Order status changed from %s to %s.', 'woocommerce' ), wc_get_order_status_name( $this->_status_transition['from'] ), wc_get_order_status_name( $this->_status_transition['to'] ) );
408
409
				do_action( 'woocommerce_order_status_' . $this->_status_transition['from'] . '_to_' . $this->_status_transition['to'], $this->get_id() );
410
				do_action( 'woocommerce_order_status_changed', $this->get_id(), $this->_status_transition['from'], $this->_status_transition['to'] );
411
			} else {
412
				$transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $this->_status_transition['to'] ) );
413
			}
414
415
			do_action( 'woocommerce_order_status_' . $this->_status_transition['to'], $this->get_id() );
416
417
			// Note the transition occured
418
			$this->add_order_note( trim( $this->_status_transition['note'] . ' ' . $transition_note ), 0, $this->_status_transition['manual'] );
419
420
			// This has ran, so reset status transition variable
421
			$this->_status_transition = false;
422
		}
423
	}
424
425
	/*
426
	|--------------------------------------------------------------------------
427
	| Getters
428
	|--------------------------------------------------------------------------
429
	|
430
	| Methods for getting data from the order object.
431
	|
432
	*/
433
434
	/**
435
	 * Get billing_first_name
436
	 * @return string
437
	 */
438
	public function get_billing_first_name() {
439
		return $this->_data['billing']['first_name'];
440
	}
441
442
	/**
443
	 * Get billing_last_name
444
	 * @return string
445
	 */
446
	public function get_billing_last_name() {
447
		return $this->_data['billing']['last_name'];
448
	}
449
450
	/**
451
	 * Get billing_company
452
	 * @return string
453
	 */
454
	public function get_billing_company() {
455
		return $this->_data['billing']['company'];
456
	}
457
458
	/**
459
	 * Get billing_address_1
460
	 * @return string
461
	 */
462
	public function get_billing_address_1() {
463
		return $this->_data['billing']['address_1'];
464
	}
465
466
	/**
467
	 * Get billing_address_2
468
	 * @return string $value
469
	 */
470
	public function get_billing_address_2() {
471
		return $this->_data['billing']['address_2'];
472
	}
473
474
	/**
475
	 * Get billing_city
476
	 * @return string $value
477
	 */
478
	public function get_billing_city() {
479
		return $this->_data['billing']['city'];
480
	}
481
482
	/**
483
	 * Get billing_state
484
	 * @return string
485
	 */
486
	public function get_billing_state() {
487
		return $this->_data['billing']['state'];
488
	}
489
490
	/**
491
	 * Get billing_postcode
492
	 * @return string
493
	 */
494
	public function get_billing_postcode() {
495
		return $this->_data['billing']['postcode'];
496
	}
497
498
	/**
499
	 * Get billing_country
500
	 * @return string
501
	 */
502
	public function get_billing_country() {
503
		return $this->_data['billing']['country'];
504
	}
505
506
	/**
507
	 * Get billing_email
508
	 * @return string
509
	 */
510
	public function get_billing_email() {
511
		return sanitize_email( $this->_data['billing']['email'] );
512
	}
513
514
	/**
515
	 * Get billing_phone
516
	 * @return string
517
	 */
518
	public function get_billing_phone() {
519
		return $this->_data['billing']['phone'];
520
	}
521
522
	/**
523
	 * Get shipping_first_name
524
	 * @return string
525
	 */
526
	public function get_shipping_first_name() {
527
		return $this->_data['shipping']['first_name'];
528
	}
529
530
	/**
531
	 * Get shipping_last_name
532
	 * @return string
533
	 */
534
	public function get_shipping_last_name() {
535
		 return $this->_data['shipping']['last_name'];
536
	}
537
538
	/**
539
	 * Get shipping_company
540
	 * @return string
541
	 */
542
	public function get_shipping_company() {
543
		return $this->_data['shipping']['company'];
544
	}
545
546
	/**
547
	 * Get shipping_address_1
548
	 * @return string
549
	 */
550
	public function get_shipping_address_1() {
551
		return $this->_data['shipping']['address_1'];
552
	}
553
554
	/**
555
	 * Get shipping_address_2
556
	 * @return string
557
	 */
558
	public function get_shipping_address_2() {
559
		return $this->_data['shipping']['address_2'];
560
	}
561
562
	/**
563
	 * Get shipping_city
564
	 * @return string
565
	 */
566
	public function get_shipping_city() {
567
		return $this->_data['shipping']['city'];
568
	}
569
570
	/**
571
	 * Get shipping_state
572
	 * @return string
573
	 */
574
	public function get_shipping_state() {
575
		return $this->_data['shipping']['state'];
576
	}
577
578
	/**
579
	 * Get shipping_postcode
580
	 * @return string
581
	 */
582
	public function get_shipping_postcode() {
583
		return $this->_data['shipping']['postcode'];
584
	}
585
586
	/**
587
	 * Get shipping_country
588
	 * @return string
589
	 */
590
	public function get_shipping_country() {
591
		return $this->_data['shipping']['country'];
592
	}
593
594
	/**
595
	 * Get the payment method.
596
	 * @return string
597
	 */
598
	public function get_payment_method() {
599
		return $this->_data['payment_method'];
600
	}
601
602
	/**
603
	 * Get payment_method_title
604
	 * @return string
605
	 */
606
	public function get_payment_method_title() {
607
		return $this->_data['payment_method_title'];
608
	}
609
610
	/**
611
	 * Get transaction_id
612
	 * @return string
613
	 */
614
	public function get_transaction_id() {
615
		return $this->_data['transaction_id'];
616
	}
617
618
	/**
619
	 * Get customer_ip_address
620
	 * @return string
621
	 */
622
	public function get_customer_ip_address() {
623
		return $this->_data['customer_ip_address'];
624
	}
625
626
	/**
627
	 * Get customer_user_agent
628
	 * @return string
629
	 */
630
	public function get_customer_user_agent() {
631
		return $this->_data['customer_user_agent'];
632
	}
633
634
	/**
635
	 * Get created_via
636
	 * @return string
637
	 */
638
	public function get_created_via() {
639
		return $this->_data['created_via'];
640
	}
641
642
	/**
643
	 * Get customer_note
644
	 * @return string
645
	 */
646
	public function get_customer_note() {
647
		return $this->_data['customer_note'];
648
	}
649
650
	/**
651
	 * Get date_completed
652
	 * @return int
653
	 */
654
	public function get_date_completed() {
655
		return absint( $this->_data['date_completed'] );
656
	}
657
658
	/**
659
	 * Get date_paid
660
	 * @return int
661
	 */
662
	public function get_date_paid() {
663
		return absint( $this->_data['date_paid'] );
664
	}
665
666
	/**
667
	 * Returns the requested address in raw, non-formatted way.
668
	 * @since  2.4.0
669
	 * @param  string $type Billing or shipping. Anything else besides 'billing' will return shipping address.
670
	 * @return array The stored address after filter.
671
	 */
672
	public function get_address( $type = 'billing' ) {
673
		return apply_filters( 'woocommerce_get_order_address', isset( $this->_data[ $type ] ) ? $this->_data[ $type ] : array(), $type, $this );
674
	}
675
676
	/**
677
	 * Get a formatted shipping address for the order.
678
	 *
679
	 * @return string
680
	 */
681
	public function get_shipping_address_map_url() {
682
		$address = apply_filters( 'woocommerce_shipping_address_map_url_parts', $this->get_address( 'shipping' ), $this );
683
		return apply_filters( 'woocommerce_shipping_address_map_url', 'http://maps.google.com/maps?&q=' . urlencode( implode( ', ', $address ) ) . '&z=16', $this );
684
	}
685
686
	/**
687
	 * Get a formatted billing full name.
688
	 *
689
	 * @since 2.4.0
690
	 *
691
	 * @return string
692
	 */
693
	public function get_formatted_billing_full_name() {
694
		return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $this->get_billing_first_name(), $this->get_billing_last_name() );
695
	}
696
697
	/**
698
	 * Get a formatted shipping full name.
699
	 *
700
	 * @since 2.4.0
701
	 *
702
	 * @return string
703
	 */
704
	public function get_formatted_shipping_full_name() {
705
		return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $this->get_shipping_first_name(), $this->get_shipping_last_name() );
706
	}
707
708
	/**
709
	 * Get a formatted billing address for the order.
710
	 * @return string
711
	 */
712
	public function get_formatted_billing_address() {
713
		return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_billing_address', $this->get_address( 'billing' ), $this ) );
714
	}
715
716
	/**
717
	 * Get a formatted shipping address for the order.
718
	 * @return string
719
	 */
720
	public function get_formatted_shipping_address() {
721
		if ( $this->get_shipping_address_1() || $this->get_shipping_address_2() ) {
722
			return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this ) );
723
		} else {
724
			return '';
725
		}
726
	}
727
728
	/**
729
	 * Get cart hash
730
	 * @return string
731
	 */
732
	public function get_cart_hash() {
733
		return $this->_data['cart_hash'];
734
	}
735
736
	/*
737
	|--------------------------------------------------------------------------
738
	| Setters
739
	|--------------------------------------------------------------------------
740
	|
741
	| Functions for setting order data. These should not update anything in the
742
	| database itself and should only change what is stored in the class
743
	| object. However, for backwards compatibility pre 2.7.0 some of these
744
	| setters may handle both.
745
	|
746
	*/
747
748
	/**
749
	 * Set billing_first_name
750
	 * @param string $value
751
	 */
752
	public function set_billing_first_name( $value ) {
753
		$this->_data['billing']['first_name'] = $value;
754
	}
755
756
	/**
757
	 * Set billing_last_name
758
	 * @param string $value
759
	 */
760
	public function set_billing_last_name( $value ) {
761
		$this->_data['billing']['last_name'] = $value;
762
	}
763
764
	/**
765
	 * Set billing_company
766
	 * @param string $value
767
	 */
768
	public function set_billing_company( $value ) {
769
		$this->_data['billing']['company'] = $value;
770
	}
771
772
	/**
773
	 * Set billing_address_1
774
	 * @param string $value
775
	 */
776
	public function set_billing_address_1( $value ) {
777
		$this->_data['billing']['address_1'] = $value;
778
	}
779
780
	/**
781
	 * Set billing_address_2
782
	 * @param string $value
783
	 */
784
	public function set_billing_address_2( $value ) {
785
		$this->_data['billing']['address_2'] = $value;
786
	}
787
788
	/**
789
	 * Set billing_city
790
	 * @param string $value
791
	 */
792
	public function set_billing_city( $value ) {
793
		$this->_data['billing']['city'] = $value;
794
	}
795
796
	/**
797
	 * Set billing_state
798
	 * @param string $value
799
	 */
800
	public function set_billing_state( $value ) {
801
		$this->_data['billing']['state'] = $value;
802
	}
803
804
	/**
805
	 * Set billing_postcode
806
	 * @param string $value
807
	 */
808
	public function set_billing_postcode( $value ) {
809
		$this->_data['billing']['postcode'] = $value;
810
	}
811
812
	/**
813
	 * Set billing_country
814
	 * @param string $value
815
	 */
816
	public function set_billing_country( $value ) {
817
		$this->_data['billing']['country'] = $value;
818
	}
819
820
	/**
821
	 * Set billing_email
822
	 * @param string $value
823
	 */
824
	public function set_billing_email( $value ) {
825
		$value = sanitize_email( $value );
826
		$this->_data['billing']['email'] = is_email( $value ) ? $value : '';
827
	}
828
829
	/**
830
	 * Set billing_phone
831
	 * @param string $value
832
	 */
833
	public function set_billing_phone( $value ) {
834
		$this->_data['billing']['phone'] = $value;
835
	}
836
837
	/**
838
	 * Set shipping_first_name
839
	 * @param string $value
840
	 */
841
	public function set_shipping_first_name( $value ) {
842
		$this->_data['shipping']['first_name'] = $value;
843
	}
844
845
	/**
846
	 * Set shipping_last_name
847
	 * @param string $value
848
	 */
849
	public function set_shipping_last_name( $value ) {
850
		$this->_data['shipping']['last_name'] = $value;
851
	}
852
853
	/**
854
	 * Set shipping_company
855
	 * @param string $value
856
	 */
857
	public function set_shipping_company( $value ) {
858
		$this->_data['shipping']['company'] = $value;
859
	}
860
861
	/**
862
	 * Set shipping_address_1
863
	 * @param string $value
864
	 */
865
	public function set_shipping_address_1( $value ) {
866
		$this->_data['shipping']['address_1'] = $value;
867
	}
868
869
	/**
870
	 * Set shipping_address_2
871
	 * @param string $value
872
	 */
873
	public function set_shipping_address_2( $value ) {
874
		$this->_data['shipping']['address_2'] = $value;
875
	}
876
877
	/**
878
	 * Set shipping_city
879
	 * @param string $value
880
	 */
881
	public function set_shipping_city( $value ) {
882
		$this->_data['shipping']['city'] = $value;
883
	}
884
885
	/**
886
	 * Set shipping_state
887
	 * @param string $value
888
	 */
889
	public function set_shipping_state( $value ) {
890
		$this->_data['shipping']['state'] = $value;
891
	}
892
893
	/**
894
	 * Set shipping_postcode
895
	 * @param string $value
896
	 */
897
	public function set_shipping_postcode( $value ) {
898
		$this->_data['shipping']['postcode'] = $value;
899
	}
900
901
	/**
902
	 * Set shipping_country
903
	 * @param string $value
904
	 */
905
	public function set_shipping_country( $value ) {
906
		$this->_data['shipping']['country'] = $value;
907
	}
908
909
	/**
910
	 * Set the payment method.
911
	 * @since 2.2.0
912
	 * @param string $payment_method Supports WC_Payment_Gateway for bw compatibility with < 2.7
913
	 */
914
	public function set_payment_method( $payment_method = '' ) {
915
		if ( is_object( $payment_method ) ) {
916
			$this->set_payment_method( $payment_method->id );
917
			$this->set_payment_method_title( $payment_method->get_title() );
918
		} elseif ( '' === $payment_method ) {
919
			$this->_data['payment_method']       = '';
920
			$this->_data['payment_method_title'] = '';
921
		} else {
922
			$this->_data['payment_method']       = $payment_method;
923
		}
924
	}
925
926
	/**
927
	 * Set payment_method_title
928
	 * @param string $value
929
	 */
930
	public function set_payment_method_title( $value ) {
931
		$this->_data['payment_method_title'] = $value;
932
	}
933
934
	/**
935
	 * Set transaction_id
936
	 * @param string $value
937
	 */
938
	public function set_transaction_id( $value ) {
939
		$this->_data['transaction_id'] = $value;
940
	}
941
942
	/**
943
	 * Set customer_ip_address
944
	 * @param string $value
945
	 */
946
	public function set_customer_ip_address( $value ) {
947
		$this->_data['customer_ip_address'] = $value;
948
	}
949
950
	/**
951
	 * Set customer_user_agent
952
	 * @param string $value
953
	 */
954
	public function set_customer_user_agent( $value ) {
955
		$this->_data['customer_user_agent'] = $value;
956
	}
957
958
	/**
959
	 * Set created_via
960
	 * @param string $value
961
	 */
962
	public function set_created_via( $value ) {
963
		$this->_data['created_via'] = $value;
964
	}
965
966
	/**
967
	 * Set customer_note
968
	 * @param string $value
969
	 */
970
	public function set_customer_note( $value ) {
971
		$this->_data['customer_note'] = $value;
972
	}
973
974
	/**
975
	 * Set date_completed
976
	 * @param string $timestamp
977
	 */
978
	public function set_date_completed( $timestamp ) {
979
		$this->_data['date_completed'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
980
	}
981
982
	/**
983
	 * Set date_paid
984
	 * @param string $timestamp
985
	 */
986
	public function set_date_paid( $timestamp ) {
987
		$this->_data['date_paid'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
988
	}
989
990
	/**
991
	 * Set cart hash
992
	 * @param string $value
993
	 */
994
	public function set_cart_hash( $value ) {
995
		$this->_data['cart_hash'] = $value;
996
	}
997
998
	/*
999
	|--------------------------------------------------------------------------
1000
	| Conditionals
1001
	|--------------------------------------------------------------------------
1002
	|
1003
	| Checks if a condition is true or false.
1004
	|
1005
	*/
1006
1007
	/**
1008
	 * See if order matches cart_hash.
1009
	 * @return bool
1010
	 */
1011
	public function has_cart_hash( $cart_hash = '' ) {
1012
		return hash_equals( $this->get_cart_hash(), $cart_hash );
1013
	}
1014
1015
	/**
1016
	 * Checks if an order can be edited, specifically for use on the Edit Order screen.
1017
	 * @return bool
1018
	 */
1019
	public function is_editable() {
1020
		return apply_filters( 'wc_order_is_editable', in_array( $this->get_status(), array( 'pending', 'on-hold', 'auto-draft' ) ), $this );
1021
	}
1022
1023
	/**
1024
	 * Returns if an order has been paid for based on the order status.
1025
	 * @since 2.5.0
1026
	 * @return bool
1027
	 */
1028
	public function is_paid() {
1029
		return apply_filters( 'woocommerce_order_is_paid', $this->has_status( apply_filters( 'woocommerce_order_is_paid_statuses', array( 'processing', 'completed' ) ) ), $this );
1030
	}
1031
1032
	/**
1033
	 * Checks if product download is permitted.
1034
	 *
1035
	 * @return bool
1036
	 */
1037
	public function is_download_permitted() {
1038
		return apply_filters( 'woocommerce_order_is_download_permitted', $this->has_status( 'completed' ) || ( 'yes' === get_option( 'woocommerce_downloads_grant_access_after_payment' ) && $this->has_status( 'processing' ) ), $this );
1039
	}
1040
1041
	/**
1042
	 * Checks if an order needs display the shipping address, based on shipping method.
1043
	 * @return bool
1044
	 */
1045
	public function needs_shipping_address() {
1046
		if ( 'no' === get_option( 'woocommerce_calc_shipping' ) ) {
1047
			return false;
1048
		}
1049
1050
		$hide  = apply_filters( 'woocommerce_order_hide_shipping_address', array( 'local_pickup' ), $this );
1051
		$needs_address = false;
1052
1053
		foreach ( $this->get_shipping_methods() as $shipping_method ) {
1054
			if ( ! in_array( $shipping_method['method_id'], $hide ) ) {
1055
				$needs_address = true;
1056
				break;
1057
			}
1058
		}
1059
1060
		return apply_filters( 'woocommerce_order_needs_shipping_address', $needs_address, $hide, $this );
1061
	}
1062
1063
	/**
1064
	 * Returns true if the order contains a downloadable product.
1065
	 * @return bool
1066
	 */
1067
	public function has_downloadable_item() {
1068
		foreach ( $this->get_items() as $item ) {
1069
			if ( $item->is_type( 'line_item' ) && ( $product = $item->get_product() ) && $product->is_downloadable() && $product->has_file() ) {
1070
				return true;
1071
			}
1072
		}
1073
		return false;
1074
	}
1075
1076
	/**
1077
	 * Checks if an order needs payment, based on status and order total.
1078
	 *
1079
	 * @return bool
1080
	 */
1081
	public function needs_payment() {
1082
		$valid_order_statuses = apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this );
1083
		return apply_filters( 'woocommerce_order_needs_payment', ( $this->has_status( $valid_order_statuses ) && $this->get_total() > 0 ), $this, $valid_order_statuses );
1084
	}
1085
1086
	/*
1087
	|--------------------------------------------------------------------------
1088
	| URLs and Endpoints
1089
	|--------------------------------------------------------------------------
1090
	*/
1091
1092
	/**
1093
	 * Generates a URL so that a customer can pay for their (unpaid - pending) order. Pass 'true' for the checkout version which doesn't offer gateway choices.
1094
	 *
1095
	 * @param  bool $on_checkout
1096
	 * @return string
1097
	 */
1098
	public function get_checkout_payment_url( $on_checkout = false ) {
1099
		$pay_url = wc_get_endpoint_url( 'order-pay', $this->get_id(), wc_get_page_permalink( 'checkout' ) );
1100
1101 View Code Duplication
		if ( 'yes' == get_option( 'woocommerce_force_ssl_checkout' ) || is_ssl() ) {
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...
1102
			$pay_url = str_replace( 'http:', 'https:', $pay_url );
1103
		}
1104
1105
		if ( $on_checkout ) {
1106
			$pay_url = add_query_arg( 'key', $this->get_order_key(), $pay_url );
1107
		} else {
1108
			$pay_url = add_query_arg( array( 'pay_for_order' => 'true', 'key' => $this->get_order_key() ), $pay_url );
1109
		}
1110
1111
		return apply_filters( 'woocommerce_get_checkout_payment_url', $pay_url, $this );
1112
	}
1113
1114
	/**
1115
	 * Generates a URL for the thanks page (order received).
1116
	 *
1117
	 * @return string
1118
	 */
1119
	public function get_checkout_order_received_url() {
1120
		$order_received_url = wc_get_endpoint_url( 'order-received', $this->get_id(), wc_get_page_permalink( 'checkout' ) );
1121
1122
		if ( 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) || is_ssl() ) {
1123
			$order_received_url = str_replace( 'http:', 'https:', $order_received_url );
1124
		}
1125
1126
		$order_received_url = add_query_arg( 'key', $this->get_order_key(), $order_received_url );
1127
1128
		return apply_filters( 'woocommerce_get_checkout_order_received_url', $order_received_url, $this );
1129
	}
1130
1131
	/**
1132
	 * Generates a URL so that a customer can cancel their (unpaid - pending) order.
1133
	 *
1134
	 * @param string $redirect
1135
	 *
1136
	 * @return string
1137
	 */
1138 View Code Duplication
	public function get_cancel_order_url( $redirect = '' ) {
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...
1139
		return apply_filters( 'woocommerce_get_cancel_order_url', wp_nonce_url( add_query_arg( array(
1140
			'cancel_order' => 'true',
1141
			'order'        => $this->get_order_key(),
1142
			'order_id'     => $this->get_id(),
1143
			'redirect'     => $redirect
1144
		), $this->get_cancel_endpoint() ), 'woocommerce-cancel_order' ) );
1145
	}
1146
1147
	/**
1148
	 * Generates a raw (unescaped) cancel-order URL for use by payment gateways.
1149
	 *
1150
	 * @param string $redirect
1151
	 *
1152
	 * @return string The unescaped cancel-order URL.
1153
	 */
1154 View Code Duplication
	public function get_cancel_order_url_raw( $redirect = '' ) {
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...
1155
		return apply_filters( 'woocommerce_get_cancel_order_url_raw', add_query_arg( array(
1156
			'cancel_order' => 'true',
1157
			'order'        => $this->get_order_key(),
1158
			'order_id'     => $this->get_id(),
1159
			'redirect'     => $redirect,
1160
			'_wpnonce'     => wp_create_nonce( 'woocommerce-cancel_order' )
1161
		), $this->get_cancel_endpoint() ) );
1162
	}
1163
1164
	/**
1165
	 * Helper method to return the cancel endpoint.
1166
	 *
1167
	 * @return string the cancel endpoint; either the cart page or the home page.
1168
	 */
1169
	public function get_cancel_endpoint() {
1170
		$cancel_endpoint = wc_get_page_permalink( 'cart' );
1171
		if ( ! $cancel_endpoint ) {
1172
			$cancel_endpoint = home_url();
1173
		}
1174
1175
		if ( false === strpos( $cancel_endpoint, '?' ) ) {
1176
			$cancel_endpoint = trailingslashit( $cancel_endpoint );
1177
		}
1178
1179
		return $cancel_endpoint;
1180
	}
1181
1182
	/**
1183
	 * Generates a URL to view an order from the my account page.
1184
	 *
1185
	 * @return string
1186
	 */
1187
	public function get_view_order_url() {
1188
		return apply_filters( 'woocommerce_get_view_order_url', wc_get_endpoint_url( 'view-order', $this->get_id(), wc_get_page_permalink( 'myaccount' ) ), $this );
1189
	}
1190
1191
	/*
1192
	|--------------------------------------------------------------------------
1193
	| Order notes.
1194
	|--------------------------------------------------------------------------
1195
	*/
1196
1197
	/**
1198
	 * Adds a note (comment) to the order.
1199
	 *
1200
	 * @param string $note Note to add.
1201
	 * @param int $is_customer_note (default: 0) Is this a note for the customer?
1202
	 * @param  bool added_by_user Was the note added by a user?
1203
	 * @return int Comment ID.
1204
	 */
1205
	public function add_order_note( $note, $is_customer_note = 0, $added_by_user = false ) {
1206
		if ( is_user_logged_in() && current_user_can( 'edit_shop_order', $this->get_id() ) && $added_by_user ) {
1207
			$user                 = get_user_by( 'id', get_current_user_id() );
1208
			$comment_author       = $user->display_name;
1209
			$comment_author_email = $user->user_email;
1210
		} else {
1211
			$comment_author       = __( 'WooCommerce', 'woocommerce' );
1212
			$comment_author_email = strtolower( __( 'WooCommerce', 'woocommerce' ) ) . '@';
1213
			$comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
1214
			$comment_author_email = sanitize_email( $comment_author_email );
1215
		}
1216
		$commentdata = apply_filters( 'woocommerce_new_order_note_data', array(
1217
			'comment_post_ID'      => $this->get_id(),
1218
			'comment_author'       => $comment_author,
1219
			'comment_author_email' => $comment_author_email,
1220
			'comment_author_url'   => '',
1221
			'comment_content'      => $note,
1222
			'comment_agent'        => 'WooCommerce',
1223
			'comment_type'         => 'order_note',
1224
			'comment_parent'       => 0,
1225
			'comment_approved'     => 1,
1226
		), array( 'order_id' => $this->get_id(), 'is_customer_note' => $is_customer_note ) );
1227
1228
		$comment_id = wp_insert_comment( $commentdata );
1229
1230
		if ( $is_customer_note ) {
1231
			add_comment_meta( $comment_id, 'is_customer_note', 1 );
1232
1233
			do_action( 'woocommerce_new_customer_note', array( 'order_id' => $this->get_id(), 'customer_note' => $commentdata['comment_content'] ) );
1234
		}
1235
1236
		return $comment_id;
1237
	}
1238
1239
	/**
1240
	 * List order notes (public) for the customer.
1241
	 *
1242
	 * @return array
1243
	 */
1244
	public function get_customer_order_notes() {
1245
		$notes = array();
1246
		$args  = array(
1247
			'post_id' => $this->get_id(),
1248
			'approve' => 'approve',
1249
			'type'    => ''
1250
		);
1251
1252
		remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
1253
1254
		$comments = get_comments( $args );
1255
1256
		foreach ( $comments as $comment ) {
1257
			if ( ! get_comment_meta( $comment->comment_ID, 'is_customer_note', true ) ) {
1258
				continue;
1259
			}
1260
			$comment->comment_content = make_clickable( $comment->comment_content );
1261
			$notes[] = $comment;
1262
		}
1263
1264
		add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
1265
1266
		return $notes;
1267
	}
1268
1269
	/*
1270
	|--------------------------------------------------------------------------
1271
	| Refunds
1272
	|--------------------------------------------------------------------------
1273
	*/
1274
1275
	/**
1276
	 * Get order refunds.
1277
	 * @since 2.2
1278
	 * @return array of WC_Order_Refund objects
1279
	 */
1280
	public function get_refunds() {
1281
		if ( empty( $this->refunds ) || ! is_array( $this->refunds ) ) {
1282
			$this->refunds = wc_get_orders( array(
1283
				'type'   => 'shop_order_refund',
1284
				'parent' => $this->get_id(),
1285
				'limit'  => -1,
1286
			) );
1287
		}
1288
		return $this->refunds;
1289
	}
1290
1291
	/**
1292
	 * Get amount already refunded.
1293
	 *
1294
	 * @since 2.2
1295
	 * @return string
1296
	 */
1297 View Code Duplication
	public function get_total_refunded() {
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...
1298
		global $wpdb;
1299
1300
		$total = $wpdb->get_var( $wpdb->prepare( "
1301
			SELECT SUM( postmeta.meta_value )
1302
			FROM $wpdb->postmeta AS postmeta
1303
			INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
1304
			WHERE postmeta.meta_key = '_refund_amount'
1305
			AND postmeta.post_id = posts.ID
1306
		", $this->get_id() ) );
1307
1308
		return $total;
1309
	}
1310
1311
	/**
1312
	 * Get the total tax refunded.
1313
	 *
1314
	 * @since  2.3
1315
	 * @return float
1316
	 */
1317 View Code Duplication
	public function get_total_tax_refunded() {
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...
1318
		global $wpdb;
1319
1320
		$total = $wpdb->get_var( $wpdb->prepare( "
1321
			SELECT SUM( order_itemmeta.meta_value )
1322
			FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
1323
			INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
1324
			INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'tax' )
1325
			WHERE order_itemmeta.order_item_id = order_items.order_item_id
1326
			AND order_itemmeta.meta_key IN ('tax_amount', 'shipping_tax_amount')
1327
		", $this->get_id() ) );
1328
1329
		return abs( $total );
1330
	}
1331
1332
	/**
1333
	 * Get the total shipping refunded.
1334
	 *
1335
	 * @since  2.4
1336
	 * @return float
1337
	 */
1338 View Code Duplication
	public function get_total_shipping_refunded() {
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...
1339
		global $wpdb;
1340
1341
		$total = $wpdb->get_var( $wpdb->prepare( "
1342
			SELECT SUM( order_itemmeta.meta_value )
1343
			FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
1344
			INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
1345
			INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'shipping' )
1346
			WHERE order_itemmeta.order_item_id = order_items.order_item_id
1347
			AND order_itemmeta.meta_key IN ('cost')
1348
		", $this->get_id() ) );
1349
1350
		return abs( $total );
1351
	}
1352
1353
	/**
1354
	 * Gets the count of order items of a certain type that have been refunded.
1355
	 * @since  2.4.0
1356
	 * @param string $item_type
1357
	 * @return string
1358
	 */
1359
	public function get_item_count_refunded( $item_type = '' ) {
1360
		if ( empty( $item_type ) ) {
1361
			$item_type = array( 'line_item' );
1362
		}
1363
		if ( ! is_array( $item_type ) ) {
1364
			$item_type = array( $item_type );
1365
		}
1366
		$count = 0;
1367
1368
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1369
			foreach ( $refund->get_items( $item_type ) as $refunded_item ) {
1370
				$count += $refunded_item->get_qty();
1371
			}
1372
		}
1373
1374
		return apply_filters( 'woocommerce_get_item_count_refunded', $count, $item_type, $this );
1375
	}
1376
1377
	/**
1378
	 * Get the total number of items refunded.
1379
	 *
1380
	 * @since  2.4.0
1381
	 * @param  string $item_type type of the item we're checking, if not a line_item
1382
	 * @return integer
1383
	 */
1384
	public function get_total_qty_refunded( $item_type = 'line_item' ) {
1385
		$qty = 0;
1386
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1387
			foreach ( $refund->get_items( $item_type ) as $refunded_item ) {
1388
				$qty += $refunded_item->get_qty();
1389
			}
1390
		}
1391
		return $qty;
1392
	}
1393
1394
	/**
1395
	 * Get the refunded amount for a line item.
1396
	 *
1397
	 * @param  int $item_id ID of the item we're checking
1398
	 * @param  string $item_type type of the item we're checking, if not a line_item
1399
	 * @return integer
1400
	 */
1401 View Code Duplication
	public function get_qty_refunded_for_item( $item_id, $item_type = 'line_item' ) {
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...
1402
		$qty = 0;
1403
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1404
			foreach ( $refund->get_items( $item_type ) as $refunded_item ) {
1405
				if ( absint( $refunded_item->get_meta( '_refunded_item_id' ) ) === $item_id ) {
1406
					$qty += $refunded_item->get_qty();
1407
				}
1408
			}
1409
		}
1410
		return $qty;
1411
	}
1412
1413
	/**
1414
	 * Get the refunded amount for a line item.
1415
	 *
1416
	 * @param  int $item_id ID of the item we're checking
1417
	 * @param  string $item_type type of the item we're checking, if not a line_item
1418
	 * @return integer
1419
	 */
1420 View Code Duplication
	public function get_total_refunded_for_item( $item_id, $item_type = 'line_item' ) {
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...
1421
		$total = 0;
1422
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1423
			foreach ( $refund->get_items( $item_type ) as $refunded_item ) {
1424
				if ( absint( $refunded_item->get_meta( '_refunded_item_id' ) ) === $item_id ) {
1425
					$total += $refunded_item->get_total();
1426
				}
1427
			}
1428
		}
1429
		return $total * -1;
1430
	}
1431
1432
	/**
1433
	 * Get the refunded amount for a line item.
1434
	 *
1435
	 * @param  int $item_id ID of the item we're checking
1436
	 * @param  int $tax_id ID of the tax we're checking
1437
	 * @param  string $item_type type of the item we're checking, if not a line_item
1438
	 * @return double
1439
	 */
1440 View Code Duplication
	public function get_tax_refunded_for_item( $item_id, $tax_id, $item_type = 'line_item' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $tax_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
1441
		$total = 0;
1442
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1443
			foreach ( $refund->get_items( $item_type ) as $refunded_item ) {
1444
				if ( absint( $refunded_item->get_meta( '_refunded_item_id' ) ) === $item_id ) {
1445
					$total += $refunded_item->get_total_tax();
1446
				}
1447
			}
1448
		}
1449
		return wc_round_tax_total( $total ) * -1;
1450
	}
1451
1452
	/**
1453
	 * Get total tax refunded by rate ID.
1454
	 *
1455
	 * @param  int $rate_id
1456
	 *
1457
	 * @return float
1458
	 */
1459
	public function get_total_tax_refunded_by_rate_id( $rate_id ) {
1460
		$total = 0;
1461
		foreach ( $this->get_refunds() as $refund ) {
0 ignored issues
show
Bug introduced by
The expression $this->get_refunds() of type array|object<stdClass> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1462
			foreach ( $refund->get_items( 'tax' ) as $refunded_item ) {
1463
				if ( absint( $refunded_item->get_rate_id() ) === $rate_id ) {
1464
					$total += abs( $refunded_item->tax_total() ) + abs( $refunded_item->shipping_tax_total() );
1465
				}
1466
			}
1467
		}
1468
1469
		return $total;
1470
	}
1471
1472
	/**
1473
	 * How much money is left to refund?
1474
	 * @return string
1475
	 */
1476
	public function get_remaining_refund_amount() {
1477
		return wc_format_decimal( $this->get_total() - $this->get_total_refunded(), wc_get_price_decimals() );
1478
	}
1479
1480
	/**
1481
	 * How many items are left to refund?
1482
	 * @return int
1483
	 */
1484
	public function get_remaining_refund_items() {
1485
		return absint( $this->get_item_count() - $this->get_item_count_refunded() );
1486
	}
1487
}
1488