Completed
Pull Request — master (#10259)
by Mike
09:41
created

WC_Abstract_Order::get_taxes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 19 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Abstract Order
8
 *
9
 * Handles order data and database interaction.
10
 *
11
 * @class       WC_Abstract_Order
12
 * @version     2.6.0
13
 * @package     WooCommerce/Classes
14
 * @category    Class
15
 * @author      WooThemes
16
 *
17
 * @todo check date formats are bw compat and consistant
18
 */
19
abstract class WC_Abstract_Order implements WC_Data {
20
21
    /**
22
     * Stores data about status changes so relevant hooks can be fired.
23
     * @var bool|array
24
     */
25
    protected $_status_transition = false;
26
27
    /**
28
     * Stores meta data.
29
     * @var array
30
     */
31
    protected $_meta = array();
32
33
    /**
34
     * Stores line item objects.
35
     * @var array
36
     */
37
    protected $_items = array(
38
        'line_items'     => array(),
39
		'tax_lines'      => array(),
40
		'shipping_lines' => array(),
41
		'fees'           => array(),
42
		'fee_lines'      => array(),
43
		'coupon_lines'   => array(),
44
    );
45
46
    /**
47
     * Data array, with defaults.
48
     *
49
     * @todo when migrating to custom tables, these will be columns
50
     * @since 2.6.0
51
     * @var array
52
     */
53
    protected $_data = array(
54
		'order_id'             => 0,
55
        'parent_id'            => 0,
56
		'status'               => '',
57
        /**
58
         * @todo confusion. Was 'simple'. But this was not the same as post_type, which is shop_order. Other post types in core are shop_order_refund
59
         * The order type for shop_order_refund is refund.
60
         * Why do we need two separate variables? This should be unified, especially once this is in a custom table and post_type is redundent.
61
         * Switching to 'shop_order', and then using this value in the order factory instead of post_type. @thenbrent might have feedback on this.
62
         */
63
		'order_type'           => 'shop_order',
64
		'order_key'            => '',
65
		'order_currency'       => '',
66
		'date_created'         => '',
67
		'date_modified'        => '',
68
		'customer_id'          => 0,
69
		'billing_first_name'   => '',
70
		'billing_last_name'    => '',
71
		'billing_company'      => '',
72
		'billing_address_1'    => '',
73
		'billing_address_2'    => '',
74
		'billing_city'         => '',
75
		'billing_state'        => '',
76
		'billing_postcode'     => '',
77
		'billing_country'      => '',
78
		'billing_email'        => '',
79
		'billing_phone'        => '',
80
		'shipping_first_name'  => '',
81
		'shipping_last_name'   => '',
82
		'shipping_company'     => '',
83
		'shipping_address_1'   => '',
84
		'shipping_address_2'   => '',
85
		'shipping_city'        => '',
86
		'shipping_state'       => '',
87
		'shipping_postcode'    => '',
88
		'shipping_country'     => '',
89
		'discount_total'       => 0,
90
		'discount_tax'         => 0,
91
		'shipping_total'       => 0,
92
		'shipping_tax'         => 0,
93
		'cart_tax'             => 0, // cart_tax is the new name for the legacy 'order_tax' which is the tax for items only, not shipping.
94
		'order_total'          => 0,
95
		'order_tax'            => 0, // Sum of all taxes.
96
97
		// These will be meta when moving to custom data
98
		'payment_method'       => '',
99
		'payment_method_title' => '',
100
		'transaction_id'       => '',
101
		'customer_ip_address'  => '',
102
		'customer_user_agent'  => '',
103
		'created_via'          => '',
104
		'order_version'        => '',
105
		'prices_include_tax'   => false,
106
		'customer_note'        => '',
107
		'date_completed'       => '',
108
		'date_paid'            => ''
109
    );
110
111
    /**
112
     * Get the order if ID is passed, otherwise the order is new and empty.
113
     * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory.
114
     * should be used. It is possible, but the aforementioned are preferred and are the only.
115
     * methods that will be maintained going forward.
116
     *
117
     * @param  int|object|WC_Order $order Order to init.
118
     */
119 View Code Duplication
    public function __construct( $order = 0 ) {
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...
120
		if ( is_numeric( $order ) ) {
121
            $this->read( $order );
122
        } elseif ( $order instanceof WC_Order ) {
123
            $this->read( absint( $order->get_id() ) );
124
        } elseif ( ! empty( $order->ID ) ) {
125
            $this->read( absint( $order->ID ) );
126
        }
127
    }
128
129
    /**
130
     * Change data to JSON format.
131
     * @return string Data in JSON format.
132
     */
133
    public function __toString() {
134
        return json_encode( $this->get_data() );
135
    }
136
137
    public function add_meta_data(){} // @todo
138
    public function get_meta_data(){} // @todo
139
    public function get_order_meta( $key ){} // @todo
0 ignored issues
show
Unused Code introduced by
The parameter $key 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...
140
141
    /*
142
    |--------------------------------------------------------------------------
143
    | Getters
144
    |--------------------------------------------------------------------------
145
    |
146
    | Methods for getting data from the order object.
147
    |
148
    */
149
150
    /**
151
     * Get all class data in array format.
152
     * @since 2.6.0
153
     * @return array
154
     */
155
    public function get_data() {
156
        return $this->_data;
157
    }
158
159
    /**
160
     * Get order ID.
161
     * @since 2.6.0
162
     * @return integer
163
     */
164
    public function get_id() {
165
        return $this->get_order_id();
166
    }
167
168
    /**
169
     * Get order ID.
170
     * @since 2.6.0
171
     * @return integer
172
     */
173
    public function get_order_id() {
174
        return absint( $this->_data['order_id'] );
175
    }
176
177
    /**
178
     * Get parent order ID.
179
     * @since 2.6.0
180
     * @return integer
181
     */
182
    public function get_parent_id() {
183
        return absint( $this->_data['parent_id'] );
184
    }
185
186
    /**
187
     * get_order_number function.
188
     *
189
     * Gets the order number for display (by default, order ID).
190
     *
191
     * @return string
192
     */
193
    public function get_order_number() {
194
        return apply_filters( 'woocommerce_order_number', $this->get_id(), $this );
195
    }
196
197
    /**
198
     * Get order key.
199
     * @since 2.6.0
200
     * @return string
201
     */
202
    public function get_order_key() {
203
        return $this->_data['order_key'];
204
    }
205
206
    /**
207
     * Gets order currency.
208
     * @return string
209
     */
210
    public function get_order_currency() {
211
        return apply_filters( 'woocommerce_get_order_currency', $this->_data['order_currency'], $this );
212
    }
213
214
    /**
215
     * Get Order Type
216
     * @return string
217
     */
218
    public function get_order_type() {
219
        return $this->_data['order_type'];
220
    }
221
222
    /**
223
     * Get date_created
224
     * @return string
225
     */
226
    public function get_date_created() {
227
        return $this->_data['date_created'];
228
    }
229
230
    /**
231
     * Get date_modified
232
     * @return string
233
     */
234
    public function get_date_modified() {
235
        return $this->_data['date_modified'];
236
    }
237
238
    /**
239
     * Get date_completed
240
     * @return string
241
     */
242
    public function get_date_completed() {
243
        return $this->_data['date_completed'];
244
    }
245
246
	/**
247
     * Get date_paid
248
     * @return string
249
     */
250
    public function get_date_paid() {
251
        return $this->_data['date_paid'];
252
    }
253
254
    /**
255
     * Get customer_id
256
     * @return int
257
     */
258
    public function get_customer_id() {
259
        return absint( $this->_data['customer_id'] );
260
    }
261
262
    /**
263
     * Get billing_first_name
264
     * @return string
265
     */
266
    public function get_billing_first_name() {
267
        return $this->_data['billing_first_name'];
268
    }
269
270
    /**
271
     * Get billing_last_name
272
     * @return string
273
     */
274
    public function get_billing_last_name() {
275
        return $this->_data['billing_last_name'];
276
    }
277
278
    /**
279
     * Get billing_company
280
     * @return string
281
     */
282
    public function get_billing_company() {
283
        return $this->_data['billing_company'];
284
    }
285
286
    /**
287
     * Get billing_address_1
288
     * @return string
289
     */
290
    public function get_billing_address_1() {
291
        return $this->_data['billing_address_1'];
292
    }
293
294
    /**
295
     * Get billing_address_2
296
     * @return string $value
297
     */
298
    public function get_billing_address_2() {
299
        return $this->_data['billing_address_2'];
300
    }
301
302
    /**
303
     * Get billing_city
304
     * @return string $value
305
     */
306
    public function get_billing_city() {
307
        return $this->_data['billing_city'];
308
    }
309
310
    /**
311
     * Get billing_state
312
     * @return string
313
     */
314
    public function get_billing_state() {
315
        return $this->_data['billing_state'];
316
    }
317
318
    /**
319
     * Get billing_postcode
320
     * @return string
321
     */
322
    public function get_billing_postcode() {
323
        return $this->_data['billing_postcode'];
324
    }
325
326
    /**
327
     * Get billing_country
328
     * @return string
329
     */
330
    public function get_billing_country() {
331
        return $this->_data['billing_country'];
332
    }
333
334
    /**
335
     * Get billing_email
336
     * @return string
337
     */
338
    public function get_billing_email() {
339
        return sanitize_email( $this->_data['billing_email'] );
340
    }
341
342
    /**
343
     * Get billing_phone
344
     * @return string
345
     */
346
    public function get_billing_phone() {
347
        return $this->_data['billing_phone'];
348
    }
349
350
    /**
351
     * Get shipping_first_name
352
     * @return string
353
     */
354
    public function get_shipping_first_name() {
355
        return $this->_data['shipping_first_name'];
356
    }
357
358
    /**
359
     * Get shipping_last_name
360
     * @return string
361
     */
362
    public function get_shipping_last_name() {
363
         return $this->_data['shipping_last_name'];
364
    }
365
366
    /**
367
     * Get shipping_company
368
     * @return string
369
     */
370
    public function get_shipping_company() {
371
        return $this->_data['shipping_company'];
372
    }
373
374
    /**
375
     * Get shipping_address_1
376
     * @return string
377
     */
378
    public function get_shipping_address_1() {
379
        return $this->_data['shipping_address_1'];
380
    }
381
382
    /**
383
     * Get shipping_address_2
384
     * @return string
385
     */
386
    public function get_shipping_address_2() {
387
        return $this->_data['shipping_address_2'];
388
    }
389
390
    /**
391
     * Get shipping_city
392
     * @return string
393
     */
394
    public function get_shipping_city() {
395
        return $this->_data['shipping_city'];
396
    }
397
398
    /**
399
     * Get shipping_state
400
     * @return string
401
     */
402
    public function get_shipping_state() {
403
        return $this->_data['shipping_state'];
404
    }
405
406
    /**
407
     * Get shipping_postcode
408
     * @return string
409
     */
410
    public function get_shipping_postcode() {
411
        return $this->_data['shipping_postcode'];
412
    }
413
414
    /**
415
     * Get shipping_country
416
     * @return string
417
     */
418
    public function get_shipping_country() {
419
        return $this->_data['shipping_country'];
420
    }
421
422
    /**
423
     * Get the payment method.
424
     * @return string
425
     */
426
    public function get_payment_method() {
427
        return $this->_data['payment_method'];
428
    }
429
430
    /**
431
     * Get payment_method_title
432
     * @return string
433
     */
434
    public function get_payment_method_title() {
435
        return $this->_data['payment_method_title'];
436
    }
437
438
    /**
439
     * Get transaction_id
440
     * @return string
441
     */
442
    public function get_transaction_id() {
443
        return $this->_data['transaction_id'];
444
    }
445
446
    /**
447
     * Get customer_ip_address
448
     * @return string
449
     */
450
    public function get_customer_ip_address() {
451
        return $this->_data['customer_ip_address'];
452
    }
453
454
    /**
455
     * Get customer_user_agent
456
     * @return string
457
     */
458
    public function get_customer_user_agent() {
459
        return $this->_data['customer_user_agent'];
460
    }
461
462
    /**
463
     * Get created_via
464
     * @return string
465
     */
466
    public function get_created_via() {
467
        return $this->_data['created_via'];
468
    }
469
470
    /**
471
     * Get order_version
472
     * @return string
473
     */
474
    public function get_order_version() {
475
        return $this->_data['order_version'];
476
    }
477
478
    /**
479
     * Get prices_include_tax
480
     * @return bool
481
     */
482
    public function get_prices_include_tax() {
483
        return (bool) $this->_data['prices_include_tax'];
484
    }
485
486
    /**
487
     * Get customer_note
488
     * @return string
489
     */
490
    public function get_customer_note() {
491
        return $this->_data['customer_note'];
492
    }
493
494
    /**
495
     * Returns the requested address in raw, non-formatted way.
496
     * @since  2.4.0
497
     * @param  string $type Billing or shipping. Anything else besides 'billing' will return shipping address.
498
     * @return array The stored address after filter.
499
     */
500
    public function get_address( $type = 'billing' ) {
501
        if ( 'billing' === $type ) {
502
            $address = array(
503
                'first_name' => $this->get_billing_first_name(),
504
                'last_name'  => $this->get_billing_last_name(),
505
                'company'    => $this->get_billing_company(),
506
                'address_1'  => $this->get_billing_address_1(),
507
                'address_2'  => $this->get_billing_address_2(),
508
                'city'       => $this->get_billing_city(),
509
                'state'      => $this->get_billing_state(),
510
                'postcode'   => $this->get_billing_postcode(),
511
                'country'    => $this->get_billing_country(),
512
                'email'      => $this->get_billing_email(),
513
                'phone'      => $this->get_billing_phone()
514
            );
515
        } else {
516
            $address = array(
517
                'first_name' => $this->get_shipping_first_name(),
518
                'last_name'  => $this->get_shipping_last_name(),
519
                'company'    => $this->get_shipping_company(),
520
                'address_1'  => $this->get_shipping_address_1(),
521
                'address_2'  => $this->get_shipping_address_2(),
522
                'city'       => $this->get_shipping_city(),
523
                'state'      => $this->get_shipping_state(),
524
                'postcode'   => $this->get_shipping_postcode(),
525
                'country'    => $this->get_shipping_country()
526
            );
527
        }
528
        return apply_filters( 'woocommerce_get_order_address', $address, $type, $this );
529
    }
530
531
    /**
532
     * Return the order statuses without wc- internal prefix.
533
     * @return string
534
     */
535
    public function get_status() {
536
        return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->_data['status'], 0, 3 ) ? substr( $this->_data['status'], 3 ) : $this->_data['status'], $this );
537
    }
538
539
    /**
540
     * Alias for get_customer_id().
541
     * @since  2.2
542
     * @return int
543
     */
544
    public function get_user_id() {
545
        return $this->get_customer_id();
546
    }
547
548
    /**
549
     * Get the user associated with the order. False for guests.
550
     *
551
     * @since  2.2
552
     * @return WP_User|false
553
     */
554
    public function get_user() {
555
        return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
556
    }
557
558
    /**
559
     * Get a formatted billing address for the order.
560
     * @return string
561
     */
562
    public function get_formatted_billing_address() {
563
        return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_billing_address', $this->get_address( 'billing' ), $this ) );
564
    }
565
566
    /**
567
     * Get a formatted shipping address for the order.
568
     * @return string
569
     */
570
    public function get_formatted_shipping_address() {
571
        if ( $this->get_shipping_address_1() || $this->get_shipping_address_2() ) {
572
            return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this ) );
573
        } else {
574
            return '';
575
        }
576
    }
577
578
    /**
579
     * Get a formatted shipping address for the order.
580
     *
581
     * @return string
582
     */
583
    public function get_shipping_address_map_url() {
584
        $address = apply_filters( 'woocommerce_shipping_address_map_url_parts', array(
585
            'address_1' => $this->get_shipping_address_1(),
586
            'address_2' => $this->get_shipping_address_2(),
587
            'city'      => $this->get_shipping_city(),
588
            'state'     => $this->get_shipping_state(),
589
            'postcode'  => $this->get_shipping_postcode(),
590
            'country'   => $this->get_shipping_country()
591
        ), $this );
592
        return apply_filters( 'woocommerce_shipping_address_map_url', 'http://maps.google.com/maps?&q=' . urlencode( implode( ', ', $address ) ) . '&z=16', $this );
593
    }
594
595
    /**
596
     * Get a formatted billing full name.
597
     *
598
     * @since 2.4.0
599
     *
600
     * @return string
601
     */
602
    public function get_formatted_billing_full_name() {
603
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_billing_first_name(), $this->get_billing_last_name() );
604
    }
605
606
    /**
607
     * Get a formatted shipping full name.
608
     *
609
     * @since 2.4.0
610
     *
611
     * @return string
612
     */
613
    public function get_formatted_shipping_full_name() {
614
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_shipping_first_name(), $this->get_shipping_last_name() );
615
    }
616
617
    /**
618
     * Get discount_total
619
     * @return string
620
     */
621
    public function get_discount_total() {
622
        $discount_total = wc_format_decimal( $this->_data['discount_total'] );
623
624
        // Backwards compatible total calculation - totals were not stored consistently in old versions.
625
        if ( ( ! $this->get_order_version() || version_compare( $this->get_order_version(), '2.3.7', '<' ) ) && $this->get_prices_include_tax() ) {
626
            $discount_total = $discount_total - $this->get_discount_tax();
627
        }
628
629
        return $discount_total;
630
    }
631
632
    /**
633
     * Get discount_tax
634
     * @return string
635
     */
636
    public function get_discount_tax() {
637
        return wc_format_decimal( $this->_data['discount_tax'] );
638
    }
639
640
    /**
641
     * Get shipping_total
642
     * woocommerce_order_amount_total_shipping filter has been removed to avoid
643
     * these values being modified and then saved back to the DB. There are
644
     * other, later hooks available to change totals on display. e.g.
645
     * woocommerce_get_order_item_totals.
646
     * @return string
647
     */
648
    public function get_shipping_total() {
649
        return wc_format_decimal( $this->_data['shipping_total'] );
650
    }
651
652
    /**
653
     * Gets cart tax amount.
654
     *
655
     * @since 2.6.0 woocommerce_order_amount_cart_tax filter has been removed to avoid
656
     * these values being modified and then saved back to the DB or used in
657
     * calculations. There are other, later hooks available to change totals on
658
     * display. e.g. woocommerce_get_order_item_totals.
659
     * @return float
660
     */
661
    public function get_cart_tax() {
662
        return wc_format_decimal( $this->_data['cart_tax'] );
663
    }
664
665
    /**
666
     * Get shipping_tax.
667
     *
668
     * @since 2.6.0 woocommerce_order_amount_shipping_tax filter has been removed to avoid
669
     * these values being modified and then saved back to the DB or used in
670
     * calculations. There are other, later hooks available to change totals on
671
     * display. e.g. woocommerce_get_order_item_totals.
672
     * @return string
673
     */
674
    public function get_shipping_tax() {
675
        return wc_format_decimal( $this->_data['shipping_tax'] );
676
    }
677
678
    /**
679
     * Order tax is the sum of all taxes.
680
     * @return string
681
     */
682
    public function get_order_tax() {
683
        return wc_round_tax_total( $this->_data['order_tax'] );
684
    }
685
686
    /**
687
     * Get the stored order total. Includes taxes and everything else.
688
     * @return string
689
     */
690
    public function get_order_total() {
691
        return wc_format_decimal( $this->_data['order_total'], wc_get_price_decimals() );
692
    }
693
694
    /**
695
     * Gets the total discount amount.
696
     * @param  bool $ex_tax Show discount excl any tax.
697
     * @return float
698
     */
699
    public function get_total_discount( $ex_tax = true ) {
700
        if ( $ex_tax ) {
701
            $total_discount = $this->get_discount_total();
702
        } else {
703
            $total_discount = $this->get_discount_total() + $this->get_discount_tax();
704
        }
705
        return apply_filters( 'woocommerce_order_amount_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this );
706
    }
707
708
    /**
709
     * Get total tax amount. Alias for get_order_tax().
710
     *
711
     * @since 2.6.0 woocommerce_order_amount_total_tax filter has been removed to avoid
712
     * these values being modified and then saved back to the DB. There are
713
     * other, later hooks available to change totals on display. e.g.
714
     * woocommerce_get_order_item_totals.
715
     * @return float
716
     */
717
    public function get_total_tax() {
718
        return $this->get_order_tax();
719
    }
720
721
    /**
722
     * Gets shipping total. Alias of WC_Order::get_shipping_total().
723
     *
724
     * @since 2.6.0 woocommerce_order_amount_total_shipping filter has been removed to avoid
725
     * these values being modified and then saved back to the DB or used in
726
     * calculations. There are other, later hooks available to change totals on
727
     * display. e.g. woocommerce_get_order_item_totals.
728
     * @return float
729
     */
730
    public function get_total_shipping() {
731
        return $this->get_shipping_total();
732
    }
733
734
    /**
735
     * Gets order grand total. incl. taxes. Used in gateways. Filtered.
736
     * @return float
737
     */
738
    public function get_total() {
739
        return apply_filters( 'woocommerce_order_amount_total', $this->get_order_total(), $this );
740
    }
741
742
    /**
743
     * Gets order subtotal.
744
     * @return float
745
     */
746
    public function get_subtotal() {
747
        $subtotal = 0;
748
749
        foreach ( $this->get_items() as $item ) {
750
            $subtotal += isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
751
        }
752
753
        return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
754
    }
755
756
    /**
757
     * Get taxes, merged by code, formatted ready for output.
758
     *
759
     * @return array
760
     */
761
    public function get_tax_totals() {
762
        $tax_totals = array();
763
764
        foreach ( $this->get_items( 'tax' ) as $key => $tax ) {
765
            $code = $tax[ 'name' ];
766
767 View Code Duplication
            if ( ! isset( $tax_totals[ $code ] ) ) {
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...
768
                $tax_totals[ $code ] = new stdClass();
769
                $tax_totals[ $code ]->amount = 0;
770
            }
771
772
            $tax_totals[ $code ]->id                = $key;
773
            $tax_totals[ $code ]->rate_id           = $tax['rate_id'];
774
            $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
775
            $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
776
            $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
777
            $tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
778
        }
779
780
        return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
781
    }
782
783
    /**
784
     * Gets formatted shipping method title.
785
     * @return string
786
     */
787
    public function get_shipping_method() {
788
        $names = array();
789
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
790
            $names[] = $shipping_method->get_name();
791
        }
792
        return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this );
793
    }
794
795
    /*
796
    |--------------------------------------------------------------------------
797
    | Setters
798
    |--------------------------------------------------------------------------
799
    |
800
    | Functions for setting order data. These should not update anything in the
801
    | database itself and should only change what is stored in the class
802
    | object. However, for backwards compatibility pre 2.6.0 some of these
803
    | setters may handle both.
804
    |
805
    */
806
807
    /**
808
     * Set order ID.
809
     * @since 2.6.0
810
     * @param int $value
811
     */
812
    public function set_order_id( $value ) {
813
        $this->_data['order_id'] = absint( $value );
814
    }
815
816
    /**
817
     * Set parent order ID.
818
     * @since 2.6.0
819
     * @param int $value
820
     */
821
    public function set_parent_id( $value ) {
822
        $this->_data['parent_id'] = absint( $value );
823
    }
824
825
    /**
826
     * Set order status.
827
     * @since 2.6.0
828
     * @param string $new_status Status to change the order to. No internal wc- prefix is required.
829
     * @param string $note (default: '') Optional note to add.
830
     * @param bool $manual is this a manual order status change?
0 ignored issues
show
Documentation introduced by
There is no parameter named $manual. Did you maybe mean $manual_update?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
831
     */
832
    public function set_status( $new_status, $note = '', $manual_update = false ) {
833
        // Remove prefixes and standardize
834
        $current_status = $this->get_status();
835
        $new_status     = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
836
837
        if ( in_array( 'wc-' . $new_status, array_keys( wc_get_order_statuses() ) ) && $new_status !== $current_status ) {
838
            if ( ! empty( $current_status ) ) {
839
                $this->_status_transition = array(
840
                    'original' => ! empty( $this->_status_transition['original'] ) ? $this->_status_transition['original'] : $current_status,
841
                    'note'     => $note ? $note : '',
842
                    'manual'   => (bool) $manual_update
843
                );
844
				if ( 'completed' === $new_status ) {
845
					$this->set_date_completed( current_time( 'timestamp' ) );
846
				}
847
            }
848
            $this->_data['status'] = 'wc-' . $new_status;
849
        }
850
    }
851
852
	/**
853
     * Updates status of order immediately.
854
     * @uses WC_Order::set_status()
855
     */
856
    public function update_status( $new_status, $note = '', $manual = false ) {
857
        if ( ! $this->get_id() ) {
858
            return false;
859
        }
860
		$this->set_status( $new_status, $note, $manual );
861
		$this->save();
862
        return true;
863
    }
864
865
    /**
866
     * Set Order Type
867
     * @param string $value
868
     */
869
    public function set_order_type( $value ) {
870
        $this->_data['order_type'] = $value;
871
    }
872
873
    /**
874
     * Set order_key
875
     * @param string $value
876
     */
877
    public function set_order_key( $value ) {
878
        $this->_data['order_key'] = $value;
879
    }
880
881
    /**
882
     * Set order_currency
883
     * @param string $value
884
     */
885
    public function set_order_currency( $value ) {
886
        $this->_data['order_currency'] = $value;
887
    }
888
889
    /**
890
     * Set date_created
891
     * @param string $timestamp Timestamp
892
     */
893
    public function set_date_created( $timestamp ) {
894
        $this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
895
    }
896
897
    /**
898
     * Set date_modified
899
     * @param string $timestamp
900
     */
901
    public function set_date_modified( $timestamp ) {
902
        $this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
903
    }
904
905
    /**
906
     * Set date_completed
907
     * @param string $timestamp
908
     */
909
    public function set_date_completed( $timestamp ) {
910
        $this->_data['date_completed'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
911
    }
912
913
	/**
914
     * Set date_paid
915
     * @param string $timestamp
916
     */
917
    public function set_date_paid( $timestamp ) {
918
        $this->_data['date_paid'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
919
    }
920
921
    /**
922
     * Set customer_id
923
     * @param int $value
924
     */
925
    public function set_customer_id( $value ) {
926
        $this->_data['customer_id'] = absint( $value );
927
    }
928
929
    /**
930
     * Set billing_first_name
931
     * @param string $value
932
     */
933
    public function set_billing_first_name( $value ) {
934
        $this->_data['billing_first_name'] = $value;
935
    }
936
937
    /**
938
     * Set billing_last_name
939
     * @param string $value
940
     */
941
    public function set_billing_last_name( $value ) {
942
        $this->_data['billing_last_name'] = $value;
943
    }
944
945
    /**
946
     * Set billing_company
947
     * @param string $value
948
     */
949
    public function set_billing_company( $value ) {
950
        $this->_data['billing_company'] = $value;
951
    }
952
953
    /**
954
     * Set billing_address_1
955
     * @param string $value
956
     */
957
    public function set_billing_address_1( $value ) {
958
        $this->_data['billing_address_1'] = $value;
959
    }
960
961
    /**
962
     * Set billing_address_2
963
     * @param string $value
964
     */
965
    public function set_billing_address_2( $value ) {
966
        $this->_data['billing_address_2'] = $value;
967
    }
968
969
    /**
970
     * Set billing_city
971
     * @param string $value
972
     */
973
    public function set_billing_city( $value ) {
974
        $this->_data['billing_city'] = $value;
975
    }
976
977
    /**
978
     * Set billing_state
979
     * @param string $value
980
     */
981
    public function set_billing_state( $value ) {
982
        $this->_data['billing_state'] = $value;
983
    }
984
985
    /**
986
     * Set billing_postcode
987
     * @param string $value
988
     */
989
    public function set_billing_postcode( $value ) {
990
        $this->_data['billing_postcode'] = $value;
991
    }
992
993
    /**
994
     * Set billing_country
995
     * @param string $value
996
     */
997
    public function set_billing_country( $value ) {
998
        $this->_data['billing_country'] = $value;
999
    }
1000
1001
    /**
1002
     * Set billing_email
1003
     * @param string $value
1004
     */
1005
    public function set_billing_email( $value ) {
1006
		$value = sanitize_email( $value );
1007
        $this->_data['billing_email'] = is_email( $value ) ? $value : '';
1008
    }
1009
1010
    /**
1011
     * Set billing_phone
1012
     * @param string $value
1013
     */
1014
    public function set_billing_phone( $value ) {
1015
        $this->_data['billing_phone'] = $value;
1016
    }
1017
1018
    /**
1019
     * Set shipping_first_name
1020
     * @param string $value
1021
     */
1022
    public function set_shipping_first_name( $value ) {
1023
        $this->_data['shipping_first_name'] = $value;
1024
    }
1025
1026
    /**
1027
     * Set shipping_last_name
1028
     * @param string $value
1029
     */
1030
    public function set_shipping_last_name( $value ) {
1031
        $this->_data['shipping_last_name'] = $value;
1032
    }
1033
1034
    /**
1035
     * Set shipping_company
1036
     * @param string $value
1037
     */
1038
    public function set_shipping_company( $value ) {
1039
        $this->_data['shipping_company'] = $value;
1040
    }
1041
1042
    /**
1043
     * Set shipping_address_1
1044
     * @param string $value
1045
     */
1046
    public function set_shipping_address_1( $value ) {
1047
        $this->_data['shipping_address_1'] = $value;
1048
    }
1049
1050
    /**
1051
     * Set shipping_address_2
1052
     * @param string $value
1053
     */
1054
    public function set_shipping_address_2( $value ) {
1055
        $this->_data['shipping_address_2'] = $value;
1056
    }
1057
1058
    /**
1059
     * Set shipping_city
1060
     * @param string $value
1061
     */
1062
    public function set_shipping_city( $value ) {
1063
        $this->_data['shipping_city'] = $value;
1064
    }
1065
1066
    /**
1067
     * Set shipping_state
1068
     * @param string $value
1069
     */
1070
    public function set_shipping_state( $value ) {
1071
        $this->_data['shipping_state'] = $value;
1072
    }
1073
1074
    /**
1075
     * Set shipping_postcode
1076
     * @param string $value
1077
     */
1078
    public function set_shipping_postcode( $value ) {
1079
        $this->_data['shipping_postcode'] = $value;
1080
    }
1081
1082
    /**
1083
     * Set shipping_country
1084
     * @param string $value
1085
     */
1086
    public function set_shipping_country( $value ) {
1087
        $this->_data['shipping_country'] = $value;
1088
    }
1089
1090
    /**
1091
     * Set discount_total
1092
     * @param string $value
1093
     */
1094
    public function set_discount_total( $value ) {
1095
        $this->_data['discount_total'] = wc_format_decimal( $value );
1096
    }
1097
1098
    /**
1099
     * Set discount_tax
1100
     * @param string $value
1101
     */
1102
    public function set_discount_tax( $value ) {
1103
        $this->_data['discount_tax'] = wc_format_decimal( $value );
1104
    }
1105
1106
    /**
1107
     * Set shipping_total
1108
     * @param string $value
1109
     */
1110
    public function set_shipping_total( $value ) {
1111
        $this->_data['shipping_total'] = wc_format_decimal( $value );
1112
    }
1113
1114
    /**
1115
     * Set shipping_tax
1116
     * @param string $value
1117
     */
1118
    public function set_shipping_tax( $value ) {
1119
        $this->_data['shipping_tax'] = wc_format_decimal( $value );
1120
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1121
    }
1122
1123
    /**
1124
     * Set cart tax
1125
     * @param string $value
1126
     */
1127
    public function set_cart_tax( $value ) {
1128
        $this->_data['cart_tax'] = wc_format_decimal( $value );
1129
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1130
    }
1131
1132
    /**
1133
     * Sets order tax (sum of cart and shipping tax). Used internaly only.
1134
     * @access protected
1135
     * @param string $value
1136
     */
1137
    protected function set_order_tax( $value ) {
1138
        $this->_data['order_tax'] = wc_format_decimal( $value );
1139
    }
1140
1141
    /**
1142
     * Set order_total
1143
     * @param string $value
1144
     */
1145
    public function set_order_total( $value ) {
1146
        $this->_data['order_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
1147
    }
1148
1149
    /**
1150
     * Set the payment method ID.
1151
     * @since 2.2.0
1152
     * @param string $value Supports WC_Payment_Gateway for bw compatibility with < 2.6
1153
     */
1154
    public function set_payment_method( $value ) {
1155
        if ( is_object( $value ) ) {
1156
            update_post_meta( $this->get_id(), '_payment_method', $value->id );
1157
            update_post_meta( $this->get_id(), '_payment_method_title', $value->get_title() );
1158
            $this->set_payment_method( $value->id );
1159
            $this->set_payment_method_title( $value->get_title() );
1160
        } else {
1161
            $this->_data['payment_method'] = $value;
1162
        }
1163
    }
1164
1165
    /**
1166
     * Set payment_method_title
1167
     * @param string $value
1168
     */
1169
    public function set_payment_method_title( $value ) {
1170
        $this->_data['payment_method_title'] = $value;
1171
    }
1172
1173
    /**
1174
     * Set transaction_id
1175
     * @param string $value
1176
     */
1177
    public function set_transaction_id( $value ) {
1178
        $this->_data['transaction_id'] = $value;
1179
    }
1180
1181
    /**
1182
     * Set customer_ip_address
1183
     * @param string $value
1184
     */
1185
    public function set_customer_ip_address( $value ) {
1186
        $this->_data['customer_ip_address'] = $value;
1187
    }
1188
1189
    /**
1190
     * Set customer_user_agent
1191
     * @param string $value
1192
     */
1193
    public function set_customer_user_agent( $value ) {
1194
        $this->_data['customer_user_agent'] = $value;
1195
    }
1196
1197
    /**
1198
     * Set created_via
1199
     * @param string $value
1200
     */
1201
    public function set_created_via( $value ) {
1202
        $this->_data['created_via'] = $value;
1203
    }
1204
1205
    /**
1206
     * Set order_version
1207
     * @param string $value
1208
     */
1209
    public function set_order_version( $value ) {
1210
        $this->_data['order_version'] = $value;
1211
    }
1212
1213
    /**
1214
     * Set prices_include_tax
1215
     * @param bool $value
1216
     */
1217
    public function set_prices_include_tax( $value ) {
1218
        $this->_data['prices_include_tax'] = (bool) $value;
1219
    }
1220
1221
    /**
1222
     * Set customer_note
1223
     * @param string $value
1224
     */
1225
    public function set_customer_note( $value ) {
1226
        $this->_data['customer_note'] = $value;
1227
    }
1228
1229
    /**
1230
     * Set the customer address.
1231
     * @since 2.2.0
1232
     * @param array $address Address data.
1233
     * @param string $type billing or shipping.
1234
     */
1235
    public function set_address( $address, $type = 'billing' ) {
1236
        foreach ( $address as $key => $value ) {
1237
            update_post_meta( $this->get_id(), "_{$type}_" . $key, $value );
1238
            if ( method_exists( $this, "set_{$type}_{$key}" ) ) {
1239
                $this->{"set_{$type}_{$key}"}( $value );
1240
            }
1241
        }
1242
    }
1243
1244
    /**
1245
     * Set an order total.
1246
     * @since 2.2.0
1247
     * @param float $amount
1248
     * @param string $total_type
1249
     * @return bool
1250
     */
1251
    public function set_total( $amount, $total_type = 'total' ) {
1252
        if ( ! in_array( $total_type, array( 'shipping', 'tax', 'shipping_tax', 'total', 'cart_discount', 'cart_discount_tax' ) ) ) {
1253
            return false;
1254
        }
1255
1256
        switch ( $total_type ) {
1257
            case 'total' :
1258
                $amount = wc_format_decimal( $amount, wc_get_price_decimals() );
1259
                $this->set_order_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amoun...c_get_price_decimals()) on line 1258 can also be of type array; however, WC_Abstract_Order::set_order_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1260
                update_post_meta( $this->get_id(), '_order_total', $amount );
1261
                break;
1262
            case 'cart_discount' :
1263
                $amount = wc_format_decimal( $amount );
1264
                $this->set_discount_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1263 can also be of type array; however, WC_Abstract_Order::set_discount_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1265
                update_post_meta( $this->get_id(), '_cart_discount', $amount );
1266
                break;
1267
            case 'cart_discount_tax' :
1268
                $amount = wc_format_decimal( $amount );
1269
                $this->set_discount_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1268 can also be of type array; however, WC_Abstract_Order::set_discount_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1270
                update_post_meta( $this->get_id(), '_cart_discount_tax', $amount );
1271
                break;
1272
            case 'shipping' :
1273
                $amount = wc_format_decimal( $amount );
1274
                $this->set_shipping_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1273 can also be of type array; however, WC_Abstract_Order::set_shipping_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1275
                update_post_meta( $this->get_id(), '_order_shipping', $amount );
1276
                break;
1277
            case 'shipping_tax' :
1278
                $amount = wc_format_decimal( $amount );
1279
                $this->set_shipping_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1278 can also be of type array; however, WC_Abstract_Order::set_shipping_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1280
                update_post_meta( $this->get_id(), '_order_shipping_tax', $amount );
1281
                break;
1282
            case 'tax' :
1283
                $amount = wc_format_decimal( $amount );
1284
                $this->set_cart_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1283 can also be of type array; however, WC_Abstract_Order::set_cart_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1285
                update_post_meta( $this->get_id(), '_order_tax', $amount );
1286
                break;
1287
        }
1288
1289
        return true;
1290
    }
1291
1292
    /*
1293
    |--------------------------------------------------------------------------
1294
    | CRUD methods
1295
    |--------------------------------------------------------------------------
1296
    |
1297
    | Methods which create, read, update and delete orders from the database.
1298
    | Written in abstract fashion so that the way orders are stored can be
1299
    | changed more easily in the future.
1300
    |
1301
    | A save method is included for convenience (chooses update or create based
1302
    | on if the order exists yet).
1303
    |
1304
    */
1305
1306
    /**
1307
     * Insert data into the database.
1308
     * @since 2.6.0
1309
     * @access protected
1310
     */
1311
    public function create() {
1312
        // Set random key
1313
        $this->set_order_key( uniqid( 'order_' ) );
1314
1315
        $order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', array(
1316
            'post_type'     => $this->get_order_type(),
1317
            'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1318
            'ping_status'   => 'closed',
1319
            'post_author'   => 1,
1320
            'post_title'    => sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) ),
1321
            'post_password' => $this->get_order_key(),
1322
            'post_parent'   => $this->get_parent_id()
1323
        ) ), true );
1324
1325
        if ( $order_id ) {
1326
            $this->set_order_id( $order_id );
1327
1328
            // Set meta data
1329
            update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1330
            update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1331
            update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1332
            update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1333
            update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1334
            update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1335
            update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1336
            update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1337
            update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1338
            update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1339
            update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1340
            update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1341
            update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1342
            update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1343
            update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1344
            update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1345
            update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1346
            update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1347
            update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1348
            update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1349
            update_post_meta( $order_id, '_payment_method', $this->get_payment_method() );
1350
            update_post_meta( $order_id, '_payment_method_title', $this->get_payment_method_title() );
1351
            update_post_meta( $order_id, '_transaction_id', $this->get_transaction_id() );
1352
            update_post_meta( $order_id, '_customer_user', $this->get_customer_id() );
1353
            update_post_meta( $order_id, '_customer_ip_address', $this->get_customer_ip_address() );
1354
            update_post_meta( $order_id, '_customer_user_agent', $this->get_customer_user_agent() );
1355
            update_post_meta( $order_id, '_created_via', $this->get_created_via() );
1356
            update_post_meta( $order_id, '_order_version', $this->get_order_version() );
1357
            update_post_meta( $order_id, '_prices_include_tax', $this->get_prices_include_tax() );
1358
            update_post_meta( $order_id, '_completed_date', $this->get_date_completed() );
1359
			update_post_meta( $order_id, '_paid_date', $this->get_date_paid() );
1360
            update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1361
            update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1362
            update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1363
            update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1364
            update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1365
            update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1366
            update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1367
            update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1368
        }
1369
    }
1370
1371
    /**
1372
     * Read from the database.
1373
     * @since 2.6.0
1374
     * @access protected
1375
     * @param int $id ID of object to read.
1376
     */
1377
    public function read( $id ) {
1378
        $post_object = get_post( $id );
1379
        $order_id    = absint( $post_object->ID );
1380
1381
        // Map standard post data
1382
        $this->set_order_id( $order_id );
1383
        $this->set_date_created( $post_object->post_date );
1384
        $this->set_date_modified( $post_object->post_modified );
1385
        $this->set_status( $post_object->post_status );
1386
        $this->set_customer_note( $post_object->post_excerpt );
1387
1388
        // Map meta data
1389
        $this->set_customer_id( get_post_meta( $order_id, '_customer_user', true ) );
1390
        $this->set_order_key( get_post_meta( $order_id, '_order_key', true ) );
1391
        $this->set_order_currency( get_post_meta( $order_id, '_order_currency', true ) );
1392
        $this->set_billing_first_name( get_post_meta( $order_id, '_billing_first_name', true ) );
1393
        $this->set_billing_last_name( get_post_meta( $order_id, '_billing_last_name', true ) );
1394
        $this->set_billing_company( get_post_meta( $order_id, '_billing_company', true ) );
1395
        $this->set_billing_address_1( get_post_meta( $order_id, '_billing_address_1', true ) );
1396
        $this->set_billing_address_2( get_post_meta( $order_id, '_billing_address_2', true ) );
1397
        $this->set_billing_city( get_post_meta( $order_id, '_billing_city', true ) );
1398
        $this->set_billing_state( get_post_meta( $order_id, '_billing_state', true ) );
1399
        $this->set_billing_postcode( get_post_meta( $order_id, '_billing_postcode', true ) );
1400
        $this->set_billing_country( get_post_meta( $order_id, '_billing_country', true ) );
1401
        $this->set_billing_email( get_post_meta( $order_id, 'billing_email', true ) );
1402
        $this->set_billing_phone( get_post_meta( $order_id, '_billing_phone', true ) );
1403
        $this->set_shipping_first_name( get_post_meta( $order_id, '_shipping_first_name', true ) );
1404
        $this->set_shipping_last_name( get_post_meta( $order_id, '_shipping_last_name', true ) );
1405
        $this->set_shipping_company( get_post_meta( $order_id, '_shipping_company', true ) );
1406
        $this->set_shipping_address_1( get_post_meta( $order_id, '_shipping_address_1', true ) );
1407
        $this->set_shipping_address_2( get_post_meta( $order_id, '_shipping_address_2', true ) );
1408
        $this->set_shipping_city( get_post_meta( $order_id, '_shipping_city', true ) );
1409
        $this->set_shipping_state( get_post_meta( $order_id, '_shipping_state', true ) );
1410
        $this->set_shipping_postcode( get_post_meta( $order_id, '_shipping_postcode', true ) );
1411
        $this->set_shipping_country( get_post_meta( $order_id, '_shipping_country', true ) );
1412
        $this->set_payment_method( get_post_meta( $order_id, '_payment_method', true ) );
1413
        $this->set_payment_method_title( get_post_meta( $order_id, '_payment_method_title', true ) );
1414
        $this->set_transaction_id( get_post_meta( $order_id, '_transaction_id', true ) );
1415
        $this->set_customer_ip_address( get_post_meta( $order_id, '_customer_ip_address', true ) );
1416
        $this->set_customer_user_agent(get_post_meta( $order_id, '_customer_user_agent', true ) );
1417
        $this->set_created_via( get_post_meta( $order_id, '_created_via', true ) );
1418
        $this->set_order_version( get_post_meta( $order_id, '_order_version', true ) );
1419
        $this->set_prices_include_tax( get_post_meta( $order_id, '_prices_include_tax', true ) );
1420
        $this->set_date_completed( get_post_meta( $order_id, '_completed_date', true ) );
1421
		$this->set_date_paid( get_post_meta( $order_id, '_paid_date', true ) );
1422
1423
        // Map totals
1424
        $this->set_discount_total( get_post_meta( $order_id, '_cart_discount', true ) );
1425
        $this->set_discount_tax( get_post_meta( $order_id, '_cart_discount_tax', true ) );
1426
        $this->set_shipping_total( get_post_meta( $order_id, '_order_shipping', true ) );
1427
        $this->set_shipping_tax( get_post_meta( $order_id, '_order_shipping_tax', true ) );
1428
        $this->set_cart_tax( get_post_meta( $order_id, '_order_tax', true ) );
1429
        $this->set_order_total( get_post_meta( $order_id, '_order_total', true ) );
1430
1431
        // Map user data
1432
        if ( empty( $this->get_billing_email() ) && ( $user = $this->get_user() ) ) {
1433
            $this->set_billing_email( $user->user_email );
1434
        }
1435
1436
        // Orders store the state of prices including tax when created.
1437
        $this->prices_include_tax = metadata_exists( 'post', $order_id, '_prices_include_tax' ) ? 'yes' === get_post_meta( $order_id, '_prices_include_tax', true ) : 'yes' === get_option( 'woocommerce_prices_include_tax' );
1438
    }
1439
1440
    /**
1441
     * Update data in the database.
1442
     * @since 2.6.0
1443
     * @access protected
1444
     */
1445
    public function update() {
1446
        global $wpdb;
1447
1448
        $order_id = $this->get_id();
1449
1450
        $wpdb->update(
1451
            $wpdb->posts,
1452
            array(
1453
                'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
1454
                'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
1455
                'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1456
                'post_parent'   => $this->get_parent_id()
1457
            ),
1458
            array(
1459
                'ID' => $order_id
1460
            )
1461
        );
1462
1463
        // Update meta data
1464
        update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1465
        update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1466
        update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1467
        update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1468
        update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1469
        update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1470
        update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1471
        update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1472
        update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1473
        update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1474
        update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1475
        update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1476
        update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1477
        update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1478
        update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1479
        update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1480
        update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1481
        update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1482
        update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1483
        update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1484
        update_post_meta( $order_id, '_payment_method', $this->get_payment_method() );
1485
        update_post_meta( $order_id, '_payment_method_title', $this->get_payment_method_title() );
1486
        update_post_meta( $order_id, '_transaction_id', $this->get_transaction_id() );
1487
        update_post_meta( $order_id, '_customer_user', $this->get_customer_id() );
1488
        update_post_meta( $order_id, '_customer_ip_address', $this->get_customer_ip_address() );
1489
        update_post_meta( $order_id, '_customer_user_agent', $this->get_customer_user_agent() );
1490
        update_post_meta( $order_id, '_created_via', $this->get_created_via() );
1491
        update_post_meta( $order_id, '_order_version', $this->get_order_version() );
1492
        update_post_meta( $order_id, '_prices_include_tax', $this->get_prices_include_tax() );
1493
        update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1494
        update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1495
        update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1496
        update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1497
        update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1498
        update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1499
        update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1500
        update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1501
1502
        if ( $this->_status_transition ) {
1503
            if ( ! empty( $this->_status_transition['original'] ) ) {
1504
                $transition_note = sprintf( __( 'Order status changed from %s to %s.', 'woocommerce' ), wc_get_order_status_name( $this->_status_transition['original'] ), wc_get_order_status_name( $this->get_status() ) );
1505
1506
                do_action( 'woocommerce_order_status_' . $this->_status_transition['original'] . '_to_' . $this->get_status(), $this->get_id() );
1507
                do_action( 'woocommerce_order_status_changed', $this->get_id(), $this->_status_transition['original'], $this->get_status() );
1508
            } else {
1509
                $transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $this->get_status() ) );
1510
            }
1511
1512
            do_action( 'woocommerce_order_status_' . $this->get_status(), $this->get_id() );
1513
1514
            // Note the transition occured
1515
            $this->add_order_note( trim( $this->_status_transition['note'] . ' ' . $transition_note ), 0, $this->_status_transition['manual'] );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Abstract_Order as the method add_order_note() does only exist in the following sub-classes of WC_Abstract_Order: WC_Order. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1516
1517
            // This has ran, so reset status transition variable
1518
            $this->_status_transition = false;
1519
        }
1520
    }
1521
1522
    /**
1523
     * Delete data from the database.
1524
     * @since 2.6.0
1525
     * @access protected
1526
     */
1527
    public function delete() {
1528
        wp_delete_post( $this->get_id() );
1529
    }
1530
1531
    /**
1532
     * Save data to the database.
1533
     * @since 2.6.0
1534
     * @access protected
1535
     */
1536
    public function save() {
1537
        if ( ! $this->get_id() ) {
1538
            $this->create();
1539
        } else {
1540
            $this->update();
1541
        }
1542
        wc_delete_shop_order_transients( $this->get_id() );
1543
    }
1544
1545
    /*
1546
    |--------------------------------------------------------------------------
1547
    | Order Item Handling
1548
    |--------------------------------------------------------------------------
1549
    |
1550
    | Order items are used for products, taxes, shipping, and fees within
1551
    | each order.
1552
    |
1553
    */
1554
1555
    /**
1556
     * Return an array of items/products within this order.
1557
     *
1558
     * @param string|array $type Types of line items to get (array or string).
1559
     * @return Array of WC_Order_item
1560
     */
1561
    public function get_items( $type = 'line_item' ) {
1562
        global $wpdb;
1563
1564
        $type            = ! is_array( $type ) ? array( $type ) : $type;
1565
        $items           = array();
1566
        $get_items_sql   = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d ", $this->get_id() );
1567
        $get_items_sql  .= "AND order_item_type IN ( '" . implode( "','", array_map( 'esc_sql', $type ) ) . "' ) ORDER BY order_item_id;";
1568
        $raw_items       = $wpdb->get_results( $get_items_sql );
1569
1570
        foreach ( $raw_items as $item ) {
1571
            $item                                = $this->get_item( $item );
1572
            $items[ $item->get_order_item_id() ] = $item;
1573
        }
1574
1575
        return apply_filters( 'woocommerce_order_get_items', $items, $this );
1576
    }
1577
1578
    /**
1579
     * Get an order item object, based on it's type.
1580
     * @param  int $item_id
1581
     * @return WC_Order_Item
1582
     */
1583
    public function get_item( $item_id ) {
1584
        return WC_Order_Factory::get_order_item( $item_id );
1585
    }
1586
1587
    /**
1588
     * Display meta data belonging to an item. @todo
1589
     * @param  array $item
1590
     */
1591
    public function display_item_meta( $item ) {
1592
        $product   = $this->get_product_from_item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type array, but the function expects a object.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1593
        $item_meta = new WC_Order_Item_Meta( $item, $product );
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->get_product_from_item($item) on line 1592 can also be of type boolean; however, WC_Order_Item_Meta::__construct() does only seem to accept object<WC_Product>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1594
        $item_meta->display();
1595
    }
1596
1597
    /**
1598
     * Return an array of fees within this order.
1599
     *
1600
     * @return array
1601
     */
1602
    public function get_fees() {
1603
        return $this->get_items( 'fee' );
1604
    }
1605
1606
    /**
1607
     * Return an array of taxes within this order.
1608
     *
1609
     * @return array
1610
     */
1611
    public function get_taxes() {
1612
        return $this->get_items( 'tax' );
1613
    }
1614
1615
    /**
1616
     * Return an array of shipping costs within this order.
1617
     *
1618
     * @return array
1619
     */
1620
    public function get_shipping_methods() {
1621
        return $this->get_items( 'shipping' );
1622
    }
1623
1624
    /**
1625
     * Get coupon codes only.
1626
     *
1627
     * @return array
1628
     */
1629
    public function get_used_coupons() {
1630
        return array_map( 'trim', wp_list_pluck( $this->get_items( 'coupon' ), 'name' ) );
1631
    }
1632
1633
    /**
1634
     * Gets the count of order items of a certain type.
1635
     *
1636
     * @param string $item_type
1637
     * @return string
1638
     */
1639
    public function get_item_count( $item_type = '' ) {
1640
        if ( empty( $item_type ) ) {
1641
            $item_type = array( 'line_item' );
1642
        }
1643
        if ( ! is_array( $item_type ) ) {
1644
            $item_type = array( $item_type );
1645
        }
1646
1647
        $items = $this->get_items( $item_type );
1648
        $count = 0;
1649
1650
        foreach ( $items as $item ) {
1651
            $count += $item->get_qty();
1652
        }
1653
1654
        return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this );
1655
    }
1656
1657
    /**
1658
     * Remove all line items (products, coupons, shipping, taxes) from the order.
1659
     *
1660
     * @param string $type Order item type. Default null.
1661
     */
1662
    public function remove_order_items( $type = null ) {
1663
        global $wpdb;
1664
1665
        if ( ! empty( $type ) ) {
1666
            $wpdb->query( $wpdb->prepare( "DELETE FROM itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id AND items.order_id = %d AND items.order_item_type = %s", $this->get_id(), $type ) );
1667
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->get_id(), $type ) );
1668
        } else {
1669
            $wpdb->query( $wpdb->prepare( "DELETE FROM itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id and items.order_id = %d", $this->get_id() ) );
1670
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->get_id() ) );
1671
        }
1672
    }
1673
1674
    /**
1675
     * Add a product line item to the order.
1676
     * Order must be saved prior to adding items.
1677
     *
1678
     * @since 2.2
1679
     * @param \WC_Product $product
1680
     * @param int $qty Line item quantity.
1681
     * @param array $args
1682
     * @return int updated order item ID
1683
     */
1684
    public function add_product( $product, $qty = 1, $args = array() ) {
1685
        $args = wp_parse_args( $args, array(
1686
            'name'         => $product->get_title(),
1687
            'qty'          => absint( $qty ),
1688
            'tax_class'    => $product->get_tax_class(),
1689
            'product_id'   => $product->id,
1690
            'variation_id' => isset( $product->variation_id ) ? $product->variation_id : 0,
1691
            'variation'    => array(),
1692
            'subtotal'     => $product->get_price_excluding_tax( $qty ),
1693
            'subtotal_tax' => 0,
1694
            'total'        => $product->get_price_excluding_tax( $qty ),
1695
            'total_tax'    => 0,
1696
            'taxes'        => array(
1697
                'subtotal' => array(),
1698
                'total'    => array()
1699
            )
1700
        ) );
1701
        $item = new WC_Order_Item_Product();
1702
        $item_id = $this->update_product( $item, $product, $args );
1703
1704
		do_action( 'woocommerce_order_add_product', $this->get_id(), $item->get_order_item_id(), $product, $qty, $args );
1705
1706
		return $item_id;
1707
    }
1708
1709
    /**
1710
     * Update a line item for the order.
1711
     *
1712
     * Note this does not update order totals.
1713
     *
1714
     * @since 2.2
1715
     * @param object|int $item order item ID or item object.
1716
     * @param WC_Product $product
1717
     * @param array $args data to update.
1718
     * @return int updated order item ID
1719
     */
1720
    public function update_product( $item, $product, $args ) {
1721
        if ( is_numeric( $item ) ) {
1722
            $item = $this->get_item( $item );
1723
        }
1724
1725
        if ( ! is_object( $product ) || ! $item->is_type( 'line_item' ) ) {
1726
            return false;
1727
        }
1728
1729
        if ( ! $this->get_id() ) {
1730
            $this->save();
1731
        }
1732
1733
		$item->set_order_id( $this->get_id() );
1734
1735
        if ( ! $item->get_order_item_id() ) {
1736
            $inserting = true;
1737
        } else {
1738
            $inserting = false;
1739
        }
1740
1741
        if ( isset( $args['name'] ) ) {
1742
            $item->set_name( $args['name'] );
1743
        }
1744
1745
        if ( isset( $args['qty'] ) ) {
1746
            $item->set_qty( $args['qty'] );
1747
1748
            if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
1749
                $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ), true );
1750
            }
1751
1752
            $item->set_line_subtotal( $product->get_price_excluding_tax( $args['qty'] ) );
1753
            $item->set_line_total( $product->get_price_excluding_tax( $args['qty'] ) );
1754
        }
1755
1756
        if ( isset( $args['tax_class'] ) ) {
1757
            $item->set_tax_class( $args['tax_class'] );
1758
        }
1759
1760
        if ( isset( $args['product_id'] ) ) {
1761
            $item->set_product_id( $args['product_id'] );
1762
        }
1763
1764
        if ( isset( $args['variation_id'] ) ) {
1765
            $item->set_variation_id( $args['variation_id'] );
1766
        }
1767
1768
        if ( isset( $args['variation'] ) && is_array( $args['variation'] ) ) {
1769
            foreach ( $args['variation'] as $key => $value ) {
1770
                $item->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true );
1771
            }
1772
        }
1773
1774
        if ( isset( $args['totals'] ) ) {
1775
            // BW compatibility with old args
1776
            if ( isset( $args['totals']['subtotal'] ) ) {
1777
                $args['subtotal'] = $args['totals']['subtotal'];
1778
            }
1779
            if ( isset( $args['totals']['total'] ) ) {
1780
                $args['total'] = $args['totals']['total'];
1781
            }
1782
            if ( isset( $args['totals']['subtotal_tax'] ) ) {
1783
                $args['subtotal_tax'] = $args['totals']['subtotal_tax'];
1784
            }
1785
            if ( isset( $args['totals']['tax'] ) ) {
1786
                $args['total_tax'] = $args['totals']['tax'];
1787
            }
1788
            if ( isset( $args['totals']['tax_data'] ) ) {
1789
                $args['taxes'] = $args['totals']['tax_data'];
1790
            }
1791
        }
1792
1793
        if ( isset( $args['subtotal'] ) ) {
1794
            $item->set_subtotal( $args['subtotal'] );
1795
        }
1796
        if ( isset( $args['total'] ) ) {
1797
            $item->set_total( $args['total'] );
1798
        }
1799
        if ( isset( $args['subtotal_tax'] ) ) {
1800
            $item->set_line_subtotal_tax( $args['subtotal_tax'] );
1801
        }
1802
        if ( isset( $args['total_tax'] ) ) {
1803
            $item->set_total_tax( $args['total_tax'] );
1804
        }
1805
        if ( isset( $args['taxes'] ) ) {
1806
            $item->set_taxes( $args['taxes'] );
1807
        }
1808
1809
        $item->save();
1810
1811
        if ( ! $inserting ) {
1812
            do_action( 'woocommerce_order_edit_product', $this->get_id(), $item->get_order_item_id(), $args, $product );
1813
        }
1814
1815
        return $item->get_order_item_id();
1816
    }
1817
1818
    /**
1819
     * Add coupon code to the order.
1820
     * Order must be saved prior to adding items.
1821
     *
1822
     * @param string $code
1823
     * @param int $discount_amount
1824
     * @param int $discount_amount_tax "Discounted" tax - used for tax inclusive prices.
1825
     * @return int updated order item ID
1826
     */
1827
    public function add_coupon( $code, $discount_amount = 0, $discount_amount_tax = 0 ) {
1828
        $args = wp_parse_args( $args, array(
1829
            'code'                => $code,
1830
            'discount_amount'     => $discount_amount,
1831
            'discount_amount_tax' => $discount_amount_tax
1832
        ) );
1833
        $item = new WC_Order_Item_Coupon();
1834
        $item_id = $this->update_coupon( $item, $args );
1835
1836
		do_action( 'woocommerce_order_add_coupon', $this->get_id(), $item->get_order_item_id(), $code, $discount_amount, $discount_amount_tax );
1837
1838
		return $item_id;
1839
    }
1840
1841
    /**
1842
     * Update coupon for order. Note this does not update order totals.
1843
     * @since 2.2
1844
     * @param object|int $item
1845
     * @param array $args
1846
     * @return int updated order item ID
1847
     */
1848
    public function update_coupon( $item, $args ) {
1849
        if ( is_numeric( $item ) ) {
1850
            $item = $this->get_item( $item );
1851
        }
1852
1853
        if ( ! is_object( $product ) || ! $item->is_type( 'coupon' ) ) {
1854
            return false;
1855
        }
1856
1857
        if ( ! $this->get_id() ) {
1858
            $this->save();
1859
        }
1860
1861
		$item->set_order_id( $this->get_id() );
1862
1863
        if ( ! $item->get_order_item_id() ) {
1864
            $inserting = true;
1865
        } else {
1866
            $inserting = false;
1867
        }
1868
1869
        if ( isset( $args['code'] ) ) {
1870
            $item->set_coupon_code( $args['code'] );
1871
        }
1872
        if ( isset( $args['discount_amount'] ) ) {
1873
            $item->set_discount_amount( $args['discount_amount'] );
1874
        }
1875
        if ( isset( $args['discount_amount_tax'] ) ) {
1876
            $item->set_discount_amount_tax( $args['discount_amount_tax'] );
1877
        }
1878
1879
        $item->save();
1880
1881
        if ( ! $inserting ) {
1882
            do_action( 'woocommerce_order_update_coupon', $this->get_id(), $item->get_order_item_id(), $args );
1883
        }
1884
1885
        return $item->get_order_item_id();
1886
    }
1887
1888
    /**
1889
     * Add a shipping row to the order.
1890
     * Order must be saved prior to adding items.
1891
     *
1892
     * @param WC_Shipping_Rate shipping_rate
1893
     * @return int updated order item ID
1894
     */
1895
    public function add_shipping( $shipping_rate ) {
1896
        $args = wp_parse_args( $args, array(
1897
            'method_title' => $shipping_rate->label,
1898
            'method_id'    => $shipping_rate->id,
1899
            'cost'         => wc_format_decimal( $shipping_rate->cost ),
1900
            'taxes'        => $shipping_rate->taxes,
1901
            'meta'         => $shipping_rate->get_meta_data(),
1902
        ) );
1903
1904
        $item = new WC_Order_Item_Shipping();
1905
        $item_id = $this->update_shipping( $item, $args );
1906
1907
		do_action( 'woocommerce_order_add_shipping', $this->get_id(), $item->get_order_item_id(), $shipping_rate );
1908
1909
		return $item_id;
1910
    }
1911
1912
    /**
1913
     * Update shipping method for order.
1914
     *
1915
     * Note this does not update the order total.
1916
     *
1917
     * @since 2.2
1918
     * @param object|int $item
1919
     * @param array $args
1920
     * @return int updated order item ID
1921
     */
1922
    public function update_shipping( $item, $args ) {
1923
        if ( is_numeric( $item ) ) {
1924
            $item = $this->get_item( $item );
1925
        }
1926
1927
        if ( ! is_object( $product ) || ! $item->is_type( 'shipping' ) ) {
1928
            return false;
1929
        }
1930
1931
        if ( ! $this->get_id() ) {
1932
            $this->save();
1933
        }
1934
1935
		$item->set_order_id( $this->get_id() );
1936
1937
        if ( ! $item->get_order_item_id() ) {
1938
            $inserting = true;
1939
        } else {
1940
            $inserting = false;
1941
        }
1942
1943
        if ( isset( $args['method_title'] ) ) {
1944
            $item->set_method_title( $args['method_title'] );
1945
        }
1946
1947
        if ( isset( $args['method_id'] ) ) {
1948
            $item->set_method_id( $args['method_id'] );
1949
        }
1950
1951
        if ( isset( $args['cost'] ) ) {
1952
            // Get old cost before updating
1953
            $old_cost = $item->get_cost();
1954
1955
            // Update
1956
            $item->set_cost( $args['cost'] );
1957
1958
            // Update total
1959
            $this->set_total( $this->get_total_shipping() - wc_format_decimal( $old_cost ) + $item->get_cost(), 'shipping' );
1960
        }
1961
1962
        if ( isset( $args['taxes'] ) && is_array( $args['taxes'] ) ) {
1963
            $item->set_taxes( $args['taxes'] );
1964
        }
1965
1966
        if ( isset( $args['meta'] ) && is_array( $args['meta'] ) ) {
1967
			foreach ( $args['meta'] as $key => $value ) {
1968
				$item->update_meta_data( $key, $value );
1969
			}
1970
		}
1971
1972
        $item->save();
1973
1974
        if ( ! $inserting ) {
1975
            do_action( 'woocommerce_order_update_shipping', $this->get_id(), $item->get_order_item_id(), $args );
1976
        }
1977
1978
        return $item->get_order_item_id();
1979
    }
1980
1981
    /**
1982
     * Add a fee to the order.
1983
     * Order must be saved prior to adding items.
1984
     * @param object $fee
1985
     * @return int updated order item ID
1986
     */
1987
    public function add_fee( $fee ) {
1988
        $args = wp_parse_args( $args, array(
1989
            'name'      => $fee->name,
1990
            'tax_class' => $fee->taxable ? $fee->tax_class : 0,
1991
            'total'     => $fee->amount,
1992
            'total_tax' => $fee->tax,
1993
            'taxes'     => array(
1994
                'total' => $fee->tax_data
1995
            )
1996
        ) );
1997
        $item = new WC_Order_Item_Fee();
1998
        $item_id = $this->update_fee( $item, $args );
1999
2000
        do_action( 'woocommerce_order_add_fee', $this->get_id(), $item->get_order_item_id(), $fee );
2001
2002
        return $item_id;
2003
    }
2004
2005
    /**
2006
     * Update fee for order.
2007
     *
2008
     * Note this does not update order totals.
2009
     *
2010
     * @since 2.2
2011
     * @param object|int $item
2012
     * @param array $args
2013
     * @return int updated order item ID
2014
     */
2015
    public function update_fee( $item, $args ) {
2016
        if ( is_numeric( $item ) ) {
2017
            $item = $this->get_item( $item );
2018
        }
2019
2020
        if ( ! is_object( $product ) || ! $item->is_type( 'fee' ) ) {
2021
            return false;
2022
        }
2023
2024
        if ( ! $this->get_id() ) {
2025
            $this->save();
2026
        }
2027
2028
		$item->set_order_id( $this->get_id() );
2029
2030
        if ( ! $item->get_order_item_id() ) {
2031
            $inserting = true;
2032
        } else {
2033
            $inserting = false;
2034
        }
2035
2036
        if ( isset( $args['name'] ) ) {
2037
            $item->set_name( $args['name'] );
2038
        }
2039
2040
        if ( isset( $args['tax_class'] ) ) {
2041
            $item->set_tax_class( $args['tax_class'] );
2042
        }
2043
2044
        if ( isset( $args['total'] ) ) {
2045
            $item->set_total( $args['total'] );
2046
        }
2047
2048
        if ( isset( $args['total_tax'] ) ) {
2049
            $item->set_total_tax( $args['total_tax'] );
2050
        }
2051
2052
        if ( isset( $args['taxes'] ) ) {
2053
            $item->set_taxes( $args['taxes'] );
2054
        }
2055
2056
        $item->save();
2057
2058
        if ( ! $inserting ) {
2059
            do_action( 'woocommerce_order_update_fee', $this->get_id(), $item->get_order_item_id(), $args );
2060
        }
2061
2062
        return $item->get_order_item_id();
2063
    }
2064
2065
    /**
2066
     * Add a tax row to the order.
2067
     * Order must be saved prior to adding items.
2068
     * @since 2.2
2069
     * @param int tax_rate_id
2070
     * @return int updated order item ID
2071
     */
2072
    public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
2073
        if ( ! $code = WC_Tax::get_rate_code( $tax_rate_id ) ) {
2074
            return false;
2075
        }
2076
2077
        $args = wp_parse_args( $args, array(
2078
            'rate_code'          => $code,
2079
            'rate_id'            => $tax_rate_id,
2080
            'label'              => WC_Tax::get_rate_label( $tax_rate_id ),
2081
            'compound'           => WC_Tax::is_compound( $tax_rate_id ),
2082
            'tax_total'          => $tax_amount,
2083
            'shipping_tax_total' => $shipping_tax_amount
2084
        ) );
2085
        $item = new WC_Order_Item_Tax();
2086
        $item_id = $this->update_tax( $item, $args );
2087
2088
        do_action( 'woocommerce_order_add_tax', $this->get_id(), $item->get_order_item_id(), $tax_rate_id, $tax_amount, $shipping_tax_amount );
2089
2090
        return $item_id;
2091
    }
2092
2093
    /**
2094
     * Update tax line on order.
2095
     * Note this does not update order totals.
2096
     *
2097
     * @since 2.6
2098
     * @param object|int $item
2099
     * @param array $args
2100
     * @return int updated order item ID
2101
     */
2102
    public function update_tax( $item, $args ) {
2103
        if ( is_numeric( $item ) ) {
2104
            $item = $this->get_item( $item );
2105
        }
2106
2107
        if ( ! is_object( $product ) || ! $item->is_type( 'tax' ) ) {
2108
            return false;
2109
        }
2110
2111
        if ( ! $this->get_id() ) {
2112
            $this->save();
2113
        }
2114
2115
		$item->set_order_id( $this->get_id() );
2116
2117
        if ( ! $item->get_order_item_id() ) {
2118
            $inserting = true;
2119
        } else {
2120
            $inserting = false;
2121
        }
2122
2123
        if ( isset( $args['rate_code'] ) ) {
2124
            $item->set_rate_code( $args['rate_code'] );
2125
        }
2126
2127
        if ( isset( $args['rate_id'] ) ) {
2128
            $item->set_rate_id( $args['rate_id'] );
2129
        }
2130
2131
        if ( isset( $args['label'] ) ) {
2132
            $item->set_label( $args['label'] );
2133
        }
2134
2135
        if ( isset( $args['compound'] ) ) {
2136
            $item->set_compound( $args['compound'] );
2137
        }
2138
2139
        if ( isset( $args['tax_total'] ) ) {
2140
            $item->set_tax_total( $args['tax_total'] );
2141
        }
2142
2143
        if ( isset( $args['shipping_tax_total'] ) ) {
2144
            $item->set_shipping_tax_total( $args['shipping_tax_total'] );
2145
        }
2146
2147
        $item->save();
2148
2149
        if ( ! $inserting ) {
2150
            do_action( 'woocommerce_order_update_tax', $this->get_id(), $item->get_order_item_id(), $args );
2151
        }
2152
2153
        return $item->get_order_item_id();
2154
    }
2155
2156
	/**
2157
     * Update tax lines at order level by looking at the line item taxes themselves.
2158
     * @return bool success or fail.
2159
     */
2160
    public function update_taxes() {
2161
        $cart_taxes     = array();
2162
        $shipping_taxes = array();
2163
2164 View Code Duplication
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
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...
2165
            $taxes = $item->get_taxes();
2166
            if ( isset( $taxes['total'] ) ) {
2167
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2168
                    if ( ! isset( $cart_taxes[ $tax_rate_id ] ) ) {
2169
                        $cart_taxes[ $tax_rate_id ] = 0;
2170
                    }
2171
                    $cart_taxes[ $tax_rate_id ] += $tax;
2172
                }
2173
            }
2174
        }
2175
2176 View Code Duplication
        foreach ( $this->get_items( array( 'shipping' ) ) as $item_id => $item ) {
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...
2177
			$taxes = $item->get_taxes();
2178
            if ( isset( $taxes['total'] ) ) {
2179
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2180
                    if ( ! isset( $shipping_taxes[ $tax_rate_id ] ) ) {
2181
                        $shipping_taxes[ $tax_rate_id ] = 0;
2182
                    }
2183
                    $shipping_taxes[ $tax_rate_id ] += $tax;
2184
                }
2185
            }
2186
        }
2187
2188
        // Remove old existing tax rows.
2189
        $this->remove_order_items( 'tax' );
2190
2191
        // Now merge to keep tax rows.
2192 View Code Duplication
        foreach ( array_keys( $cart_taxes + $shipping_taxes ) as $tax_rate_id ) {
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...
2193
            $this->add_tax( $tax_rate_id, isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
2194
        }
2195
2196
        // Save tax totals
2197
        $this->set_total( WC_Tax::round( array_sum( $shipping_taxes ) ), 'shipping_tax' );
2198
        $this->set_total( WC_Tax::round( array_sum( $cart_taxes ) ), 'tax' );
2199
2200
        return true;
2201
    }
2202
2203
    /*
2204
    |--------------------------------------------------------------------------
2205
    | Calculations.
2206
    |--------------------------------------------------------------------------
2207
    |
2208
    | These methods calculate order totals and taxes based on the current data.
2209
    |
2210
    */
2211
2212
    /**
2213
     * Calculate shipping total.
2214
     *
2215
     * @since 2.2
2216
     * @return float
2217
     */
2218
    public function calculate_shipping() {
2219
        $shipping_total = 0;
2220
2221
        foreach ( $this->get_shipping_methods() as $shipping ) {
2222
            $shipping_total += $shipping->get_total();
2223
        }
2224
2225
        $this->set_total( $shipping_total, 'shipping' );
2226
2227
        return $this->get_shipping_total();
2228
    }
2229
2230
    /**
2231
     * Calculate taxes for all line items and shipping, and store the totals and tax rows.
2232
     *
2233
     * Will use the base country unless customer addresses are set.
2234
     *
2235
     * @return bool success or fail.
2236
     */
2237
    public function calculate_taxes() {
2238
        $cart_tax     = 0;
2239
        $cart_taxes   = array();
2240
        $tax_based_on = get_option( 'woocommerce_tax_based_on' );
2241
2242
        if ( 'billing' === $tax_based_on ) {
2243
            $country  = $this->get_billing_country();
2244
            $state    = $this->get_billing_state();
2245
            $postcode = $this->get_billing_postcode();
2246
            $city     = $this->get_billing_city();
2247
        } elseif ( 'shipping' === $tax_based_on ) {
2248
            $country  = $this->get_shipping_country();
2249
            $state    = $this->get_shipping_state();
2250
            $postcode = $this->get_shipping_postcode();
2251
            $city     = $this->get_shipping_city();
2252
        }
2253
2254
        // Default to base
2255 View Code Duplication
        if ( 'base' === $tax_based_on || empty( $country ) ) {
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...
2256
            $default  = wc_get_base_location();
2257
            $country  = $default['country'];
2258
            $state    = $default['state'];
2259
            $postcode = '';
2260
            $city     = '';
2261
        }
2262
2263
        // Get items
2264
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
2265
			$tax_class  = $item->get_tax_class();
2266
			$tax_status = $item->get_tax_status();
0 ignored issues
show
Unused Code introduced by
$tax_status is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2267
2268
            if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
2269
2270
                $tax_rates = WC_Tax::find_rates( array(
2271
                    'country'   => $country,
2272
                    'state'     => $state,
2273
                    'postcode'  => $postcode,
2274
                    'city'      => $city,
2275
                    'tax_class' => $tax_class
2276
                ) );
2277
2278
				$total     = $item->get_total();
2279
				$taxes     = WC_Tax::calc_tax( $total, $tax_rates, false );
2280
				$total_tax = max( 0, array_sum( $taxes ) );
2281
				$cart_tax += $total_tax;
2282
				$item->set_total_tax( $total_tax );
2283
2284
				if ( $item->is_type( 'line_item' ) ) {
2285
					$subtotal       = $item->get_subtotal();
2286
					$subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false );
2287
					$subtotal_tax   = max( 0, array_sum( $subtotal_taxes ) );
2288
					$item->set_subtotal_tax( $subtotal_tax );
2289
					$item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) );
2290
				} else {
2291
					$item->set_taxes( array( 'total' => $taxes ) );
2292
				}
2293
2294
				$item->save(); //@todo store items to self, don't save right away
2295
2296
                // Sum the item taxes
2297
                foreach ( array_keys( $cart_taxes + $taxes ) as $key ) {
2298
                    $cart_taxes[ $key ] = ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $cart_taxes[ $key ] : 0 );
2299
                }
2300
            }
2301
        }
2302
2303
        // Now calculate shipping tax
2304
        $shipping_methods = $this->get_shipping_methods();
2305
2306
        if ( ! empty( $shipping_methods ) ) {
2307
            $matched_tax_rates = array();
2308
            $tax_rates         = WC_Tax::find_rates( array(
2309
                'country'   => $country,
2310
                'state'     => $state,
2311
                'postcode'  => $postcode,
2312
                'city'      => $city,
2313
                'tax_class' => ''
2314
            ) );
2315
2316 View Code Duplication
            if ( ! empty( $tax_rates ) ) {
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...
2317
                foreach ( $tax_rates as $key => $rate ) {
2318
                    if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
2319
                        $matched_tax_rates[ $key ] = $rate;
2320
                    }
2321
                }
2322
            }
2323
2324
            $shipping_taxes     = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
2325
            $shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
2326
        } else {
2327
            $shipping_taxes     = array();
2328
            $shipping_tax_total = 0;
2329
        }
2330
2331
        // Save tax totals
2332
        $this->set_total( $shipping_tax_total, 'shipping_tax' );
2333
        $this->set_total( $tax_total, 'tax' );
2334
2335
        // Tax rows
2336
        $this->remove_order_items( 'tax' );
2337
2338
        // Now merge to keep tax rows
2339 View Code Duplication
        foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
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...
2340
            $this->add_tax( $tax_rate_id, isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
2341
        }
2342
2343
        return true;
2344
    }
2345
2346
    /**
2347
     * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
2348
     *
2349
     * @since 2.2
2350
     * @param  bool $and_taxes Calc taxes if true.
2351
     * @return float calculated grand total.
2352
     */
2353
    public function calculate_totals( $and_taxes = true ) {
2354
        $cart_subtotal     = 0;
2355
        $cart_total        = 0;
2356
        $fee_total         = 0;
2357
        $cart_subtotal_tax = 0;
2358
        $cart_total_tax    = 0;
2359
2360
        if ( $and_taxes && wc_tax_enabled() ) {
2361
            $this->calculate_taxes();
2362
        }
2363
2364
        // line items
2365
        foreach ( $this->get_items() as $item ) {
2366
            $cart_subtotal     += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
2367
            $cart_total        += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
2368
            $cart_subtotal_tax += wc_format_decimal( isset( $item['line_subtotal_tax'] ) ? $item['line_subtotal_tax'] : 0 );
2369
            $cart_total_tax    += wc_format_decimal( isset( $item['line_tax'] ) ? $item['line_tax'] : 0 );
2370
        }
2371
2372
        $this->calculate_shipping();
2373
2374
        foreach ( $this->get_fees() as $item ) {
2375
            $fee_total += $item['line_total'];
2376
        }
2377
2378
        $this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
2379
        $this->set_total( $cart_subtotal_tax - $cart_total_tax, 'cart_discount_tax' );
2380
2381
        $grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() );
2382
2383
        $this->set_total( $grand_total, 'total' );
2384
2385
        return $grand_total;
2386
    }
2387
2388
    /*
2389
    |--------------------------------------------------------------------------
2390
    | Total Getters
2391
    |--------------------------------------------------------------------------
2392
    |
2393
    | Methods for getting totals e.g. for display on the frontend.
2394
    |
2395
    */
2396
2397
    /**
2398
     * Get a product (either product or variation).
2399
     *
2400
     * @param object $item
2401
     * @return WC_Product|bool
2402
     */
2403
    public function get_product_from_item( $item ) {
2404
        if ( is_callable( array( $item, 'get_product' ) ) ) {
2405
            $product = $item->get_product();
2406
        } else {
2407
            $product = false;
2408
        }
2409
        return apply_filters( 'woocommerce_get_product_from_item', $product, $item, $this );
2410
    }
2411
2412
    /**
2413
     * Get item subtotal - this is the cost before discount.
2414
     *
2415
     * @param object $item
2416
     * @param bool $inc_tax (default: false).
2417
     * @param bool $round (default: true).
2418
     * @return float
2419
     */
2420 View Code Duplication
    public function get_item_subtotal( $item, $inc_tax = false, $round = true ) {
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...
2421
        $subtotal = 0;
2422
2423
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2424
            if ( $inc_tax ) {
2425
                $subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_qty() );
2426
            } else {
2427
                $subtotal = ( $item->get_subtotal() / max( 1, $item->get_qty() ) );
2428
            }
2429
2430
            $subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
2431
        }
2432
2433
        return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2434
    }
2435
2436
    /**
2437
     * Get line subtotal - this is the cost before discount.
2438
     *
2439
     * @param object $item
2440
     * @param bool $inc_tax (default: false).
2441
     * @param bool $round (default: true).
2442
     * @return float
2443
     */
2444 View Code Duplication
    public function get_line_subtotal( $item, $inc_tax = false, $round = true ) {
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...
2445
        $subtotal = 0;
2446
2447
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2448
            if ( $inc_tax ) {
2449
                $subtotal = $item->get_subtotal() + $item->get_subtotal_tax();
2450
            } else {
2451
                $subtotal = $item->get_subtotal();
2452
            }
2453
2454
            $subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal;
2455
        }
2456
2457
        return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2458
    }
2459
2460
    /**
2461
     * Calculate item cost - useful for gateways.
2462
     *
2463
     * @param object $item
2464
     * @param bool $inc_tax (default: false).
2465
     * @param bool $round (default: true).
2466
     * @return float
2467
     */
2468 View Code Duplication
    public function get_item_total( $item, $inc_tax = false, $round = true ) {
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...
2469
        $total = 0;
2470
2471
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2472
            if ( $inc_tax ) {
2473
                $total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_qty() );
2474
            } else {
2475
                $total = $item->get_total() / max( 1, $item->get_qty() );
2476
            }
2477
2478
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2479
        }
2480
2481
        return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round );
2482
    }
2483
2484
    /**
2485
     * Calculate line total - useful for gateways.
2486
     *
2487
     * @param object $item
2488
     * @param bool $inc_tax (default: false).
2489
     * @param bool $round (default: true).
2490
     * @return float
2491
     */
2492 View Code Duplication
    public function get_line_total( $item, $inc_tax = false, $round = true ) {
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...
2493
        $total = 0;
2494
2495
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2496
            // Check if we need to add line tax to the line total.
2497
            $total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total();
2498
2499
            // Check if we need to round.
2500
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2501
        }
2502
2503
        return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round );
2504
    }
2505
2506
    /**
2507
     * Get item tax - useful for gateways.
2508
     *
2509
     * @param mixed $item
2510
     * @param bool $round (default: true).
2511
     * @return float
2512
     */
2513
    public function get_item_tax( $item, $round = true ) {
2514
        $tax = 0;
2515
2516
        if ( is_callable( array( $item, 'get_total_tax' ) ) ) {
2517
            $tax = $item->get_total_tax() / max( 1, $item->get_qty() );
2518
            $tax = $round ? wc_round_tax_total( $tax ) : $tax;
2519
        }
2520
2521
        return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this );
2522
    }
2523
2524
    /**
2525
     * Get line tax - useful for gateways.
2526
     *
2527
     * @param mixed $item
2528
     * @return float
2529
     */
2530
    public function get_line_tax( $item ) {
2531
        return apply_filters( 'woocommerce_order_amount_line_tax', is_callable( array( $item, 'get_total_tax' ) ) ? wc_round_tax_total( $item->get_total_tax() ) : 0, $item, $this );
2532
    }
2533
2534
    /**
2535
     * Gets line subtotal - formatted for display.
2536
     *
2537
     * @param array  $item
2538
     * @param string $tax_display
2539
     * @return string
2540
     */
2541
    public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
2542
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2543
2544
        if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
2545
            return '';
2546
        }
2547
2548
        if ( 'excl' == $tax_display ) {
2549
            $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0;
2550
2551
            $subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_order_currency() ) );
0 ignored issues
show
Documentation introduced by
$item is of type array<string,?,{"line_su...ine_subtotal_tax":"?"}>, but the function expects a object.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2552
        } else {
2553
            $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array('currency' => $this->get_order_currency()) );
0 ignored issues
show
Documentation introduced by
$item is of type array<string,?,{"line_su...ine_subtotal_tax":"?"}>, but the function expects a object.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2554
        }
2555
2556
        return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
2557
    }
2558
2559
    /**
2560
     * Gets order total - formatted for display.
2561
     *
2562
     * @return string
2563
     */
2564
    public function get_formatted_order_total() {
2565
        $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
2566
        return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
2567
    }
2568
2569
    /**
2570
     * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
2571
     *
2572
     * @param bool $compound (default: false).
2573
     * @param string $tax_display (default: the tax_display_cart value).
2574
     * @return string
2575
     */
2576
    public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
2577
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2578
        $subtotal    = 0;
2579
2580
        if ( ! $compound ) {
2581
            foreach ( $this->get_items() as $item ) {
2582
                $subtotal += $item->get_subtotal();
2583
2584
                if ( 'incl' === $tax_display ) {
2585
                    $subtotal += $item->get_subtotal_tax();
2586
                }
2587
            }
2588
2589
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2590
2591
            if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) {
2592
                $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
2593
            }
2594
2595
        } else {
2596
            if ( 'incl' === $tax_display ) {
2597
                return '';
2598
            }
2599
2600
            foreach ( $this->get_items() as $item ) {
2601
                $subtotal += $item->get_subtotal();
2602
            }
2603
2604
            // Add Shipping Costs.
2605
            $subtotal += $this->get_total_shipping();
2606
2607
            // Remove non-compound taxes.
2608
            foreach ( $this->get_taxes() as $tax ) {
2609
                if ( ! empty( $tax['compound'] ) ) {
2610
                    continue;
2611
                }
2612
                $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
2613
            }
2614
2615
            // Remove discounts.
2616
            $subtotal = $subtotal - $this->get_total_discount();
2617
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2618
        }
2619
2620
        return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
2621
    }
2622
2623
    /**
2624
     * Gets shipping (formatted).
2625
     *
2626
     * @return string
2627
     */
2628
    public function get_shipping_to_display( $tax_display = '' ) {
2629
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2630
2631
        if ( $this->order_shipping != 0 ) {
2632
2633
            if ( $tax_display == 'excl' ) {
2634
2635
                // Show shipping excluding tax.
2636
                $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
2637
2638 View Code Duplication
                if ( $this->order_shipping_tax != 0 && $this->prices_include_tax ) {
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...
2639
                    $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', '&nbsp;<small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $this, $tax_display );
2640
                }
2641
2642
            } else {
2643
2644
                // Show shipping including tax.
2645
                $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
2646
2647 View Code Duplication
                if ( $this->order_shipping_tax != 0 && ! $this->prices_include_tax ) {
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...
2648
                    $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', '&nbsp;<small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display );
2649
                }
2650
2651
            }
2652
2653
            $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_shipped_via', '&nbsp;<small class="shipped_via">' . sprintf( __( 'via %s', 'woocommerce' ), $this->get_shipping_method() ) . '</small>', $this );
2654
2655
        } elseif ( $this->get_shipping_method() ) {
2656
            $shipping = $this->get_shipping_method();
2657
        } else {
2658
            $shipping = __( 'Free!', 'woocommerce' );
2659
        }
2660
2661
        return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
2662
    }
2663
2664
    /**
2665
     * Get the discount amount (formatted).
2666
     * @since  2.3.0
2667
     * @return string
2668
     */
2669
    public function get_discount_to_display( $tax_display = '' ) {
2670
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2671
        return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display && 'excl' === get_option( 'woocommerce_tax_display_cart' ) ), array( 'currency' => $this->get_order_currency() ) ), $this );
2672
    }
2673
2674
    /**
2675
     * Get totals for display on pages and in emails.
2676
     *
2677
     * @param mixed $tax_display
2678
     * @return array
2679
     */
2680
    public function get_order_item_totals( $tax_display = '' ) {
2681
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2682
        $total_rows  = array();
2683
2684
        if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
2685
            $total_rows['cart_subtotal'] = array(
2686
                'label' => __( 'Subtotal:', 'woocommerce' ),
2687
                'value'    => $subtotal
2688
            );
2689
        }
2690
2691
        if ( $this->get_total_discount() > 0 ) {
2692
            $total_rows['discount'] = array(
2693
                'label' => __( 'Discount:', 'woocommerce' ),
2694
                'value'    => '-' . $this->get_discount_to_display( $tax_display )
2695
            );
2696
        }
2697
2698
        if ( $this->get_shipping_method() ) {
2699
            $total_rows['shipping'] = array(
2700
                'label' => __( 'Shipping:', 'woocommerce' ),
2701
                'value'    => $this->get_shipping_to_display( $tax_display )
2702
            );
2703
        }
2704
2705
        if ( $fees = $this->get_fees() ) {
2706
            foreach ( $fees as $id => $fee ) {
2707
                if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) {
2708
                    continue;
2709
                }
2710
                $total_rows[ 'fee_' . $fee->get_order_item_id() ] = array(
2711
                    'label' => $fee->get_name() . ':',
2712
                    'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array('currency' => $this->get_order_currency()) )
2713
                );
2714
            }
2715
        }
2716
2717
        // Tax for tax exclusive prices.
2718
        if ( 'excl' === $tax_display ) {
2719
2720
            if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
2721
2722
                foreach ( $this->get_tax_totals() as $code => $tax ) {
2723
2724
                    $total_rows[ sanitize_title( $code ) ] = array(
2725
                        'label' => $tax->label . ':',
2726
                        'value'    => $tax->formatted_amount
2727
                    );
2728
                }
2729
2730
            } else {
2731
2732
                $total_rows['tax'] = array(
2733
                    'label' => WC()->countries->tax_or_vat() . ':',
2734
                    'value'    => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_order_currency() ) )
2735
                );
2736
            }
2737
        }
2738
2739
        if ( $this->get_total() > 0 && $this->get_payment_method_title() ) {
2740
            $total_rows['payment_method'] = array(
2741
                'label' => __( 'Payment Method:', 'woocommerce' ),
2742
                'value' => $this->get_payment_method_title()
2743
            );
2744
        }
2745
2746
        if ( $refunds = $this->get_refunds() ) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Abstract_Order as the method get_refunds() does only exist in the following sub-classes of WC_Abstract_Order: WC_Order. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
2747
            foreach ( $refunds as $id => $refund ) {
2748
                $total_rows[ 'refund_' . $id ] = array(
2749
                    'label' => $refund->get_refund_reason() ? $refund->get_refund_reason() : __( 'Refund', 'woocommerce' ) . ':',
2750
                    'value'    => wc_price( '-' . $refund->get_refund_amount(), array( 'currency' => $this->get_order_currency() ) )
2751
                );
2752
            }
2753
        }
2754
2755
        $total_rows['order_total'] = array(
2756
            'label' => __( 'Total:', 'woocommerce' ),
2757
            'value'    => $this->get_formatted_order_total( $tax_display )
0 ignored issues
show
Unused Code introduced by
The call to WC_Abstract_Order::get_formatted_order_total() has too many arguments starting with $tax_display.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2758
        );
2759
2760
        return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
2761
    }
2762
2763
    /*
2764
    |--------------------------------------------------------------------------
2765
    | Conditionals
2766
    |--------------------------------------------------------------------------
2767
    |
2768
    | Checks if a condition is true or false.
2769
    |
2770
    */
2771
2772
    /**
2773
     * Checks the order status against a passed in status.
2774
     *
2775
     * @return bool
2776
     */
2777
    public function has_status( $status ) {
2778
        return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
2779
    }
2780
2781
    /**
2782
     * has_meta function for order items.
2783
     *
2784
     * @param string $order_item_id
2785
     * @return array of meta data.
2786
     */
2787
    public function has_meta( $order_item_id ) {
2788
        global $wpdb;
2789
2790
        return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
2791
            FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
2792
            ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
2793
    }
2794
2795
    /**
2796
     * Check whether this order has a specific shipping method or not.
2797
     *
2798
     * @param string $method_id
2799
     * @return bool
2800
     */
2801
    public function has_shipping_method( $method_id ) {
2802
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
2803
            if ( $shipping_method->get_method_id() === $method_id ) {
2804
                return true;
2805
            }
2806
        }
2807
        return false;
2808
    }
2809
2810
    /**
2811
     * Check if an order key is valid.
2812
     *
2813
     * @param mixed $key
2814
     * @return bool
2815
     */
2816
    public function key_is_valid( $key ) {
2817
        return $key === $this->get_order_key();
2818
    }
2819
2820
    /**
2821
     * Returns true if the order contains a free product.
2822
     * @since 2.5.0
2823
     * @return bool
2824
     */
2825
    public function has_free_item() {
2826
        foreach ( $this->get_items() as $item ) {
2827
            if ( ! $item->get_total() ) {
2828
                return true;
2829
            }
2830
        }
2831
        return false;
2832
    }
2833
2834
    /*
2835
    |--------------------------------------------------------------------------
2836
    | Deprecated methods
2837
    |--------------------------------------------------------------------------
2838
    |
2839
    | Will be removed after 2 major releases, or 1 year.
2840
    |
2841
    */
2842
2843
    /**
2844
     * Magic __isset method for backwards compatibility.
2845
     * @param string $key
2846
     * @return bool
2847
     */
2848
    public function __isset( $key ) {
2849
        // Legacy properties which could be accessed directly in the past.
2850
        $legacy_props = array( 'completed_date', 'id', 'order_type', 'post', 'status', 'post_status', 'customer_note', 'customer_message', 'user_id', 'customer_user', 'prices_include_tax', 'tax_display_cart', 'display_totals_ex_tax', 'display_cart_ex_tax', 'order_date', 'modified_date', 'cart_discount', 'cart_discount_tax', 'order_shipping', 'order_shipping_tax', 'order_total', 'order_tax', 'billing_first_name', 'billing_last_name', 'billing_company', 'billing_address_1', 'billing_address_2', 'billing_city', 'billing_state', 'billing_postcode', 'billing_country', 'billing_phone', 'billing_email', 'shipping_first_name', 'shipping_last_name', 'shipping_company', 'shipping_address_1', 'shipping_address_2', 'shipping_city', 'shipping_state', 'shipping_postcode', 'shipping_country', 'customer_ip_address', 'customer_user_agent', 'payment_method_title', 'payment_method', 'order_currency' );
2851
        return $this->get_id() ? ( in_array( $key, $legacy_props ) || metadata_exists( 'post', $this->get_id(), '_' . $key ) ) : false;
2852
    }
2853
2854
    /**
2855
     * Magic __get method for backwards compatibility.
2856
     * @param string $key
2857
     * @return mixed
2858
     */
2859
    public function __get( $key ) {
2860
        /**
2861
         * Maps legacy vars to new getters.
2862
         */
2863
        if ( 'completed_date' === $key ) {
2864
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2865
            return $this->get_date_completed();
2866
		} elseif ( 'paid_date' === $key ) {
2867
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2868
            return $this->get_date_paid();
2869
        } elseif ( 'modified_date' === $key ) {
2870
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2871
            return $this->get_date_modified();
2872
        } elseif ( 'order_date' === $key ) {
2873
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2874
            return $this->get_date_created();
2875
        } elseif ( 'id' === $key ) {
2876
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2877
            return $this->get_id();
2878
		} elseif ( 'post' === $key ) {
2879
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2880
            return get_post( $this->get_id() );
2881
		} elseif ( 'status' === $key || 'post_status' === $key ) {
2882
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2883
            return $this->get_status();
2884
		} elseif ( 'customer_message' === $key || 'customer_note' === $key ) {
2885
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2886
            return $this->get_customer_note();
2887
		} elseif ( in_array( $key, array( 'user_id', 'customer_user' ) ) ) {
2888
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2889
            return $this->get_customer_id();
2890
		} elseif ( 'tax_display_cart' === $key ) {
2891
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2892
			return get_option( 'woocommerce_tax_display_cart' );
2893
		} elseif ( 'display_totals_ex_tax' === $key ) {
2894
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2895
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2896
		} elseif ( 'display_cart_ex_tax' === $key ) {
2897
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2898
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2899
        } elseif ( 'cart_discount' === $key ) {
2900
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2901
			return $this->get_discount();
0 ignored issues
show
Bug introduced by
The method get_discount() does not exist on WC_Abstract_Order. Did you maybe mean get_discount_total()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2902
        } elseif ( 'cart_discount_tax' === $key ) {
2903
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2904
			return $this->get_discount_tax();
2905
        } elseif ( 'order_tax' === $key ) {
2906
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2907
			return $this->get_cart_tax();
2908
        } elseif ( 'order_shipping_tax' === $key ) {
2909
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2910
            return $this->get_shipping_tax();
2911
        } elseif ( 'order_shipping' === $key ) {
2912
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2913
            return $this->get_shipping();
0 ignored issues
show
Bug introduced by
The method get_shipping() does not exist on WC_Abstract_Order. Did you maybe mean get_shipping_first_name()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2914
        /**
2915
         * Map vars to getters with warning.
2916
         */
2917
	 	} elseif ( is_callable( array( $this, "get_{$key}" ) ) ) {
2918
			_doing_it_wrong( $key, 'Order properties should not be accessed directly Use get_' . $key . '().', '2.6' );
2919
            return $this->{"get_{$key}"}();
2920
        /**
2921
         * Handle post meta
2922
         */
2923
        } else {
2924
            _doing_it_wrong( $key, 'Meta should not be accessed directly. Use WC_Order::get_order_meta( $key )', '2.6' );
2925
			$value = get_post_meta( $this->get_id(), '_' . $key, true );
2926
		}
2927
2928
        return $value;
2929
    }
2930
2931
    /**
2932
     * Get order item meta.
2933
     * @deprecated 2.6.0
2934
     * @param mixed $order_item_id
2935
     * @param string $key (default: '')
2936
     * @param bool $single (default: false)
2937
     * @return array|string
2938
     */
2939
    public function get_item_meta( $order_item_id, $key = '', $single = false ) {
2940
        _deprecated_function( 'get_item_meta', '2.6', 'wc_get_order_item_meta' );
2941
        return get_metadata( 'order_item', $order_item_id, $key, $single );
2942
    }
2943
2944
    /**
2945
     * Get all item meta data in array format in the order it was saved. Does not group meta by key like get_item_meta().
2946
     *
2947
     * @param mixed $order_item_id
2948
     * @return array of objects
2949
     */
2950
    public function get_item_meta_array( $order_item_id ) {
2951
        _deprecated_function( 'get_item_meta_array', '2.6', 'WC_Order_Item::get_meta_data()' );
2952
        $item = $this->get_item( $order_item_id );
2953
        return $item->get_meta_data();
2954
    }
2955
2956
    /**
2957
     * Expand item meta into the $item array.
2958
     * @deprecated 2.6.0 Item meta no longer expanded due to new order item
2959
     *        classes. This function now does nothing to avoid data breakage.
2960
     * @since 2.4.0
2961
     * @param array $item before expansion.
2962
     * @return array
2963
     */
2964
    public function expand_item_meta( $item ) {
2965
        _deprecated_function( 'expand_item_meta', '2.6', '' );
2966
        return $item;
2967
    }
2968
2969
	/**
2970
     * Load the order object. Called from the constructor.
2971
     * @deprecated 2.6.0 Logic moved to constructor
2972
     * @param int|object|WC_Order $order Order to init.
2973
     */
2974 View Code Duplication
    protected function init( $order ) {
1 ignored issue
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...
2975
		_deprecated_function( 'init', '2.6', 'Logic moved to constructor' );
2976
        if ( is_numeric( $order ) ) {
2977
            $this->read( $order );
2978
        } elseif ( $order instanceof WC_Order ) {
2979
            $this->read( absint( $order->get_id() ) );
2980
        } elseif ( isset( $order->ID ) ) {
2981
            $this->read( absint( $order->ID ) );
2982
        }
2983
    }
2984
2985
    /**
2986
     * Gets an order from the database.
2987
     * @deprecated 2.6
2988
     * @param int $id (default: 0).
2989
     * @return bool
2990
     */
2991 View Code Duplication
    public function get_order( $id = 0 ) {
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...
2992
        _deprecated_function( 'get_order', '2.6', 'read' );
2993
        if ( ! $id ) {
2994
            return false;
2995
        }
2996
        if ( $result = get_post( $id ) ) {
2997
            $this->populate( $result );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Abstract_Order::populate() has been deprecated with message: 2.6

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

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

Loading history...
2998
            return true;
2999
        }
3000
        return false;
3001
    }
3002
3003
    /**
3004
     * Populates an order from the loaded post data.
3005
     * @deprecated 2.6
3006
     * @param mixed $result
3007
     */
3008
    public function populate( $result ) {
3009
        _deprecated_function( 'populate', '2.6', 'read' );
3010
        $this->read( $result->ID );
3011
    }
3012
3013
	/**
3014
     * Cancel the order and restore the cart (before payment).
3015
     * @deprecated 2.6.0 Moved to event handler.
3016
     * @param string $note (default: '') Optional note to add.
3017
     */
3018
    public function cancel_order( $note = '' ) {
3019
		_deprecated_function( 'cancel_order', '2.6', 'update_status' );
3020
        WC()->session->set( 'order_awaiting_payment', false );
3021
        $this->update_status( 'cancelled', $note );
3022
    }
3023
3024
	/**
3025
     * Record sales.
3026
     * @deprecated 2.6.0
3027
     */
3028
    public function record_product_sales() {
3029
		_deprecated_function( 'record_product_sales', '2.6', 'wc_update_total_sales_counts' );
3030
		wc_update_total_sales_counts( $this->get_id() );
3031
    }
3032
3033
	/**
3034
     * Increase applied coupon counts.
3035
     * @deprecated 2.6.0
3036
     */
3037
    public function increase_coupon_usage_counts() {
3038
		_deprecated_function( 'increase_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3039
		wc_update_coupon_usage_counts( $this->get_id() );
3040
    }
3041
3042
    /**
3043
     * Decrease applied coupon counts.
3044
     * @deprecated 2.6.0
3045
     */
3046
    public function decrease_coupon_usage_counts() {
3047
		_deprecated_function( 'decrease_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3048
		wc_update_coupon_usage_counts( $this->get_id() );
3049
    }
3050
3051
	/**
3052
     * Reduce stock levels for all line items in the order.
3053
	 * @deprecated 2.6.0
3054
     */
3055
    public function reduce_order_stock() {
3056
        _deprecated_function( 'reduce_order_stock', '2.6', 'wc_reduce_stock_levels' );
3057
		wc_reduce_stock_levels( $this->get_id() );
3058
    }
3059
3060
	/**
3061
     * Send the stock notifications.
3062
	 * @deprecated 2.6.0 No longer needs to be called directly.
3063
     */
3064
    public function send_stock_notifications( $product, $new_stock, $qty_ordered ) {
0 ignored issues
show
Unused Code introduced by
The parameter $product 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...
Unused Code introduced by
The parameter $new_stock 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...
Unused Code introduced by
The parameter $qty_ordered 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...
3065
        _deprecated_function( 'send_stock_notifications', '2.6' );
3066
    }
3067
3068
	/**
3069
	 * Output items for display in html emails.
3070
	 * @deprecated 2.6.0 Moved to template functions.
3071
	 * @param array $args Items args.
3072
	 * @return string
3073
	 */
3074
	public function email_order_items_table( $args = array() ) {
3075
		return wc_get_email_order_items( $this, $args );
0 ignored issues
show
Compatibility introduced by
$this of type object<WC_Abstract_Order> is not a sub-type of object<WC_Order>. It seems like you assume a child class of the class WC_Abstract_Order to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
3076
	}
3077
}
3078