Completed
Push — master ( df624b...09e43c )
by Mike
11:25
created

WC_Order::has_downloadable_item()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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