/wp-content/plugins/woocommerce/includes/abstracts/abstract-wc-order.php

https://github.com/Canuckaholic/Pop-Digital · PHP · 1811 lines · 935 code · 379 blank · 497 comment · 156 complexity · a3d2f0591fef88b9f8cf0d4f62463a99 MD5 · raw file

  1. <?php
  2. /**
  3. * Abstract Order
  4. *
  5. * The WooCommerce order class handles order data.
  6. *
  7. * @class WC_Order
  8. * @version 2.2.0
  9. * @package WooCommerce/Classes
  10. * @category Class
  11. * @author WooThemes
  12. */
  13. abstract class WC_Abstract_Order {
  14. /** @public int Order (post) ID */
  15. public $id;
  16. /** @public string Order type */
  17. public $order_type = null;
  18. /**
  19. * Get the order if ID is passed, otherwise the order is new and empty.
  20. * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory
  21. * should be used. It is possible, but the aforementioned are preferred and are the only
  22. * methods that will be maintained going forward.
  23. *
  24. */
  25. public function __construct( $order = '' ) {
  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. $this->display_totals_ex_tax = $this->tax_display_cart == 'excl' ? true : false;
  29. $this->display_cart_ex_tax = $this->tax_display_cart == 'excl' ? true : false;
  30. $this->order_type = 'simple';
  31. if ( is_numeric( $order ) ) {
  32. $this->id = absint( $order );
  33. $this->post = get_post( $order );
  34. $this->get_order( $this->id );
  35. } elseif ( $order instanceof WC_Order ) {
  36. $this->id = absint( $order->id );
  37. $this->post = $order->post;
  38. $this->get_order( $this->id );
  39. } elseif ( $order instanceof WP_Post || isset( $order->ID ) ) {
  40. $this->id = absint( $order->ID );
  41. $this->post = $order;
  42. $this->get_order( $this->id );
  43. }
  44. }
  45. /**
  46. * Remove all line items (products, coupons, shipping, taxes) from the order.
  47. *
  48. * @param string $type Order item type. Default null.
  49. */
  50. public function remove_order_items( $type = null ) {
  51. global $wpdb;
  52. if ( $type ) {
  53. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s )", $this->id, $type ) );
  54. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->id, $type ) );
  55. } else {
  56. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d )", $this->id ) );
  57. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->id ) );
  58. }
  59. }
  60. /**
  61. * Set the payment method for the order
  62. *
  63. * @param WC_Payment_Gateway
  64. * @param WC_Payment_Gateway $payment_method
  65. */
  66. public function set_payment_method( $payment_method ) {
  67. if ( is_object( $payment_method ) ) {
  68. update_post_meta( $this->id, '_payment_method', $payment_method->id );
  69. update_post_meta( $this->id, '_payment_method_title', $payment_method->get_title() );
  70. }
  71. }
  72. /**
  73. * Set the customer address
  74. *
  75. * @param array $address Address data
  76. * @param string $type billing or shipping
  77. */
  78. public function set_address( $address, $type = 'billing' ) {
  79. foreach ( $address as $key => $value ) {
  80. update_post_meta( $this->id, "_{$type}_" . $key, $value );
  81. }
  82. }
  83. /**
  84. * Add a product line item to the order
  85. *
  86. * @since 2.2
  87. * @param \WC_Product $product
  88. * @param int $qty Line item quantity
  89. * @param array $args
  90. * @return int|bool Item ID or false
  91. */
  92. public function add_product( $product, $qty = 1, $args = array() ) {
  93. $default_args = array(
  94. 'variation' => array(),
  95. 'totals' => array()
  96. );
  97. $args = wp_parse_args( $args, $default_args );
  98. $item_id = wc_add_order_item( $this->id, array(
  99. 'order_item_name' => $product->get_title(),
  100. 'order_item_type' => 'line_item'
  101. ) );
  102. if ( ! $item_id ) {
  103. return false;
  104. }
  105. wc_add_order_item_meta( $item_id, '_qty', wc_stock_amount( $qty ) );
  106. wc_add_order_item_meta( $item_id, '_tax_class', $product->get_tax_class() );
  107. wc_add_order_item_meta( $item_id, '_product_id', $product->id );
  108. wc_add_order_item_meta( $item_id, '_variation_id', isset( $product->variation_id ) ? $product->variation_id : 0 );
  109. // Set line item totals, either passed in or from the product
  110. wc_add_order_item_meta( $item_id, '_line_subtotal', wc_format_decimal( isset( $args['totals']['subtotal'] ) ? $args['totals']['subtotal'] : $product->get_price_excluding_tax( $qty ) ) );
  111. wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( isset( $args['totals']['total'] ) ? $args['totals']['total'] : $product->get_price_excluding_tax( $qty ) ) );
  112. wc_add_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( isset( $args['totals']['subtotal_tax'] ) ? $args['totals']['subtotal_tax'] : 0 ) );
  113. wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( isset( $args['totals']['tax'] ) ? $args['totals']['tax'] : 0 ) );
  114. // Save tax data - Since 2.2
  115. if ( isset( $args['totals']['tax_data'] ) ) {
  116. $tax_data = array();
  117. $tax_data['total'] = array_map( 'wc_format_decimal', $args['totals']['tax_data']['total'] );
  118. $tax_data['subtotal'] = array_map( 'wc_format_decimal', $args['totals']['tax_data']['subtotal'] );
  119. wc_add_order_item_meta( $item_id, '_line_tax_data', $tax_data );
  120. } else {
  121. wc_add_order_item_meta( $item_id, '_line_tax_data', array( 'total' => array(), 'subtotal' => array() ) );
  122. }
  123. // Add variation meta
  124. if ( ! empty( $args['variation'] ) ) {
  125. foreach ( $args['variation'] as $key => $value ) {
  126. wc_add_order_item_meta( $item_id, str_replace( 'attribute_', '', $key ), $value );
  127. }
  128. }
  129. // Backorders
  130. if ( $product->backorders_require_notification() && $product->is_on_backorder( $qty ) ) {
  131. wc_add_order_item_meta( $item_id, apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $qty - max( 0, $product->get_total_stock() ) );
  132. }
  133. do_action( 'woocommerce_order_add_product', $this->id, $item_id, $product, $qty, $args );
  134. return $item_id;
  135. }
  136. /**
  137. * Update a line item for the order
  138. *
  139. * Note this does not update order totals
  140. *
  141. * @since 2.2
  142. * @param int $item_id order item ID
  143. * @param array $args data to update
  144. * @return bool
  145. */
  146. public function update_product( $item_id, $product, $args ) {
  147. if ( ! $item_id || ! is_object( $product ) ) {
  148. return false;
  149. }
  150. // quantity
  151. if ( isset( $args['qty'] ) ) {
  152. wc_update_order_item_meta( $item_id, '_qty', wc_stock_amount( $args['qty'] ) );
  153. }
  154. // tax class
  155. if ( isset( $args['tax_class'] ) ) {
  156. wc_update_order_item_meta( $item_id, '_tax_class', $args['tax_class'] );
  157. }
  158. // set item totals, either provided or from product
  159. if ( isset( $args['qty'] ) ) {
  160. wc_update_order_item_meta( $item_id, '_line_subtotal', wc_format_decimal( isset( $args['totals']['subtotal'] ) ? $args['totals']['subtotal'] : $product->get_price_excluding_tax( $args['qty'] ) ) );
  161. wc_update_order_item_meta( $item_id, '_line_total', wc_format_decimal( isset( $args['totals']['total'] ) ? $args['totals']['total'] : $product->get_price_excluding_tax( $args['qty'] ) ) );
  162. }
  163. // set item tax totals
  164. wc_update_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( isset( $args['totals']['subtotal_tax'] ) ? $args['totals']['subtotal_tax'] : 0 ) );
  165. wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( isset( $args['totals']['tax'] ) ? $args['totals']['tax'] : 0 ) );
  166. // variation meta
  167. if ( isset( $args['variation'] ) && is_array( $args['variation'] ) ) {
  168. foreach ( $args['variation'] as $key => $value ) {
  169. wc_update_order_item_meta( $item_id, str_replace( 'attribute_', '', $key ), $value );
  170. }
  171. }
  172. // backorders
  173. if ( isset( $args['qty'] ) && $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
  174. wc_update_order_item_meta( $item_id, apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ) );
  175. }
  176. do_action( 'woocommerce_order_edit_product', $this->id, $item_id, $args, $product );
  177. return true;
  178. }
  179. /**
  180. * Add coupon code to the order
  181. *
  182. * @param string $code
  183. * @param integer $discount_amount
  184. * @return int|bool Item ID or false
  185. */
  186. public function add_coupon( $code, $discount_amount = 0 ) {
  187. $item_id = wc_add_order_item( $this->id, array(
  188. 'order_item_name' => $code,
  189. 'order_item_type' => 'coupon'
  190. ) );
  191. if ( ! $item_id ) {
  192. return false;
  193. }
  194. wc_add_order_item_meta( $item_id, 'discount_amount', $discount_amount );
  195. do_action( 'woocommerce_order_add_coupon', $this->id, $item_id, $code, $discount_amount );
  196. return $item_id;
  197. }
  198. /**
  199. * Update coupon for order
  200. *
  201. * Note this does not update order totals
  202. *
  203. * @since 2.2
  204. * @param int $item_id
  205. * @param array $args
  206. * @return bool
  207. */
  208. public function update_coupon( $item_id, $args ) {
  209. if ( ! $item_id ) {
  210. return false;
  211. }
  212. // code
  213. if ( isset( $args['code'] ) ) {
  214. wc_update_order_item( $item_id, array( 'order_item_name' => $args['code'] ) );
  215. }
  216. // amount
  217. if ( isset( $args['discount_amount'] ) ) {
  218. wc_update_order_item_meta( $item_id, 'discount_amount', wc_format_decimal( $args['discount_amount'] ) );
  219. }
  220. do_action( 'woocommerce_order_update_coupon', $this->id, $item_id, $args );
  221. return true;
  222. }
  223. /**
  224. * Add a tax row to the order
  225. *
  226. * @since 2.2
  227. * @param int tax_rate_id
  228. * @return int|bool Item ID or false
  229. */
  230. public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
  231. $code = WC_Tax::get_rate_code( $tax_rate_id );
  232. if ( ! $code ) {
  233. return false;
  234. }
  235. $item_id = wc_add_order_item( $this->id, array(
  236. 'order_item_name' => $code,
  237. 'order_item_type' => 'tax'
  238. ) );
  239. if ( ! $item_id ) {
  240. return false;
  241. }
  242. wc_add_order_item_meta( $item_id, 'rate_id', $tax_rate_id );
  243. wc_add_order_item_meta( $item_id, 'label', WC_Tax::get_rate_label( $tax_rate_id ) );
  244. wc_add_order_item_meta( $item_id, 'compound', WC_Tax::is_compound( $tax_rate_id ) ? 1 : 0 );
  245. wc_add_order_item_meta( $item_id, 'tax_amount', wc_format_decimal( $tax_amount ) );
  246. wc_add_order_item_meta( $item_id, 'shipping_tax_amount', wc_format_decimal( $shipping_tax_amount ) );
  247. do_action( 'woocommerce_order_add_tax', $this->id, $item_id, $tax_rate_id, $tax_amount, $shipping_tax_amount );
  248. return $item_id;
  249. }
  250. /**
  251. * Add a shipping row to the order
  252. *
  253. * @param WC_Shipping_Rate shipping_rate
  254. * @return int|bool Item ID or false
  255. */
  256. public function add_shipping( $shipping_rate ) {
  257. $item_id = wc_add_order_item( $this->id, array(
  258. 'order_item_name' => $shipping_rate->label,
  259. 'order_item_type' => 'shipping'
  260. ) );
  261. if ( ! $item_id ) {
  262. return false;
  263. }
  264. wc_add_order_item_meta( $item_id, 'method_id', $shipping_rate->id );
  265. wc_add_order_item_meta( $item_id, 'cost', wc_format_decimal( $shipping_rate->cost ) );
  266. // Save shipping taxes - Since 2.2
  267. $taxes = array_map( 'wc_format_decimal', $shipping_rate->taxes );
  268. wc_add_order_item_meta( $item_id, 'taxes', $taxes );
  269. do_action( 'woocommerce_order_add_shipping', $this->id, $item_id, $shipping_rate );
  270. // Update total
  271. $this->set_total( $this->order_shipping + wc_format_decimal( $shipping_rate->cost ), 'shipping' );
  272. return $item_id;
  273. }
  274. /**
  275. * Update shipping method for order
  276. *
  277. * Note this does not update the order total
  278. *
  279. * @since 2.2
  280. * @param int $item_id
  281. * @param array $args
  282. * @return bool
  283. */
  284. public function update_shipping( $item_id, $args ) {
  285. if ( ! $item_id ) {
  286. return false;
  287. }
  288. // method title
  289. if ( isset( $args['method_title'] ) ) {
  290. wc_update_order_item( $item_id, array( 'order_item_name' => $args['method_title'] ) );
  291. }
  292. // method ID
  293. if ( isset( $args['method_id'] ) ) {
  294. wc_update_order_item_meta( $item_id, 'method_id', $args['method_id'] );
  295. }
  296. // method cost
  297. if ( isset( $args['cost'] ) ) {
  298. wc_update_order_item_meta( $item_id, 'cost', wc_format_decimal( $args['cost'] ) );
  299. }
  300. do_action( 'woocommerce_order_update_shipping', $this->id, $item_id, $args );
  301. return true;
  302. }
  303. /**
  304. * Add a fee to the order
  305. *
  306. * @param object $fee
  307. * @return int|bool Item ID or false
  308. */
  309. public function add_fee( $fee ) {
  310. $item_id = wc_add_order_item( $this->id, array(
  311. 'order_item_name' => $fee->name,
  312. 'order_item_type' => 'fee'
  313. ) );
  314. if ( ! $item_id ) {
  315. return false;
  316. }
  317. if ( $fee->taxable ) {
  318. wc_add_order_item_meta( $item_id, '_tax_class', $fee->tax_class );
  319. } else {
  320. wc_add_order_item_meta( $item_id, '_tax_class', '0' );
  321. }
  322. wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $fee->amount ) );
  323. wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $fee->tax ) );
  324. // Save tax data - Since 2.2
  325. $tax_data = array_map( 'wc_format_decimal', $fee->tax_data );
  326. wc_add_order_item_meta( $item_id, '_line_tax_data', array( 'total' => $tax_data ) );
  327. do_action( 'woocommerce_order_add_fee', $this->id, $item_id, $fee );
  328. return $item_id;
  329. }
  330. /**
  331. * Update fee for order
  332. *
  333. * Note this does not update order totals
  334. *
  335. * @since 2.2
  336. * @param int $item_id
  337. * @param array $args
  338. * @return bool
  339. */
  340. public function update_fee( $item_id, $args ) {
  341. if ( ! $item_id ) {
  342. return false;
  343. }
  344. // name
  345. if ( isset( $args['name'] ) ) {
  346. wc_update_order_item( $item_id, array( 'order_item_name' => $args['name'] ) );
  347. }
  348. // tax class
  349. if ( isset( $args['tax_class'] ) ) {
  350. wc_update_order_item_meta( $item_id, '_tax_class', $args['tax_class'] );
  351. }
  352. // total
  353. if ( isset( $args['line_total'] ) ) {
  354. wc_update_order_item_meta( $item_id, '_line_total', wc_format_decimal( $args['line_total'] ) );
  355. }
  356. // total tax
  357. if ( isset( $args['line_tax'] ) ) {
  358. wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $args['line_tax'] ) );
  359. }
  360. do_action( 'woocommerce_order_update_fee', $this->id, $item_id, $args );
  361. return true;
  362. }
  363. /**
  364. * Set an order total
  365. *
  366. * @param float $amount
  367. * @param string $total_type
  368. */
  369. public function set_total( $amount, $total_type = 'total' ) {
  370. if ( ! in_array( $total_type, array( 'shipping', 'order_discount', 'tax', 'shipping_tax', 'total', 'cart_discount' ) ) ) {
  371. return false;
  372. }
  373. switch ( $total_type ) {
  374. case 'total' :
  375. $key = '_order_total';
  376. $amount = wc_format_decimal( $amount, get_option( 'woocommerce_price_num_decimals' ) );
  377. break;
  378. case 'order_discount' :
  379. case 'cart_discount' :
  380. $key = '_' . $total_type;
  381. $amount = wc_format_decimal( $amount );
  382. break;
  383. default :
  384. $key = '_order_' . $total_type;
  385. $amount = wc_format_decimal( $amount );
  386. break;
  387. }
  388. update_post_meta( $this->id, $key, $amount );
  389. }
  390. /**
  391. * Calculate taxes for all line items and shipping, and store the totals and tax rows.
  392. *
  393. * Will use the base country unless customer addresses are set.
  394. *
  395. * @return bool success or fail
  396. */
  397. public function calculate_taxes() {
  398. $shipping_tax_total = 0;
  399. $tax_total = 0;
  400. $taxes = array();
  401. $tax_based_on = get_option( 'woocommerce_tax_based_on' );
  402. if ( 'base' === $tax_based_on ) {
  403. $default = get_option( 'woocommerce_default_country' );
  404. $postcode = '';
  405. $city = '';
  406. if ( strstr( $default, ':' ) ) {
  407. list( $country, $state ) = explode( ':', $default );
  408. } else {
  409. $country = $default;
  410. $state = '';
  411. }
  412. } elseif ( 'billing' === $tax_based_on ) {
  413. $country = $this->billing_country;
  414. $state = $this->billing_state;
  415. $postcode = $this->billing_postcode;
  416. $city = $this->billing_city;
  417. } else {
  418. $country = $this->shipping_country;
  419. $state = $this->shipping_state;
  420. $postcode = $this->shipping_postcode;
  421. $city = $this->shipping_city;
  422. }
  423. // Get items
  424. foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
  425. $product = $this->get_product_from_item( $item );
  426. $line_total = isset( $item['line_total'] ) ? $item['line_total'] : 0;
  427. $line_subtotal = isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
  428. $tax_class = $item['tax_class'];
  429. $item_tax_status = $product ? $product->get_tax_status() : 'taxable';
  430. if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
  431. $tax_rates = WC_Tax::find_rates( array(
  432. 'country' => $country,
  433. 'state' => $state,
  434. 'postcode' => $postcode,
  435. 'city' => $city,
  436. 'tax_class' => $tax_class
  437. ) );
  438. $line_subtotal_taxes = WC_Tax::calc_tax( $line_subtotal, $tax_rates, false );
  439. $line_taxes = WC_Tax::calc_tax( $line_total, $tax_rates, false );
  440. $line_subtotal_tax = max( 0, array_sum( $line_subtotal_taxes ) );
  441. $line_tax = max( 0, array_sum( $line_taxes ) );
  442. $tax_total += $line_tax;
  443. wc_update_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $line_subtotal_tax ) );
  444. wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $line_tax ) );
  445. // Sum the item taxes
  446. foreach ( array_keys( $taxes + $line_taxes ) as $key ) {
  447. $taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
  448. }
  449. }
  450. }
  451. // Now calculate shipping tax
  452. $matched_tax_rates = array();
  453. $tax_rates = WC_Tax::find_rates( array(
  454. 'country' => $country,
  455. 'state' => $state,
  456. 'postcode' => $postcode,
  457. 'city' => $city,
  458. 'tax_class' => ''
  459. ) );
  460. if ( $tax_rates ) {
  461. foreach ( $tax_rates as $key => $rate ) {
  462. if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
  463. $matched_tax_rates[ $key ] = $rate;
  464. }
  465. }
  466. }
  467. $shipping_taxes = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
  468. $shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
  469. // Save tax totals
  470. $this->set_total( $shipping_tax_total, 'shipping_tax' );
  471. $this->set_total( $tax_total, 'tax' );
  472. // Tax rows
  473. $this->remove_order_items( 'tax' );
  474. // Now merge to keep tax rows
  475. foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
  476. $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 );
  477. }
  478. return true;
  479. }
  480. /**
  481. * Calculate shipping total
  482. *
  483. * @since 2.2
  484. * @return float
  485. */
  486. public function calculate_shipping() {
  487. $shipping_total = 0;
  488. foreach ( $this->get_shipping_methods() as $shipping ) {
  489. $shipping_total += $shipping['cost'];
  490. }
  491. $this->set_total( $shipping_total, 'shipping' );
  492. return $this->get_total_shipping();
  493. }
  494. /**
  495. * Update tax lines at order level by looking at the line item taxes themselves.
  496. *
  497. * @return bool success or fail
  498. */
  499. public function update_taxes() {
  500. $order_taxes = array();
  501. $order_shipping_taxes = array();
  502. foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
  503. $line_tax_data = maybe_unserialize( $item['line_tax_data'] );
  504. if ( isset( $line_tax_data['total'] ) ) {
  505. foreach ( $line_tax_data['total'] as $tax_rate_id => $tax ) {
  506. if ( ! isset( $order_taxes[ $tax_rate_id ] ) ) {
  507. $order_taxes[ $tax_rate_id ] = 0;
  508. }
  509. $order_taxes[ $tax_rate_id ] += $tax;
  510. }
  511. }
  512. }
  513. foreach ( $this->get_items( array( 'shipping' ) ) as $item_id => $item ) {
  514. $line_tax_data = maybe_unserialize( $item['taxes'] );
  515. if ( isset( $line_tax_data ) ) {
  516. foreach ( $line_tax_data as $tax_rate_id => $tax ) {
  517. if ( ! isset( $order_shipping_taxes[ $tax_rate_id ] ) ) {
  518. $order_shipping_taxes[ $tax_rate_id ] = 0;
  519. }
  520. $order_shipping_taxes[ $tax_rate_id ] += $tax;
  521. }
  522. }
  523. }
  524. // Remove old existing tax rows
  525. $this->remove_order_items( 'tax' );
  526. // Now merge to keep tax rows
  527. foreach ( array_keys( $order_taxes + $order_shipping_taxes ) as $tax_rate_id ) {
  528. $this->add_tax( $tax_rate_id, isset( $order_taxes[ $tax_rate_id ] ) ? $order_taxes[ $tax_rate_id ] : 0, isset( $order_shipping_taxes[ $tax_rate_id ] ) ? $order_shipping_taxes[ $tax_rate_id ] : 0 );
  529. }
  530. // Save tax totals
  531. $this->set_total( WC_Tax::round( array_sum( $order_shipping_taxes ) ), 'shipping_tax' );
  532. $this->set_total( WC_Tax::round( array_sum( $order_taxes ) ), 'tax' );
  533. return true;
  534. }
  535. /**
  536. * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
  537. *
  538. * @since 2.2
  539. * @param $and_taxes bool Calc taxes if true
  540. * @return float calculated grand total
  541. */
  542. public function calculate_totals( $and_taxes = true ) {
  543. $cart_subtotal = 0;
  544. $cart_total = 0;
  545. $fee_total = 0;
  546. if ( $and_taxes ) {
  547. $this->calculate_taxes();
  548. }
  549. // line items
  550. foreach ( $this->get_items() as $item ) {
  551. $cart_subtotal += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
  552. $cart_total += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
  553. }
  554. $this->calculate_shipping();
  555. foreach ( $this->get_fees() as $item ) {
  556. $fee_total += $item['line_total'];
  557. }
  558. $this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
  559. $grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() - $this->get_order_discount() + $this->get_cart_tax() + $this->get_shipping_tax(), absint( get_option( 'woocommerce_price_num_decimals' ) ) );
  560. $this->set_total( $grand_total, 'total' );
  561. return $grand_total;
  562. }
  563. /**
  564. * Gets an order from the database.
  565. *
  566. * @param int $id (default: 0)
  567. * @return bool
  568. */
  569. public function get_order( $id = 0 ) {
  570. if ( ! $id ) {
  571. return false;
  572. }
  573. if ( $result = get_post( $id ) ) {
  574. $this->populate( $result );
  575. return true;
  576. }
  577. return false;
  578. }
  579. /**
  580. * Populates an order from the loaded post data.
  581. *
  582. * @param mixed $result
  583. */
  584. public function populate( $result ) {
  585. // Standard post data
  586. $this->id = $result->ID;
  587. $this->order_date = $result->post_date;
  588. $this->modified_date = $result->post_modified;
  589. $this->customer_message = $result->post_excerpt;
  590. $this->customer_note = $result->post_excerpt;
  591. $this->post_status = $result->post_status;
  592. // Billing email cam default to user if set
  593. if ( empty( $this->billing_email ) && ! empty( $this->customer_user ) ) {
  594. $user = get_user_by( 'id', $this->customer_user );
  595. $this->billing_email = $user->user_email;
  596. }
  597. }
  598. /**
  599. * __isset function.
  600. *
  601. * @param mixed $key
  602. * @return bool
  603. */
  604. public function __isset( $key ) {
  605. if ( ! $this->id ) {
  606. return false;
  607. }
  608. return metadata_exists( 'post', $this->id, '_' . $key );
  609. }
  610. /**
  611. * __get function.
  612. *
  613. * @param mixed $key
  614. * @return mixed
  615. */
  616. public function __get( $key ) {
  617. // Get values or default if not set
  618. if ( 'completed_date' === $key ) {
  619. $value = ( $value = get_post_meta( $this->id, '_completed_date', true ) ) ? $value : $this->modified_date;
  620. } elseif ( 'user_id' === $key ) {
  621. $value = ( $value = get_post_meta( $this->id, '_customer_user', true ) ) ? absint( $value ) : '';
  622. } elseif ( 'status' === $key ) {
  623. $value = $this->get_status();
  624. } else {
  625. $value = get_post_meta( $this->id, '_' . $key, true );
  626. }
  627. return $value;
  628. }
  629. /**
  630. * Return the order statuses without wc- internal prefix
  631. *
  632. * @return string
  633. */
  634. public function get_status() {
  635. return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->post_status, 0, 3 ) ? substr( $this->post_status, 3 ) : $this->post_status, $this );
  636. }
  637. /**
  638. * Checks the order status against a passed in status.
  639. *
  640. * @return bool
  641. */
  642. public function has_status( $status ) {
  643. return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
  644. }
  645. /**
  646. * Gets the user ID associated with the order. Guests are 0.
  647. *
  648. * @since 2.2
  649. * @return int|false
  650. */
  651. public function get_user_id() {
  652. return $this->customer_user ? $this->customer_user : 0;
  653. }
  654. /**
  655. * Get the user associated with the order. False for guests.
  656. *
  657. * @since 2.2
  658. * @return WP_User|false
  659. */
  660. public function get_user() {
  661. return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
  662. }
  663. /**
  664. * Get transaction id for the order
  665. *
  666. * @return string
  667. */
  668. public function get_transaction_id() {
  669. return get_post_meta( $this->id, '_transaction_id', true );
  670. }
  671. /**
  672. * Check if an order key is valid.
  673. *
  674. * @param mixed $key
  675. * @return bool
  676. */
  677. public function key_is_valid( $key ) {
  678. if ( $key == $this->order_key ) {
  679. return true;
  680. }
  681. return false;
  682. }
  683. /**
  684. * get_order_number function.
  685. *
  686. * Gets the order number for display (by default, order ID)
  687. *
  688. * @return string
  689. */
  690. public function get_order_number() {
  691. return apply_filters( 'woocommerce_order_number', _x( '#', 'hash before order number', 'woocommerce' ) . $this->id, $this );
  692. }
  693. /**
  694. * Get a formatted billing address for the order.
  695. *
  696. * @return string
  697. */
  698. public function get_formatted_billing_address() {
  699. if ( ! $this->formatted_billing_address ) {
  700. // Formatted Addresses
  701. $address = apply_filters( 'woocommerce_order_formatted_billing_address', array(
  702. 'first_name' => $this->billing_first_name,
  703. 'last_name' => $this->billing_last_name,
  704. 'company' => $this->billing_company,
  705. 'address_1' => $this->billing_address_1,
  706. 'address_2' => $this->billing_address_2,
  707. 'city' => $this->billing_city,
  708. 'state' => $this->billing_state,
  709. 'postcode' => $this->billing_postcode,
  710. 'country' => $this->billing_country
  711. ), $this );
  712. $this->formatted_billing_address = WC()->countries->get_formatted_address( $address );
  713. }
  714. return $this->formatted_billing_address;
  715. }
  716. /**
  717. * Get the billing address in an array.
  718. *
  719. * @return string
  720. */
  721. public function get_billing_address() {
  722. if ( ! $this->billing_address ) {
  723. // Formatted Addresses
  724. $address = array(
  725. 'address_1' => $this->billing_address_1,
  726. 'address_2' => $this->billing_address_2,
  727. 'city' => $this->billing_city,
  728. 'state' => $this->billing_state,
  729. 'postcode' => $this->billing_postcode,
  730. 'country' => $this->billing_country
  731. );
  732. $joined_address = array();
  733. foreach ( $address as $part ) {
  734. if ( ! empty( $part ) ) {
  735. $joined_address[] = $part;
  736. }
  737. }
  738. $this->billing_address = implode( ', ', $joined_address );
  739. }
  740. return $this->billing_address;
  741. }
  742. /**
  743. * Get a formatted shipping address for the order.
  744. *
  745. * @return string
  746. */
  747. public function get_formatted_shipping_address() {
  748. if ( ! $this->formatted_shipping_address ) {
  749. if ( $this->shipping_address_1 ) {
  750. // Formatted Addresses
  751. $address = apply_filters( 'woocommerce_order_formatted_shipping_address', array(
  752. 'first_name' => $this->shipping_first_name,
  753. 'last_name' => $this->shipping_last_name,
  754. 'company' => $this->shipping_company,
  755. 'address_1' => $this->shipping_address_1,
  756. 'address_2' => $this->shipping_address_2,
  757. 'city' => $this->shipping_city,
  758. 'state' => $this->shipping_state,
  759. 'postcode' => $this->shipping_postcode,
  760. 'country' => $this->shipping_country
  761. ), $this );
  762. $this->formatted_shipping_address = WC()->countries->get_formatted_address( $address );
  763. }
  764. }
  765. return $this->formatted_shipping_address;
  766. }
  767. /**
  768. * Get the shipping address in an array.
  769. *
  770. * @return array
  771. */
  772. public function get_shipping_address() {
  773. if ( ! $this->shipping_address ) {
  774. if ( $this->shipping_address_1 ) {
  775. // Formatted Addresses
  776. $address = array(
  777. 'address_1' => $this->shipping_address_1,
  778. 'address_2' => $this->shipping_address_2,
  779. 'city' => $this->shipping_city,
  780. 'state' => $this->shipping_state,
  781. 'postcode' => $this->shipping_postcode,
  782. 'country' => $this->shipping_country
  783. );
  784. $joined_address = array();
  785. foreach ( $address as $part ) {
  786. if ( ! empty( $part ) ) {
  787. $joined_address[] = $part;
  788. }
  789. }
  790. $this->shipping_address = implode( ', ', $joined_address );
  791. }
  792. }
  793. return $this->shipping_address;
  794. }
  795. /**
  796. * Return an array of items/products within this order.
  797. *
  798. * @param string|array $type Types of line items to get (array or string)
  799. * @return array
  800. */
  801. public function get_items( $type = '' ) {
  802. global $wpdb;
  803. if ( empty( $type ) ) {
  804. $type = array( 'line_item' );
  805. }
  806. if ( ! is_array( $type ) ) {
  807. $type = array( $type );
  808. }
  809. $type = array_map( 'esc_attr', $type );
  810. $line_items = $wpdb->get_results( $wpdb->prepare( "
  811. SELECT order_item_id, order_item_name, order_item_type
  812. FROM {$wpdb->prefix}woocommerce_order_items
  813. WHERE order_id = %d
  814. AND order_item_type IN ( '" . implode( "','", $type ) . "' )
  815. ORDER BY order_item_id
  816. ", $this->id ) );
  817. $items = array();
  818. // Reserved meta keys
  819. $reserved_item_meta_keys = array(
  820. 'name',
  821. 'type',
  822. 'item_meta',
  823. 'qty',
  824. 'tax_class',
  825. 'product_id',
  826. 'variation_id',
  827. 'line_subtotal',
  828. 'line_total',
  829. 'line_tax',
  830. 'line_subtotal_tax'
  831. );
  832. // Loop items
  833. foreach ( $line_items as $item ) {
  834. // Place line item into array to return
  835. $items[ $item->order_item_id ]['name'] = $item->order_item_name;
  836. $items[ $item->order_item_id ]['type'] = $item->order_item_type;
  837. $items[ $item->order_item_id ]['item_meta'] = $this->get_item_meta( $item->order_item_id );
  838. // Expand meta data into the array
  839. if ( $items[ $item->order_item_id ]['item_meta'] ) {
  840. foreach ( $items[ $item->order_item_id ]['item_meta'] as $name => $value ) {
  841. if ( in_array( $name, $reserved_item_meta_keys ) ) {
  842. continue;
  843. }
  844. if ( '_' === substr( $name, 0, 1 ) ) {
  845. $items[ $item->order_item_id ][ substr( $name, 1 ) ] = $value[0];
  846. } elseif ( ! in_array( $name, $reserved_item_meta_keys ) ) {
  847. $items[ $item->order_item_id ][ $name ] = $value[0];
  848. }
  849. }
  850. }
  851. }
  852. return apply_filters( 'woocommerce_order_get_items', $items, $this );
  853. }
  854. /**
  855. * Gets order total - formatted for display.
  856. *
  857. * @return string
  858. */
  859. public function get_item_count( $type = '' ) {
  860. if ( empty( $type ) ) {
  861. $type = array( 'line_item' );
  862. }
  863. if ( ! is_array( $type ) ) {
  864. $type = array( $type );
  865. }
  866. $items = $this->get_items( $type );
  867. $count = 0;
  868. foreach ( $items as $item ) {
  869. if ( ! empty( $item['qty'] ) ) {
  870. $count += $item['qty'];
  871. } else {
  872. $count ++;
  873. }
  874. }
  875. return apply_filters( 'woocommerce_get_item_count', $count, $type, $this );
  876. }
  877. /**
  878. * Return an array of fees within this order.
  879. *
  880. * @return array
  881. */
  882. public function get_fees() {
  883. return $this->get_items( 'fee' );
  884. }
  885. /**
  886. * Return an array of taxes within this order.
  887. *
  888. * @return array
  889. */
  890. public function get_taxes() {
  891. return $this->get_items( 'tax' );
  892. }
  893. /**
  894. * Return an array of shipping costs within this order.
  895. *
  896. * @return array
  897. */
  898. public function get_shipping_methods() {
  899. return $this->get_items( 'shipping' );
  900. }
  901. /**
  902. * Check whether this order has a specific shipping method or not
  903. *
  904. * @param string $method_id
  905. */
  906. public function has_shipping_method( $method_id ) {
  907. $shipping_methods = $this->get_shipping_methods();
  908. $has_method = false;
  909. if ( ! $shipping_methods ) {
  910. return false;
  911. }
  912. foreach ( $shipping_methods as $shipping_method ) {
  913. if ( $shipping_method['method_id'] == $method_id ) {
  914. $has_method = true;
  915. }
  916. }
  917. return $has_method;
  918. }
  919. /**
  920. * Get taxes, merged by code, formatted ready for output.
  921. *
  922. * @return array
  923. */
  924. public function get_tax_totals() {
  925. $taxes = $this->get_items( 'tax' );
  926. $tax_totals = array();
  927. foreach ( $taxes as $key => $tax ) {
  928. $code = $tax[ 'name' ];
  929. if ( ! isset( $tax_totals[ $code ] ) ) {
  930. $tax_totals[ $code ] = new stdClass();
  931. $tax_totals[ $code ]->amount = 0;
  932. }
  933. $tax_totals[ $code ]->id = $key;
  934. $tax_totals[ $code ]->rate_id = $tax['rate_id'];
  935. $tax_totals[ $code ]->is_compound = $tax[ 'compound' ];
  936. $tax_totals[ $code ]->label = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
  937. $tax_totals[ $code ]->amount += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
  938. $tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
  939. }
  940. return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
  941. }
  942. /**
  943. * has_meta function for order items.
  944. *
  945. * @param string $order_item_id
  946. * @return array of meta data
  947. */
  948. public function has_meta( $order_item_id ) {
  949. global $wpdb;
  950. return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
  951. FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
  952. ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
  953. }
  954. /**
  955. * Get order item meta.
  956. *
  957. * @param mixed $order_item_id
  958. * @param string $key (default: '')
  959. * @param bool $single (default: false)
  960. * @return array|string
  961. */
  962. public function get_item_meta( $order_item_id, $key = '', $single = false ) {
  963. return get_metadata( 'order_item', $order_item_id, $key, $single );
  964. }
  965. /** Total Getters *******************************************************/
  966. /**
  967. * Gets the total (product) discount amount - these are applied before tax.
  968. *
  969. * @return float
  970. */
  971. public function get_cart_discount() {
  972. return apply_filters( 'woocommerce_order_amount_cart_discount', (double) $this->cart_discount, $this );
  973. }
  974. /**
  975. * Gets the total (product) discount amount - these are applied before tax.
  976. *
  977. * @return float
  978. */
  979. public function get_order_discount() {
  980. return apply_filters( 'woocommerce_order_amount_order_discount', (double) $this->order_discount, $this );
  981. }
  982. /**
  983. * Gets the total discount amount - both kinds
  984. *
  985. * @return float
  986. */
  987. public function get_total_discount() {
  988. return apply_filters( 'woocommerce_order_amount_total_discount', $this->get_cart_discount() + $this->get_order_discount(), $this );
  989. }
  990. /**
  991. * Gets cart tax amount.
  992. *
  993. * @return float
  994. */
  995. public function get_cart_tax() {
  996. return apply_filters( 'woocommerce_order_amount_cart_tax', (double) $this->order_tax, $this );
  997. }
  998. /**
  999. * Gets shipping tax amount.
  1000. *
  1001. * @return float
  1002. */
  1003. public function get_shipping_tax() {
  1004. return apply_filters( 'woocommerce_order_amount_shipping_tax', (double) $this->order_shipping_tax, $this );
  1005. }
  1006. /**
  1007. * Gets shipping and product tax.
  1008. *
  1009. * @return float
  1010. */
  1011. public function get_total_tax() {
  1012. return apply_filters( 'woocommerce_order_amount_total_tax', wc_round_tax_total( $this->get_cart_tax() + $this->get_shipping_tax() ), $this );
  1013. }
  1014. /**
  1015. * Gets shipping total.
  1016. *
  1017. * @return float
  1018. */
  1019. public function get_total_shipping() {
  1020. return apply_filters( 'woocommerce_order_amount_total_shipping', (double) $this->order_shipping, $this );
  1021. }
  1022. /**
  1023. * Gets order total.
  1024. *
  1025. * @return float
  1026. */
  1027. public function get_total() {
  1028. return apply_filters( 'woocommerce_order_amount_total', (double) $this->order_total, $this );
  1029. }
  1030. /**
  1031. * Gets order subtotal.
  1032. *
  1033. * @return mixed|void
  1034. */
  1035. public function get_subtotal() {
  1036. $subtotal = 0;
  1037. foreach ( $this->get_items() as $item ) {
  1038. $subtotal += ( isset( $item['line_subtotal'] ) ) ? $item['line_subtotal'] : 0;
  1039. }
  1040. return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
  1041. }
  1042. /**
  1043. * Get item subtotal - this is the cost before discount.
  1044. *
  1045. * @param mixed $item
  1046. * @param bool $inc_tax (default: false)
  1047. * @param bool $round (default: true)
  1048. * @return float
  1049. */
  1050. public function get_item_subtotal( $item, $inc_tax = false, $round = true ) {
  1051. if ( $inc_tax ) {
  1052. $price = ( $item['line_subtotal'] + $item['line_subtotal_tax'] ) / max( 1, $item['qty'] );
  1053. } else {
  1054. $price = ( $item['line_subtotal'] / $item['qty'] );
  1055. }
  1056. $price = $round ? round( $price, 2 ) : $price;
  1057. return apply_filters( 'woocommerce_order_amount_item_subtotal', $price, $this, $item );
  1058. }
  1059. /**
  1060. * Get line subtotal - this is the cost before discount.
  1061. *
  1062. * @param mixed $item
  1063. * @param bool $inc_tax (default: false)
  1064. * @param bool $round (default: true)
  1065. * @return float
  1066. */
  1067. public function get_line_subtotal( $item, $inc_tax = false, $round = true ) {
  1068. if ( $inc_tax ) {
  1069. $price = $item['line_subtotal'] + $item['line_subtotal_tax'];
  1070. } else {
  1071. $price = $item['line_subtotal'];
  1072. }
  1073. $price = $round ? round( $price, 2 ) : $price;
  1074. return apply_filters( 'woocommerce_order_amount_line_subtotal', $price, $this, $item );
  1075. }
  1076. /**
  1077. * Calculate item cost - useful for gateways.
  1078. *
  1079. * @param mixed $item
  1080. * @param bool $inc_tax (default: false)
  1081. * @param bool $round (default: true)
  1082. * @return float
  1083. */
  1084. public function get_item_total( $item, $inc_tax = false, $round = true ) {
  1085. $qty = ( ! empty( $item['qty'] ) ) ? $item['qty'] : 1;
  1086. if ( $inc_tax ) {
  1087. $price = ( $item['line_total'] + $item['line_tax'] ) / max( 1, $qty );
  1088. } else {
  1089. $price = $item['line_total'] / $qty;
  1090. }
  1091. $price = $round ? round( $price, 2 ) : $price;
  1092. return apply_filters( 'woocommerce_order_amount_item_total', $price, $this );
  1093. }
  1094. /**
  1095. * Calculate line total - useful for gateways.
  1096. *
  1097. * @param mixed $item
  1098. * @param bool $inc_tax (default: false)
  1099. * @return float
  1100. */
  1101. public function get_line_total( $item, $inc_tax = false ) {
  1102. $line_total = $inc_tax ? round( $item['line_total'] + $item['line_tax'], 2 ) : round( $item['line_total'], 2 );
  1103. return apply_filters( 'woocommerce_order_amount_line_total', $line_total, $this );
  1104. }
  1105. /**
  1106. * Calculate item tax - useful for gateways.
  1107. *
  1108. * @param mixed $item
  1109. * @param bool $round (default: true)
  1110. * @return float
  1111. */
  1112. public function get_item_tax( $item, $round = true ) {
  1113. $price = $item['line_tax'] / max( 1, $item['qty'] );
  1114. $price = $round ? wc_round_tax_total( $price ) : $price;
  1115. return apply_filters( 'woocommerce_order_amount_item_tax', $price, $item, $round, $this );
  1116. }
  1117. /**
  1118. * Calculate line tax - useful for gateways.
  1119. *
  1120. * @param mixed $item
  1121. * @return float
  1122. */
  1123. public function get_line_tax( $item ) {
  1124. return apply_filters( 'woocommerce_order_amount_line_tax', wc_round_tax_total( $item['line_tax'] ), $item, $this );
  1125. }
  1126. /**
  1127. * Gets shipping total.
  1128. *
  1129. * @deprecated As of 2.1, use of get_total_shipping() is preferred
  1130. * @return float
  1131. */
  1132. public function get_shipping() {
  1133. _deprecated_function( 'get_shipping', '2.1', 'get_total_shipping' );
  1134. return $this->get_total_shipping();
  1135. }
  1136. /**
  1137. * get_order_total function. Alias for get_total()
  1138. *
  1139. * @deprecated As of 2.1, use of get_total() is preferred
  1140. * @return float
  1141. */
  1142. public function get_order_total() {
  1143. _deprecated_function( 'get_order_total', '2.1', 'get_total' );
  1144. return $this->get_total();
  1145. }
  1146. /** End Total Getters *******************************************************/
  1147. /**
  1148. * Gets formatted shipping method title.
  1149. *
  1150. * @return string
  1151. */
  1152. public function get_shipping_method() {
  1153. $labels = array();
  1154. // Backwards compat < 2.1 - get shipping title stored in meta
  1155. if ( $this->shipping_method_title ) {
  1156. $labels[] = $this->shipping_method_title;
  1157. } else {
  1158. // 2.1+ get line items for shipping
  1159. $shipping_methods = $this->get_shipping_methods();
  1160. foreach ( $shipping_methods as $shipping ) {
  1161. $labels[] = $shipping['name'];
  1162. }
  1163. }
  1164. return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $labels ), $this );
  1165. }
  1166. /**
  1167. * Gets line subtotal - formatted for display.
  1168. *
  1169. * @param array $item
  1170. * @param string $tax_display
  1171. * @return string
  1172. */
  1173. public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
  1174. if ( ! $tax_display ) {
  1175. $tax_display = $this->tax_display_cart;
  1176. }
  1177. if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
  1178. return '';
  1179. }
  1180. if ( 'excl' == $tax_display ) {
  1181. $ex_tax_label = $this->prices_include_tax ? 1 : 0;
  1182. $subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_order_currency() ) );
  1183. } else {
  1184. $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array('currency' => $this->get_order_currency()) );
  1185. }
  1186. return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
  1187. }
  1188. /**
  1189. * Gets order currency
  1190. *
  1191. * @return string
  1192. */
  1193. public function get_order_currency() {
  1194. return apply_filters( 'woocommerce_get_order_currency', $this->order_currency, $this );
  1195. }
  1196. /**
  1197. * Gets order total - formatted for display.
  1198. *
  1199. * @return string
  1200. */
  1201. public function get_formatted_order_total() {
  1202. $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
  1203. return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
  1204. }
  1205. /**
  1206. * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
  1207. *
  1208. * @param bool $compound (default: false)
  1209. * @param string $tax_display (default: the tax_display_cart value)
  1210. * @return string
  1211. */
  1212. public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
  1213. if ( ! $tax_display ) {
  1214. $tax_display = $this->tax_display_cart;
  1215. }
  1216. $subtotal = 0;
  1217. if ( ! $compound ) {
  1218. foreach ( $this->get_items() as $item ) {
  1219. if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
  1220. return '';
  1221. }
  1222. $subtotal += $item['line_subtotal'];
  1223. if ( 'incl' == $tax_display ) {
  1224. $subtotal += $item['line_subtotal_tax'];
  1225. }
  1226. }
  1227. $subtotal = wc_price( $subtotal, array('currency' => $this->get_order_currency()) );
  1228. if ( $tax_display == 'excl' && $this->prices_include_tax ) {
  1229. $subtotal .= ' <small>' . WC()->countries->ex_tax_or_vat() . '</small>';
  1230. }
  1231. } else {
  1232. if ( 'incl' == $tax_display ) {
  1233. return '';
  1234. }
  1235. foreach ( $this->get_items() as $item ) {
  1236. $subtotal += $item['line_subtotal'];
  1237. }
  1238. // Add Shipping Costs
  1239. $subtotal += $this->get_total_shipping();
  1240. // Remove non-compound taxes
  1241. foreach ( $this->get_taxes() as $tax ) {
  1242. if ( ! empty( $tax['compound'] ) ) {
  1243. continue;
  1244. }
  1245. $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
  1246. }
  1247. // Remove discounts
  1248. $subtotal = $subtotal - $this->get_cart_discount();
  1249. $subtotal = wc_price( $subtotal, array('currency' => $this->get_order_currency()) );
  1250. }
  1251. return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
  1252. }
  1253. /**
  1254. * Gets shipping (formatted).
  1255. *
  1256. * @return string
  1257. */
  1258. public function get_shipping_to_display( $tax_display = '' ) {
  1259. if ( ! $tax_display ) {
  1260. $tax_display = $this->tax_display_cart;
  1261. }
  1262. if ( $this->order_shipping > 0 ) {
  1263. $tax_text = '';
  1264. if ( $tax_display == 'excl' ) {
  1265. // Show shipping excluding tax
  1266. $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
  1267. if ( $this->order_shipping_tax > 0 && $this->prices_include_tax ) {
  1268. $tax_text = WC()->countries->ex_tax_or_vat() . ' ';
  1269. }
  1270. } else {
  1271. // Show shipping including tax
  1272. $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
  1273. if ( $this->order_shipping_tax > 0 && ! $this->prices_include_tax ) {
  1274. $tax_text = WC()->countries->inc_tax_or_vat() . ' ';
  1275. }
  1276. }
  1277. $shipping .= sprintf( __( '&nbsp;<small>%svia %s</small>', 'woocommerce' ), $tax_text, $this->get_shipping_method() );
  1278. } elseif ( $this->get_shipping_method() ) {
  1279. $shipping = $this->get_shipping_method();
  1280. } else {
  1281. $shipping = __( 'Free!', 'woocommerce' );
  1282. }
  1283. return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
  1284. }
  1285. /**
  1286. * Get cart discount (formatted).
  1287. *
  1288. * @return string.
  1289. */
  1290. public function get_cart_discount_to_display() {
  1291. return apply_filters( 'woocommerce_order_cart_discount_to_display', wc_price( $this->get_cart_discount(), array( 'currency' => $this->get_order_currency() ) ), $this );
  1292. }
  1293. /**
  1294. * Get cart discount (formatted).
  1295. *
  1296. * @return string
  1297. */
  1298. public function get_order_discount_to_display() {
  1299. return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_order_discount(), array( 'currency' => $this->get_order_currency() ) ), $this );
  1300. }
  1301. /**
  1302. * Get a product (either product or variation).
  1303. *
  1304. * @param mixed $item
  1305. * @return WC_Product
  1306. */
  1307. public function get_product_from_item( $item ) {
  1308. if ( ! empty( $item['variation_id'] ) && 'product_variation' === get_post_type( $item['variation_id'] ) ) {
  1309. $_product = wc_get_product( $item['variation_id'] );
  1310. } elseif ( ! empty( $item['product_id'] ) ) {
  1311. $_product = wc_get_product( $item['product_id'] );
  1312. } else {
  1313. $_product = false;
  1314. }
  1315. return apply_filters( 'woocommerce_get_product_from_item', $_product, $item, $this );
  1316. }
  1317. /**
  1318. * Get totals for display on pages and in emails.
  1319. *
  1320. * @return array
  1321. */
  1322. public function get_order_item_totals( $tax_display = '' ) {
  1323. if ( ! $tax_display ) {
  1324. $tax_display = $this->tax_display_cart;
  1325. }
  1326. $total_rows = array();
  1327. if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
  1328. $total_rows['cart_subtotal'] = array(
  1329. 'label' => __( 'Cart Subtotal:', 'woocommerce' ),
  1330. 'value' => $subtotal
  1331. );
  1332. }
  1333. if ( $this->get_cart_discount() > 0 ) {
  1334. $total_rows['cart_discount'] = array(
  1335. 'label' => __( 'Cart Discount:', 'woocommerce' ),
  1336. 'value' => '-' . $this->get_cart_discount_to_display()
  1337. );
  1338. }
  1339. if ( $this->get_shipping_method() ) {
  1340. $total_rows['shipping'] = array(
  1341. 'label' => __( 'Shipping:', 'woocommerce' ),
  1342. 'value' => $this->get_shipping_to_display()
  1343. );
  1344. }
  1345. if ( $fees = $this->get_fees() )
  1346. foreach( $fees as $id => $fee ) {
  1347. if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', $fee['line_total'] + $fee['line_tax'] == 0, $id ) ) {
  1348. continue;
  1349. }
  1350. if ( 'excl' == $tax_display ) {
  1351. $total_rows[ 'fee_' . $id ] = array(
  1352. 'label' => $fee['name'] . ':',
  1353. 'value' => wc_price( $fee['line_total'], array('currency' => $this->get_order_currency()) )
  1354. );
  1355. } else {
  1356. $total_rows[ 'fee_' . $id ] = array(
  1357. 'label' => $fee['name'] . ':',
  1358. 'value' => wc_price( $fee['line_total'] + $fee['line_tax'], array('currency' => $this->get_order_currency()) )
  1359. );
  1360. }
  1361. }
  1362. // Tax for tax exclusive prices
  1363. if ( 'excl' == $tax_display ) {
  1364. if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
  1365. foreach ( $this->get_tax_totals() as $code => $tax ) {
  1366. $total_rows[ sanitize_title( $code ) ] = array(
  1367. 'label' => $tax->label . ':',
  1368. 'value' => $tax->formatted_amount
  1369. );
  1370. }
  1371. } else {
  1372. $total_rows['tax'] = array(
  1373. 'label' => WC()->countries->tax_or_vat() . ':',
  1374. 'value' => wc_price( $this->get_total_tax(), array('currency' => $this->get_order_currency()) )
  1375. );
  1376. }
  1377. }
  1378. if ( $this->get_order_discount() > 0 ) {
  1379. $total_rows['order_discount'] = array(
  1380. 'label' => __( 'Order Discount:', 'woocommerce' ),
  1381. 'value' => '-' . $this->get_order_discount_to_display()
  1382. );
  1383. }
  1384. if ( $this->get_total() > 0 ) {
  1385. $total_rows['payment_method'] = array(
  1386. 'label' => __( 'Payment Method:', 'woocommerce' ),
  1387. 'value' => $this->payment_method_title
  1388. );
  1389. }
  1390. $total_rows['order_total'] = array(
  1391. 'label' => __( 'Order Total:', 'woocommerce' ),
  1392. 'value' => $this->get_formatted_order_total()
  1393. );
  1394. // Tax for inclusive prices
  1395. if ( 'yes' == get_option( 'woocommerce_calc_taxes' ) && 'incl' == $tax_display ) {
  1396. $tax_string_array = array();
  1397. if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
  1398. foreach ( $this->get_tax_totals() as $code => $tax ) {
  1399. $tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label );
  1400. }
  1401. } else {
  1402. $tax_string_array[] = sprintf( '%s %s', wc_price( $this->get_total_tax(), array('currency' => $this->get_order_currency()) ), WC()->countries->tax_or_vat() );
  1403. }
  1404. if ( ! empty( $tax_string_array ) ) {
  1405. $total_rows['order_total']['value'] .= ' ' . sprintf( __( '(Includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
  1406. }
  1407. }
  1408. return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
  1409. }
  1410. /**
  1411. * Output items for display in html emails.
  1412. *
  1413. * @param bool $show_download_links (default: false)
  1414. * @param bool $show_sku (default: false)
  1415. * @param bool $show_purchase_note (default: false)
  1416. * @param bool $show_image (default: false)
  1417. * @param array $image_size (default: array( 32, 32 )
  1418. * @param bool plain text
  1419. * @return string
  1420. */
  1421. 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 ) {
  1422. ob_start();
  1423. $template = $plain_text ? 'emails/plain/email-order-items.php' : 'emails/email-order-items.php';
  1424. wc_get_template( $template, array(
  1425. 'order' => $this,
  1426. 'items' => $this->get_items(),
  1427. 'show_download_links' => $show_download_links,
  1428. 'show_sku' => $show_sku,
  1429. 'show_purchase_note' => $show_purchase_note,
  1430. 'show_image' => $show_image,
  1431. 'image_size' => $image_size
  1432. ) );