1 <?php
   2 /**
   3  * Order
   4  *
   5  * The WooCommerce order class handles order data.
   6  *
   7  * @class       WC_Order
   8  * @version     2.1.0
   9  * @package     WooCommerce/Classes
  10  * @category    Class
  11  * @author      WooThemes
  12  */
  13 class WC_Order {
  14 
  15     /** @public int Order (post) ID */
  16     public $id;
  17 
  18     /**
  19      * Get the order if ID is passed, otherwise the order is new and empty.
  20      *
  21      * @access public
  22      * @param string $id (default: '')
  23      * @return void
  24      */
  25     public function __construct( $id = '' ) {
  26         $this->prices_include_tax = get_option('woocommerce_prices_include_tax') == 'yes' ? true : false;
  27         $this->tax_display_cart   = get_option( 'woocommerce_tax_display_cart' );
  28 
  29         $this->display_totals_ex_tax = $this->tax_display_cart == 'excl' ? true : false;
  30         $this->display_cart_ex_tax   = $this->tax_display_cart == 'excl' ? true : false;
  31 
  32         if ( $id > 0 ) {
  33             $this->get_order( $id );
  34         }
  35     }
  36 
  37 
  38     /**
  39      * Gets an order from the database.
  40      *
  41      * @access public
  42      * @param int $id (default: 0)
  43      * @return bool
  44      */
  45     public function get_order( $id = 0 ) {
  46         if ( ! $id ) {
  47             return false;
  48         }
  49         if ( $result = get_post( $id ) ) {
  50             $this->populate( $result );
  51             return true;
  52         }
  53         return false;
  54     }
  55 
  56 
  57     /**
  58      * Populates an order from the loaded post data.
  59      *
  60      * @access public
  61      * @param mixed $result
  62      * @return void
  63      */
  64     public function populate( $result ) {
  65         // Standard post data
  66         $this->id                  = $result->ID;
  67         $this->order_date          = $result->post_date;
  68         $this->modified_date       = $result->post_modified;
  69         $this->customer_message    = $result->post_excerpt;
  70         $this->customer_note       = $result->post_excerpt;
  71         $this->post_status         = $result->post_status;
  72 
  73         // Billing email cam default to user if set
  74         if ( empty( $this->billing_email ) && ! empty( $this->customer_user ) ) {
  75             $user                = get_user_by( 'id', $this->customer_user );
  76             $this->billing_email = $user->user_email;
  77         }
  78 
  79         // Get status
  80         $terms        = wp_get_object_terms( $this->id, 'shop_order_status', array( 'fields' => 'slugs' ) );
  81         $this->status = isset( $terms[0] ) ? $terms[0] : apply_filters( 'woocommerce_default_order_status', 'pending' );
  82     }
  83 
  84     /**
  85      * __isset function.
  86      *
  87      * @access public
  88      * @param mixed $key
  89      * @return bool
  90      */
  91     public function __isset( $key ) {
  92         if ( ! $this->id ) {
  93             return false;
  94         }
  95         return metadata_exists( 'post', $this->id, '_' . $key );
  96     }
  97 
  98     /**
  99      * __get function.
 100      *
 101      * @access public
 102      * @param mixed $key
 103      * @return mixed
 104      */
 105     public function __get( $key ) {
 106         // Get values or default if not set
 107         if ( 'completed_date' == $key ) {
 108             $value = ( $value = get_post_meta( $this->id, '_completed_date', true ) ) ? $value : $this->modified_date;
 109         } elseif ( 'user_id' == $key ) {
 110             $value = ( $value = get_post_meta( $this->id, '_customer_user', true ) ) ? absint( $value ) : '';
 111         } else {
 112             $value = get_post_meta( $this->id, '_' . $key, true );
 113         }
 114 
 115         return $value;
 116     }
 117 
 118     /**
 119      * Check if an order key is valid.
 120      *
 121      * @access public
 122      * @param mixed $key
 123      * @return bool
 124      */
 125     public function key_is_valid( $key ) {
 126         if ( $key == $this->order_key ) {
 127             return true;
 128         }
 129 
 130         return false;
 131     }
 132 
 133     /**
 134      * get_order_number function.
 135      *
 136      * Gets the order number for display (by default, order ID)
 137      *
 138      * @access public
 139      * @return string
 140      */
 141     public function get_order_number() {
 142         return apply_filters( 'woocommerce_order_number', _x( '#', 'hash before order number', 'woocommerce' ) . $this->id, $this );
 143     }
 144 
 145     /**
 146      * Get a formatted billing address for the order.
 147      *
 148      * @access public
 149      * @return string
 150      */
 151     public function get_formatted_billing_address() {
 152         if ( ! $this->formatted_billing_address ) {
 153 
 154             // Formatted Addresses
 155             $address = apply_filters( 'woocommerce_order_formatted_billing_address', array(
 156                 'first_name'    => $this->billing_first_name,
 157                 'last_name'     => $this->billing_last_name,
 158                 'company'       => $this->billing_company,
 159                 'address_1'     => $this->billing_address_1,
 160                 'address_2'     => $this->billing_address_2,
 161                 'city'          => $this->billing_city,
 162                 'state'         => $this->billing_state,
 163                 'postcode'      => $this->billing_postcode,
 164                 'country'       => $this->billing_country
 165             ), $this );
 166 
 167             $this->formatted_billing_address = WC()->countries->get_formatted_address( $address );
 168         }
 169         return $this->formatted_billing_address;
 170     }
 171 
 172     /**
 173      * Get the billing address in an array.
 174      *
 175      * @access public
 176      * @return string
 177      */
 178     public function get_billing_address() {
 179         if ( ! $this->billing_address ) {
 180             // Formatted Addresses
 181             $address = array(
 182                 'address_1'     => $this->billing_address_1,
 183                 'address_2'     => $this->billing_address_2,
 184                 'city'          => $this->billing_city,
 185                 'state'         => $this->billing_state,
 186                 'postcode'      => $this->billing_postcode,
 187                 'country'       => $this->billing_country
 188             );
 189             $joined_address = array();
 190             foreach ( $address as $part ) {
 191                 if ( ! empty( $part ) ) {
 192                     $joined_address[] = $part;
 193                 }
 194             }
 195             $this->billing_address = implode( ', ', $joined_address );
 196         }
 197         return $this->billing_address;
 198     }
 199 
 200     /**
 201      * Get a formatted shipping address for the order.
 202      *
 203      * @access public
 204      * @return string
 205      */
 206     public function get_formatted_shipping_address() {
 207         if ( ! $this->formatted_shipping_address ) {
 208             if ( $this->shipping_address_1 ) {
 209 
 210                 // Formatted Addresses
 211                 $address = apply_filters( 'woocommerce_order_formatted_shipping_address', array(
 212                     'first_name'    => $this->shipping_first_name,
 213                     'last_name'     => $this->shipping_last_name,
 214                     'company'       => $this->shipping_company,
 215                     'address_1'     => $this->shipping_address_1,
 216                     'address_2'     => $this->shipping_address_2,
 217                     'city'          => $this->shipping_city,
 218                     'state'         => $this->shipping_state,
 219                     'postcode'      => $this->shipping_postcode,
 220                     'country'       => $this->shipping_country
 221                 ), $this );
 222 
 223                 $this->formatted_shipping_address = WC()->countries->get_formatted_address( $address );
 224             }
 225         }
 226         return $this->formatted_shipping_address;
 227     }
 228 
 229 
 230     /**
 231      * Get the shipping address in an array.
 232      *
 233      * @access public
 234      * @return array
 235      */
 236     public function get_shipping_address() {
 237         if ( ! $this->shipping_address ) {
 238             if ( $this->shipping_address_1 ) {
 239                 // Formatted Addresses
 240                 $address = array(
 241                     'address_1'     => $this->shipping_address_1,
 242                     'address_2'     => $this->shipping_address_2,
 243                     'city'          => $this->shipping_city,
 244                     'state'         => $this->shipping_state,
 245                     'postcode'      => $this->shipping_postcode,
 246                     'country'       => $this->shipping_country
 247                 );
 248                 $joined_address = array();
 249                 foreach ( $address as $part ) {
 250                     if ( ! empty( $part ) ) {
 251                         $joined_address[] = $part;
 252                     }
 253                 }
 254                 $this->shipping_address = implode( ', ', $joined_address );
 255             }
 256         }
 257         return $this->shipping_address;
 258     }
 259 
 260     /**
 261      * Return an array of items/products within this order.
 262      *
 263      * @access public
 264      * @param string|array $type Types of line items to get (array or string)
 265      * @return array
 266      */
 267     public function get_items( $type = '' ) {
 268         global $wpdb;
 269 
 270         if ( empty( $type ) ) {
 271             $type = array( 'line_item' );
 272         }
 273 
 274         if ( ! is_array( $type ) ) {
 275             $type = array( $type );
 276         }
 277 
 278         $type = array_map( 'esc_attr', $type );
 279 
 280         $line_items = $wpdb->get_results( $wpdb->prepare( "
 281             SELECT      order_item_id, order_item_name, order_item_type
 282             FROM        {$wpdb->prefix}woocommerce_order_items
 283             WHERE       order_id = %d
 284             AND         order_item_type IN ( '" . implode( "','", $type ) . "' )
 285             ORDER BY    order_item_id
 286         ", $this->id ) );
 287 
 288         $items = array();
 289 
 290         // Reserved meta keys
 291         $reserved_item_meta_keys = array(
 292             'name',
 293             'type',
 294             'item_meta',
 295             'qty',
 296             'tax_class',
 297             'product_id',
 298             'variation_id',
 299             'line_subtotal',
 300             'line_total',
 301             'line_tax',
 302             'line_subtotal_tax'
 303         );
 304 
 305         // Loop items
 306         foreach ( $line_items as $item ) {
 307             // Place line item into array to return
 308             $items[ $item->order_item_id ]['name']      = $item->order_item_name;
 309             $items[ $item->order_item_id ]['type']      = $item->order_item_type;
 310             $items[ $item->order_item_id ]['item_meta'] = $this->get_item_meta( $item->order_item_id );
 311 
 312             // Expand meta data into the array
 313             foreach ( $items[ $item->order_item_id ]['item_meta'] as $name => $value ) {
 314                 if ( in_array( $name, $reserved_item_meta_keys ) ) {
 315                     continue;
 316                 }
 317                 if ( '_' === substr( $name, 0, 1 ) ) {
 318                     $items[ $item->order_item_id ][ substr( $name, 1 ) ] = $value[0];
 319                 } elseif ( ! in_array( $name, $reserved_item_meta_keys ) ) {
 320                     $items[ $item->order_item_id ][ $name ] = $value[0];
 321                 }
 322             }
 323         }
 324 
 325         return apply_filters( 'woocommerce_order_get_items', $items, $this );
 326     }
 327 
 328     /**
 329      * Gets order total - formatted for display.
 330      *
 331      * @access public
 332      * @return string
 333      */
 334     public function get_item_count( $type = '' ) {
 335         global $wpdb;
 336 
 337         if ( empty( $type ) ) {
 338             $type = array( 'line_item' );
 339         }
 340 
 341         if ( ! is_array( $type ) ) {
 342             $type = array( $type );
 343         }
 344 
 345         $items = $this->get_items( $type );
 346 
 347         $count = 0;
 348 
 349         foreach ( $items as $item ) {
 350             if ( ! empty( $item['qty'] ) ) {
 351                 $count += $item['qty'];
 352             } else {
 353                 $count ++;
 354             }
 355         }
 356 
 357         return apply_filters( 'woocommerce_get_item_count', $count, $type, $this );
 358     }
 359 
 360     /**
 361      * Return an array of fees within this order.
 362      *
 363      * @access public
 364      * @return array
 365      */
 366     public function get_fees() {
 367         return $this->get_items( 'fee' );
 368     }
 369 
 370     /**
 371      * Return an array of taxes within this order.
 372      *
 373      * @access public
 374      * @return array
 375      */
 376     public function get_taxes() {
 377         return $this->get_items( 'tax' );
 378     }
 379 
 380     /**
 381      * Return an array of shipping costs within this order.
 382      *
 383      * @return array
 384      */
 385     public function get_shipping_methods() {
 386         return $this->get_items( 'shipping' );
 387     }
 388 
 389     /**
 390      * Check whether this order has a specific shipping method or not
 391      * @param string $method_id
 392      * @return bool
 393      */
 394     public function has_shipping_method( $method_id ) {
 395         $shipping_methods = $this->get_shipping_methods();
 396         $has_method = false;
 397 
 398         if ( ! $shipping_methods ) {
 399             return false;
 400         }
 401 
 402         foreach ( $shipping_methods as $shipping_method ) {
 403             if ( $shipping_method['method_id'] == $method_id ) {
 404                 $has_method = true;
 405             }
 406         }
 407 
 408         return $has_method;
 409     }
 410 
 411     /**
 412      * Get taxes, merged by code, formatted ready for output.
 413      *
 414      * @access public
 415      * @return array
 416      */
 417     public function get_tax_totals() {
 418         $taxes      = $this->get_items( 'tax' );
 419         $tax_totals = array();
 420 
 421         foreach ( $taxes as $key => $tax ) {
 422 
 423             $code = $tax[ 'name' ];
 424 
 425             if ( ! isset( $tax_totals[ $code ] ) ) {
 426                 $tax_totals[ $code ] = new stdClass();
 427                 $tax_totals[ $code ]->amount = 0;
 428             }
 429 
 430             $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
 431             $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
 432             $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
 433             $tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
 434         }
 435 
 436         return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
 437     }
 438 
 439     /**
 440      * has_meta function for order items.
 441      * @access public
 442      * @param string $order_item_id
 443      * @return array of meta data
 444      */
 445     public function has_meta( $order_item_id ) {
 446         global $wpdb;
 447 
 448         return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
 449             FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
 450             ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
 451     }
 452 
 453     /**
 454      * Get order item meta.
 455      *
 456      * @access public
 457      * @param mixed $order_item_id
 458      * @param string $key (default: '')
 459      * @param bool $single (default: false)
 460      * @return array|string
 461      */
 462     public function get_item_meta( $order_item_id, $key = '', $single = false ) {
 463         return get_metadata( 'order_item', $order_item_id, $key, $single );
 464     }
 465 
 466     /** Total Getters *******************************************************/
 467 
 468     /**
 469      * Gets the total (product) discount amount - these are applied before tax.
 470      *
 471      * @access public
 472      * @return float
 473      */
 474     public function get_cart_discount() {
 475         return apply_filters( 'woocommerce_order_amount_cart_discount', (double) $this->cart_discount, $this );
 476     }
 477 
 478     /**
 479      * Gets the total (product) discount amount - these are applied before tax.
 480      *
 481      * @access public
 482      * @return float
 483      */
 484     public function get_order_discount() {
 485         return apply_filters( 'woocommerce_order_amount_order_discount', (double) $this->order_discount, $this );
 486     }
 487 
 488     /**
 489      * Gets the total discount amount - both kinds
 490      *
 491      * @access public
 492      * @return float
 493      */
 494     public function get_total_discount() {
 495         return apply_filters( 'woocommerce_order_amount_total_discount', $this->get_cart_discount() + $this->get_order_discount(), $this );
 496     }
 497 
 498     /**
 499      * Gets shipping tax amount.
 500      *
 501      * @access public
 502      * @return float
 503      */
 504     public function get_cart_tax() {
 505         return apply_filters( 'woocommerce_order_amount_cart_tax', (double) $this->order_tax, $this );
 506     }
 507 
 508     /**
 509      * Gets shipping tax amount.
 510      *
 511      * @access public
 512      * @return float
 513      */
 514     public function get_shipping_tax() {
 515         return apply_filters( 'woocommerce_order_amount_shipping_tax', (double) $this->order_shipping_tax, $this );
 516     }
 517 
 518     /**
 519      * Gets shipping and product tax.
 520      *
 521      * @access public
 522      * @return float
 523      */
 524     public function get_total_tax() {
 525         return apply_filters( 'woocommerce_order_amount_total_tax', wc_round_tax_total( $this->get_cart_tax() + $this->get_shipping_tax() ), $this );
 526     }
 527 
 528     /**
 529      * Gets shipping total.
 530      *
 531      * @access public
 532      * @return float
 533      */
 534     public function get_total_shipping() {
 535         return apply_filters( 'woocommerce_order_amount_total_shipping', (double) $this->order_shipping, $this );
 536     }
 537 
 538     /**
 539      * Gets order total.
 540      *
 541      * @access public
 542      * @return float
 543      */
 544     public function get_total() {
 545         return apply_filters( 'woocommerce_order_amount_total', (double) $this->order_total, $this );
 546     }
 547 
 548     /**
 549      * Get item subtotal - this is the cost before discount.
 550      *
 551      * @access public
 552      * @param mixed $item
 553      * @param bool $inc_tax (default: false)
 554      * @param bool $round (default: true)
 555      * @return float
 556      */
 557     public function get_item_subtotal( $item, $inc_tax = false, $round = true ) {
 558         if ( $inc_tax ) {
 559             $price = ( $item['line_subtotal'] + $item['line_subtotal_tax'] ) / $item['qty'];
 560         } else {
 561             $price = ( $item['line_subtotal'] / $item['qty'] );
 562         }
 563 
 564         $price = $round ? round( $price, 2 ) : $price;
 565 
 566         return apply_filters( 'woocommerce_order_amount_item_subtotal', $price, $this );
 567     }
 568 
 569     /**
 570      * Get line subtotal - this is the cost before discount.
 571      *
 572      * @access public
 573      * @param mixed $item
 574      * @param bool $inc_tax (default: false)
 575      * @param bool $round (default: true)
 576      * @return float
 577      */
 578     public function get_line_subtotal( $item, $inc_tax = false, $round = true ) {
 579         if ( $inc_tax ) {
 580             $price = $item['line_subtotal'] + $item['line_subtotal_tax'];
 581         } else {
 582             $price = $item['line_subtotal'];
 583         }
 584 
 585         $price = $round ? round( $price, 2 ) : $price;
 586 
 587         return apply_filters( 'woocommerce_order_amount_line_subtotal', $price, $this );
 588     }
 589 
 590     /**
 591      * Calculate item cost - useful for gateways.
 592      *
 593      * @access public
 594      * @param mixed $item
 595      * @param bool $inc_tax (default: false)
 596      * @param bool $round (default: true)
 597      * @return float
 598      */
 599     public function get_item_total( $item, $inc_tax = false, $round = true ) {
 600         if ( $inc_tax ) {
 601             $price = ( $item['line_total'] + $item['line_tax'] ) / $item['qty'];
 602         } else {
 603             $price = $item['line_total'] / $item['qty'];
 604         }
 605 
 606         $price = $round ? round( $price, 2 ) : $price;
 607 
 608         return apply_filters( 'woocommerce_order_amount_item_total', $price, $this );
 609     }
 610 
 611     /**
 612      * Calculate line total - useful for gateways.
 613      *
 614      * @access public
 615      * @param mixed $item
 616      * @param bool $inc_tax (default: false)
 617      * @return float
 618      */
 619     public function get_line_total( $item, $inc_tax = false ) {
 620         $line_total = $inc_tax ? round( $item['line_total'] + $item['line_tax'], 2 ) : round( $item['line_total'], 2 );
 621         return apply_filters( 'woocommerce_order_amount_line_total', $line_total, $this );
 622     }
 623 
 624     /**
 625      * Calculate item tax - useful for gateways.
 626      *
 627      * @access public
 628      * @param mixed $item
 629      * @param bool $round (default: true)
 630      * @return float
 631      */
 632     public function get_item_tax( $item, $round = true ) {
 633         $price = $item['line_tax'] / $item['qty'];
 634         $price = $round ? wc_round_tax_total( $price ) : $price;
 635         return apply_filters( 'woocommerce_order_amount_item_tax', $price, $item, $round, $this );
 636     }
 637 
 638     /**
 639      * Calculate line tax - useful for gateways.
 640      *
 641      * @access public
 642      * @param mixed $item
 643      * @return float
 644      */
 645     public function get_line_tax( $item ) {
 646         return apply_filters( 'woocommerce_order_amount_line_tax', wc_round_tax_total( $item['line_tax'] ), $item, $this );
 647     }
 648 
 649     /**
 650      * Gets shipping total.
 651      *
 652      * @deprecated As of 2.1, use of get_total_shipping() is preferred
 653      * @access public
 654      * @return float
 655      */
 656     public function get_shipping() {
 657         _deprecated_function( 'get_shipping', '2.1', 'get_total_shipping' );
 658         return $this->get_total_shipping();
 659     }
 660 
 661     /**
 662      * get_order_total function. Alias for get_total()
 663      *
 664      * @deprecated As of 2.1, use of get_total() is preferred
 665      * @access public
 666      * @return float
 667      */
 668     public function get_order_total() {
 669         _deprecated_function( 'get_order_total', '2.1', 'get_total' );
 670         return $this->get_total();
 671     }
 672 
 673     /** End Total Getters *******************************************************/
 674 
 675     /**
 676      * Gets formatted shipping method title.
 677      *
 678      * @return string
 679      */
 680     public function get_shipping_method() {
 681         $labels = array();
 682 
 683         // Backwards compat < 2.1 - get shipping title stored in meta
 684         if ( $this->shipping_method_title ) {
 685             $labels[] = $this->shipping_method_title;
 686         } else {
 687             // 2.1+ get line items for shipping
 688             $shipping_methods = $this->get_shipping_methods();
 689 
 690             foreach ( $shipping_methods as $shipping ) {
 691                 $labels[] = $shipping['name'];
 692             }
 693         }
 694 
 695         return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $labels ), $this );
 696     }
 697 
 698     /**
 699      * Gets line subtotal - formatted for display.
 700      *
 701      * @access public
 702      * @param array  $item
 703      * @param string $tax_display
 704      * @return string
 705      */
 706     public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
 707         if ( ! $tax_display ) {
 708             $tax_display = $this->tax_display_cart;
 709         }
 710 
 711         if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
 712             return '';
 713         }
 714 
 715         if ( 'excl' == $tax_display ) {
 716             $ex_tax_label = $this->prices_include_tax ? 1 : 0;
 717 
 718             $subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_order_currency() ) );
 719         } else {
 720             $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array('currency' => $this->get_order_currency()) );
 721         }
 722 
 723         return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
 724     }
 725 
 726     /**
 727      * Gets order currency
 728      *
 729      * @access public
 730      * @return string
 731      */
 732     public function get_order_currency() {
 733 
 734         $currency = $this->order_currency;
 735 
 736         return apply_filters( 'woocommerce_get_order_currency', $currency, $this );
 737     }
 738 
 739     /**
 740      * Gets order total - formatted for display.
 741      *
 742      * @access public
 743      * @return string
 744      */
 745     public function get_formatted_order_total() {
 746 
 747         $formatted_total = wc_price( $this->order_total , array('currency' => $this->get_order_currency()));
 748 
 749         return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
 750     }
 751 
 752 
 753     /**
 754      * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
 755      *
 756      * @access public
 757      * @param bool $compound (default: false)
 758      * @param string $tax_display (default: the tax_display_cart value)
 759      * @return string
 760      */
 761     public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
 762         if ( ! $tax_display ) {
 763             $tax_display = $this->tax_display_cart;
 764         }
 765 
 766         $subtotal = 0;
 767 
 768         if ( ! $compound ) {
 769             foreach ( $this->get_items() as $item ) {
 770 
 771                 if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
 772                     return '';
 773                 }
 774 
 775                 $subtotal += $item['line_subtotal'];
 776 
 777                 if ( 'incl' == $tax_display ) {
 778                     $subtotal += $item['line_subtotal_tax'];
 779                 }
 780             }
 781 
 782             $subtotal = wc_price( $subtotal, array('currency' => $this->get_order_currency()) );
 783 
 784             if ( $tax_display == 'excl' && $this->prices_include_tax ) {
 785                 $subtotal .= ' <small>' . WC()->countries->ex_tax_or_vat() . '</small>';
 786             }
 787 
 788         } else {
 789 
 790             if ( 'incl' == $tax_display ) {
 791                 return '';
 792             }
 793 
 794             foreach ( $this->get_items() as $item ) {
 795 
 796                 $subtotal += $item['line_subtotal'];
 797 
 798             }
 799 
 800             // Add Shipping Costs
 801             $subtotal += $this->get_total_shipping();
 802 
 803             // Remove non-compound taxes
 804             foreach ( $this->get_taxes() as $tax ) {
 805 
 806                 if ( ! empty( $tax['compound'] ) ) {
 807                     continue;
 808                 }
 809 
 810                 $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
 811 
 812             }
 813 
 814             // Remove discounts
 815             $subtotal = $subtotal - $this->get_cart_discount();
 816 
 817             $subtotal = wc_price( $subtotal, array('currency' => $this->get_order_currency()) );
 818         }
 819 
 820         return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
 821     }
 822 
 823 
 824     /**
 825      * Gets shipping (formatted).
 826      *
 827      * @access public
 828      * @return string
 829      */
 830     public function get_shipping_to_display( $tax_display = '' ) {
 831         if ( ! $tax_display ) {
 832             $tax_display = $this->tax_display_cart;
 833         }
 834 
 835         if ( $this->order_shipping > 0 ) {
 836 
 837             $tax_text = '';
 838 
 839             if ( $tax_display == 'excl' ) {
 840 
 841                 // Show shipping excluding tax
 842                 $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
 843 
 844                 if ( $this->order_shipping_tax > 0 && $this->prices_include_tax ) {
 845                     $tax_text = WC()->countries->ex_tax_or_vat() . ' ';
 846                 }
 847 
 848             } else {
 849 
 850                 // Show shipping including tax
 851                 $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
 852 
 853                 if ( $this->order_shipping_tax > 0 && ! $this->prices_include_tax ) {
 854                     $tax_text = WC()->countries->inc_tax_or_vat() . ' ';
 855                 }
 856 
 857             }
 858 
 859             $shipping .= sprintf( __( '&nbsp;<small>%svia %s</small>', 'woocommerce' ), $tax_text, $this->get_shipping_method() );
 860 
 861         } elseif ( $this->get_shipping_method() ) {
 862             $shipping = $this->get_shipping_method();
 863         } else {
 864             $shipping = __( 'Free!', 'woocommerce' );
 865         }
 866 
 867         return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
 868     }
 869 
 870 
 871     /**
 872      * Get cart discount (formatted).
 873      *
 874      * @access public
 875      * @return string.
 876      */
 877     public function get_cart_discount_to_display() {
 878         return apply_filters( 'woocommerce_order_cart_discount_to_display', wc_price( $this->get_cart_discount(), array( 'currency' => $this->get_order_currency() ) ), $this );
 879     }
 880 
 881 
 882     /**
 883      * Get cart discount (formatted).
 884      *
 885      * @access public
 886      * @return string
 887      */
 888     public function get_order_discount_to_display() {
 889         return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_order_discount(), array( 'currency' => $this->get_order_currency() ) ), $this );
 890     }
 891 
 892 
 893     /**
 894      * Get a product (either product or variation).
 895      *
 896      * @access public
 897      * @param mixed $item
 898      * @return WC_Product
 899      */
 900     public function get_product_from_item( $item ) {
 901         $_product = get_product( $item['variation_id'] ? $item['variation_id'] : $item['product_id'] );
 902 
 903         return apply_filters( 'woocommerce_get_product_from_item', $_product, $item, $this );
 904     }
 905 
 906 
 907     /**
 908      * Get totals for display on pages and in emails.
 909      *
 910      * @access public
 911      * @return array
 912      */
 913     public function get_order_item_totals( $tax_display = '' ) {
 914         if ( ! $tax_display ) {
 915             $tax_display = $this->tax_display_cart;
 916         }
 917 
 918         $total_rows = array();
 919 
 920         if ( $subtotal = $this->get_subtotal_to_display() ) {
 921             $total_rows['cart_subtotal'] = array(
 922                 'label' => __( 'Cart Subtotal:', 'woocommerce' ),
 923                 'value' => $subtotal
 924             );
 925         }
 926 
 927         if ( $this->get_cart_discount() > 0 ) {
 928             $total_rows['cart_discount'] = array(
 929                 'label' => __( 'Cart Discount:', 'woocommerce' ),
 930                 'value' => '-' . $this->get_cart_discount_to_display()
 931             );
 932         }
 933 
 934         if ( $this->get_shipping_method() ) {
 935             $total_rows['shipping'] = array(
 936                 'label' => __( 'Shipping:', 'woocommerce' ),
 937                 'value' => $this->get_shipping_to_display()
 938             );
 939         }
 940 
 941         if ( $fees = $this->get_fees() )
 942             foreach( $fees as $id => $fee ) {
 943                 if ( $fee['line_total'] + $fee['line_tax'] == 0 ) {
 944                     continue;
 945                 }
 946 
 947                 if ( 'excl' == $tax_display ) {
 948 
 949                     $total_rows[ 'fee_' . $id ] = array(
 950                         'label' => $fee['name'],
 951                         'value' => wc_price( $fee['line_total'], array('currency' => $this->get_order_currency()) )
 952                     );
 953 
 954                 } else {
 955 
 956                     $total_rows[ 'fee_' . $id ] = array(
 957                         'label' => $fee['name'],
 958                         'value' => wc_price( $fee['line_total'] + $fee['line_tax'], array('currency' => $this->get_order_currency()) )
 959                     );
 960 
 961                 }
 962             }
 963 
 964         // Tax for tax exclusive prices
 965         if ( 'excl' == $tax_display ) {
 966             if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
 967                 foreach ( $this->get_tax_totals() as $code => $tax ) {
 968                     $total_rows[ sanitize_title( $code ) ] = array(
 969                         'label' => $tax->label . ':',
 970                         'value' => $tax->formatted_amount
 971                     );
 972                 }
 973             } else {
 974                 $total_rows['tax'] = array(
 975                     'label' => WC()->countries->tax_or_vat() . ':',
 976                     'value' => wc_price( $this->get_total_tax(), array('currency' => $this->get_order_currency()) )
 977                 );
 978             }
 979         }
 980 
 981         if ( $this->get_order_discount() > 0 ) {
 982             $total_rows['order_discount'] = array(
 983                 'label' => __( 'Order Discount:', 'woocommerce' ),
 984                 'value' => '-' . $this->get_order_discount_to_display()
 985             );
 986         }
 987 
 988         $total_rows['order_total'] = array(
 989             'label' => __( 'Order Total:', 'woocommerce' ),
 990             'value' => $this->get_formatted_order_total()
 991         );
 992 
 993         // Tax for inclusive prices
 994         if ( 'yes' == get_option( 'woocommerce_calc_taxes' ) && 'incl' == $tax_display ) {
 995 
 996             $tax_string_array = array();
 997 
 998             if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
 999                 foreach ( $this->get_tax_totals() as $code => $tax ) {
1000                     $tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label );
1001                 }
1002             } else {
1003                 $tax_string_array[] = sprintf( '%s %s', wc_price( $this->get_total_tax(), array('currency' => $this->get_order_currency()) ), WC()->countries->tax_or_vat() );
1004             }
1005 
1006             if ( ! empty( $tax_string_array ) ) {
1007                 $total_rows['order_total']['value'] .= ' ' . sprintf( __( '(Includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
1008             }
1009         }
1010 
1011         return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
1012     }
1013 
1014 
1015     /**
1016      * Output items for display in html emails.
1017      *
1018      * @access public
1019      * @param bool $show_download_links (default: false)
1020      * @param bool $show_sku (default: false)
1021      * @param bool $show_purchase_note (default: false)
1022      * @param bool $show_image (default: false)
1023      * @param array $image_size (default: array( 32, 32 )
1024      * @param bool plain text
1025      * @return string
1026      */
1027     public function email_order_items_table( $show_download_links = false, $show_sku = false, $show_purchase_note = false, $show_image = false, $image_size = array( 32, 32 ), $plain_text = false ) {
1028 
1029         ob_start();
1030 
1031         $template = $plain_text ? 'emails/plain/email-order-items.php' : 'emails/email-order-items.php';
1032 
1033         wc_get_template( $template, array(
1034             'order'                 => $this,
1035             'items'                 => $this->get_items(),
1036             'show_download_links'   => $show_download_links,
1037             'show_sku'              => $show_sku,
1038             'show_purchase_note'    => $show_purchase_note,
1039             'show_image'            => $show_image,
1040             'image_size'            => $image_size
1041         ) );
1042 
1043         $return = apply_filters( 'woocommerce_email_order_items_table', ob_get_clean(), $this );
1044 
1045         return $return;
1046     }
1047 
1048     /**
1049      * Checks if product download is permitted
1050      *
1051      * @access public
1052      * @return bool
1053      */
1054     public function is_download_permitted() {
1055         return apply_filters( 'woocommerce_order_is_download_permitted', $this->status == 'completed' || ( get_option( 'woocommerce_downloads_grant_access_after_payment' ) == 'yes' && $this->status == 'processing' ), $this );
1056     }
1057 
1058     /**
1059      * Returns true if the order contains a downloadable product.
1060      *
1061      * @access public
1062      * @return bool
1063      */
1064     public function has_downloadable_item() {
1065         $has_downloadable_item = false;
1066 
1067         foreach ( $this->get_items() as $item ) {
1068 
1069             $_product = $this->get_product_from_item( $item );
1070 
1071             if ( $_product && $_product->exists() && $_product->is_downloadable() && $_product->has_file() ) {
1072                 $has_downloadable_item = true;
1073             }
1074 
1075         }
1076 
1077         return $has_downloadable_item;
1078     }
1079 
1080 
1081     /**
1082      * Generates a URL so that a customer can pay for their (unpaid - pending) order. Pass 'true' for the checkout version which doesn't offer gateway choices.
1083      *
1084      * @access public
1085      * @param  boolean $on_checkout
1086      * @return string
1087      */
1088     public function get_checkout_payment_url( $on_checkout = false ) {
1089 
1090         $pay_url = wc_get_endpoint_url( 'order-pay', $this->id, get_permalink( wc_get_page_id( 'checkout' ) ) );
1091 
1092         if ( 'yes' == get_option( 'woocommerce_force_ssl_checkout' ) || is_ssl() ) {
1093             $pay_url = str_replace( 'http:', 'https:', $pay_url );
1094         }
1095 
1096         if ( $on_checkout ) {
1097             $pay_url = add_query_arg( 'key', $this->order_key, $pay_url );
1098         } else {
1099             $pay_url = add_query_arg( array( 'pay_for_order' => 'true', 'key' => $this->order_key ), $pay_url );
1100         }
1101 
1102         return apply_filters( 'woocommerce_get_checkout_payment_url', $pay_url, $this );
1103     }
1104 
1105 
1106     /**
1107      * Generates a URL for the thanks page (order received)
1108      *
1109      * @access public
1110      * @return string
1111      */
1112     public function get_checkout_order_received_url() {
1113 
1114         $order_received_url = wc_get_endpoint_url( 'order-received', $this->id, get_permalink( wc_get_page_id( 'checkout' ) ) );
1115 
1116         if ( 'yes' == get_option( 'woocommerce_force_ssl_checkout' ) || is_ssl() ) {
1117             $order_received_url = str_replace( 'http:', 'https:', $order_received_url );
1118         }
1119 
1120         $order_received_url = add_query_arg( 'key', $this->order_key, $order_received_url );
1121 
1122         return apply_filters( 'woocommerce_get_checkout_order_received_url', $order_received_url, $this );
1123     }
1124 
1125 
1126     /**
1127      * Generates a URL so that a customer can cancel their (unpaid - pending) order.
1128      *
1129      * @access public
1130      * @return string
1131      */
1132     public function get_cancel_order_url( $redirect = '' ) {
1133         $cancel_endpoint = get_permalink( wc_get_page_id( 'cart' ) );
1134         if ( ! $cancel_endpoint ) {
1135             $cancel_endpoint = home_url();
1136         }
1137 
1138         if ( false === strpos( $cancel_endpoint, '?' ) ) {
1139             $cancel_endpoint = trailingslashit( $cancel_endpoint );
1140         }
1141 
1142         return apply_filters('woocommerce_get_cancel_order_url', wp_nonce_url( add_query_arg( array( 'cancel_order' => 'true', 'order' => $this->order_key, 'order_id' => $this->id, 'redirect' => $redirect ), $cancel_endpoint ), 'woocommerce-cancel_order' ) );
1143     }
1144 
1145     /**
1146      * Generates a URL to view an order from the my account page
1147      *
1148      * @return string
1149      */
1150     public function get_view_order_url() {
1151         $view_order_url = wc_get_endpoint_url( 'view-order', $this->id, get_permalink( wc_get_page_id( 'myaccount' ) ) );
1152 
1153         return apply_filters( 'woocommerce_get_view_order_url', $view_order_url, $this );
1154     }
1155 
1156     /**
1157      * Gets any downloadable product file urls.
1158      *
1159      * @deprecated as of 2.1 get_item_downloads is preferred as downloads are more than just file urls
1160      * @param int $product_id product identifier
1161      * @param int $variation_id variation identifier, or null
1162      * @param array $item the item
1163      * @return array available downloadable file urls
1164      */
1165     public function get_downloadable_file_urls( $product_id, $variation_id, $item ) {
1166         global $wpdb;
1167 
1168         _deprecated_function( 'get_downloadable_file_urls', '2.1', 'get_item_downloads' );
1169 
1170         $download_file = $variation_id > 0 ? $variation_id : $product_id;
1171         $_product = get_product( $download_file );
1172 
1173         $user_email = $this->billing_email;
1174 
1175         $results = $wpdb->get_results( $wpdb->prepare("
1176             SELECT download_id
1177             FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
1178             WHERE user_email = %s
1179             AND order_key = %s
1180             AND product_id = %s
1181         ", $user_email, $this->order_key, $download_file ) );
1182 
1183         $file_urls = array();
1184         foreach ( $results as $result ) {
1185             if ( $_product->has_file( $result->download_id ) ) {
1186                 $file_urls[ $_product->get_file_download_path( $result->download_id ) ] = $this->get_download_url( $download_file, $result->download_id );
1187             }
1188         }
1189 
1190         return apply_filters( 'woocommerce_get_downloadable_file_urls', $file_urls, $product_id, $variation_id, $item );
1191     }
1192 
1193     /**
1194      * Get the downloadable files for an item in this order
1195      * @param  array $item
1196      * @return array
1197      */
1198     public function get_item_downloads( $item ) {
1199         global $wpdb;
1200 
1201         $product_id   = $item['variation_id'] > 0 ? $item['variation_id'] : $item['product_id'];
1202         $product      = get_product( $product_id );
1203         $download_ids = $wpdb->get_col( $wpdb->prepare("
1204             SELECT download_id
1205             FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
1206             WHERE user_email = %s
1207             AND order_key = %s
1208             AND product_id = %s
1209             ORDER BY permission_id
1210         ", $this->billing_email, $this->order_key, $product_id ) );
1211 
1212         $files = array();
1213 
1214         foreach ( $download_ids as $download_id ) {
1215             if ( $product->has_file( $download_id ) ) {
1216                 $files[ $download_id ]                 = $product->get_file( $download_id );
1217                 $files[ $download_id ]['download_url'] = $this->get_download_url( $product_id, $download_id );
1218             }
1219         }
1220 
1221         return apply_filters( 'woocommerce_get_item_downloads', $files, $item, $this );
1222     }
1223 
1224     /**
1225      * Get the Download URL
1226      * @param  int $product_id
1227      * @param  int $download_id
1228      * @return string
1229      */
1230     public function get_download_url( $product_id, $download_id ) {
1231         return add_query_arg( array(
1232             'download_file' => $product_id,
1233             'order'         => $this->order_key,
1234             'email'         => urlencode( $this->billing_email ),
1235             'key'           => $download_id
1236         ), trailingslashit( home_url() ) );
1237     }
1238 
1239     /**
1240      * Adds a note (comment) to the order
1241      *
1242      * @access public
1243      * @param string $note Note to add
1244      * @param int $is_customer_note (default: 0) Is this a note for the customer?
1245      * @return id Comment ID
1246      */
1247     public function add_order_note( $note, $is_customer_note = 0 ) {
1248 
1249         $is_customer_note = intval( $is_customer_note );
1250 
1251         if ( is_user_logged_in() && current_user_can( 'edit_shop_order', $this->id ) ) {
1252             $user                 = get_user_by( 'id', get_current_user_id() );
1253             $comment_author       = $user->display_name;
1254             $comment_author_email = $user->user_email;
1255         } else {
1256             $comment_author       = __( 'WooCommerce', 'woocommerce' );
1257             $comment_author_email = strtolower( __( 'WooCommerce', 'woocommerce' ) ) . '@';
1258             $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
1259             $comment_author_email = sanitize_email( $comment_author_email );
1260         }
1261 
1262         $comment_post_ID        = $this->id;
1263         $comment_author_url     = '';
1264         $comment_content        = $note;
1265         $comment_agent          = 'WooCommerce';
1266         $comment_type           = 'order_note';
1267         $comment_parent         = 0;
1268         $comment_approved       = 1;
1269         $commentdata            = apply_filters( 'woocommerce_new_order_note_data', compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_agent', 'comment_type', 'comment_parent', 'comment_approved' ), array( 'order_id' => $this->id, 'is_customer_note' => $is_customer_note ) );
1270 
1271         $comment_id = wp_insert_comment( $commentdata );
1272 
1273         add_comment_meta( $comment_id, 'is_customer_note', $is_customer_note );
1274 
1275         if ( $is_customer_note ) {
1276             do_action( 'woocommerce_new_customer_note', array( 'order_id' => $this->id, 'customer_note' => $note ) );
1277         }
1278 
1279         return $comment_id;
1280     }
1281 
1282 
1283     /**
1284      * Updates status of order
1285      *
1286      * @access public
1287      * @param string $new_status_slug Status to change the order to
1288      * @param string $note (default: '') Optional note to add
1289      * @return void
1290      */
1291     public function update_status( $new_status_slug, $note = '' ) {
1292 
1293         if ( $note ) {
1294             $note .= ' ';
1295         }
1296 
1297         $old_status = get_term_by( 'slug', sanitize_title( $this->status ), 'shop_order_status' );
1298         $new_status = get_term_by( 'slug', sanitize_title( $new_status_slug ), 'shop_order_status' );
1299 
1300         if ( $new_status ) {
1301 
1302             wp_set_object_terms( $this->id, array( $new_status->slug ), 'shop_order_status', false );
1303 
1304             if ( $this->id && $this->status != $new_status->slug ) {
1305 
1306                 // Status was changed
1307                 do_action( 'woocommerce_order_status_' . $new_status->slug, $this->id );
1308                 do_action( 'woocommerce_order_status_' . $this->status . '_to_' . $new_status->slug, $this->id );
1309                 do_action( 'woocommerce_order_status_changed', $this->id, $this->status, $new_status->slug );
1310 
1311                 if ( $old_status ) {
1312                     $this->add_order_note( $note . sprintf( __( 'Order status changed from %s to %s.', 'woocommerce' ), __( $old_status->name, 'woocommerce' ), __( $new_status->name, 'woocommerce' ) ) );
1313                 }
1314 
1315                 // Record the completed date of the order
1316                 if ( 'completed' == $new_status->slug ) {
1317                     update_post_meta( $this->id, '_completed_date', current_time('mysql') );
1318                 }
1319 
1320                 if ( 'processing' == $new_status->slug || 'completed' == $new_status->slug || 'on-hold' == $new_status->slug ) {
1321 
1322                     // Record the sales
1323                     $this->record_product_sales();
1324 
1325                     // Increase coupon usage counts
1326                     $this->increase_coupon_usage_counts();
1327                 }
1328 
1329                 // If the order is cancelled, restore used coupons
1330                 if ( 'cancelled' == $new_status->slug ) {
1331                     $this->decrease_coupon_usage_counts();
1332                 }
1333 
1334                 // Update last modified
1335                 wp_update_post( array( 'ID' => $this->id ) );
1336 
1337                 $this->status = $new_status->slug;
1338             }
1339 
1340         }
1341 
1342         wc_delete_shop_order_transients( $this->id );
1343     }
1344 
1345 
1346     /**
1347      * Cancel the order and restore the cart (before payment)
1348      *
1349      * @access public
1350      * @param string $note (default: '') Optional note to add
1351      * @return void
1352      */
1353     public function cancel_order( $note = '' ) {
1354         unset( WC()->session->order_awaiting_payment );
1355 
1356         $this->update_status( 'cancelled', $note );
1357 
1358     }
1359 
1360     /**
1361      * When a payment is complete this function is called
1362      *
1363      * Most of the time this should mark an order as 'processing' so that admin can process/post the items
1364      * If the cart contains only downloadable items then the order is 'complete' since the admin needs to take no action
1365      * Stock levels are reduced at this point
1366      * Sales are also recorded for products
1367      * Finally, record the date of payment
1368      *
1369      * @access public
1370      * @return void
1371      */
1372     public function payment_complete() {
1373 
1374         do_action( 'woocommerce_pre_payment_complete', $this->id );
1375 
1376         if ( ! empty( WC()->session->order_awaiting_payment ) ) {
1377             unset( WC()->session->order_awaiting_payment );
1378         }
1379 
1380         if ( $this->id && ( 'on-hold' == $this->status || 'pending' == $this->status || 'failed' == $this->status ) ) {
1381 
1382             $order_needs_processing = true;
1383 
1384             if ( sizeof( $this->get_items() ) > 0 ) {
1385 
1386                 foreach( $this->get_items() as $item ) {
1387 
1388                     if ( $item['product_id'] > 0 ) {
1389 
1390                         $_product = $this->get_product_from_item( $item );
1391 
1392                         if ( false !== $_product && ( $_product->is_downloadable() && $_product->is_virtual() ) || ! apply_filters( 'woocommerce_order_item_needs_processing', true, $_product, $this->id ) ) {
1393                             $order_needs_processing = false;
1394                             continue;
1395                         }
1396 
1397                     }
1398                     $order_needs_processing = true;
1399                     break;
1400                 }
1401             }
1402 
1403             $new_order_status = $order_needs_processing ? 'processing' : 'completed';
1404 
1405             $new_order_status = apply_filters( 'woocommerce_payment_complete_order_status', $new_order_status, $this->id );
1406 
1407             $this->update_status( $new_order_status );
1408 
1409             add_post_meta( $this->id, '_paid_date', current_time('mysql'), true );
1410 
1411             $this_order = array(
1412                 'ID' => $this->id,
1413                 'post_date' => current_time( 'mysql', 0 ),
1414                 'post_date_gmt' => current_time( 'mysql', 1 )
1415             );
1416             wp_update_post( $this_order );
1417 
1418             if ( apply_filters( 'woocommerce_payment_complete_reduce_order_stock', true, $this->id ) ) {
1419                 $this->reduce_order_stock(); // Payment is complete so reduce stock levels
1420             }
1421 
1422             do_action( 'woocommerce_payment_complete', $this->id );
1423 
1424         } else {
1425 
1426             do_action( 'woocommerce_payment_complete_order_status_' . $this->status, $this->id );
1427 
1428         }
1429     }
1430 
1431 
1432     /**
1433      * Record sales
1434      *
1435      * @access public
1436      * @return void
1437      */
1438     public function record_product_sales() {
1439 
1440         if ( 'yes' == get_post_meta( $this->id, '_recorded_sales', true ) ) {
1441             return;
1442         }
1443 
1444         if ( sizeof( $this->get_items() ) > 0 ) {
1445             foreach ( $this->get_items() as $item ) {
1446                 if ( $item['product_id'] > 0 ) {
1447                     $sales = (int) get_post_meta( $item['product_id'], 'total_sales', true );
1448                     $sales += (int) $item['qty'];
1449                     if ( $sales ) {
1450                         update_post_meta( $item['product_id'], 'total_sales', $sales );
1451                     }
1452                 }
1453             }
1454         }
1455 
1456         update_post_meta( $this->id, '_recorded_sales', 'yes' );
1457     }
1458 
1459 
1460     /**
1461      * Get coupon codes only.
1462      *
1463      * @access public
1464      * @return array
1465      */
1466     public function get_used_coupons() {
1467 
1468         $codes   = array();
1469         $coupons = $this->get_items( 'coupon' );
1470 
1471         foreach ( $coupons as $item_id => $item ) {
1472             $codes[] = trim( $item['name'] );
1473         }
1474 
1475         return $codes;
1476     }
1477 
1478 
1479     /**
1480      * Increase applied coupon counts
1481      *
1482      * @access public
1483      * @return void
1484      */
1485     public function increase_coupon_usage_counts() {
1486 
1487         if ( 'yes' == get_post_meta( $this->id, '_recorded_coupon_usage_counts', true ) ) {
1488             return;
1489         }
1490 
1491         if ( sizeof( $this->get_used_coupons() ) > 0 ) {
1492             foreach ( $this->get_used_coupons() as $code ) {
1493                 if ( ! $code ) {
1494                     continue;
1495                 }
1496 
1497                 $coupon = new WC_Coupon( $code );
1498 
1499                 $used_by = $this->user_id;
1500                 if ( ! $used_by ) {
1501                     $used_by = $this->billing_email;
1502                 }
1503 
1504                 $coupon->inc_usage_count( $used_by );
1505             }
1506         }
1507 
1508         update_post_meta( $this->id, '_recorded_coupon_usage_counts', 'yes' );
1509     }
1510 
1511 
1512     /**
1513      * Decrease applied coupon counts
1514      *
1515      * @access public
1516      * @return void
1517      */
1518     public function decrease_coupon_usage_counts() {
1519 
1520         if ( 'yes' != get_post_meta( $this->id, '_recorded_coupon_usage_counts', true ) ) {
1521             return;
1522         }
1523 
1524         if ( sizeof( $this->get_used_coupons() ) > 0 ) {
1525             foreach ( $this->get_used_coupons() as $code ) {
1526                 if ( ! $code ) {
1527                     continue;
1528                 }
1529 
1530                 $coupon = new WC_Coupon( $code );
1531 
1532                 $used_by = $this->user_id;
1533                 if ( ! $used_by ) {
1534                     $used_by = $this->billing_email;
1535                 }
1536 
1537                 $coupon->dcr_usage_count( $used_by );
1538             }
1539         }
1540 
1541         delete_post_meta( $this->id, '_recorded_coupon_usage_counts' );
1542     }
1543 
1544 
1545     /**
1546      * Reduce stock levels
1547      *
1548      * @access public
1549      * @return void
1550      */
1551     public function reduce_order_stock() {
1552 
1553         if ( 'yes' == get_option('woocommerce_manage_stock') && sizeof( $this->get_items() ) > 0 ) {
1554 
1555             // Reduce stock levels and do any other actions with products in the cart
1556             foreach ( $this->get_items() as $item ) {
1557 
1558                 if ( $item['product_id'] > 0 ) {
1559                     $_product = $this->get_product_from_item( $item );
1560 
1561                     if ( $_product && $_product->exists() && $_product->managing_stock() ) {
1562 
1563                         $old_stock = $_product->stock;
1564 
1565                         $qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );
1566 
1567                         $new_quantity = $_product->reduce_stock( $qty );
1568 
1569                         $this->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $item['product_id'], $old_stock, $new_quantity) );
1570 
1571                         $this->send_stock_notifications( $_product, $new_quantity, $item['qty'] );
1572 
1573                     }
1574 
1575                 }
1576 
1577             }
1578 
1579             do_action( 'woocommerce_reduce_order_stock', $this );
1580 
1581             $this->add_order_note( __( 'Order item stock reduced successfully.', 'woocommerce' ) );
1582         }
1583 
1584     }
1585 
1586 
1587     /**
1588      * send_stock_notifications function.
1589      *
1590      * @access public
1591      * @param object $product
1592      * @param int $new_stock
1593      * @param int $qty_ordered
1594      * @return void
1595      */
1596     public function send_stock_notifications( $product, $new_stock, $qty_ordered ) {
1597 
1598         // Backorders
1599         if ( $new_stock < 0 ) {
1600             do_action( 'woocommerce_product_on_backorder', array( 'product' => $product, 'order_id' => $this->id, 'quantity' => $qty_ordered ) );
1601         }
1602 
1603         // stock status notifications
1604         $notification_sent = false;
1605 
1606         if ( 'yes' == get_option( 'woocommerce_notify_no_stock' ) && get_option('woocommerce_notify_no_stock_amount') >= $new_stock ) {
1607             do_action( 'woocommerce_no_stock', $product );
1608             $notification_sent = true;
1609         }
1610         if ( ! $notification_sent && 'yes' == get_option( 'woocommerce_notify_low_stock' ) && get_option('woocommerce_notify_low_stock_amount') >= $new_stock ) {
1611             do_action( 'woocommerce_low_stock', $product );
1612             $notification_sent = true;
1613         }
1614 
1615     }
1616 
1617 
1618     /**
1619      * List order notes (public) for the customer
1620      *
1621      * @access public
1622      * @return array
1623      */
1624     public function get_customer_order_notes() {
1625 
1626         $notes = array();
1627 
1628         $args = array(
1629             'post_id' => $this->id,
1630             'approve' => 'approve',
1631             'type' => ''
1632         );
1633 
1634         remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
1635 
1636         $comments = get_comments( $args );
1637 
1638         foreach ( $comments as $comment ) {
1639             $is_customer_note = get_comment_meta( $comment->comment_ID, 'is_customer_note', true );
1640             $comment->comment_content = make_clickable( $comment->comment_content );
1641             if ( $is_customer_note ) {
1642                 $notes[] = $comment;
1643             }
1644         }
1645 
1646         add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
1647 
1648         return (array) $notes;
1649 
1650     }
1651 
1652     /**
1653      * Checks if an order needs payment, based on status and order total
1654      *
1655      * @access public
1656      * @return bool
1657      */
1658     public function needs_payment() {
1659         $valid_order_statuses = apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this );
1660 
1661         if ( in_array( $this->status, $valid_order_statuses ) && $this->get_total() > 0 ) {
1662             $needs_payment = true;
1663         } else {
1664             $needs_payment = false;
1665         }
1666 
1667         return apply_filters( 'woocommerce_order_needs_payment', $needs_payment, $this, $valid_order_statuses );
1668     }
1669 }
1670 
WooCommerce API documentation generated by ApiGen 2.8.0