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

WC_Abstract_Order::set_order_id()   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 1
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
include_once( 'abstract-wc-legacy-order.php' );
7
8
/**
9
 * Abstract Order
10
 *
11
 * Handles order data and database interaction.
12
 *
13
 * @class       WC_Abstract_Order
14
 * @version     2.6.0
15
 * @package     WooCommerce/Classes
16
 * @category    Class
17
 * @author      WooThemes
18
 */
19
abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order implements WC_Data {
20
21
    /**
22
     * Data array, with defaults.
23
     *
24
     * @todo when migrating to custom tables, these will be columns
25
     * @since 2.6.0
26
     * @var array
27
     */
28
    protected $_data = array(
29
		'order_id'             => 0,
30
        'parent_id'            => 0,
31
		'status'               => '',
32
        /**
33
         * @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
34
         * The order type for shop_order_refund is refund.
35
         * Why do we need two separate variables? This should be unified, especially once this is in a custom table and post_type is redundent.
36
         * Switching to 'shop_order', and then using this value in the order factory instead of post_type. @thenbrent might have feedback on this.
37
         */
38
		'order_type'           => 'shop_order',
39
		'order_key'            => '',
40
		'order_currency'       => '',
41
		'date_created'         => '',
42
		'date_modified'        => '',
43
		'customer_id'          => 0,
44
		'billing_first_name'   => '',
45
		'billing_last_name'    => '',
46
		'billing_company'      => '',
47
		'billing_address_1'    => '',
48
		'billing_address_2'    => '',
49
		'billing_city'         => '',
50
		'billing_state'        => '',
51
		'billing_postcode'     => '',
52
		'billing_country'      => '',
53
		'billing_email'        => '',
54
		'billing_phone'        => '',
55
		'shipping_first_name'  => '',
56
		'shipping_last_name'   => '',
57
		'shipping_company'     => '',
58
		'shipping_address_1'   => '',
59
		'shipping_address_2'   => '',
60
		'shipping_city'        => '',
61
		'shipping_state'       => '',
62
		'shipping_postcode'    => '',
63
		'shipping_country'     => '',
64
		'discount_total'       => 0,
65
		'discount_tax'         => 0,
66
		'shipping_total'       => 0,
67
		'shipping_tax'         => 0,
68
		'cart_tax'             => 0, // cart_tax is the new name for the legacy 'order_tax' which is the tax for items only, not shipping.
69
		'order_total'          => 0,
70
		'order_tax'            => 0, // Sum of all taxes.
71
    );
72
73
    /**
74
     * Stores meta data.
75
     * @var array
76
     */
77
    protected $_meta = array(
78
        'payment_method'       => '',
79
		'payment_method_title' => '',
80
		'transaction_id'       => '',
81
		'customer_ip_address'  => '',
82
		'customer_user_agent'  => '',
83
		'created_via'          => '',
84
		'order_version'        => '',
85
		'prices_include_tax'   => false,
86
		'customer_note'        => '',
87
		'date_completed'       => '',
88
		'date_paid'            => '',
89
    );
90
91
    /**
92
     * Stores data about status changes so relevant hooks can be fired.
93
     * @var bool|array
94
     */
95
    protected $_status_transition = false;
96
97
    /**
98
     * Get the order if ID is passed, otherwise the order is new and empty.
99
     * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory.
100
     * should be used. It is possible, but the aforementioned are preferred and are the only.
101
     * methods that will be maintained going forward.
102
     *
103
     * @param  int|object|WC_Order $order Order to init.
104
     */
105 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...
106
		if ( is_numeric( $order ) ) {
107
            $this->read( $order );
108
        } elseif ( $order instanceof WC_Order ) {
109
            $this->read( absint( $order->get_id() ) );
110
        } elseif ( ! empty( $order->ID ) ) {
111
            $this->read( absint( $order->ID ) );
112
        }
113
    }
114
115
    /**
116
     * Change data to JSON format.
117
     * @return string Data in JSON format.
118
     */
119
    public function __toString() {
120
        return json_encode( $this->get_data() );
121
    }
122
123
    /**
124
     * Get Meta Data by Key
125
     * @param  string $key
126
     * @return mixed
127
     */
128
    public function get_meta( $key = null ){
129
		if ( is_null( $key ) ) {
130
			return $this->_meta;
131
		} else {
132
        	return isset( $this->_meta[ $key ] ) ? $this->_meta[ $key ] : null;
133
		}
134
    }
135
136
    /*
137
    |--------------------------------------------------------------------------
138
    | Getters
139
    |--------------------------------------------------------------------------
140
    |
141
    | Methods for getting data from the order object.
142
    |
143
    */
144
145
    /**
146
     * Get all class data in array format.
147
     * @since 2.6.0
148
     * @return array
149
     */
150
    public function get_data() {
151
        return array_merge(
152
            $this->_data,
153
            $this->_meta,
154
            array(
155
                'line_items'     => $this->get_items( 'line_item' ),
156
                'tax_lines'      => $this->get_items( 'tax' ),
157
                'shipping_lines' => $this->get_items( 'shipping' ),
158
                'fee_lines'      => $this->get_items( 'fee' ),
159
                'coupon_lines'   => $this->get_items( 'coupon' ),
160
            )
161
        );
162
    }
163
164
    /**
165
     * Get order ID.
166
     * @since 2.6.0
167
     * @return integer
168
     */
169
    public function get_id() {
170
        return $this->get_order_id();
171
    }
172
173
    /**
174
     * Get order ID.
175
     * @since 2.6.0
176
     * @return integer
177
     */
178
    public function get_order_id() {
179
        return absint( $this->_data['order_id'] );
180
    }
181
182
    /**
183
     * Get parent order ID.
184
     * @since 2.6.0
185
     * @return integer
186
     */
187
    public function get_parent_id() {
188
        return absint( $this->_data['parent_id'] );
189
    }
190
191
    /**
192
     * get_order_number function.
193
     *
194
     * Gets the order number for display (by default, order ID).
195
     *
196
     * @return string
197
     */
198
    public function get_order_number() {
199
        return apply_filters( 'woocommerce_order_number', $this->get_id(), $this );
200
    }
201
202
    /**
203
     * Get order key.
204
     * @since 2.6.0
205
     * @return string
206
     */
207
    public function get_order_key() {
208
        return $this->_data['order_key'];
209
    }
210
211
    /**
212
     * Gets order currency.
213
     * @return string
214
     */
215
    public function get_order_currency() {
216
        return apply_filters( 'woocommerce_get_order_currency', $this->_data['order_currency'], $this );
217
    }
218
219
    /**
220
     * Get Order Type
221
     * @return string
222
     */
223
    public function get_order_type() {
224
        return $this->_data['order_type'];
225
    }
226
227
    /**
228
     * Get date_created
229
     * @return string
230
     */
231
    public function get_date_created() {
232
        return $this->_data['date_created'];
233
    }
234
235
    /**
236
     * Get date_modified
237
     * @return string
238
     */
239
    public function get_date_modified() {
240
        return $this->_data['date_modified'];
241
    }
242
243
    /**
244
     * Get customer_id
245
     * @return int
246
     */
247
    public function get_customer_id() {
248
        return absint( $this->_data['customer_id'] );
249
    }
250
251
    /**
252
     * Get billing_first_name
253
     * @return string
254
     */
255
    public function get_billing_first_name() {
256
        return $this->_data['billing_first_name'];
257
    }
258
259
    /**
260
     * Get billing_last_name
261
     * @return string
262
     */
263
    public function get_billing_last_name() {
264
        return $this->_data['billing_last_name'];
265
    }
266
267
    /**
268
     * Get billing_company
269
     * @return string
270
     */
271
    public function get_billing_company() {
272
        return $this->_data['billing_company'];
273
    }
274
275
    /**
276
     * Get billing_address_1
277
     * @return string
278
     */
279
    public function get_billing_address_1() {
280
        return $this->_data['billing_address_1'];
281
    }
282
283
    /**
284
     * Get billing_address_2
285
     * @return string $value
286
     */
287
    public function get_billing_address_2() {
288
        return $this->_data['billing_address_2'];
289
    }
290
291
    /**
292
     * Get billing_city
293
     * @return string $value
294
     */
295
    public function get_billing_city() {
296
        return $this->_data['billing_city'];
297
    }
298
299
    /**
300
     * Get billing_state
301
     * @return string
302
     */
303
    public function get_billing_state() {
304
        return $this->_data['billing_state'];
305
    }
306
307
    /**
308
     * Get billing_postcode
309
     * @return string
310
     */
311
    public function get_billing_postcode() {
312
        return $this->_data['billing_postcode'];
313
    }
314
315
    /**
316
     * Get billing_country
317
     * @return string
318
     */
319
    public function get_billing_country() {
320
        return $this->_data['billing_country'];
321
    }
322
323
    /**
324
     * Get billing_email
325
     * @return string
326
     */
327
    public function get_billing_email() {
328
        return sanitize_email( $this->_data['billing_email'] );
329
    }
330
331
    /**
332
     * Get billing_phone
333
     * @return string
334
     */
335
    public function get_billing_phone() {
336
        return $this->_data['billing_phone'];
337
    }
338
339
    /**
340
     * Get shipping_first_name
341
     * @return string
342
     */
343
    public function get_shipping_first_name() {
344
        return $this->_data['shipping_first_name'];
345
    }
346
347
    /**
348
     * Get shipping_last_name
349
     * @return string
350
     */
351
    public function get_shipping_last_name() {
352
         return $this->_data['shipping_last_name'];
353
    }
354
355
    /**
356
     * Get shipping_company
357
     * @return string
358
     */
359
    public function get_shipping_company() {
360
        return $this->_data['shipping_company'];
361
    }
362
363
    /**
364
     * Get shipping_address_1
365
     * @return string
366
     */
367
    public function get_shipping_address_1() {
368
        return $this->_data['shipping_address_1'];
369
    }
370
371
    /**
372
     * Get shipping_address_2
373
     * @return string
374
     */
375
    public function get_shipping_address_2() {
376
        return $this->_data['shipping_address_2'];
377
    }
378
379
    /**
380
     * Get shipping_city
381
     * @return string
382
     */
383
    public function get_shipping_city() {
384
        return $this->_data['shipping_city'];
385
    }
386
387
    /**
388
     * Get shipping_state
389
     * @return string
390
     */
391
    public function get_shipping_state() {
392
        return $this->_data['shipping_state'];
393
    }
394
395
    /**
396
     * Get shipping_postcode
397
     * @return string
398
     */
399
    public function get_shipping_postcode() {
400
        return $this->_data['shipping_postcode'];
401
    }
402
403
    /**
404
     * Get shipping_country
405
     * @return string
406
     */
407
    public function get_shipping_country() {
408
        return $this->_data['shipping_country'];
409
    }
410
411
    /**
412
     * Get the payment method.
413
     * @return string
414
     */
415
    public function get_payment_method() {
416
        return $this->_meta['payment_method'];
417
    }
418
419
    /**
420
     * Get payment_method_title
421
     * @return string
422
     */
423
    public function get_payment_method_title() {
424
        return $this->_meta['payment_method_title'];
425
    }
426
427
    /**
428
     * Get transaction_id
429
     * @return string
430
     */
431
    public function get_transaction_id() {
432
        return $this->_meta['transaction_id'];
433
    }
434
435
    /**
436
     * Get customer_ip_address
437
     * @return string
438
     */
439
    public function get_customer_ip_address() {
440
        return $this->_meta['customer_ip_address'];
441
    }
442
443
    /**
444
     * Get customer_user_agent
445
     * @return string
446
     */
447
    public function get_customer_user_agent() {
448
        return $this->_meta['customer_user_agent'];
449
    }
450
451
    /**
452
     * Get created_via
453
     * @return string
454
     */
455
    public function get_created_via() {
456
        return $this->_meta['created_via'];
457
    }
458
459
    /**
460
     * Get order_version
461
     * @return string
462
     */
463
    public function get_order_version() {
464
        return $this->_meta['order_version'];
465
    }
466
467
    /**
468
     * Get prices_include_tax
469
     * @return bool
470
     */
471
    public function get_prices_include_tax() {
472
        return (bool) $this->_meta['prices_include_tax'];
473
    }
474
475
    /**
476
     * Get customer_note
477
     * @return string
478
     */
479
    public function get_customer_note() {
480
        return $this->_meta['customer_note'];
481
    }
482
483
	/**
484
     * Get date_completed
485
     * @return string
486
     */
487
    public function get_date_completed() {
488
        return $this->_meta['date_completed'];
489
    }
490
491
	/**
492
     * Get date_paid
493
     * @return string
494
     */
495
    public function get_date_paid() {
496
        return $this->_meta['date_paid'];
497
    }
498
499
    /**
500
     * Returns the requested address in raw, non-formatted way.
501
     * @since  2.4.0
502
     * @param  string $type Billing or shipping. Anything else besides 'billing' will return shipping address.
503
     * @return array The stored address after filter.
504
     */
505
    public function get_address( $type = 'billing' ) {
506
        if ( 'billing' === $type ) {
507
            $address = array(
508
                'first_name' => $this->get_billing_first_name(),
509
                'last_name'  => $this->get_billing_last_name(),
510
                'company'    => $this->get_billing_company(),
511
                'address_1'  => $this->get_billing_address_1(),
512
                'address_2'  => $this->get_billing_address_2(),
513
                'city'       => $this->get_billing_city(),
514
                'state'      => $this->get_billing_state(),
515
                'postcode'   => $this->get_billing_postcode(),
516
                'country'    => $this->get_billing_country(),
517
                'email'      => $this->get_billing_email(),
518
                'phone'      => $this->get_billing_phone()
519
            );
520
        } else {
521
            $address = array(
522
                'first_name' => $this->get_shipping_first_name(),
523
                'last_name'  => $this->get_shipping_last_name(),
524
                'company'    => $this->get_shipping_company(),
525
                'address_1'  => $this->get_shipping_address_1(),
526
                'address_2'  => $this->get_shipping_address_2(),
527
                'city'       => $this->get_shipping_city(),
528
                'state'      => $this->get_shipping_state(),
529
                'postcode'   => $this->get_shipping_postcode(),
530
                'country'    => $this->get_shipping_country()
531
            );
532
        }
533
        return apply_filters( 'woocommerce_get_order_address', $address, $type, $this );
534
    }
535
536
    /**
537
     * Return the order statuses without wc- internal prefix.
538
     * @return string
539
     */
540
    public function get_status() {
541
        return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->_data['status'], 0, 3 ) ? substr( $this->_data['status'], 3 ) : $this->_data['status'], $this );
542
    }
543
544
    /**
545
     * Alias for get_customer_id().
546
     * @since  2.2
547
     * @return int
548
     */
549
    public function get_user_id() {
550
        return $this->get_customer_id();
551
    }
552
553
    /**
554
     * Get the user associated with the order. False for guests.
555
     *
556
     * @since  2.2
557
     * @return WP_User|false
558
     */
559
    public function get_user() {
560
        return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
561
    }
562
563
    /**
564
     * Get a formatted billing address for the order.
565
     * @return string
566
     */
567
    public function get_formatted_billing_address() {
568
        return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_billing_address', $this->get_address( 'billing' ), $this ) );
569
    }
570
571
    /**
572
     * Get a formatted shipping address for the order.
573
     * @return string
574
     */
575
    public function get_formatted_shipping_address() {
576
        if ( $this->get_shipping_address_1() || $this->get_shipping_address_2() ) {
577
            return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this ) );
578
        } else {
579
            return '';
580
        }
581
    }
582
583
    /**
584
     * Get a formatted shipping address for the order.
585
     *
586
     * @return string
587
     */
588
    public function get_shipping_address_map_url() {
589
        $address = apply_filters( 'woocommerce_shipping_address_map_url_parts', array(
590
            'address_1' => $this->get_shipping_address_1(),
591
            'address_2' => $this->get_shipping_address_2(),
592
            'city'      => $this->get_shipping_city(),
593
            'state'     => $this->get_shipping_state(),
594
            'postcode'  => $this->get_shipping_postcode(),
595
            'country'   => $this->get_shipping_country()
596
        ), $this );
597
        return apply_filters( 'woocommerce_shipping_address_map_url', 'http://maps.google.com/maps?&q=' . urlencode( implode( ', ', $address ) ) . '&z=16', $this );
598
    }
599
600
    /**
601
     * Get a formatted billing full name.
602
     *
603
     * @since 2.4.0
604
     *
605
     * @return string
606
     */
607
    public function get_formatted_billing_full_name() {
608
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_billing_first_name(), $this->get_billing_last_name() );
609
    }
610
611
    /**
612
     * Get a formatted shipping full name.
613
     *
614
     * @since 2.4.0
615
     *
616
     * @return string
617
     */
618
    public function get_formatted_shipping_full_name() {
619
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_shipping_first_name(), $this->get_shipping_last_name() );
620
    }
621
622
    /**
623
     * Get discount_total
624
     * @return string
625
     */
626
    public function get_discount_total() {
627
        $discount_total = wc_format_decimal( $this->_data['discount_total'] );
628
629
        // Backwards compatible total calculation - totals were not stored consistently in old versions.
630
        if ( ( ! $this->get_order_version() || version_compare( $this->get_order_version(), '2.3.7', '<' ) ) && $this->get_prices_include_tax() ) {
631
            $discount_total = $discount_total - $this->get_discount_tax();
632
        }
633
634
        return $discount_total;
635
    }
636
637
    /**
638
     * Get discount_tax
639
     * @return string
640
     */
641
    public function get_discount_tax() {
642
        return wc_format_decimal( $this->_data['discount_tax'] );
643
    }
644
645
    /**
646
     * Get shipping_total
647
     * woocommerce_order_amount_total_shipping filter has been removed to avoid
648
     * these values being modified and then saved back to the DB. There are
649
     * other, later hooks available to change totals on display. e.g.
650
     * woocommerce_get_order_item_totals.
651
     * @return string
652
     */
653
    public function get_shipping_total() {
654
        return wc_format_decimal( $this->_data['shipping_total'] );
655
    }
656
657
    /**
658
     * Gets cart tax amount.
659
     *
660
     * @since 2.6.0 woocommerce_order_amount_cart_tax filter has been removed to avoid
661
     * these values being modified and then saved back to the DB or used in
662
     * calculations. There are other, later hooks available to change totals on
663
     * display. e.g. woocommerce_get_order_item_totals.
664
     * @return float
665
     */
666
    public function get_cart_tax() {
667
        return wc_format_decimal( $this->_data['cart_tax'] );
668
    }
669
670
    /**
671
     * Get shipping_tax.
672
     *
673
     * @since 2.6.0 woocommerce_order_amount_shipping_tax filter has been removed to avoid
674
     * these values being modified and then saved back to the DB or used in
675
     * calculations. There are other, later hooks available to change totals on
676
     * display. e.g. woocommerce_get_order_item_totals.
677
     * @return string
678
     */
679
    public function get_shipping_tax() {
680
        return wc_format_decimal( $this->_data['shipping_tax'] );
681
    }
682
683
    /**
684
     * Order tax is the sum of all taxes.
685
     * @return string
686
     */
687
    public function get_order_tax() {
688
        return wc_round_tax_total( $this->_data['order_tax'] );
689
    }
690
691
    /**
692
     * Get the stored order total. Includes taxes and everything else.
693
     * @return string
694
     */
695
    public function get_order_total() {
696
        return wc_format_decimal( $this->_data['order_total'], wc_get_price_decimals() );
697
    }
698
699
    /**
700
     * Gets the total discount amount.
701
     * @param  bool $ex_tax Show discount excl any tax.
702
     * @return float
703
     */
704
    public function get_total_discount( $ex_tax = true ) {
705
        if ( $ex_tax ) {
706
            $total_discount = $this->get_discount_total();
707
        } else {
708
            $total_discount = $this->get_discount_total() + $this->get_discount_tax();
709
        }
710
        return apply_filters( 'woocommerce_order_amount_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this );
711
    }
712
713
    /**
714
     * Get total tax amount. Alias for get_order_tax().
715
     *
716
     * @since 2.6.0 woocommerce_order_amount_total_tax filter has been removed to avoid
717
     * these values being modified and then saved back to the DB. There are
718
     * other, later hooks available to change totals on display. e.g.
719
     * woocommerce_get_order_item_totals.
720
     * @return float
721
     */
722
    public function get_total_tax() {
723
        return $this->get_order_tax();
724
    }
725
726
    /**
727
     * Gets shipping total. Alias of WC_Order::get_shipping_total().
728
     *
729
     * @since 2.6.0 woocommerce_order_amount_total_shipping filter has been removed to avoid
730
     * these values being modified and then saved back to the DB or used in
731
     * calculations. There are other, later hooks available to change totals on
732
     * display. e.g. woocommerce_get_order_item_totals.
733
     * @return float
734
     */
735
    public function get_total_shipping() {
736
        return $this->get_shipping_total();
737
    }
738
739
    /**
740
     * Gets order grand total. incl. taxes. Used in gateways. Filtered.
741
     * @return float
742
     */
743
    public function get_total() {
744
        return apply_filters( 'woocommerce_order_amount_total', $this->get_order_total(), $this );
745
    }
746
747
    /**
748
     * Gets order subtotal.
749
     * @return float
750
     */
751
    public function get_subtotal() {
752
        $subtotal = 0;
753
754
        foreach ( $this->get_items() as $item ) {
755
            $subtotal += isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
756
        }
757
758
        return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
759
    }
760
761
    /**
762
     * Get taxes, merged by code, formatted ready for output.
763
     *
764
     * @return array
765
     */
766
    public function get_tax_totals() {
767
        $tax_totals = array();
768
769
        foreach ( $this->get_items( 'tax' ) as $key => $tax ) {
770
            $code = $tax[ 'name' ];
771
772 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...
773
                $tax_totals[ $code ] = new stdClass();
774
                $tax_totals[ $code ]->amount = 0;
775
            }
776
777
            $tax_totals[ $code ]->id                = $key;
778
            $tax_totals[ $code ]->rate_id           = $tax['rate_id'];
779
            $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
780
            $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
781
            $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
782
            $tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
783
        }
784
785
        return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
786
    }
787
788
    /**
789
     * Gets formatted shipping method title.
790
     * @return string
791
     */
792
    public function get_shipping_method() {
793
        $names = array();
794
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
795
            $names[] = $shipping_method->get_name();
796
        }
797
        return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this );
798
    }
799
800
    /*
801
    |--------------------------------------------------------------------------
802
    | Setters
803
    |--------------------------------------------------------------------------
804
    |
805
    | Functions for setting order data. These should not update anything in the
806
    | database itself and should only change what is stored in the class
807
    | object. However, for backwards compatibility pre 2.6.0 some of these
808
    | setters may handle both.
809
    |
810
    */
811
812
    /**
813
     * Set order ID.
814
     * @since 2.6.0
815
     * @param int $value
816
     */
817
    public function set_order_id( $value ) {
818
        $this->_data['order_id'] = absint( $value );
819
    }
820
821
    /**
822
     * Set parent order ID.
823
     * @since 2.6.0
824
     * @param int $value
825
     */
826
    public function set_parent_id( $value ) {
827
        $this->_data['parent_id'] = absint( $value );
828
    }
829
830
    /**
831
     * Set order status.
832
     * @since 2.6.0
833
     * @param string $new_status Status to change the order to. No internal wc- prefix is required.
834
     * @param string $note (default: '') Optional note to add.
835
     * @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...
836
     */
837
    public function set_status( $new_status, $note = '', $manual_update = false ) {
838
        // Remove prefixes and standardize
839
        $current_status = $this->get_status();
840
        $new_status     = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
841
842
        if ( in_array( 'wc-' . $new_status, array_keys( wc_get_order_statuses() ) ) && $new_status !== $current_status ) {
843
            if ( ! empty( $current_status ) ) {
844
                $this->_status_transition = array(
845
                    'original' => ! empty( $this->_status_transition['original'] ) ? $this->_status_transition['original'] : $current_status,
846
                    'note'     => $note ? $note : '',
847
                    'manual'   => (bool) $manual_update
848
                );
849
				if ( 'completed' === $new_status ) {
850
					$this->set_date_completed( current_time( 'timestamp' ) );
851
				}
852
            }
853
            $this->_data['status'] = 'wc-' . $new_status;
854
        }
855
    }
856
857
	/**
858
     * Updates status of order immediately.
859
     * @uses WC_Order::set_status()
860
     */
861
    public function update_status( $new_status, $note = '', $manual = false ) {
862
        if ( ! $this->get_id() ) {
863
            return false;
864
        }
865
		$this->set_status( $new_status, $note, $manual );
866
		$this->save();
867
        return true;
868
    }
869
870
    /**
871
     * Set Order Type
872
     * @param string $value
873
     */
874
    public function set_order_type( $value ) {
875
        $this->_data['order_type'] = $value;
876
    }
877
878
    /**
879
     * Set order_key
880
     * @param string $value
881
     */
882
    public function set_order_key( $value ) {
883
        $this->_data['order_key'] = $value;
884
    }
885
886
    /**
887
     * Set order_currency
888
     * @param string $value
889
     */
890
    public function set_order_currency( $value ) {
891
        $this->_data['order_currency'] = $value;
892
    }
893
894
    /**
895
     * Set date_created
896
     * @param string $timestamp Timestamp
897
     */
898
    public function set_date_created( $timestamp ) {
899
        $this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
900
    }
901
902
    /**
903
     * Set date_modified
904
     * @param string $timestamp
905
     */
906
    public function set_date_modified( $timestamp ) {
907
        $this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
908
    }
909
910
    /**
911
     * Set customer_id
912
     * @param int $value
913
     */
914
    public function set_customer_id( $value ) {
915
        $this->_data['customer_id'] = absint( $value );
916
    }
917
918
    /**
919
     * Set billing_first_name
920
     * @param string $value
921
     */
922
    public function set_billing_first_name( $value ) {
923
        $this->_data['billing_first_name'] = $value;
924
    }
925
926
    /**
927
     * Set billing_last_name
928
     * @param string $value
929
     */
930
    public function set_billing_last_name( $value ) {
931
        $this->_data['billing_last_name'] = $value;
932
    }
933
934
    /**
935
     * Set billing_company
936
     * @param string $value
937
     */
938
    public function set_billing_company( $value ) {
939
        $this->_data['billing_company'] = $value;
940
    }
941
942
    /**
943
     * Set billing_address_1
944
     * @param string $value
945
     */
946
    public function set_billing_address_1( $value ) {
947
        $this->_data['billing_address_1'] = $value;
948
    }
949
950
    /**
951
     * Set billing_address_2
952
     * @param string $value
953
     */
954
    public function set_billing_address_2( $value ) {
955
        $this->_data['billing_address_2'] = $value;
956
    }
957
958
    /**
959
     * Set billing_city
960
     * @param string $value
961
     */
962
    public function set_billing_city( $value ) {
963
        $this->_data['billing_city'] = $value;
964
    }
965
966
    /**
967
     * Set billing_state
968
     * @param string $value
969
     */
970
    public function set_billing_state( $value ) {
971
        $this->_data['billing_state'] = $value;
972
    }
973
974
    /**
975
     * Set billing_postcode
976
     * @param string $value
977
     */
978
    public function set_billing_postcode( $value ) {
979
        $this->_data['billing_postcode'] = $value;
980
    }
981
982
    /**
983
     * Set billing_country
984
     * @param string $value
985
     */
986
    public function set_billing_country( $value ) {
987
        $this->_data['billing_country'] = $value;
988
    }
989
990
    /**
991
     * Set billing_email
992
     * @param string $value
993
     */
994
    public function set_billing_email( $value ) {
995
		$value = sanitize_email( $value );
996
        $this->_data['billing_email'] = is_email( $value ) ? $value : '';
997
    }
998
999
    /**
1000
     * Set billing_phone
1001
     * @param string $value
1002
     */
1003
    public function set_billing_phone( $value ) {
1004
        $this->_data['billing_phone'] = $value;
1005
    }
1006
1007
    /**
1008
     * Set shipping_first_name
1009
     * @param string $value
1010
     */
1011
    public function set_shipping_first_name( $value ) {
1012
        $this->_data['shipping_first_name'] = $value;
1013
    }
1014
1015
    /**
1016
     * Set shipping_last_name
1017
     * @param string $value
1018
     */
1019
    public function set_shipping_last_name( $value ) {
1020
        $this->_data['shipping_last_name'] = $value;
1021
    }
1022
1023
    /**
1024
     * Set shipping_company
1025
     * @param string $value
1026
     */
1027
    public function set_shipping_company( $value ) {
1028
        $this->_data['shipping_company'] = $value;
1029
    }
1030
1031
    /**
1032
     * Set shipping_address_1
1033
     * @param string $value
1034
     */
1035
    public function set_shipping_address_1( $value ) {
1036
        $this->_data['shipping_address_1'] = $value;
1037
    }
1038
1039
    /**
1040
     * Set shipping_address_2
1041
     * @param string $value
1042
     */
1043
    public function set_shipping_address_2( $value ) {
1044
        $this->_data['shipping_address_2'] = $value;
1045
    }
1046
1047
    /**
1048
     * Set shipping_city
1049
     * @param string $value
1050
     */
1051
    public function set_shipping_city( $value ) {
1052
        $this->_data['shipping_city'] = $value;
1053
    }
1054
1055
    /**
1056
     * Set shipping_state
1057
     * @param string $value
1058
     */
1059
    public function set_shipping_state( $value ) {
1060
        $this->_data['shipping_state'] = $value;
1061
    }
1062
1063
    /**
1064
     * Set shipping_postcode
1065
     * @param string $value
1066
     */
1067
    public function set_shipping_postcode( $value ) {
1068
        $this->_data['shipping_postcode'] = $value;
1069
    }
1070
1071
    /**
1072
     * Set shipping_country
1073
     * @param string $value
1074
     */
1075
    public function set_shipping_country( $value ) {
1076
        $this->_data['shipping_country'] = $value;
1077
    }
1078
1079
    /**
1080
     * Set discount_total
1081
     * @param string $value
1082
     */
1083
    public function set_discount_total( $value ) {
1084
        $this->_data['discount_total'] = wc_format_decimal( $value );
1085
    }
1086
1087
    /**
1088
     * Set discount_tax
1089
     * @param string $value
1090
     */
1091
    public function set_discount_tax( $value ) {
1092
        $this->_data['discount_tax'] = wc_format_decimal( $value );
1093
    }
1094
1095
    /**
1096
     * Set shipping_total
1097
     * @param string $value
1098
     */
1099
    public function set_shipping_total( $value ) {
1100
        $this->_data['shipping_total'] = wc_format_decimal( $value );
1101
    }
1102
1103
    /**
1104
     * Set shipping_tax
1105
     * @param string $value
1106
     */
1107
    public function set_shipping_tax( $value ) {
1108
        $this->_data['shipping_tax'] = wc_format_decimal( $value );
1109
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1110
    }
1111
1112
    /**
1113
     * Set cart tax
1114
     * @param string $value
1115
     */
1116
    public function set_cart_tax( $value ) {
1117
        $this->_data['cart_tax'] = wc_format_decimal( $value );
1118
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1119
    }
1120
1121
    /**
1122
     * Sets order tax (sum of cart and shipping tax). Used internaly only.
1123
     * @access protected
1124
     * @param string $value
1125
     */
1126
    protected function set_order_tax( $value ) {
1127
        $this->_data['order_tax'] = wc_format_decimal( $value );
1128
    }
1129
1130
    /**
1131
     * Set order_total
1132
     * @param string $value
1133
     */
1134
    public function set_order_total( $value ) {
1135
        $this->_data['order_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
1136
    }
1137
1138
    /**
1139
     * Set the payment method.
1140
     * @since 2.2.0
1141
     * @param string $value Supports WC_Payment_Gateway for bw compatibility with < 2.6
1142
     */
1143
    public function set_payment_method( $value ) {
1144
        if ( is_object( $value ) ) {
1145
            $this->set_payment_method( $value->id );
1146
            $this->set_payment_method_title( $value->get_title() );
1147
        } else {
1148
            $this->_meta['payment_method'] = $value;
1149
        }
1150
    }
1151
1152
    /**
1153
     * Set payment_method_title
1154
     * @param string $value
1155
     */
1156
    public function set_payment_method_title( $value ) {
1157
        $this->_meta['payment_method_title'] = $value;
1158
    }
1159
1160
    /**
1161
     * Set transaction_id
1162
     * @param string $value
1163
     */
1164
    public function set_transaction_id( $value ) {
1165
        $this->_meta['transaction_id'] = $value;
1166
    }
1167
1168
    /**
1169
     * Set customer_ip_address
1170
     * @param string $value
1171
     */
1172
    public function set_customer_ip_address( $value ) {
1173
        $this->_meta['customer_ip_address'] = $value;
1174
    }
1175
1176
    /**
1177
     * Set customer_user_agent
1178
     * @param string $value
1179
     */
1180
    public function set_customer_user_agent( $value ) {
1181
        $this->_meta['customer_user_agent'] = $value;
1182
    }
1183
1184
    /**
1185
     * Set created_via
1186
     * @param string $value
1187
     */
1188
    public function set_created_via( $value ) {
1189
        $this->_meta['created_via'] = $value;
1190
    }
1191
1192
    /**
1193
     * Set order_version
1194
     * @param string $value
1195
     */
1196
    public function set_order_version( $value ) {
1197
        $this->_meta['order_version'] = $value;
1198
    }
1199
1200
    /**
1201
     * Set prices_include_tax
1202
     * @param bool $value
1203
     */
1204
    public function set_prices_include_tax( $value ) {
1205
        $this->_meta['prices_include_tax'] = (bool) $value;
1206
    }
1207
1208
    /**
1209
     * Set customer_note
1210
     * @param string $value
1211
     */
1212
    public function set_customer_note( $value ) {
1213
        $this->_meta['customer_note'] = $value;
1214
    }
1215
1216
	/**
1217
     * Set date_completed
1218
     * @param string $timestamp
1219
     */
1220
    public function set_date_completed( $timestamp ) {
1221
        $this->_meta['date_completed'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
1222
    }
1223
1224
	/**
1225
     * Set date_paid
1226
     * @param string $timestamp
1227
     */
1228
    public function set_date_paid( $timestamp ) {
1229
        $this->_meta['date_paid'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
1230
    }
1231
1232
    /*
1233
    |--------------------------------------------------------------------------
1234
    | CRUD methods
1235
    |--------------------------------------------------------------------------
1236
    |
1237
    | Methods which create, read, update and delete orders from the database.
1238
    | Written in abstract fashion so that the way orders are stored can be
1239
    | changed more easily in the future.
1240
    |
1241
    | A save method is included for convenience (chooses update or create based
1242
    | on if the order exists yet).
1243
    |
1244
    */
1245
1246
    /**
1247
     * Insert data into the database.
1248
     * @since 2.6.0
1249
     * @access protected
1250
     */
1251
    public function create() {
1252
        // Set random key
1253
        $this->set_order_key( uniqid( 'order_' ) );
1254
1255
        $order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', array(
1256
            'post_type'     => $this->get_order_type(),
1257
            'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1258
            'ping_status'   => 'closed',
1259
            'post_author'   => 1,
1260
            'post_title'    => sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) ),
1261
            'post_password' => $this->get_order_key(),
1262
            'post_parent'   => $this->get_parent_id()
1263
        ) ), true );
1264
1265
        if ( $order_id ) {
1266
            $this->set_order_id( $order_id );
1267
1268
            // Set meta data
1269
            update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1270
            update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1271
            update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1272
            update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1273
            update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1274
            update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1275
            update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1276
            update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1277
            update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1278
            update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1279
            update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1280
            update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1281
            update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1282
            update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1283
            update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1284
            update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1285
            update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1286
            update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1287
            update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1288
            update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1289
            update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1290
            update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1291
            update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1292
            update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1293
            update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1294
            update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1295
            update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1296
            update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1297
1298
			foreach ( $this->get_meta() as $key => $value ) {
1299
				update_post_meta( $order_id, '_' . $key, $value );
1300
			}
1301
        }
1302
    }
1303
1304
    /**
1305
     * Read from the database.
1306
     * @since 2.6.0
1307
     * @param int $id ID of object to read.
1308
     */
1309
    public function read( $id ) {
1310
        $post_object = get_post( $id );
1311
        $order_id    = absint( $post_object->ID );
1312
1313
        // Map standard post data
1314
        $this->set_order_id( $order_id );
1315
        $this->set_date_created( $post_object->post_date );
1316
        $this->set_date_modified( $post_object->post_modified );
1317
        $this->set_status( $post_object->post_status );
1318
        $this->set_customer_note( $post_object->post_excerpt );
1319
1320
        // Map meta data
1321
        $this->set_customer_id( get_post_meta( $order_id, '_customer_user', true ) );
1322
        $this->set_order_key( get_post_meta( $order_id, '_order_key', true ) );
1323
        $this->set_order_currency( get_post_meta( $order_id, '_order_currency', true ) );
1324
        $this->set_billing_first_name( get_post_meta( $order_id, '_billing_first_name', true ) );
1325
        $this->set_billing_last_name( get_post_meta( $order_id, '_billing_last_name', true ) );
1326
        $this->set_billing_company( get_post_meta( $order_id, '_billing_company', true ) );
1327
        $this->set_billing_address_1( get_post_meta( $order_id, '_billing_address_1', true ) );
1328
        $this->set_billing_address_2( get_post_meta( $order_id, '_billing_address_2', true ) );
1329
        $this->set_billing_city( get_post_meta( $order_id, '_billing_city', true ) );
1330
        $this->set_billing_state( get_post_meta( $order_id, '_billing_state', true ) );
1331
        $this->set_billing_postcode( get_post_meta( $order_id, '_billing_postcode', true ) );
1332
        $this->set_billing_country( get_post_meta( $order_id, '_billing_country', true ) );
1333
        $this->set_billing_email( get_post_meta( $order_id, '_billing_email', true ) );
1334
        $this->set_billing_phone( get_post_meta( $order_id, '_billing_phone', true ) );
1335
        $this->set_shipping_first_name( get_post_meta( $order_id, '_shipping_first_name', true ) );
1336
        $this->set_shipping_last_name( get_post_meta( $order_id, '_shipping_last_name', true ) );
1337
        $this->set_shipping_company( get_post_meta( $order_id, '_shipping_company', true ) );
1338
        $this->set_shipping_address_1( get_post_meta( $order_id, '_shipping_address_1', true ) );
1339
        $this->set_shipping_address_2( get_post_meta( $order_id, '_shipping_address_2', true ) );
1340
        $this->set_shipping_city( get_post_meta( $order_id, '_shipping_city', true ) );
1341
        $this->set_shipping_state( get_post_meta( $order_id, '_shipping_state', true ) );
1342
        $this->set_shipping_postcode( get_post_meta( $order_id, '_shipping_postcode', true ) );
1343
        $this->set_shipping_country( get_post_meta( $order_id, '_shipping_country', true ) );
1344
1345
		// Map totals
1346
        $this->set_discount_total( get_post_meta( $order_id, '_cart_discount', true ) );
1347
        $this->set_discount_tax( get_post_meta( $order_id, '_cart_discount_tax', true ) );
1348
        $this->set_shipping_total( get_post_meta( $order_id, '_order_shipping', true ) );
1349
        $this->set_shipping_tax( get_post_meta( $order_id, '_order_shipping_tax', true ) );
1350
        $this->set_cart_tax( get_post_meta( $order_id, '_order_tax', true ) );
1351
        $this->set_order_total( get_post_meta( $order_id, '_order_total', true ) );
1352
1353
        // Map user data
1354
        if ( empty( $this->get_billing_email() ) && ( $user = $this->get_user() ) ) {
1355
            $this->set_billing_email( $user->user_email );
1356
        }
1357
1358
        // This is meta data, but due to the keys not matching the props, load here
1359
		$this->set_date_completed( get_post_meta( $order_id, '_completed_date', true ) );
1360
		$this->set_date_paid( get_post_meta( $order_id, '_paid_date', true ) );
1361
1362
		// Load meta data, including anything custom set.
1363
		$ignore_keys = array( '_customer_user', '_order_key', '_order_currency', '_billing_first_name', '_billing_last_name', '_billing_company', '_billing_address_1', '_billing_address_2', '_billing_city', '_billing_state', '_billing_postcode', '_billing_country', '_billing_email', '_billing_phone', '_shipping_first_name', '_shipping_last_name', '_shipping_company', '_shipping_address_1', '_shipping_address_2', '_shipping_city', '_shipping_state', '_shipping_postcode', '_shipping_country', '_completed_date', '_paid_date', '_edit_lock', '_cart_discount', '_cart_discount_tax', '_order_shipping', '_order_shipping_tax', '_order_tax', '_order_total', '_order_total' );
1364
		$meta_data   = get_post_meta( $order_id );
1365
		$meta_data   = array_diff_key( $meta_data, array_fill_keys( $ignore_keys, '' ) );
1366
1367
		// Set meta data. Remove _ from key for hidden meta.
1368
		foreach ( $meta_data as $key => $value ) {
1369
			$key = ltrim( $key, '_' );
1370
			if ( is_callable( array( $this, "set_$key" ) ) ) {
1371
				$this->{"set_$key"}( $value[0] );
1372
			} else {
1373
				$this->_meta[ $key ] = $value[0];
1374
			}
1375
		}
1376
1377
        // Orders store the state of prices including tax when created.
1378
        $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' );
1379
    }
1380
1381
    /**
1382
     * Update data in the database.
1383
     * @since 2.6.0
1384
     * @access protected
1385
     */
1386
    public function update() {
1387
        global $wpdb;
1388
1389
        $order_id = $this->get_id();
1390
1391
        $wpdb->update(
1392
            $wpdb->posts,
1393
            array(
1394
                'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
1395
                'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
1396
                'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1397
                'post_parent'   => $this->get_parent_id()
1398
            ),
1399
            array(
1400
                'ID' => $order_id
1401
            )
1402
        );
1403
1404
        // Update meta data
1405
        update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1406
        update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1407
        update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1408
        update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1409
        update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1410
        update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1411
        update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1412
        update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1413
        update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1414
        update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1415
        update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1416
        update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1417
        update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1418
        update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1419
        update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1420
        update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1421
        update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1422
        update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1423
        update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1424
        update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1425
        update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1426
        update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1427
        update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1428
        update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1429
        update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1430
        update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1431
        update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1432
        update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1433
1434
		foreach ( $this->get_meta() as $key => $value ) {
1435
			update_post_meta( $order_id, '_' . $key, $value );
1436
		}
1437
1438
        if ( $this->_status_transition ) {
1439
            if ( ! empty( $this->_status_transition['original'] ) ) {
1440
                $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() ) );
1441
1442
                do_action( 'woocommerce_order_status_' . $this->_status_transition['original'] . '_to_' . $this->get_status(), $this->get_id() );
1443
                do_action( 'woocommerce_order_status_changed', $this->get_id(), $this->_status_transition['original'], $this->get_status() );
1444
            } else {
1445
                $transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $this->get_status() ) );
1446
            }
1447
1448
            do_action( 'woocommerce_order_status_' . $this->get_status(), $this->get_id() );
1449
1450
            // Note the transition occured
1451
            $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...
1452
1453
            // This has ran, so reset status transition variable
1454
            $this->_status_transition = false;
1455
        }
1456
    }
1457
1458
    /**
1459
     * Delete data from the database.
1460
     * @since 2.6.0
1461
     */
1462
    public function delete() {
1463
        wp_delete_post( $this->get_id() );
1464
    }
1465
1466
    /**
1467
     * Save data to the database.
1468
     * @since 2.6.0
1469
     * @access protected
1470
     */
1471
    public function save() {
1472
        if ( ! $this->get_id() ) {
1473
            $this->create();
1474
        } else {
1475
            $this->update();
1476
        }
1477
        wc_delete_shop_order_transients( $this->get_id() );
1478
    }
1479
1480
    /*
1481
    |--------------------------------------------------------------------------
1482
    | Order Item Handling
1483
    |--------------------------------------------------------------------------
1484
    |
1485
    | Order items are used for products, taxes, shipping, and fees within
1486
    | each order.
1487
    |
1488
    */
1489
1490
    /**
1491
     * Return an array of items/products within this order.
1492
     *
1493
     * @param string|array $type Types of line items to get (array or string).
1494
     * @return Array of WC_Order_item
1495
     */
1496
    public function get_items( $type = 'line_item' ) {
1497
        global $wpdb;
1498
1499
        $type            = ! is_array( $type ) ? array( $type ) : $type;
1500
        $items           = array();
1501
        $get_items_sql   = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d ", $this->get_id() );
1502
        $get_items_sql  .= "AND order_item_type IN ( '" . implode( "','", array_map( 'esc_sql', $type ) ) . "' ) ORDER BY order_item_id;";
1503
        $raw_items       = $wpdb->get_results( $get_items_sql );
1504
1505
        foreach ( $raw_items as $item ) {
1506
            $item                                = $this->get_item( $item );
1507
            $items[ $item->get_order_item_id() ] = $item;
1508
        }
1509
1510
        return apply_filters( 'woocommerce_order_get_items', $items, $this );
1511
    }
1512
1513
    /**
1514
     * Get an order item object, based on it's type.
1515
     * @param  int $item_id
1516
     * @return WC_Order_Item
1517
     */
1518
    public function get_item( $item_id ) {
1519
        return WC_Order_Factory::get_order_item( $item_id );
1520
    }
1521
1522
    /**
1523
     * Display meta data belonging to an item. @todo
1524
     * @param  array $item
1525
     */
1526
    public function display_item_meta( $item ) {
1527
        $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...
1528
        $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 1527 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...
1529
        $item_meta->display();
1530
    }
1531
1532
    /**
1533
     * Return an array of fees within this order.
1534
     *
1535
     * @return array
1536
     */
1537
    public function get_fees() {
1538
        return $this->get_items( 'fee' );
1539
    }
1540
1541
    /**
1542
     * Return an array of taxes within this order.
1543
     *
1544
     * @return array
1545
     */
1546
    public function get_taxes() {
1547
        return $this->get_items( 'tax' );
1548
    }
1549
1550
    /**
1551
     * Return an array of shipping costs within this order.
1552
     *
1553
     * @return array
1554
     */
1555
    public function get_shipping_methods() {
1556
        return $this->get_items( 'shipping' );
1557
    }
1558
1559
    /**
1560
     * Get coupon codes only.
1561
     *
1562
     * @return array
1563
     */
1564
    public function get_used_coupons() {
1565
        return array_map( 'trim', wp_list_pluck( $this->get_items( 'coupon' ), 'name' ) );
1566
    }
1567
1568
    /**
1569
     * Gets the count of order items of a certain type.
1570
     *
1571
     * @param string $item_type
1572
     * @return string
1573
     */
1574
    public function get_item_count( $item_type = '' ) {
1575
        if ( empty( $item_type ) ) {
1576
            $item_type = array( 'line_item' );
1577
        }
1578
        if ( ! is_array( $item_type ) ) {
1579
            $item_type = array( $item_type );
1580
        }
1581
1582
        $items = $this->get_items( $item_type );
1583
        $count = 0;
1584
1585
        foreach ( $items as $item ) {
1586
            $count += $item->get_qty();
1587
        }
1588
1589
        return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this );
1590
    }
1591
1592
    /**
1593
     * Remove all line items (products, coupons, shipping, taxes) from the order.
1594
     *
1595
     * @param string $type Order item type. Default null.
1596
     */
1597
    public function remove_order_items( $type = null ) {
1598
        global $wpdb;
1599
1600
        if ( ! empty( $type ) ) {
1601
            $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 ) );
1602
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->get_id(), $type ) );
1603
        } else {
1604
            $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() ) );
1605
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->get_id() ) );
1606
        }
1607
    }
1608
1609
    /**
1610
     * Add a product line item to the order.
1611
     * Order must be saved prior to adding items.
1612
     *
1613
     * @since 2.2
1614
     * @param \WC_Product $product
1615
     * @param int $qty Line item quantity.
1616
     * @param array $args
1617
     * @return int updated order item ID
1618
     */
1619
    public function add_product( $product, $qty = 1, $args = array() ) {
1620
        $args = wp_parse_args( $args, array(
1621
            'name'         => $product->get_title(),
1622
            'qty'          => absint( $qty ),
1623
            'tax_class'    => $product->get_tax_class(),
1624
            'product_id'   => $product->id,
1625
            'variation_id' => isset( $product->variation_id ) ? $product->variation_id : 0,
1626
            'variation'    => array(),
1627
            'subtotal'     => $product->get_price_excluding_tax( $qty ),
1628
            'subtotal_tax' => 0,
1629
            'total'        => $product->get_price_excluding_tax( $qty ),
1630
            'total_tax'    => 0,
1631
            'taxes'        => array(
1632
                'subtotal' => array(),
1633
                'total'    => array()
1634
            )
1635
        ) );
1636
        $item = new WC_Order_Item_Product();
1637
        $item_id = $this->update_product( $item, $product, $args );
1638
1639
		do_action( 'woocommerce_order_add_product', $this->get_id(), $item->get_order_item_id(), $product, $qty, $args );
1640
1641
		return $item_id;
1642
    }
1643
1644
    /**
1645
     * Update a line item for the order.
1646
     *
1647
     * Note this does not update order totals.
1648
     *
1649
     * @since 2.2
1650
     * @param object|int $item order item ID or item object.
1651
     * @param WC_Product $product
1652
     * @param array $args data to update.
1653
     * @return int updated order item ID
1654
     */
1655
    public function update_product( $item, $product, $args ) {
1656
        if ( is_numeric( $item ) ) {
1657
            $item = $this->get_item( $item );
1658
        }
1659
1660
        if ( ! is_object( $product ) || ! $item->is_type( 'line_item' ) ) {
1661
            return false;
1662
        }
1663
1664
        if ( ! $this->get_id() ) {
1665
            $this->save();
1666
        }
1667
1668
		$item->set_order_id( $this->get_id() );
1669
1670
        if ( ! $item->get_order_item_id() ) {
1671
            $inserting = true;
1672
        } else {
1673
            $inserting = false;
1674
        }
1675
1676
        if ( isset( $args['name'] ) ) {
1677
            $item->set_name( $args['name'] );
1678
        }
1679
1680
        if ( isset( $args['qty'] ) ) {
1681
            $item->set_qty( $args['qty'] );
1682
1683
            if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
1684
                $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ), true );
1685
            }
1686
1687
            $item->set_line_subtotal( $product->get_price_excluding_tax( $args['qty'] ) );
1688
            $item->set_line_total( $product->get_price_excluding_tax( $args['qty'] ) );
1689
        }
1690
1691
        if ( isset( $args['tax_class'] ) ) {
1692
            $item->set_tax_class( $args['tax_class'] );
1693
        }
1694
1695
        if ( isset( $args['product_id'] ) ) {
1696
            $item->set_product_id( $args['product_id'] );
1697
        }
1698
1699
        if ( isset( $args['variation_id'] ) ) {
1700
            $item->set_variation_id( $args['variation_id'] );
1701
        }
1702
1703
        if ( isset( $args['variation'] ) && is_array( $args['variation'] ) ) {
1704
            foreach ( $args['variation'] as $key => $value ) {
1705
                $item->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true );
1706
            }
1707
        }
1708
1709
        if ( isset( $args['totals'] ) ) {
1710
            // BW compatibility with old args
1711
            if ( isset( $args['totals']['subtotal'] ) ) {
1712
                $args['subtotal'] = $args['totals']['subtotal'];
1713
            }
1714
            if ( isset( $args['totals']['total'] ) ) {
1715
                $args['total'] = $args['totals']['total'];
1716
            }
1717
            if ( isset( $args['totals']['subtotal_tax'] ) ) {
1718
                $args['subtotal_tax'] = $args['totals']['subtotal_tax'];
1719
            }
1720
            if ( isset( $args['totals']['tax'] ) ) {
1721
                $args['total_tax'] = $args['totals']['tax'];
1722
            }
1723
            if ( isset( $args['totals']['tax_data'] ) ) {
1724
                $args['taxes'] = $args['totals']['tax_data'];
1725
            }
1726
        }
1727
1728
        if ( isset( $args['subtotal'] ) ) {
1729
            $item->set_subtotal( $args['subtotal'] );
1730
        }
1731
        if ( isset( $args['total'] ) ) {
1732
            $item->set_total( $args['total'] );
1733
        }
1734
        if ( isset( $args['subtotal_tax'] ) ) {
1735
            $item->set_line_subtotal_tax( $args['subtotal_tax'] );
1736
        }
1737
        if ( isset( $args['total_tax'] ) ) {
1738
            $item->set_total_tax( $args['total_tax'] );
1739
        }
1740
        if ( isset( $args['taxes'] ) ) {
1741
            $item->set_taxes( $args['taxes'] );
1742
        }
1743
1744
        $item->save();
1745
1746
        if ( ! $inserting ) {
1747
            do_action( 'woocommerce_order_edit_product', $this->get_id(), $item->get_order_item_id(), $args, $product );
1748
        }
1749
1750
        return $item->get_order_item_id();
1751
    }
1752
1753
    /**
1754
     * Add coupon code to the order.
1755
     * Order must be saved prior to adding items.
1756
     *
1757
     * @param string $code
1758
     * @param int $discount_amount
1759
     * @param int $discount_amount_tax "Discounted" tax - used for tax inclusive prices.
1760
     * @return int updated order item ID
1761
     */
1762
    public function add_coupon( $code, $discount_amount = 0, $discount_amount_tax = 0 ) {
1763
        $args = wp_parse_args( $args, array(
1764
            'code'                => $code,
1765
            'discount_amount'     => $discount_amount,
1766
            'discount_amount_tax' => $discount_amount_tax
1767
        ) );
1768
        $item = new WC_Order_Item_Coupon();
1769
        $item_id = $this->update_coupon( $item, $args );
1770
1771
		do_action( 'woocommerce_order_add_coupon', $this->get_id(), $item->get_order_item_id(), $code, $discount_amount, $discount_amount_tax );
1772
1773
		return $item_id;
1774
    }
1775
1776
    /**
1777
     * Update coupon for order. Note this does not update order totals.
1778
     * @since 2.2
1779
     * @param object|int $item
1780
     * @param array $args
1781
     * @return int updated order item ID
1782
     */
1783
    public function update_coupon( $item, $args ) {
1784
        if ( is_numeric( $item ) ) {
1785
            $item = $this->get_item( $item );
1786
        }
1787
1788
        if ( ! is_object( $product ) || ! $item->is_type( 'coupon' ) ) {
1789
            return false;
1790
        }
1791
1792
        if ( ! $this->get_id() ) {
1793
            $this->save();
1794
        }
1795
1796
		$item->set_order_id( $this->get_id() );
1797
1798
        if ( ! $item->get_order_item_id() ) {
1799
            $inserting = true;
1800
        } else {
1801
            $inserting = false;
1802
        }
1803
1804
        if ( isset( $args['code'] ) ) {
1805
            $item->set_coupon_code( $args['code'] );
1806
        }
1807
        if ( isset( $args['discount_amount'] ) ) {
1808
            $item->set_discount_amount( $args['discount_amount'] );
1809
        }
1810
        if ( isset( $args['discount_amount_tax'] ) ) {
1811
            $item->set_discount_amount_tax( $args['discount_amount_tax'] );
1812
        }
1813
1814
        $item->save();
1815
1816
        if ( ! $inserting ) {
1817
            do_action( 'woocommerce_order_update_coupon', $this->get_id(), $item->get_order_item_id(), $args );
1818
        }
1819
1820
        return $item->get_order_item_id();
1821
    }
1822
1823
    /**
1824
     * Add a shipping row to the order.
1825
     * Order must be saved prior to adding items.
1826
     *
1827
     * @param WC_Shipping_Rate shipping_rate
1828
     * @return int updated order item ID
1829
     */
1830
    public function add_shipping( $shipping_rate ) {
1831
        $args = wp_parse_args( $args, array(
1832
            'method_title' => $shipping_rate->label,
1833
            'method_id'    => $shipping_rate->id,
1834
            'cost'         => wc_format_decimal( $shipping_rate->cost ),
1835
            'taxes'        => $shipping_rate->taxes,
1836
            'meta'         => $shipping_rate->get_meta_data(),
1837
        ) );
1838
1839
        $item = new WC_Order_Item_Shipping();
1840
        $item_id = $this->update_shipping( $item, $args );
1841
1842
		do_action( 'woocommerce_order_add_shipping', $this->get_id(), $item->get_order_item_id(), $shipping_rate );
1843
1844
		return $item_id;
1845
    }
1846
1847
    /**
1848
     * Update shipping method for order.
1849
     *
1850
     * Note this does not update the order total.
1851
     *
1852
     * @since 2.2
1853
     * @param object|int $item
1854
     * @param array $args
1855
     * @return int updated order item ID
1856
     */
1857
    public function update_shipping( $item, $args ) {
1858
        if ( is_numeric( $item ) ) {
1859
            $item = $this->get_item( $item );
1860
        }
1861
1862
        if ( ! is_object( $product ) || ! $item->is_type( 'shipping' ) ) {
1863
            return false;
1864
        }
1865
1866
        if ( ! $this->get_id() ) {
1867
            $this->save();
1868
        }
1869
1870
		$item->set_order_id( $this->get_id() );
1871
1872
        if ( ! $item->get_order_item_id() ) {
1873
            $inserting = true;
1874
        } else {
1875
            $inserting = false;
1876
        }
1877
1878
        if ( isset( $args['method_title'] ) ) {
1879
            $item->set_method_title( $args['method_title'] );
1880
        }
1881
1882
        if ( isset( $args['method_id'] ) ) {
1883
            $item->set_method_id( $args['method_id'] );
1884
        }
1885
1886
        if ( isset( $args['cost'] ) ) {
1887
            // Get old cost before updating
1888
            $old_cost = $item->get_cost();
1889
1890
            // Update
1891
            $item->set_cost( $args['cost'] );
1892
1893
            // Update total
1894
            $this->set_total( $this->get_total_shipping() - wc_format_decimal( $old_cost ) + $item->get_cost(), 'shipping' );
1895
        }
1896
1897
        if ( isset( $args['taxes'] ) && is_array( $args['taxes'] ) ) {
1898
            $item->set_taxes( $args['taxes'] );
1899
        }
1900
1901
        if ( isset( $args['meta'] ) && is_array( $args['meta'] ) ) {
1902
			foreach ( $args['meta'] as $key => $value ) {
1903
				$item->update_meta_data( $key, $value );
1904
			}
1905
		}
1906
1907
        $item->save();
1908
1909
        if ( ! $inserting ) {
1910
            do_action( 'woocommerce_order_update_shipping', $this->get_id(), $item->get_order_item_id(), $args );
1911
        }
1912
1913
        return $item->get_order_item_id();
1914
    }
1915
1916
    /**
1917
     * Add a fee to the order.
1918
     * Order must be saved prior to adding items.
1919
     * @param object $fee
1920
     * @return int updated order item ID
1921
     */
1922
    public function add_fee( $fee ) {
1923
        $args = wp_parse_args( $args, array(
1924
            'name'      => $fee->name,
1925
            'tax_class' => $fee->taxable ? $fee->tax_class : 0,
1926
            'total'     => $fee->amount,
1927
            'total_tax' => $fee->tax,
1928
            'taxes'     => array(
1929
                'total' => $fee->tax_data
1930
            )
1931
        ) );
1932
        $item = new WC_Order_Item_Fee();
1933
        $item_id = $this->update_fee( $item, $args );
1934
1935
        do_action( 'woocommerce_order_add_fee', $this->get_id(), $item->get_order_item_id(), $fee );
1936
1937
        return $item_id;
1938
    }
1939
1940
    /**
1941
     * Update fee for order.
1942
     *
1943
     * Note this does not update order totals.
1944
     *
1945
     * @since 2.2
1946
     * @param object|int $item
1947
     * @param array $args
1948
     * @return int updated order item ID
1949
     */
1950
    public function update_fee( $item, $args ) {
1951
        if ( is_numeric( $item ) ) {
1952
            $item = $this->get_item( $item );
1953
        }
1954
1955
        if ( ! is_object( $product ) || ! $item->is_type( 'fee' ) ) {
1956
            return false;
1957
        }
1958
1959
        if ( ! $this->get_id() ) {
1960
            $this->save();
1961
        }
1962
1963
		$item->set_order_id( $this->get_id() );
1964
1965
        if ( ! $item->get_order_item_id() ) {
1966
            $inserting = true;
1967
        } else {
1968
            $inserting = false;
1969
        }
1970
1971
        if ( isset( $args['name'] ) ) {
1972
            $item->set_name( $args['name'] );
1973
        }
1974
1975
        if ( isset( $args['tax_class'] ) ) {
1976
            $item->set_tax_class( $args['tax_class'] );
1977
        }
1978
1979
        if ( isset( $args['total'] ) ) {
1980
            $item->set_total( $args['total'] );
1981
        }
1982
1983
        if ( isset( $args['total_tax'] ) ) {
1984
            $item->set_total_tax( $args['total_tax'] );
1985
        }
1986
1987
        if ( isset( $args['taxes'] ) ) {
1988
            $item->set_taxes( $args['taxes'] );
1989
        }
1990
1991
        $item->save();
1992
1993
        if ( ! $inserting ) {
1994
            do_action( 'woocommerce_order_update_fee', $this->get_id(), $item->get_order_item_id(), $args );
1995
        }
1996
1997
        return $item->get_order_item_id();
1998
    }
1999
2000
    /**
2001
     * Add a tax row to the order.
2002
     * Order must be saved prior to adding items.
2003
     * @since 2.2
2004
     * @param int tax_rate_id
2005
     * @return int updated order item ID
2006
     */
2007
    public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
2008
        if ( ! $code = WC_Tax::get_rate_code( $tax_rate_id ) ) {
2009
            return false;
2010
        }
2011
2012
        $args = wp_parse_args( $args, array(
2013
            'rate_code'          => $code,
2014
            'rate_id'            => $tax_rate_id,
2015
            'label'              => WC_Tax::get_rate_label( $tax_rate_id ),
2016
            'compound'           => WC_Tax::is_compound( $tax_rate_id ),
2017
            'tax_total'          => $tax_amount,
2018
            'shipping_tax_total' => $shipping_tax_amount
2019
        ) );
2020
        $item = new WC_Order_Item_Tax();
2021
        $item_id = $this->update_tax( $item, $args );
2022
2023
        do_action( 'woocommerce_order_add_tax', $this->get_id(), $item->get_order_item_id(), $tax_rate_id, $tax_amount, $shipping_tax_amount );
2024
2025
        return $item_id;
2026
    }
2027
2028
    /**
2029
     * Update tax line on order.
2030
     * Note this does not update order totals.
2031
     *
2032
     * @since 2.6
2033
     * @param object|int $item
2034
     * @param array $args
2035
     * @return int updated order item ID
2036
     */
2037
    public function update_tax( $item, $args ) {
2038
        if ( is_numeric( $item ) ) {
2039
            $item = $this->get_item( $item );
2040
        }
2041
2042
        if ( ! is_object( $product ) || ! $item->is_type( 'tax' ) ) {
2043
            return false;
2044
        }
2045
2046
        if ( ! $this->get_id() ) {
2047
            $this->save();
2048
        }
2049
2050
		$item->set_order_id( $this->get_id() );
2051
2052
        if ( ! $item->get_order_item_id() ) {
2053
            $inserting = true;
2054
        } else {
2055
            $inserting = false;
2056
        }
2057
2058
        if ( isset( $args['rate_code'] ) ) {
2059
            $item->set_rate_code( $args['rate_code'] );
2060
        }
2061
2062
        if ( isset( $args['rate_id'] ) ) {
2063
            $item->set_rate_id( $args['rate_id'] );
2064
        }
2065
2066
        if ( isset( $args['label'] ) ) {
2067
            $item->set_label( $args['label'] );
2068
        }
2069
2070
        if ( isset( $args['compound'] ) ) {
2071
            $item->set_compound( $args['compound'] );
2072
        }
2073
2074
        if ( isset( $args['tax_total'] ) ) {
2075
            $item->set_tax_total( $args['tax_total'] );
2076
        }
2077
2078
        if ( isset( $args['shipping_tax_total'] ) ) {
2079
            $item->set_shipping_tax_total( $args['shipping_tax_total'] );
2080
        }
2081
2082
        $item->save();
2083
2084
        if ( ! $inserting ) {
2085
            do_action( 'woocommerce_order_update_tax', $this->get_id(), $item->get_order_item_id(), $args );
2086
        }
2087
2088
        return $item->get_order_item_id();
2089
    }
2090
2091
	/**
2092
     * Update tax lines at order level by looking at the line item taxes themselves.
2093
     * @return bool success or fail.
2094
     */
2095
    public function update_taxes() {
2096
        $cart_taxes     = array();
2097
        $shipping_taxes = array();
2098
2099 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...
2100
            $taxes = $item->get_taxes();
2101
            if ( isset( $taxes['total'] ) ) {
2102
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2103
                    if ( ! isset( $cart_taxes[ $tax_rate_id ] ) ) {
2104
                        $cart_taxes[ $tax_rate_id ] = 0;
2105
                    }
2106
                    $cart_taxes[ $tax_rate_id ] += $tax;
2107
                }
2108
            }
2109
        }
2110
2111 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...
2112
			$taxes = $item->get_taxes();
2113
            if ( isset( $taxes['total'] ) ) {
2114
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2115
                    if ( ! isset( $shipping_taxes[ $tax_rate_id ] ) ) {
2116
                        $shipping_taxes[ $tax_rate_id ] = 0;
2117
                    }
2118
                    $shipping_taxes[ $tax_rate_id ] += $tax;
2119
                }
2120
            }
2121
        }
2122
2123
        // Remove old existing tax rows.
2124
        $this->remove_order_items( 'tax' );
2125
2126
        // Now merge to keep tax rows.
2127 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...
2128
            $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 );
2129
        }
2130
2131
        // Save tax totals
2132
        $this->set_total( WC_Tax::round( array_sum( $shipping_taxes ) ), 'shipping_tax' );
2133
        $this->set_total( WC_Tax::round( array_sum( $cart_taxes ) ), 'tax' );
2134
2135
        return true;
2136
    }
2137
2138
    /*
2139
    |--------------------------------------------------------------------------
2140
    | Calculations.
2141
    |--------------------------------------------------------------------------
2142
    |
2143
    | These methods calculate order totals and taxes based on the current data.
2144
    |
2145
    */
2146
2147
    /**
2148
     * Calculate shipping total.
2149
     *
2150
     * @since 2.2
2151
     * @return float
2152
     */
2153
    public function calculate_shipping() {
2154
        $shipping_total = 0;
2155
2156
        foreach ( $this->get_shipping_methods() as $shipping ) {
2157
            $shipping_total += $shipping->get_total();
2158
        }
2159
2160
        $this->set_total( $shipping_total, 'shipping' );
2161
2162
        return $this->get_shipping_total();
2163
    }
2164
2165
    /**
2166
     * Calculate taxes for all line items and shipping, and store the totals and tax rows.
2167
     *
2168
     * Will use the base country unless customer addresses are set.
2169
     *
2170
     * @return bool success or fail.
2171
     */
2172
    public function calculate_taxes() {
2173
        $cart_tax     = 0;
2174
        $cart_taxes   = array();
2175
        $tax_based_on = get_option( 'woocommerce_tax_based_on' );
2176
2177
        if ( 'billing' === $tax_based_on ) {
2178
            $country  = $this->get_billing_country();
2179
            $state    = $this->get_billing_state();
2180
            $postcode = $this->get_billing_postcode();
2181
            $city     = $this->get_billing_city();
2182
        } elseif ( 'shipping' === $tax_based_on ) {
2183
            $country  = $this->get_shipping_country();
2184
            $state    = $this->get_shipping_state();
2185
            $postcode = $this->get_shipping_postcode();
2186
            $city     = $this->get_shipping_city();
2187
        }
2188
2189
        // Default to base
2190 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...
2191
            $default  = wc_get_base_location();
2192
            $country  = $default['country'];
2193
            $state    = $default['state'];
2194
            $postcode = '';
2195
            $city     = '';
2196
        }
2197
2198
        // Get items
2199
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
2200
			$tax_class  = $item->get_tax_class();
2201
			$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...
2202
2203
            if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
2204
2205
                $tax_rates = WC_Tax::find_rates( array(
2206
                    'country'   => $country,
2207
                    'state'     => $state,
2208
                    'postcode'  => $postcode,
2209
                    'city'      => $city,
2210
                    'tax_class' => $tax_class
2211
                ) );
2212
2213
				$total     = $item->get_total();
2214
				$taxes     = WC_Tax::calc_tax( $total, $tax_rates, false );
2215
				$total_tax = max( 0, array_sum( $taxes ) );
2216
				$cart_tax += $total_tax;
2217
				$item->set_total_tax( $total_tax );
2218
2219
				if ( $item->is_type( 'line_item' ) ) {
2220
					$subtotal       = $item->get_subtotal();
2221
					$subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false );
2222
					$subtotal_tax   = max( 0, array_sum( $subtotal_taxes ) );
2223
					$item->set_subtotal_tax( $subtotal_tax );
2224
					$item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) );
2225
				} else {
2226
					$item->set_taxes( array( 'total' => $taxes ) );
2227
				}
2228
2229
				$item->save(); //@todo store items to self, don't save right away
2230
2231
                // Sum the item taxes
2232 View Code Duplication
                foreach ( array_keys( $cart_taxes + $taxes ) as $key ) {
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...
2233
                    $cart_taxes[ $key ] = ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $cart_taxes[ $key ] : 0 );
2234
                }
2235
            }
2236
        }
2237
2238
        // Now calculate shipping tax
2239
        $shipping_methods = $this->get_shipping_methods();
2240
2241
        if ( ! empty( $shipping_methods ) ) {
2242
            $matched_tax_rates = array();
2243
            $tax_rates         = WC_Tax::find_rates( array(
2244
                'country'   => $country,
2245
                'state'     => $state,
2246
                'postcode'  => $postcode,
2247
                'city'      => $city,
2248
                'tax_class' => ''
2249
            ) );
2250
2251 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...
2252
                foreach ( $tax_rates as $key => $rate ) {
2253
                    if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
2254
                        $matched_tax_rates[ $key ] = $rate;
2255
                    }
2256
                }
2257
            }
2258
2259
            $shipping_taxes     = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
2260
            $shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
2261
        } else {
2262
            $shipping_taxes     = array();
2263
            $shipping_tax_total = 0;
2264
        }
2265
2266
        // Save tax totals
2267
        $this->set_total( $shipping_tax_total, 'shipping_tax' );
2268
        $this->set_total( $tax_total, 'tax' );
2269
2270
        // Tax rows
2271
        $this->remove_order_items( 'tax' );
2272
2273
        // Now merge to keep tax rows
2274 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...
2275
            $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 );
2276
        }
2277
2278
        return true;
2279
    }
2280
2281
    /**
2282
     * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
2283
     *
2284
     * @since 2.2
2285
     * @param  bool $and_taxes Calc taxes if true.
2286
     * @return float calculated grand total.
2287
     */
2288
    public function calculate_totals( $and_taxes = true ) {
2289
        $cart_subtotal     = 0;
2290
        $cart_total        = 0;
2291
        $fee_total         = 0;
2292
        $cart_subtotal_tax = 0;
2293
        $cart_total_tax    = 0;
2294
2295
        if ( $and_taxes && wc_tax_enabled() ) {
2296
            $this->calculate_taxes();
2297
        }
2298
2299
        // line items
2300
        foreach ( $this->get_items() as $item ) {
2301
            $cart_subtotal     += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
2302
            $cart_total        += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
2303
            $cart_subtotal_tax += wc_format_decimal( isset( $item['line_subtotal_tax'] ) ? $item['line_subtotal_tax'] : 0 );
2304
            $cart_total_tax    += wc_format_decimal( isset( $item['line_tax'] ) ? $item['line_tax'] : 0 );
2305
        }
2306
2307
        $this->calculate_shipping();
2308
2309
        foreach ( $this->get_fees() as $item ) {
2310
            $fee_total += $item['line_total'];
2311
        }
2312
2313
        $this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
2314
        $this->set_total( $cart_subtotal_tax - $cart_total_tax, 'cart_discount_tax' );
2315
2316
        $grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() );
2317
2318
        $this->set_total( $grand_total, 'total' );
2319
2320
        return $grand_total;
2321
    }
2322
2323
    /*
2324
    |--------------------------------------------------------------------------
2325
    | Total Getters
2326
    |--------------------------------------------------------------------------
2327
    |
2328
    | Methods for getting totals e.g. for display on the frontend.
2329
    |
2330
    */
2331
2332
    /**
2333
     * Get a product (either product or variation).
2334
     *
2335
     * @param object $item
2336
     * @return WC_Product|bool
2337
     */
2338
    public function get_product_from_item( $item ) {
2339
        if ( is_callable( array( $item, 'get_product' ) ) ) {
2340
            $product = $item->get_product();
2341
        } else {
2342
            $product = false;
2343
        }
2344
        return apply_filters( 'woocommerce_get_product_from_item', $product, $item, $this );
2345
    }
2346
2347
    /**
2348
     * Get item subtotal - this is the cost before discount.
2349
     *
2350
     * @param object $item
2351
     * @param bool $inc_tax (default: false).
2352
     * @param bool $round (default: true).
2353
     * @return float
2354
     */
2355 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...
2356
        $subtotal = 0;
2357
2358
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2359
            if ( $inc_tax ) {
2360
                $subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_qty() );
2361
            } else {
2362
                $subtotal = ( $item->get_subtotal() / max( 1, $item->get_qty() ) );
2363
            }
2364
2365
            $subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
2366
        }
2367
2368
        return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2369
    }
2370
2371
    /**
2372
     * Get line subtotal - this is the cost before discount.
2373
     *
2374
     * @param object $item
2375
     * @param bool $inc_tax (default: false).
2376
     * @param bool $round (default: true).
2377
     * @return float
2378
     */
2379 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...
2380
        $subtotal = 0;
2381
2382
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2383
            if ( $inc_tax ) {
2384
                $subtotal = $item->get_subtotal() + $item->get_subtotal_tax();
2385
            } else {
2386
                $subtotal = $item->get_subtotal();
2387
            }
2388
2389
            $subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal;
2390
        }
2391
2392
        return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2393
    }
2394
2395
    /**
2396
     * Calculate item cost - useful for gateways.
2397
     *
2398
     * @param object $item
2399
     * @param bool $inc_tax (default: false).
2400
     * @param bool $round (default: true).
2401
     * @return float
2402
     */
2403 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...
2404
        $total = 0;
2405
2406
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2407
            if ( $inc_tax ) {
2408
                $total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_qty() );
2409
            } else {
2410
                $total = $item->get_total() / max( 1, $item->get_qty() );
2411
            }
2412
2413
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2414
        }
2415
2416
        return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round );
2417
    }
2418
2419
    /**
2420
     * Calculate line total - useful for gateways.
2421
     *
2422
     * @param object $item
2423
     * @param bool $inc_tax (default: false).
2424
     * @param bool $round (default: true).
2425
     * @return float
2426
     */
2427 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...
2428
        $total = 0;
2429
2430
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2431
            // Check if we need to add line tax to the line total.
2432
            $total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total();
2433
2434
            // Check if we need to round.
2435
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2436
        }
2437
2438
        return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round );
2439
    }
2440
2441
    /**
2442
     * Get item tax - useful for gateways.
2443
     *
2444
     * @param mixed $item
2445
     * @param bool $round (default: true).
2446
     * @return float
2447
     */
2448
    public function get_item_tax( $item, $round = true ) {
2449
        $tax = 0;
2450
2451
        if ( is_callable( array( $item, 'get_total_tax' ) ) ) {
2452
            $tax = $item->get_total_tax() / max( 1, $item->get_qty() );
2453
            $tax = $round ? wc_round_tax_total( $tax ) : $tax;
2454
        }
2455
2456
        return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this );
2457
    }
2458
2459
    /**
2460
     * Get line tax - useful for gateways.
2461
     *
2462
     * @param mixed $item
2463
     * @return float
2464
     */
2465
    public function get_line_tax( $item ) {
2466
        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 );
2467
    }
2468
2469
    /**
2470
     * Gets line subtotal - formatted for display.
2471
     *
2472
     * @param array  $item
2473
     * @param string $tax_display
2474
     * @return string
2475
     */
2476
    public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
2477
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2478
2479
        if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
2480
            return '';
2481
        }
2482
2483
        if ( 'excl' == $tax_display ) {
2484
            $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0;
2485
2486
            $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...
2487
        } else {
2488
            $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...
2489
        }
2490
2491
        return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
2492
    }
2493
2494
    /**
2495
     * Gets order total - formatted for display.
2496
     *
2497
     * @return string
2498
     */
2499
    public function get_formatted_order_total() {
2500
        $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
2501
        return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
2502
    }
2503
2504
    /**
2505
     * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
2506
     *
2507
     * @param bool $compound (default: false).
2508
     * @param string $tax_display (default: the tax_display_cart value).
2509
     * @return string
2510
     */
2511
    public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
2512
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2513
        $subtotal    = 0;
2514
2515
        if ( ! $compound ) {
2516
            foreach ( $this->get_items() as $item ) {
2517
                $subtotal += $item->get_subtotal();
2518
2519
                if ( 'incl' === $tax_display ) {
2520
                    $subtotal += $item->get_subtotal_tax();
2521
                }
2522
            }
2523
2524
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2525
2526
            if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) {
2527
                $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
2528
            }
2529
2530
        } else {
2531
            if ( 'incl' === $tax_display ) {
2532
                return '';
2533
            }
2534
2535
            foreach ( $this->get_items() as $item ) {
2536
                $subtotal += $item->get_subtotal();
2537
            }
2538
2539
            // Add Shipping Costs.
2540
            $subtotal += $this->get_total_shipping();
2541
2542
            // Remove non-compound taxes.
2543
            foreach ( $this->get_taxes() as $tax ) {
2544
                if ( ! empty( $tax['compound'] ) ) {
2545
                    continue;
2546
                }
2547
                $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
2548
            }
2549
2550
            // Remove discounts.
2551
            $subtotal = $subtotal - $this->get_total_discount();
2552
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2553
        }
2554
2555
        return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
2556
    }
2557
2558
    /**
2559
     * Gets shipping (formatted).
2560
     *
2561
     * @return string
2562
     */
2563
    public function get_shipping_to_display( $tax_display = '' ) {
2564
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2565
2566
        if ( $this->order_shipping != 0 ) {
2567
2568
            if ( $tax_display == 'excl' ) {
2569
2570
                // Show shipping excluding tax.
2571
                $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
2572
2573 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...
2574
                    $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 );
2575
                }
2576
2577
            } else {
2578
2579
                // Show shipping including tax.
2580
                $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
2581
2582 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...
2583
                    $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 );
2584
                }
2585
2586
            }
2587
2588
            $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 );
2589
2590
        } elseif ( $this->get_shipping_method() ) {
2591
            $shipping = $this->get_shipping_method();
2592
        } else {
2593
            $shipping = __( 'Free!', 'woocommerce' );
2594
        }
2595
2596
        return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
2597
    }
2598
2599
    /**
2600
     * Get the discount amount (formatted).
2601
     * @since  2.3.0
2602
     * @return string
2603
     */
2604
    public function get_discount_to_display( $tax_display = '' ) {
2605
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2606
        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 );
2607
    }
2608
2609
    /**
2610
     * Get totals for display on pages and in emails.
2611
     *
2612
     * @param mixed $tax_display
2613
     * @return array
2614
     */
2615
    public function get_order_item_totals( $tax_display = '' ) {
2616
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2617
        $total_rows  = array();
2618
2619
        if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
2620
            $total_rows['cart_subtotal'] = array(
2621
                'label' => __( 'Subtotal:', 'woocommerce' ),
2622
                'value'    => $subtotal
2623
            );
2624
        }
2625
2626
        if ( $this->get_total_discount() > 0 ) {
2627
            $total_rows['discount'] = array(
2628
                'label' => __( 'Discount:', 'woocommerce' ),
2629
                'value'    => '-' . $this->get_discount_to_display( $tax_display )
2630
            );
2631
        }
2632
2633
        if ( $this->get_shipping_method() ) {
2634
            $total_rows['shipping'] = array(
2635
                'label' => __( 'Shipping:', 'woocommerce' ),
2636
                'value'    => $this->get_shipping_to_display( $tax_display )
2637
            );
2638
        }
2639
2640
        if ( $fees = $this->get_fees() ) {
2641
            foreach ( $fees as $id => $fee ) {
2642
                if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) {
2643
                    continue;
2644
                }
2645
                $total_rows[ 'fee_' . $fee->get_order_item_id() ] = array(
2646
                    'label' => $fee->get_name() . ':',
2647
                    'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array('currency' => $this->get_order_currency()) )
2648
                );
2649
            }
2650
        }
2651
2652
        // Tax for tax exclusive prices.
2653
        if ( 'excl' === $tax_display ) {
2654
2655
            if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
2656
2657
                foreach ( $this->get_tax_totals() as $code => $tax ) {
2658
2659
                    $total_rows[ sanitize_title( $code ) ] = array(
2660
                        'label' => $tax->label . ':',
2661
                        'value'    => $tax->formatted_amount
2662
                    );
2663
                }
2664
2665
            } else {
2666
2667
                $total_rows['tax'] = array(
2668
                    'label' => WC()->countries->tax_or_vat() . ':',
2669
                    'value'    => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_order_currency() ) )
2670
                );
2671
            }
2672
        }
2673
2674
        if ( $this->get_total() > 0 && $this->get_payment_method_title() ) {
2675
            $total_rows['payment_method'] = array(
2676
                'label' => __( 'Payment Method:', 'woocommerce' ),
2677
                'value' => $this->get_payment_method_title()
2678
            );
2679
        }
2680
2681
        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...
2682
            foreach ( $refunds as $id => $refund ) {
2683
                $total_rows[ 'refund_' . $id ] = array(
2684
                    'label' => $refund->get_refund_reason() ? $refund->get_refund_reason() : __( 'Refund', 'woocommerce' ) . ':',
2685
                    'value'    => wc_price( '-' . $refund->get_refund_amount(), array( 'currency' => $this->get_order_currency() ) )
2686
                );
2687
            }
2688
        }
2689
2690
        $total_rows['order_total'] = array(
2691
            'label' => __( 'Total:', 'woocommerce' ),
2692
            '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...
2693
        );
2694
2695
        return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
2696
    }
2697
2698
    /*
2699
    |--------------------------------------------------------------------------
2700
    | Conditionals
2701
    |--------------------------------------------------------------------------
2702
    |
2703
    | Checks if a condition is true or false.
2704
    |
2705
    */
2706
2707
    /**
2708
     * Checks the order status against a passed in status.
2709
     *
2710
     * @return bool
2711
     */
2712
    public function has_status( $status ) {
2713
        return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
2714
    }
2715
2716
    /**
2717
     * has_meta function for order items.
2718
     *
2719
     * @param string $order_item_id
2720
     * @return array of meta data.
2721
     */
2722
    public function has_meta( $order_item_id ) {
2723
        global $wpdb;
2724
2725
        return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
2726
            FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
2727
            ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
2728
    }
2729
2730
    /**
2731
     * Check whether this order has a specific shipping method or not.
2732
     *
2733
     * @param string $method_id
2734
     * @return bool
2735
     */
2736
    public function has_shipping_method( $method_id ) {
2737
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
2738
            if ( $shipping_method->get_method_id() === $method_id ) {
2739
                return true;
2740
            }
2741
        }
2742
        return false;
2743
    }
2744
2745
    /**
2746
     * Check if an order key is valid.
2747
     *
2748
     * @param mixed $key
2749
     * @return bool
2750
     */
2751
    public function key_is_valid( $key ) {
2752
        return $key === $this->get_order_key();
2753
    }
2754
2755
    /**
2756
     * Returns true if the order contains a free product.
2757
     * @since 2.5.0
2758
     * @return bool
2759
     */
2760
    public function has_free_item() {
2761
        foreach ( $this->get_items() as $item ) {
2762
            if ( ! $item->get_total() ) {
2763
                return true;
2764
            }
2765
        }
2766
        return false;
2767
    }
2768
}
2769