/includes/abstracts/abstract-wc-order.php

  1. <?php 
  2. if ( ! defined( 'ABSPATH' ) ) { 
  3. exit; 
  4.  
  5. include_once( WC_ABSPATH . 'includes/legacy/abstract-wc-legacy-order.php' ); 
  6.  
  7. /** 
  8. * Abstract Order 
  9. * 
  10. * Handles generic order data and database interaction which is extended by both 
  11. * WC_Order (regular orders) and WC_Order_Refund (refunds are negative orders). 
  12. * 
  13. * @class WC_Abstract_Order 
  14. * @version 3.0.0 
  15. * @package WooCommerce/Classes 
  16. * @category Class 
  17. * @author WooThemes 
  18. */ 
  19. abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { 
  20.  
  21. /** 
  22. * Order Data array. This is the core order data exposed in APIs since 3.0.0. 
  23. * 
  24. * Notes: cart_tax = cart_tax is the new name for the legacy 'order_tax' 
  25. * which is the tax for items only, not shipping. 
  26. * 
  27. * @since 3.0.0 
  28. * @var array 
  29. */ 
  30. protected $data = array( 
  31. 'parent_id' => 0,  
  32. 'status' => '',  
  33. 'currency' => '',  
  34. 'version' => '',  
  35. 'prices_include_tax' => false,  
  36. 'date_created' => null,  
  37. 'date_modified' => null,  
  38. 'discount_total' => 0,  
  39. 'discount_tax' => 0,  
  40. 'shipping_total' => 0,  
  41. 'shipping_tax' => 0,  
  42. 'cart_tax' => 0,  
  43. 'total' => 0,  
  44. 'total_tax' => 0,  
  45. ); 
  46.  
  47. /** 
  48. * Order items will be stored here, sometimes before they persist in the DB. 
  49. * 
  50. * @since 3.0.0 
  51. * @var array 
  52. */ 
  53. protected $items = array(); 
  54.  
  55. /** 
  56. * Order items that need deleting are stored here. 
  57. * 
  58. * @since 3.0.0 
  59. * @var array 
  60. */ 
  61. protected $items_to_delete = array(); 
  62.  
  63. /** 
  64. * Stores meta in cache for future reads. 
  65. * 
  66. * A group must be set to to enable caching. 
  67. * @var string 
  68. */ 
  69. protected $cache_group = 'orders'; 
  70.  
  71. /** 
  72. * Which data store to load. 
  73. * 
  74. * @var string 
  75. */ 
  76. protected $data_store_name = 'order'; 
  77.  
  78. /** 
  79. * This is the name of this object type. 
  80. * @var string 
  81. */ 
  82. protected $object_type = 'order'; 
  83.  
  84. /** 
  85. * Get the order if ID is passed, otherwise the order is new and empty. 
  86. * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory. 
  87. * should be used. It is possible, but the aforementioned are preferred and are the only. 
  88. * methods that will be maintained going forward. 
  89. * 
  90. * @param int|object|WC_Order $order Order to read. 
  91. */ 
  92. public function __construct( $order = 0 ) { 
  93. parent::__construct( $order ); 
  94.  
  95. if ( is_numeric( $order ) && $order > 0 ) { 
  96. $this->set_id( $order ); 
  97. } elseif ( $order instanceof self ) { 
  98. $this->set_id( $order->get_id() ); 
  99. } elseif ( ! empty( $order->ID ) ) { 
  100. $this->set_id( $order->ID ); 
  101. } else { 
  102. $this->set_object_read( true ); 
  103.  
  104. $this->data_store = WC_Data_Store::load( $this->data_store_name ); 
  105.  
  106. if ( $this->get_id() > 0 ) { 
  107. $this->data_store->read( $this ); 
  108.  
  109. /** 
  110. * Get internal type. 
  111. * 
  112. * @return string 
  113. */ 
  114. public function get_type() { 
  115. return 'shop_order'; 
  116.  
  117. /** 
  118. * Get all class data in array format. 
  119. * 
  120. * @since 3.0.0 
  121. * @return array 
  122. */ 
  123. public function get_data() { 
  124. return array_merge( 
  125. array( 
  126. 'id' => $this->get_id(),  
  127. ),  
  128. $this->data,  
  129. array( 
  130. 'meta_data' => $this->get_meta_data(),  
  131. 'line_items' => $this->get_items( 'line_item' ),  
  132. 'tax_lines' => $this->get_items( 'tax' ),  
  133. 'shipping_lines' => $this->get_items( 'shipping' ),  
  134. 'fee_lines' => $this->get_items( 'fee' ),  
  135. 'coupon_lines' => $this->get_items( 'coupon' ),  
  136. ); 
  137.  
  138. /** 
  139. |-------------------------------------------------------------------------- 
  140. | CRUD methods 
  141. |-------------------------------------------------------------------------- 
  142. | 
  143. | Methods which create, read, update and delete orders from the database. 
  144. | Written in abstract fashion so that the way orders are stored can be 
  145. | changed more easily in the future. 
  146. | 
  147. | A save method is included for convenience (chooses update or create based 
  148. | on if the order exists yet). 
  149. | 
  150. */ 
  151.  
  152. /** 
  153. * Save data to the database. 
  154. * 
  155. * @since 3.0.0 
  156. * @return int order ID 
  157. */ 
  158. public function save() { 
  159. if ( $this->data_store ) { 
  160. // Trigger action before saving to the DB. Allows you to adjust object props before save. 
  161. do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store ); 
  162.  
  163. if ( $this->get_id() ) { 
  164. $this->data_store->update( $this ); 
  165. } else { 
  166. $this->data_store->create( $this ); 
  167. $this->save_items(); 
  168. return $this->get_id(); 
  169.  
  170. /** 
  171. * Save all order items which are part of this order. 
  172. */ 
  173. protected function save_items() { 
  174. foreach ( $this->items_to_delete as $item ) { 
  175. $item->delete(); 
  176. $this->items_to_delete = array(); 
  177.  
  178. // Add/save items. 
  179. foreach ( $this->items as $item_group => $items ) { 
  180. if ( is_array( $items ) ) { 
  181. foreach ( array_filter( $items ) as $item_key => $item ) { 
  182. $item->set_order_id( $this->get_id() ); 
  183. $item_id = $item->save(); 
  184.  
  185. // If ID changed (new item saved to DB)... 
  186. if ( $item_id !== $item_key ) { 
  187. $this->items[ $item_group ][ $item_id ] = $item; 
  188.  
  189. unset( $this->items[ $item_group ][ $item_key ] ); 
  190.  
  191. /** 
  192. |-------------------------------------------------------------------------- 
  193. | Getters 
  194. |-------------------------------------------------------------------------- 
  195. */ 
  196.  
  197. /** 
  198. * Get parent order ID. 
  199. * 
  200. * @since 3.0.0 
  201. * @param string $context 
  202. * @return integer 
  203. */ 
  204. public function get_parent_id( $context = 'view' ) { 
  205. return $this->get_prop( 'parent_id', $context ); 
  206.  
  207. /** 
  208. * Gets order currency. 
  209. * 
  210. * @param string $context 
  211. * @return string 
  212. */ 
  213. public function get_currency( $context = 'view' ) { 
  214. return $this->get_prop( 'currency', $context ); 
  215.  
  216. /** 
  217. * Get order_version. 
  218. * 
  219. * @param string $context 
  220. * @return string 
  221. */ 
  222. public function get_version( $context = 'view' ) { 
  223. return $this->get_prop( 'version', $context ); 
  224.  
  225. /** 
  226. * Get prices_include_tax. 
  227. * 
  228. * @param string $context 
  229. * @return bool 
  230. */ 
  231. public function get_prices_include_tax( $context = 'view' ) { 
  232. return $this->get_prop( 'prices_include_tax', $context ); 
  233.  
  234. /** 
  235. * Get date_created. 
  236. * 
  237. * @param string $context 
  238. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  239. */ 
  240. public function get_date_created( $context = 'view' ) { 
  241. return $this->get_prop( 'date_created', $context ); 
  242.  
  243. /** 
  244. * Get date_modified. 
  245. * 
  246. * @param string $context 
  247. * @return WC_DateTime|NULL object if the date is set or null if there is no date. 
  248. */ 
  249. public function get_date_modified( $context = 'view' ) { 
  250. return $this->get_prop( 'date_modified', $context ); 
  251.  
  252. /** 
  253. * Return the order statuses without wc- internal prefix. 
  254. * 
  255. * @param string $context 
  256. * @return string 
  257. */ 
  258. public function get_status( $context = 'view' ) { 
  259. $status = $this->get_prop( 'status', $context ); 
  260.  
  261. if ( empty( $status ) && 'view' === $context ) { 
  262. // In view context, return the default status if no status has been set. 
  263. $status = apply_filters( 'woocommerce_default_order_status', 'pending' ); 
  264. return $status; 
  265.  
  266. /** 
  267. * Get discount_total. 
  268. * 
  269. * @param string $context 
  270. * @return string 
  271. */ 
  272. public function get_discount_total( $context = 'view' ) { 
  273. return $this->get_prop( 'discount_total', $context ); 
  274.  
  275. /** 
  276. * Get discount_tax. 
  277. * 
  278. * @param string $context 
  279. * @return string 
  280. */ 
  281. public function get_discount_tax( $context = 'view' ) { 
  282. return $this->get_prop( 'discount_tax', $context ); 
  283.  
  284. /** 
  285. * Get shipping_total. 
  286. * 
  287. * @param string $context 
  288. * @return string 
  289. */ 
  290. public function get_shipping_total( $context = 'view' ) { 
  291. return $this->get_prop( 'shipping_total', $context ); 
  292.  
  293. /** 
  294. * Get shipping_tax. 
  295. * 
  296. * @param string $context 
  297. * @return string 
  298. */ 
  299. public function get_shipping_tax( $context = 'view' ) { 
  300. return $this->get_prop( 'shipping_tax', $context ); 
  301.  
  302. /** 
  303. * Gets cart tax amount. 
  304. * 
  305. * @param string $context 
  306. * @return float 
  307. */ 
  308. public function get_cart_tax( $context = 'view' ) { 
  309. return $this->get_prop( 'cart_tax', $context ); 
  310.  
  311. /** 
  312. * Gets order grand total. incl. taxes. Used in gateways. 
  313. * 
  314. * @param string $context 
  315. * @return float 
  316. */ 
  317. public function get_total( $context = 'view' ) { 
  318. return $this->get_prop( 'total', $context ); 
  319.  
  320. /** 
  321. * Get total tax amount. Alias for get_order_tax(). 
  322. * 
  323. * @param string $context 
  324. * @return float 
  325. */ 
  326. public function get_total_tax( $context = 'view' ) { 
  327. return $this->get_prop( 'total_tax', $context ); 
  328.  
  329. /** 
  330. |-------------------------------------------------------------------------- 
  331. | Non-CRUD Getters 
  332. |-------------------------------------------------------------------------- 
  333. */ 
  334.  
  335. /** 
  336. * Gets the total discount amount. 
  337. * 
  338. * @param bool $ex_tax Show discount excl any tax. 
  339. * @return float 
  340. */ 
  341. public function get_total_discount( $ex_tax = true ) { 
  342. if ( $ex_tax ) { 
  343. $total_discount = $this->get_discount_total(); 
  344. } else { 
  345. $total_discount = $this->get_discount_total() + $this->get_discount_tax(); 
  346. return apply_filters( 'woocommerce_order_get_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this ); 
  347.  
  348. /** 
  349. * Gets order subtotal. 
  350. * @return float 
  351. */ 
  352. public function get_subtotal() { 
  353. $subtotal = 0; 
  354.  
  355. foreach ( $this->get_items() as $item ) { 
  356. $subtotal += $item->get_subtotal(); 
  357.  
  358. return apply_filters( 'woocommerce_order_get_subtotal', (double) $subtotal, $this ); 
  359.  
  360. /** 
  361. * Get taxes, merged by code, formatted ready for output. 
  362. * 
  363. * @return array 
  364. */ 
  365. public function get_tax_totals() { 
  366. $tax_totals = array(); 
  367.  
  368. foreach ( $this->get_items( 'tax' ) as $key => $tax ) { 
  369. $code = $tax->get_rate_code(); 
  370.  
  371. if ( ! isset( $tax_totals[ $code ] ) ) { 
  372. $tax_totals[ $code ] = new stdClass(); 
  373. $tax_totals[ $code ]->amount = 0; 
  374.  
  375. $tax_totals[ $code ]->id = $key; 
  376. $tax_totals[ $code ]->rate_id = $tax->get_rate_id(); 
  377. $tax_totals[ $code ]->is_compound = $tax->is_compound(); 
  378. $tax_totals[ $code ]->label = $tax->get_label(); 
  379. $tax_totals[ $code ]->amount += (float) $tax->get_tax_total() + (float) $tax->get_shipping_tax_total(); 
  380. $tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array( 'currency' => $this->get_currency() ) ); 
  381.  
  382. if ( apply_filters( 'woocommerce_order_hide_zero_taxes', true ) ) { 
  383. $amounts = array_filter( wp_list_pluck( $tax_totals, 'amount' ) ); 
  384. $tax_totals = array_intersect_key( $tax_totals, $amounts ); 
  385.  
  386. return apply_filters( 'woocommerce_order_get_tax_totals', $tax_totals, $this ); 
  387.  
  388. /** 
  389. * Get all valid statuses for this order 
  390. * 
  391. * @since 3.0.0 
  392. * @return array Internal status keys e.g. 'wc-processing' 
  393. */ 
  394. protected function get_valid_statuses() { 
  395. return array_keys( wc_get_order_statuses() ); 
  396.  
  397. /** 
  398. * Get user ID. Used by orders, not other order types like refunds. 
  399. * 
  400. * @param string $context 
  401. * @return int 
  402. */ 
  403. public function get_user_id( $context = 'view' ) { 
  404. return 0; 
  405.  
  406. /** 
  407. * Get user. Used by orders, not other order types like refunds. 
  408. * 
  409. * @return WP_User|false 
  410. */ 
  411. public function get_user() { 
  412. return false; 
  413.  
  414. /** 
  415. |-------------------------------------------------------------------------- 
  416. | Setters 
  417. |-------------------------------------------------------------------------- 
  418. | 
  419. | Functions for setting order data. These should not update anything in the 
  420. | database itself and should only change what is stored in the class 
  421. | object. However, for backwards compatibility pre 3.0.0 some of these 
  422. | setters may handle both. 
  423. */ 
  424.  
  425. /** 
  426. * Set parent order ID. 
  427. * 
  428. * @since 3.0.0 
  429. * @param int $value 
  430. * @throws WC_Data_Exception 
  431. */ 
  432. public function set_parent_id( $value ) { 
  433. if ( $value && ( $value === $this->get_id() || ! wc_get_order( $value ) ) ) { 
  434. $this->error( 'order_invalid_parent_id', __( 'Invalid parent ID', 'woocommerce' ) ); 
  435. $this->set_prop( 'parent_id', absint( $value ) ); 
  436.  
  437. /** 
  438. * Set order status. 
  439. * 
  440. * @since 3.0.0 
  441. * @param string $new_status Status to change the order to. No internal wc- prefix is required. 
  442. * @return array details of change 
  443. */ 
  444. public function set_status( $new_status ) { 
  445. $old_status = $this->get_status(); 
  446. $new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status; 
  447.  
  448. // If setting the status, ensure it's set to a valid status. 
  449. if ( true === $this->object_read ) { 
  450. // Only allow valid new status 
  451. if ( ! in_array( 'wc-' . $new_status, $this->get_valid_statuses() ) && 'trash' !== $new_status ) { 
  452. $new_status = 'pending'; 
  453.  
  454. // If the old status is set but unknown (e.g. draft) assume its pending for action usage. 
  455. if ( $old_status && ! in_array( 'wc-' . $old_status, $this->get_valid_statuses() ) && 'trash' !== $old_status ) { 
  456. $old_status = 'pending'; 
  457.  
  458. $this->set_prop( 'status', $new_status ); 
  459.  
  460. return array( 
  461. 'from' => $old_status,  
  462. 'to' => $new_status,  
  463. ); 
  464.  
  465. /** 
  466. * Set order_version. 
  467. * 
  468. * @param string $value 
  469. * @throws WC_Data_Exception 
  470. */ 
  471. public function set_version( $value ) { 
  472. $this->set_prop( 'version', $value ); 
  473.  
  474. /** 
  475. * Set order_currency. 
  476. * 
  477. * @param string $value 
  478. * @throws WC_Data_Exception 
  479. */ 
  480. public function set_currency( $value ) { 
  481. if ( $value && ! in_array( $value, array_keys( get_woocommerce_currencies() ) ) ) { 
  482. $this->error( 'order_invalid_currency', __( 'Invalid currency code', 'woocommerce' ) ); 
  483. $this->set_prop( 'currency', $value ? $value : get_woocommerce_currency() ); 
  484.  
  485. /** 
  486. * Set prices_include_tax. 
  487. * 
  488. * @param bool $value 
  489. * @throws WC_Data_Exception 
  490. */ 
  491. public function set_prices_include_tax( $value ) { 
  492. $this->set_prop( 'prices_include_tax', (bool) $value ); 
  493.  
  494. /** 
  495. * Set date_created. 
  496. * 
  497. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  498. * @throws WC_Data_Exception 
  499. */ 
  500. public function set_date_created( $date = null ) { 
  501. $this->set_date_prop( 'date_created', $date ); 
  502.  
  503. /** 
  504. * Set date_modified. 
  505. * 
  506. * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date. 
  507. * @throws WC_Data_Exception 
  508. */ 
  509. public function set_date_modified( $date = null ) { 
  510. $this->set_date_prop( 'date_modified', $date ); 
  511.  
  512. /** 
  513. * Set discount_total. 
  514. * 
  515. * @param string $value 
  516. * @throws WC_Data_Exception 
  517. */ 
  518. public function set_discount_total( $value ) { 
  519. $this->set_prop( 'discount_total', wc_format_decimal( $value ) ); 
  520.  
  521. /** 
  522. * Set discount_tax. 
  523. * 
  524. * @param string $value 
  525. * @throws WC_Data_Exception 
  526. */ 
  527. public function set_discount_tax( $value ) { 
  528. $this->set_prop( 'discount_tax', wc_format_decimal( $value ) ); 
  529.  
  530. /** 
  531. * Set shipping_total. 
  532. * 
  533. * @param string $value 
  534. * @throws WC_Data_Exception 
  535. */ 
  536. public function set_shipping_total( $value ) { 
  537. $this->set_prop( 'shipping_total', wc_format_decimal( $value ) ); 
  538.  
  539. /** 
  540. * Set shipping_tax. 
  541. * 
  542. * @param string $value 
  543. * @throws WC_Data_Exception 
  544. */ 
  545. public function set_shipping_tax( $value ) { 
  546. $this->set_prop( 'shipping_tax', wc_format_decimal( $value ) ); 
  547. $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); 
  548.  
  549. /** 
  550. * Set cart tax. 
  551. * 
  552. * @param string $value 
  553. * @throws WC_Data_Exception 
  554. */ 
  555. public function set_cart_tax( $value ) { 
  556. $this->set_prop( 'cart_tax', wc_format_decimal( $value ) ); 
  557. $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); 
  558.  
  559. /** 
  560. * Sets order tax (sum of cart and shipping tax). Used internaly only. 
  561. * 
  562. * @param string $value 
  563. * @throws WC_Data_Exception 
  564. */ 
  565. protected function set_total_tax( $value ) { 
  566. $this->set_prop( 'total_tax', wc_format_decimal( $value ) ); 
  567.  
  568. /** 
  569. * Set total. 
  570. * 
  571. * @param string $value 
  572. * @param string $deprecated Function used to set different totals based on this. 
  573. * @throws WC_Data_Exception 
  574. */ 
  575. public function set_total( $value, $deprecated = '' ) { 
  576. if ( $deprecated ) { 
  577. wc_deprecated_argument( 'total_type', '3.0', 'Use dedicated total setter methods instead.' ); 
  578. return $this->legacy_set_total( $value, $deprecated ); 
  579. $this->set_prop( 'total', wc_format_decimal( $value, wc_get_price_decimals() ) ); 
  580.  
  581. /** 
  582. |-------------------------------------------------------------------------- 
  583. | Order Item Handling 
  584. |-------------------------------------------------------------------------- 
  585. | 
  586. | Order items are used for products, taxes, shipping, and fees within 
  587. | each order. 
  588. */ 
  589.  
  590. /** 
  591. * Remove all line items (products, coupons, shipping, taxes) from the order. 
  592. * 
  593. * @param string $type Order item type. Default null. 
  594. */ 
  595. public function remove_order_items( $type = null ) { 
  596. if ( ! empty( $type ) ) { 
  597. $this->data_store->delete_items( $this, $type ); 
  598.  
  599. if ( $group = $this->type_to_group( $type ) ) { 
  600. unset( $this->items[ $group ] ); 
  601. } else { 
  602. $this->data_store->delete_items( $this ); 
  603. $this->items = array(); 
  604.  
  605. /** 
  606. * Convert a type to a types group. 
  607. * 
  608. * @param string $type 
  609. * @return string group 
  610. */ 
  611. protected function type_to_group( $type ) { 
  612. $type_to_group = apply_filters( 'woocommerce_order_type_to_group', array( 
  613. 'line_item' => 'line_items',  
  614. 'tax' => 'tax_lines',  
  615. 'shipping' => 'shipping_lines',  
  616. 'fee' => 'fee_lines',  
  617. 'coupon' => 'coupon_lines',  
  618. ) ); 
  619. return isset( $type_to_group[ $type ] ) ? $type_to_group[ $type ] : ''; 
  620.  
  621. /** 
  622. * Return an array of items/products within this order. 
  623. * 
  624. * @param string|array $types Types of line items to get (array or string). 
  625. * @return Array of WC_Order_item 
  626. */ 
  627. public function get_items( $types = 'line_item' ) { 
  628. $items = array(); 
  629. $types = array_filter( (array) $types ); 
  630.  
  631. foreach ( $types as $type ) { 
  632. if ( $group = $this->type_to_group( $type ) ) { 
  633. if ( ! isset( $this->items[ $group ] ) ) { 
  634. $this->items[ $group ] = $this->data_store->read_items( $this, $type ); 
  635. // Don't use array_merge here because keys are numeric 
  636. $items = array_filter( $items + $this->items[ $group ] ); 
  637.  
  638. return apply_filters( 'woocommerce_order_get_items', $items, $this ); 
  639.  
  640. /** 
  641. * Return an array of fees within this order. 
  642. * 
  643. * @return array 
  644. */ 
  645. public function get_fees() { 
  646. return $this->get_items( 'fee' ); 
  647.  
  648. /** 
  649. * Return an array of taxes within this order. 
  650. * 
  651. * @return array 
  652. */ 
  653. public function get_taxes() { 
  654. return $this->get_items( 'tax' ); 
  655.  
  656. /** 
  657. * Return an array of shipping costs within this order. 
  658. * 
  659. * @return array 
  660. */ 
  661. public function get_shipping_methods() { 
  662. return $this->get_items( 'shipping' ); 
  663.  
  664. /** 
  665. * Gets formatted shipping method title. 
  666. * 
  667. * @return string 
  668. */ 
  669. public function get_shipping_method() { 
  670. $names = array(); 
  671. foreach ( $this->get_shipping_methods() as $shipping_method ) { 
  672. $names[] = $shipping_method->get_name(); 
  673. return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this ); 
  674.  
  675. /** 
  676. * Get coupon codes only. 
  677. * 
  678. * @return array 
  679. */ 
  680. public function get_used_coupons() { 
  681. $coupon_codes = array(); 
  682. if ( $coupons = $this->get_items( 'coupon' ) ) { 
  683. foreach ( $coupons as $coupon ) { 
  684. $coupon_codes[] = $coupon->get_code(); 
  685. return $coupon_codes; 
  686.  
  687. /** 
  688. * Gets the count of order items of a certain type. 
  689. * 
  690. * @param string $item_type 
  691. * @return string 
  692. */ 
  693. public function get_item_count( $item_type = '' ) { 
  694. $items = $this->get_items( empty( $item_type ) ? 'line_item' : $item_type ); 
  695. $count = 0; 
  696.  
  697. foreach ( $items as $item ) { 
  698. $count += $item->get_quantity(); 
  699.  
  700. return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this ); 
  701.  
  702. /** 
  703. * Get an order item object, based on it's type. 
  704. * 
  705. * @since 3.0.0 
  706. * @param int $item_id 
  707. * @return WC_Order_Item 
  708. */ 
  709. public function get_item( $item_id ) { 
  710. return WC_Order_Factory::get_order_item( $item_id ); 
  711.  
  712. /** 
  713. * Get key for where a certain item type is stored in _items. 
  714. * 
  715. * @since 3.0.0 
  716. * @param $item object Order item (product, shipping, fee, coupon, tax) 
  717. * @return string 
  718. */ 
  719. protected function get_items_key( $item ) { 
  720. if ( is_a( $item, 'WC_Order_Item_Product' ) ) { 
  721. return 'line_items'; 
  722. } elseif ( is_a( $item, 'WC_Order_Item_Fee' ) ) { 
  723. return 'fee_lines'; 
  724. } elseif ( is_a( $item, 'WC_Order_Item_Shipping' ) ) { 
  725. return 'shipping_lines'; 
  726. } elseif ( is_a( $item, 'WC_Order_Item_Tax' ) ) { 
  727. return 'tax_lines'; 
  728. } elseif ( is_a( $item, 'WC_Order_Item_Coupon' ) ) { 
  729. return 'coupon_lines'; 
  730. } else { 
  731. return ''; 
  732.  
  733. /** 
  734. * Remove item from the order. 
  735. * 
  736. * @param int $item_id 
  737. */ 
  738. public function remove_item( $item_id ) { 
  739. $item = $this->get_item( $item_id ); 
  740.  
  741. if ( ! $item || ! ( $items_key = $this->get_items_key( $item ) ) ) { 
  742. return false; 
  743.  
  744. // Unset and remove later 
  745. $this->items_to_delete[] = $item; 
  746. unset( $this->items[ $items_key ][ $item->get_id() ] ); 
  747.  
  748. /** 
  749. * Adds an order item to this order. The order item will not persist until save. 
  750. * 
  751. * @since 3.0.0 
  752. * @param WC_Order_Item Order item object (product, shipping, fee, coupon, tax) 
  753. */ 
  754. public function add_item( $item ) { 
  755. if ( ! $items_key = $this->get_items_key( $item ) ) { 
  756. return false; 
  757.  
  758. // Make sure existing items are loaded so we can append this new one. 
  759. if ( ! isset( $this->items[ $items_key ] ) ) { 
  760. $this->items[ $items_key ] = $this->get_items( $item->get_type() ); 
  761.  
  762. // Set parent. 
  763. $item->set_order_id( $this->get_id() ); 
  764.  
  765. // Append new row with generated temporary ID 
  766. if ( $item_id = $item->get_id() ) { 
  767. $this->items[ $items_key ][ $item_id ] = $item; 
  768. } else { 
  769. $this->items[ $items_key ][ 'new:' . $items_key . sizeof( $this->items[ $items_key ] ) ] = $item; 
  770.  
  771. /** 
  772. * Add a product line item to the order. This is the only line item type with 
  773. * it's own method because it saves looking up order amounts (costs are added up for you). 
  774. * 
  775. * @param \WC_Product $product 
  776. * @param int $qty 
  777. * @param array $args 
  778. * @return int order item ID 
  779. * @throws WC_Data_Exception 
  780. */ 
  781. public function add_product( $product, $qty = 1, $args = array() ) { 
  782. if ( $product ) { 
  783. $default_args = array( 
  784. 'name' => $product->get_name(),  
  785. 'tax_class' => $product->get_tax_class(),  
  786. 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),  
  787. 'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0,  
  788. 'variation' => $product->is_type( 'variation' ) ? $product->get_attributes() : array(),  
  789. 'subtotal' => wc_get_price_excluding_tax( $product, array( 'qty' => $qty ) ),  
  790. 'total' => wc_get_price_excluding_tax( $product, array( 'qty' => $qty ) ),  
  791. 'quantity' => $qty,  
  792. ); 
  793. } else { 
  794. $default_args = array( 
  795. 'quantity' => $qty,  
  796. ); 
  797.  
  798. $args = wp_parse_args( $args, $default_args ); 
  799.  
  800. // BW compatibility with old args 
  801. if ( isset( $args['totals'] ) ) { 
  802. foreach ( $args['totals'] as $key => $value ) { 
  803. if ( 'tax' === $key ) { 
  804. $args['total_tax'] = $value; 
  805. } elseif ( 'tax_data' === $key ) { 
  806. $args['taxes'] = $value; 
  807. } else { 
  808. $args[ $key ] = $value; 
  809.  
  810. $item = new WC_Order_Item_Product(); 
  811. $item->set_props( $args ); 
  812. $item->set_backorder_meta(); 
  813. $item->set_order_id( $this->get_id() ); 
  814. $item->save(); 
  815. $this->add_item( $item ); 
  816. wc_do_deprecated_action( 'woocommerce_order_add_product', array( $this->get_id(), $item->get_id(), $product, $qty, $args ), '3.0', 'woocommerce_new_order_item action instead' ); 
  817. return $item->get_id(); 
  818.  
  819. /** 
  820. |-------------------------------------------------------------------------- 
  821. | Payment Token Handling 
  822. |-------------------------------------------------------------------------- 
  823. | 
  824. | Payment tokens are hashes used to take payments by certain gateways. 
  825. | 
  826. */ 
  827.  
  828. /** 
  829. * Add a payment token to an order 
  830. * 
  831. * @since 2.6 
  832. * @param WC_Payment_Token $token Payment token object 
  833. * @return boolean|int The new token ID or false if it failed. 
  834. */ 
  835. public function add_payment_token( $token ) { 
  836. if ( empty( $token ) || ! ( $token instanceof WC_Payment_Token ) ) { 
  837. return false; 
  838.  
  839. $token_ids = $this->data_store->get_payment_token_ids( $this ); 
  840. $token_ids[] = $token->get_id(); 
  841. $this->data_store->update_payment_token_ids( $this, $token_ids ); 
  842.  
  843. do_action( 'woocommerce_payment_token_added_to_order', $this->get_id(), $token->get_id(), $token, $token_ids ); 
  844. return $token->get_id(); 
  845.  
  846. /** 
  847. * Returns a list of all payment tokens associated with the current order 
  848. * 
  849. * @since 2.6 
  850. * @return array An array of payment token objects 
  851. */ 
  852. public function get_payment_tokens() { 
  853. return $this->data_store->get_payment_token_ids( $this ); 
  854.  
  855. /** 
  856. |-------------------------------------------------------------------------- 
  857. | Calculations. 
  858. |-------------------------------------------------------------------------- 
  859. | 
  860. | These methods calculate order totals and taxes based on the current data. 
  861. | 
  862. */ 
  863.  
  864. /** 
  865. * Calculate shipping total. 
  866. * 
  867. * @since 2.2 
  868. * @return float 
  869. */ 
  870. public function calculate_shipping() { 
  871. $shipping_total = 0; 
  872.  
  873. foreach ( $this->get_shipping_methods() as $shipping ) { 
  874. $shipping_total += $shipping->get_total(); 
  875.  
  876. $this->set_shipping_total( $shipping_total ); 
  877. $this->save(); 
  878.  
  879. return $this->get_shipping_total(); 
  880.  
  881. /** 
  882. * Get all tax classes for items in the order. 
  883. * 
  884. * @since 2.6.3 
  885. * @return array 
  886. */ 
  887. public function get_items_tax_classes() { 
  888. $found_tax_classes = array(); 
  889.  
  890. foreach ( $this->get_items() as $item ) { 
  891. if ( $_product = $item->get_product() ) { 
  892. $found_tax_classes[] = $_product->get_tax_class(); 
  893.  
  894. return array_unique( $found_tax_classes ); 
  895.  
  896. /** 
  897. * Calculate taxes for all line items and shipping, and store the totals and tax rows. 
  898. * 
  899. * Will use the base country unless customer addresses are set. 
  900. * @param $args array Added in 3.0.0 to pass things like location. 
  901. */ 
  902. public function calculate_taxes( $args = array() ) { 
  903. $tax_based_on = get_option( 'woocommerce_tax_based_on' ); 
  904. $args = wp_parse_args( $args, array( 
  905. 'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(),  
  906. 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(),  
  907. 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(),  
  908. 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(),  
  909. ) ); 
  910.  
  911. // Default to base 
  912. if ( 'base' === $tax_based_on || empty( $args['country'] ) ) { 
  913. $default = wc_get_base_location(); 
  914. $args['country'] = $default['country']; 
  915. $args['state'] = $default['state']; 
  916. $args['postcode'] = ''; 
  917. $args['city'] = ''; 
  918.  
  919. // Calc taxes for line items 
  920. foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) { 
  921. $tax_class = $item->get_tax_class(); 
  922. $tax_status = $item->get_tax_status(); 
  923.  
  924. if ( '0' !== $tax_class && 'taxable' === $tax_status ) { 
  925. $tax_rates = WC_Tax::find_rates( array( 
  926. 'country' => $args['country'],  
  927. 'state' => $args['state'],  
  928. 'postcode' => $args['postcode'],  
  929. 'city' => $args['city'],  
  930. 'tax_class' => $tax_class,  
  931. ) ); 
  932.  
  933. $total = $item->get_total(); 
  934. $taxes = WC_Tax::calc_tax( $total, $tax_rates, false ); 
  935.  
  936. if ( $item->is_type( 'line_item' ) ) { 
  937. $subtotal = $item->get_subtotal(); 
  938. $subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false ); 
  939. $item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) ); 
  940. } else { 
  941. $item->set_taxes( array( 'total' => $taxes ) ); 
  942. $item->save(); 
  943.  
  944. // Calc taxes for shipping 
  945. foreach ( $this->get_shipping_methods() as $item_id => $item ) { 
  946. $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ); 
  947.  
  948. // Inherit tax class from items 
  949. if ( 'inherit' === $shipping_tax_class ) { 
  950. $tax_rates = array(); 
  951. $tax_classes = array_merge( array( '' ), WC_Tax::get_tax_class_slugs() ); 
  952. $found_tax_classes = $this->get_items_tax_classes(); 
  953.  
  954. foreach ( $tax_classes as $tax_class ) { 
  955. if ( in_array( $tax_class, $found_tax_classes ) ) { 
  956. $tax_rates = WC_Tax::find_shipping_rates( array( 
  957. 'country' => $args['country'],  
  958. 'state' => $args['state'],  
  959. 'postcode' => $args['postcode'],  
  960. 'city' => $args['city'],  
  961. 'tax_class' => $tax_class,  
  962. ) ); 
  963. break; 
  964. } else { 
  965. $tax_rates = WC_Tax::find_shipping_rates( array( 
  966. 'country' => $args['country'],  
  967. 'state' => $args['state'],  
  968. 'postcode' => $args['postcode'],  
  969. 'city' => $args['city'],  
  970. 'tax_class' => $shipping_tax_class,  
  971. ) ); 
  972.  
  973. $item->set_taxes( array( 'total' => WC_Tax::calc_tax( $item->get_total(), $tax_rates, false ) ) ); 
  974. $item->save(); 
  975. $this->update_taxes(); 
  976.  
  977. /** 
  978. * Update tax lines for the order based on the line item taxes themselves. 
  979. */ 
  980. public function update_taxes() { 
  981. $cart_taxes = array(); 
  982. $shipping_taxes = array(); 
  983. $existing_taxes = $this->get_taxes(); 
  984. $saved_rate_ids = array(); 
  985.  
  986. foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) { 
  987. $taxes = $item->get_taxes(); 
  988. foreach ( $taxes['total'] as $tax_rate_id => $tax ) { 
  989. $cart_taxes[ $tax_rate_id ] = isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] + (float) $tax : (float) $tax; 
  990.  
  991. foreach ( $this->get_shipping_methods() as $item_id => $item ) { 
  992. $taxes = $item->get_taxes(); 
  993. foreach ( $taxes['total'] as $tax_rate_id => $tax ) { 
  994. $shipping_taxes[ $tax_rate_id ] = isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] + (float) $tax : (float) $tax; 
  995.  
  996. foreach ( $existing_taxes as $tax ) { 
  997. // Remove taxes which no longer exist for cart/shipping. 
  998. if ( ( ! array_key_exists( $tax->get_rate_id(), $cart_taxes ) && ! array_key_exists( $tax->get_rate_id(), $shipping_taxes ) ) || in_array( $tax->get_rate_id(), $saved_rate_ids ) ) { 
  999. $this->remove_item( $tax->get_id() ); 
  1000. continue; 
  1001. $saved_rate_ids[] = $tax->get_rate_id(); 
  1002. $tax->set_tax_total( isset( $cart_taxes[ $tax->get_rate_id() ] ) ? $cart_taxes[ $tax->get_rate_id() ] : 0 ); 
  1003. $tax->set_shipping_tax_total( ! empty( $shipping_taxes[ $tax->get_rate_id() ] ) ? $shipping_taxes[ $tax->get_rate_id() ] : 0 ); 
  1004. $tax->save(); 
  1005.  
  1006. $new_rate_ids = wp_parse_id_list( array_diff( array_keys( $cart_taxes + $shipping_taxes ), $saved_rate_ids ) ); 
  1007.  
  1008. // New taxes. 
  1009. foreach ( $new_rate_ids as $tax_rate_id ) { 
  1010. $item = new WC_Order_Item_Tax(); 
  1011. $item->set_rate( $tax_rate_id ); 
  1012. $item->set_tax_total( isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] : 0 ); 
  1013. $item->set_shipping_tax_total( ! empty( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 ); 
  1014. $this->add_item( $item ); 
  1015.  
  1016. // Save tax totals 
  1017. $this->set_shipping_tax( WC_Tax::round( array_sum( $shipping_taxes ) ) ); 
  1018. $this->set_cart_tax( WC_Tax::round( array_sum( $cart_taxes ) ) ); 
  1019. $this->save(); 
  1020.  
  1021. /** 
  1022. * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total. 
  1023. * 
  1024. * @since 2.2 
  1025. * @param bool $and_taxes Calc taxes if true. 
  1026. * @return float calculated grand total. 
  1027. */ 
  1028. public function calculate_totals( $and_taxes = true ) { 
  1029. $cart_subtotal = 0; 
  1030. $cart_total = 0; 
  1031. $fee_total = 0; 
  1032. $cart_subtotal_tax = 0; 
  1033. $cart_total_tax = 0; 
  1034.  
  1035. if ( $and_taxes && wc_tax_enabled() ) { 
  1036. $this->calculate_taxes(); 
  1037.  
  1038. // line items 
  1039. foreach ( $this->get_items() as $item ) { 
  1040. $cart_subtotal += $item->get_subtotal(); 
  1041. $cart_total += $item->get_total(); 
  1042. $cart_subtotal_tax += $item->get_subtotal_tax(); 
  1043. $cart_total_tax += $item->get_total_tax(); 
  1044.  
  1045. $this->calculate_shipping(); 
  1046.  
  1047. foreach ( $this->get_fees() as $item ) { 
  1048. $fee_total += $item->get_total(); 
  1049.  
  1050. $grand_total = round( $cart_total + $fee_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ); 
  1051.  
  1052. $this->set_discount_total( $cart_subtotal - $cart_total ); 
  1053. $this->set_discount_tax( $cart_subtotal_tax - $cart_total_tax ); 
  1054. $this->set_total( $grand_total ); 
  1055. $this->save(); 
  1056.  
  1057. return $grand_total; 
  1058.  
  1059. /** 
  1060. * Get item subtotal - this is the cost before discount. 
  1061. * 
  1062. * @param object $item 
  1063. * @param bool $inc_tax (default: false). 
  1064. * @param bool $round (default: true). 
  1065. * @return float 
  1066. */ 
  1067. public function get_item_subtotal( $item, $inc_tax = false, $round = true ) { 
  1068. $subtotal = 0; 
  1069.  
  1070. if ( is_callable( array( $item, 'get_subtotal' ) ) ) { 
  1071. if ( $inc_tax ) { 
  1072. $subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_quantity() ); 
  1073. } else { 
  1074. $subtotal = ( floatval( $item->get_subtotal() ) / max( 1, $item->get_quantity() ) ); 
  1075.  
  1076. $subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal; 
  1077.  
  1078. return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round ); 
  1079.  
  1080. /** 
  1081. * Get line subtotal - this is the cost before discount. 
  1082. * 
  1083. * @param object $item 
  1084. * @param bool $inc_tax (default: false). 
  1085. * @param bool $round (default: true). 
  1086. * @return float 
  1087. */ 
  1088. public function get_line_subtotal( $item, $inc_tax = false, $round = true ) { 
  1089. $subtotal = 0; 
  1090.  
  1091. if ( is_callable( array( $item, 'get_subtotal' ) ) ) { 
  1092. if ( $inc_tax ) { 
  1093. $subtotal = $item->get_subtotal() + $item->get_subtotal_tax(); 
  1094. } else { 
  1095. $subtotal = $item->get_subtotal(); 
  1096.  
  1097. $subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal; 
  1098.  
  1099. return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round ); 
  1100.  
  1101. /** 
  1102. * Calculate item cost - useful for gateways. 
  1103. * 
  1104. * @param object $item 
  1105. * @param bool $inc_tax (default: false). 
  1106. * @param bool $round (default: true). 
  1107. * @return float 
  1108. */ 
  1109. public function get_item_total( $item, $inc_tax = false, $round = true ) { 
  1110. $total = 0; 
  1111.  
  1112. if ( is_callable( array( $item, 'get_total' ) ) ) { 
  1113. if ( $inc_tax ) { 
  1114. $total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_quantity() ); 
  1115. } else { 
  1116. $total = floatval( $item->get_total() ) / max( 1, $item->get_quantity() ); 
  1117.  
  1118. $total = $round ? round( $total, wc_get_price_decimals() ) : $total; 
  1119.  
  1120. return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round ); 
  1121.  
  1122. /** 
  1123. * Calculate line total - useful for gateways. 
  1124. * 
  1125. * @param object $item 
  1126. * @param bool $inc_tax (default: false). 
  1127. * @param bool $round (default: true). 
  1128. * @return float 
  1129. */ 
  1130. public function get_line_total( $item, $inc_tax = false, $round = true ) { 
  1131. $total = 0; 
  1132.  
  1133. if ( is_callable( array( $item, 'get_total' ) ) ) { 
  1134. // Check if we need to add line tax to the line total. 
  1135. $total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total(); 
  1136.  
  1137. // Check if we need to round. 
  1138. $total = $round ? round( $total, wc_get_price_decimals() ) : $total; 
  1139.  
  1140. return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round ); 
  1141.  
  1142. /** 
  1143. * Get item tax - useful for gateways. 
  1144. * 
  1145. * @param mixed $item 
  1146. * @param bool $round (default: true). 
  1147. * @return float 
  1148. */ 
  1149. public function get_item_tax( $item, $round = true ) { 
  1150. $tax = 0; 
  1151.  
  1152. if ( is_callable( array( $item, 'get_total_tax' ) ) ) { 
  1153. $tax = $item->get_total_tax() / max( 1, $item->get_quantity() ); 
  1154. $tax = $round ? wc_round_tax_total( $tax ) : $tax; 
  1155.  
  1156. return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this ); 
  1157.  
  1158. /** 
  1159. * Get line tax - useful for gateways. 
  1160. * 
  1161. * @param mixed $item 
  1162. * @return float 
  1163. */ 
  1164. public function get_line_tax( $item ) { 
  1165. return apply_filters( 'woocommerce_order_amount_line_tax', is_callable( array( $item, 'get_total_tax' ) ) ? wc_round_tax_total( $item->get_total_tax() ) : 0, $item, $this ); 
  1166.  
  1167. /** 
  1168. * Gets line subtotal - formatted for display. 
  1169. * 
  1170. * @param array $item 
  1171. * @param string $tax_display 
  1172. * @return string 
  1173. */ 
  1174. public function get_formatted_line_subtotal( $item, $tax_display = '' ) { 
  1175. $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); 
  1176.  
  1177. if ( 'excl' == $tax_display ) { 
  1178. $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0; 
  1179.  
  1180. $subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_currency() ) ); 
  1181. } else { 
  1182. $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array( 'currency' => $this->get_currency() ) ); 
  1183.  
  1184. return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this ); 
  1185.  
  1186. /** 
  1187. * Gets order total - formatted for display. 
  1188. * @return string 
  1189. */ 
  1190. public function get_formatted_order_total() { 
  1191. $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_currency() ) ); 
  1192. return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this ); 
  1193.  
  1194. /** 
  1195. * Gets subtotal - subtotal is shown before discounts, but with localised taxes. 
  1196. * 
  1197. * @param bool $compound (default: false). 
  1198. * @param string $tax_display (default: the tax_display_cart value). 
  1199. * @return string 
  1200. */ 
  1201. public function get_subtotal_to_display( $compound = false, $tax_display = '' ) { 
  1202. $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); 
  1203. $subtotal = 0; 
  1204.  
  1205. if ( ! $compound ) { 
  1206. foreach ( $this->get_items() as $item ) { 
  1207. $subtotal += $item->get_subtotal(); 
  1208.  
  1209. if ( 'incl' === $tax_display ) { 
  1210. $subtotal += $item->get_subtotal_tax(); 
  1211.  
  1212. $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) ); 
  1213.  
  1214. if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) { 
  1215. $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; 
  1216. } else { 
  1217. if ( 'incl' === $tax_display ) { 
  1218. return ''; 
  1219.  
  1220. foreach ( $this->get_items() as $item ) { 
  1221. $subtotal += $item->get_subtotal(); 
  1222.  
  1223. // Add Shipping Costs. 
  1224. $subtotal += $this->get_shipping_total(); 
  1225.  
  1226. // Remove non-compound taxes. 
  1227. foreach ( $this->get_taxes() as $tax ) { 
  1228. if ( $tax->is_compound() ) { 
  1229. continue; 
  1230. $subtotal = $subtotal + $tax->get_tax_total() + $tax->get_shipping_tax_total(); 
  1231.  
  1232. // Remove discounts. 
  1233. $subtotal = $subtotal - $this->get_total_discount(); 
  1234. $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) ); 
  1235.  
  1236. return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this ); 
  1237.  
  1238. /** 
  1239. * Gets shipping (formatted). 
  1240. * 
  1241. * @return string 
  1242. */ 
  1243. public function get_shipping_to_display( $tax_display = '' ) { 
  1244. $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); 
  1245.  
  1246. if ( $this->get_shipping_total() != 0 ) { 
  1247.  
  1248. if ( 'excl' === $tax_display ) { 
  1249.  
  1250. // Show shipping excluding tax. 
  1251. $shipping = wc_price( $this->get_shipping_total(), array( 'currency' => $this->get_currency() ) ); 
  1252.  
  1253. if ( $this->get_shipping_tax() != 0 && $this->get_prices_include_tax() ) { 
  1254. $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $this, $tax_display ); 
  1255. } else { 
  1256.  
  1257. // Show shipping including tax. 
  1258. $shipping = wc_price( $this->get_shipping_total() + $this->get_shipping_tax(), array( 'currency' => $this->get_currency() ) ); 
  1259.  
  1260. if ( $this->get_shipping_tax() != 0 && ! $this->get_prices_include_tax() ) { 
  1261. $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display ); 
  1262.  
  1263. /** translators: %s: shipping method */ 
  1264. $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_shipped_via', ' <small class="shipped_via">' . sprintf( __( 'via %s', 'woocommerce' ), $this->get_shipping_method() ) . '</small>', $this ); 
  1265.  
  1266. } elseif ( $this->get_shipping_method() ) { 
  1267. $shipping = $this->get_shipping_method(); 
  1268. } else { 
  1269. $shipping = __( 'Free!', 'woocommerce' ); 
  1270.  
  1271. return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this ); 
  1272.  
  1273. /** 
  1274. * Get the discount amount (formatted). 
  1275. * @since 2.3.0 
  1276. * @return string 
  1277. */ 
  1278. public function get_discount_to_display( $tax_display = '' ) { 
  1279. $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); 
  1280. return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display && 'excl' === get_option( 'woocommerce_tax_display_cart' ) ), array( 'currency' => $this->get_currency() ) ), $this ); 
  1281.  
  1282. /** 
  1283. * Add total row for subtotal. 
  1284. * 
  1285. * @param array $total_rows 
  1286. * @param string $tax_display 
  1287. */ 
  1288. protected function add_order_item_totals_subtotal_row( &$total_rows, $tax_display ) { 
  1289. if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) { 
  1290. $total_rows['cart_subtotal'] = array( 
  1291. 'label' => __( 'Subtotal:', 'woocommerce' ),  
  1292. 'value' => $subtotal,  
  1293. ); 
  1294.  
  1295. /** 
  1296. * Add total row for discounts. 
  1297. * 
  1298. * @param array $total_rows 
  1299. * @param string $tax_display 
  1300. */ 
  1301. protected function add_order_item_totals_discount_row( &$total_rows, $tax_display ) { 
  1302. if ( $this->get_total_discount() > 0 ) { 
  1303. $total_rows['discount'] = array( 
  1304. 'label' => __( 'Discount:', 'woocommerce' ),  
  1305. 'value' => '-' . $this->get_discount_to_display( $tax_display ),  
  1306. ); 
  1307.  
  1308. /** 
  1309. * Add total row for shipping. 
  1310. * 
  1311. * @param array $total_rows 
  1312. * @param string $tax_display 
  1313. */ 
  1314. protected function add_order_item_totals_shipping_row( &$total_rows, $tax_display ) { 
  1315. if ( $this->get_shipping_method() ) { 
  1316. $total_rows['shipping'] = array( 
  1317. 'label' => __( 'Shipping:', 'woocommerce' ),  
  1318. 'value' => $this->get_shipping_to_display( $tax_display ),  
  1319. ); 
  1320.  
  1321. /** 
  1322. * Add total row for fees. 
  1323. * 
  1324. * @param array $total_rows 
  1325. * @param string $tax_display 
  1326. */ 
  1327. protected function add_order_item_totals_fee_rows( &$total_rows, $tax_display ) { 
  1328. if ( $fees = $this->get_fees() ) { 
  1329. foreach ( $fees as $id => $fee ) { 
  1330. if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) { 
  1331. continue; 
  1332. $total_rows[ 'fee_' . $fee->get_id() ] = array( 
  1333. 'label' => $fee->get_name() . ':',  
  1334. 'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array( 'currency' => $this->get_currency() ) ),  
  1335. ); 
  1336.  
  1337. /** 
  1338. * Add total row for taxes. 
  1339. * 
  1340. * @param array $total_rows 
  1341. * @param string $tax_display 
  1342. */ 
  1343. protected function add_order_item_totals_tax_rows( &$total_rows, $tax_display ) { 
  1344. // Tax for tax exclusive prices. 
  1345. if ( 'excl' === $tax_display ) { 
  1346. if ( 'itemized' === get_option( 'woocommerce_tax_total_display' ) ) { 
  1347. foreach ( $this->get_tax_totals() as $code => $tax ) { 
  1348. $total_rows[ sanitize_title( $code ) ] = array( 
  1349. 'label' => $tax->label . ':',  
  1350. 'value' => $tax->formatted_amount,  
  1351. ); 
  1352. } else { 
  1353. $total_rows['tax'] = array( 
  1354. 'label' => WC()->countries->tax_or_vat() . ':',  
  1355. 'value' => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_currency() ) ),  
  1356. ); 
  1357.  
  1358. /** 
  1359. * Add total row for grand total. 
  1360. * 
  1361. * @param array $total_rows 
  1362. * @param string $tax_display 
  1363. */ 
  1364. protected function add_order_item_totals_total_row( &$total_rows, $tax_display ) { 
  1365. $total_rows['order_total'] = array( 
  1366. 'label' => __( 'Total:', 'woocommerce' ),  
  1367. 'value' => $this->get_formatted_order_total( $tax_display ),  
  1368. ); 
  1369.  
  1370. /** 
  1371. * Get totals for display on pages and in emails. 
  1372. * 
  1373. * @param mixed $tax_display 
  1374. * @return array 
  1375. */ 
  1376. public function get_order_item_totals( $tax_display = '' ) { 
  1377. $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); 
  1378. $total_rows = array(); 
  1379.  
  1380. $this->add_order_item_totals_subtotal_row( $total_rows, $tax_display ); 
  1381. $this->add_order_item_totals_discount_row( $total_rows, $tax_display ); 
  1382. $this->add_order_item_totals_shipping_row( $total_rows, $tax_display ); 
  1383. $this->add_order_item_totals_fee_rows( $total_rows, $tax_display ); 
  1384. $this->add_order_item_totals_tax_rows( $total_rows, $tax_display ); 
  1385. $this->add_order_item_totals_total_row( $total_rows, $tax_display ); 
  1386.  
  1387. return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this, $tax_display ); 
  1388.  
  1389. /** 
  1390. |-------------------------------------------------------------------------- 
  1391. | Conditionals 
  1392. |-------------------------------------------------------------------------- 
  1393. | 
  1394. | Checks if a condition is true or false. 
  1395. | 
  1396. */ 
  1397.  
  1398. /** 
  1399. * Checks the order status against a passed in status. 
  1400. * 
  1401. * @return bool 
  1402. */ 
  1403. public function has_status( $status ) { 
  1404. return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status ); 
  1405.  
  1406. /** 
  1407. * Check whether this order has a specific shipping method or not. 
  1408. * 
  1409. * @param string $method_id 
  1410. * @return bool 
  1411. */ 
  1412. public function has_shipping_method( $method_id ) { 
  1413. foreach ( $this->get_shipping_methods() as $shipping_method ) { 
  1414. if ( strpos( $shipping_method->get_method_id(), $method_id ) === 0 ) { 
  1415. return true; 
  1416. return false; 
  1417.  
  1418. /** 
  1419. * Returns true if the order contains a free product. 
  1420. * @since 2.5.0 
  1421. * @return bool 
  1422. */ 
  1423. public function has_free_item() { 
  1424. foreach ( $this->get_items() as $item ) { 
  1425. if ( ! $item->get_total() ) { 
  1426. return true; 
  1427. return false; 
.