2018-03-05 11:41:04 +00:00
< ? php
2014-06-13 13:35:53 +00:00
/**
2015-11-03 13:53:50 +00:00
* Abstract Order
2014-06-13 13:35:53 +00:00
*
2016-06-21 19:03:56 +00:00
* Handles generic order data and database interaction which is extended by both
* WC_Order ( regular orders ) and WC_Order_Refund ( refunds are negative orders ) .
2014-06-13 13:35:53 +00:00
*
2016-06-21 19:03:56 +00:00
* @ class WC_Abstract_Order
2017-03-15 16:36:53 +00:00
* @ version 3.0 . 0
2020-08-05 16:36:24 +00:00
* @ package WooCommerce\Classes
2018-01-19 15:42:48 +00:00
*/
defined ( 'ABSPATH' ) || exit ;
require_once WC_ABSPATH . 'includes/legacy/abstract-wc-legacy-order.php' ;
/**
* WC_Abstract_Order class .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
2019-10-14 18:53:16 +00:00
use WC_Item_Totals ;
2015-01-20 12:07:33 +00:00
2016-06-21 19:03:56 +00:00
/**
2017-03-15 16:36:53 +00:00
* Order Data array . This is the core order data exposed in APIs since 3.0 . 0.
2016-06-21 19:03:56 +00:00
*
2016-08-24 11:34:19 +00:00
* Notes : cart_tax = cart_tax is the new name for the legacy 'order_tax'
* which is the tax for items only , not shipping .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-06-21 19:03:56 +00:00
* @ var array
*/
2016-09-09 12:34:49 +00:00
protected $data = array (
2016-06-21 19:03:56 +00:00
'parent_id' => 0 ,
2016-08-08 16:03:43 +00:00
'status' => '' ,
2016-06-21 19:03:56 +00:00
'currency' => '' ,
'version' => '' ,
'prices_include_tax' => false ,
2017-03-08 16:51:10 +00:00
'date_created' => null ,
'date_modified' => null ,
2016-06-21 19:03:56 +00:00
'discount_total' => 0 ,
'discount_tax' => 0 ,
'shipping_total' => 0 ,
'shipping_tax' => 0 ,
'cart_tax' => 0 ,
'total' => 0 ,
'total_tax' => 0 ,
);
2014-10-07 10:00:10 +00:00
2016-08-09 13:02:40 +00:00
/**
* Order items will be stored here , sometimes before they persist in the DB .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-08-09 13:02:40 +00:00
* @ var array
*/
2017-02-01 13:54:18 +00:00
protected $items = array ();
2016-08-09 13:02:40 +00:00
2016-08-17 15:53:01 +00:00
/**
* Order items that need deleting are stored here .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-08-17 15:53:01 +00:00
* @ var array
*/
2016-09-09 12:34:49 +00:00
protected $items_to_delete = array ();
2016-08-17 15:53:01 +00:00
2016-06-21 19:03:56 +00:00
/**
* Stores meta in cache for future reads .
2016-11-17 11:22:24 +00:00
*
2016-06-21 19:03:56 +00:00
* A group must be set to to enable caching .
2018-01-19 15:42:48 +00:00
*
2016-06-21 19:03:56 +00:00
* @ var string
*/
2017-03-02 17:15:39 +00:00
protected $cache_group = 'orders' ;
2015-01-20 12:07:33 +00:00
2016-11-17 16:02:09 +00:00
/**
* Which data store to load .
*
* @ var string
*/
protected $data_store_name = 'order' ;
2016-12-19 17:09:52 +00:00
/**
* This is the name of this object type .
2018-01-19 15:42:48 +00:00
*
2016-12-19 17:09:52 +00:00
* @ var string
*/
protected $object_type = 'order' ;
2014-06-13 13:35:53 +00:00
/**
* Get the order if ID is passed , otherwise the order is new and empty .
2020-06-17 15:49:40 +00:00
* This class should NOT be instantiated , but the wc_get_order function or new WC_Order_Factory
* should be used . It is possible , but the aforementioned are preferred and are the only
2014-06-14 21:36:20 +00:00
* methods that will be maintained going forward .
2015-01-20 13:20:22 +00:00
*
2016-11-17 10:53:22 +00:00
* @ param int | object | WC_Order $order Order to read .
2014-06-13 13:35:53 +00:00
*/
2016-11-17 10:53:22 +00:00
public function __construct ( $order = 0 ) {
parent :: __construct ( $order );
if ( is_numeric ( $order ) && $order > 0 ) {
$this -> set_id ( $order );
} elseif ( $order instanceof self ) {
2016-11-21 16:46:32 +00:00
$this -> set_id ( $order -> get_id () );
2016-11-17 10:53:22 +00:00
} elseif ( ! empty ( $order -> ID ) ) {
2016-11-21 16:55:36 +00:00
$this -> set_id ( $order -> ID );
2016-11-17 10:53:22 +00:00
} else {
$this -> set_object_read ( true );
2016-06-21 19:03:56 +00:00
}
2016-11-17 10:53:22 +00:00
2016-11-17 16:02:09 +00:00
$this -> data_store = WC_Data_Store :: load ( $this -> data_store_name );
2016-11-17 10:53:22 +00:00
if ( $this -> get_id () > 0 ) {
$this -> data_store -> read ( $this );
}
}
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get internal type .
*
2016-06-21 19:03:56 +00:00
* @ return string
*/
public function get_type () {
return 'shop_order' ;
2014-10-14 13:25:48 +00:00
}
2014-08-31 05:49:58 +00:00
2014-07-03 11:38:55 +00:00
/**
2016-11-17 10:53:22 +00:00
* Get all class data in array format .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-17 10:53:22 +00:00
* @ return array
2014-07-03 11:38:55 +00:00
*/
2016-11-17 10:53:22 +00:00
public function get_data () {
return array_merge (
2016-12-22 14:52:37 +00:00
array (
'id' => $this -> get_id (),
),
2016-11-17 10:53:22 +00:00
$this -> data ,
2016-06-21 19:03:56 +00:00
array (
2016-11-17 10:53:22 +00:00
'meta_data' => $this -> get_meta_data (),
'line_items' => $this -> get_items ( 'line_item' ),
'tax_lines' => $this -> get_items ( 'tax' ),
'shipping_lines' => $this -> get_items ( 'shipping' ),
'fee_lines' => $this -> get_items ( 'fee' ),
'coupon_lines' => $this -> get_items ( 'coupon' ),
2016-06-21 19:03:56 +00:00
)
);
2014-07-28 03:23:59 +00:00
}
2014-07-03 11:38:55 +00:00
2016-11-17 10:53:22 +00:00
/*
|--------------------------------------------------------------------------
| CRUD methods
|--------------------------------------------------------------------------
|
| Methods which create , read , update and delete orders from the database .
| Written in abstract fashion so that the way orders are stored can be
| changed more easily in the future .
|
| A save method is included for convenience ( chooses update or create based
| on if the order exists yet ) .
|
*/
2015-06-19 00:43:31 +00:00
2014-07-03 11:38:55 +00:00
/**
2016-06-21 19:03:56 +00:00
* Save data to the database .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-06-21 19:03:56 +00:00
* @ return int order ID
2014-07-03 11:38:55 +00:00
*/
2016-06-21 19:03:56 +00:00
public function save () {
2019-04-17 11:47:35 +00:00
if ( ! $this -> data_store ) {
return $this -> get_id ();
}
try {
/**
* Trigger action before saving to the DB . Allows you to adjust object props before save .
*
* @ param WC_Data $this The object being saved .
* @ param WC_Data_Store_WP $data_store THe data store persisting the data .
*/
2016-12-19 17:09:52 +00:00
do_action ( 'woocommerce_before_' . $this -> object_type . '_object_save' , $this , $this -> data_store );
2016-11-17 10:53:22 +00:00
if ( $this -> get_id () ) {
$this -> data_store -> update ( $this );
} else {
$this -> data_store -> create ( $this );
2016-08-09 13:02:40 +00:00
}
2019-04-17 11:47:35 +00:00
$this -> save_items ();
/**
* Trigger action after saving to the DB .
*
* @ param WC_Data $this The object being saved .
* @ param WC_Data_Store_WP $data_store THe data store persisting the data .
*/
do_action ( 'woocommerce_after_' . $this -> object_type . '_object_save' , $this , $this -> data_store );
} catch ( Exception $e ) {
2019-04-17 12:07:52 +00:00
$this -> handle_exception ( $e , __ ( 'Error saving order.' , 'woocommerce' ) );
2016-11-17 13:18:24 +00:00
}
2019-04-17 11:47:35 +00:00
2016-11-17 13:18:24 +00:00
return $this -> get_id ();
}
2019-04-17 12:07:52 +00:00
/**
* Log an error about this order is exception is encountered .
*
* @ param Exception $e Exception object .
* @ param string $message Message regarding exception thrown .
* @ since 3.7 . 0
*/
protected function handle_exception ( $e , $message = 'Error' ) {
wc_get_logger () -> error (
$message ,
array (
'order' => $this ,
'error' => $e ,
)
);
}
2016-11-17 13:18:24 +00:00
/**
* Save all order items which are part of this order .
*/
protected function save_items () {
2018-01-19 15:14:00 +00:00
$items_changed = false ;
2016-11-17 13:18:24 +00:00
foreach ( $this -> items_to_delete as $item ) {
$item -> delete ();
2018-01-19 15:14:00 +00:00
$items_changed = true ;
2016-11-17 13:18:24 +00:00
}
$this -> items_to_delete = array ();
2017-02-01 09:53:53 +00:00
// Add/save items.
2016-11-17 13:18:24 +00:00
foreach ( $this -> items as $item_group => $items ) {
if ( is_array ( $items ) ) {
2017-08-10 14:57:34 +00:00
$items = array_filter ( $items );
foreach ( $items as $item_key => $item ) {
2016-11-17 13:18:24 +00:00
$item -> set_order_id ( $this -> get_id () );
2017-08-11 15:16:09 +00:00
2016-11-17 13:18:24 +00:00
$item_id = $item -> save ();
// If ID changed (new item saved to DB)...
if ( $item_id !== $item_key ) {
$this -> items [ $item_group ][ $item_id ] = $item ;
2016-12-19 11:27:32 +00:00
2017-02-14 19:23:57 +00:00
unset ( $this -> items [ $item_group ][ $item_key ] );
2018-01-19 15:14:00 +00:00
$items_changed = true ;
2016-11-17 13:18:24 +00:00
}
}
}
2016-08-09 13:02:40 +00:00
}
2018-01-19 15:14:00 +00:00
if ( $items_changed ) {
delete_transient ( 'wc_order_' . $this -> get_id () . '_needs_processing' );
}
2016-08-09 13:02:40 +00:00
}
2016-06-21 19:03:56 +00:00
/*
|--------------------------------------------------------------------------
| Getters
|--------------------------------------------------------------------------
*/
2014-07-03 11:38:55 +00:00
2016-06-21 19:03:56 +00:00
/**
* Get parent order ID .
2016-11-17 11:22:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return integer
*/
2016-11-17 11:22:24 +00:00
public function get_parent_id ( $context = 'view' ) {
return $this -> get_prop ( 'parent_id' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
* Gets order currency .
2016-11-17 11:22:24 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_currency ( $context = 'view' ) {
return $this -> get_prop ( 'currency' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get order_version .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_version ( $context = 'view' ) {
return $this -> get_prop ( 'version' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get prices_include_tax .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return bool
*/
2016-11-17 11:22:24 +00:00
public function get_prices_include_tax ( $context = 'view' ) {
return $this -> get_prop ( 'prices_include_tax' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get date_created .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2017-03-08 16:51:10 +00:00
* @ return WC_DateTime | NULL object if the date is set or null if there is no date .
2016-06-21 19:03:56 +00:00
*/
2016-11-17 11:22:24 +00:00
public function get_date_created ( $context = 'view' ) {
return $this -> get_prop ( 'date_created' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get date_modified .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2017-03-08 16:51:10 +00:00
* @ return WC_DateTime | NULL object if the date is set or null if there is no date .
2016-06-21 19:03:56 +00:00
*/
2016-11-17 11:22:24 +00:00
public function get_date_modified ( $context = 'view' ) {
return $this -> get_prop ( 'date_modified' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
* Return the order statuses without wc - internal prefix .
2016-11-17 11:22:24 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_status ( $context = 'view' ) {
2017-03-09 13:45:33 +00:00
$status = $this -> get_prop ( 'status' , $context );
if ( empty ( $status ) && 'view' === $context ) {
// In view context, return the default status if no status has been set.
$status = apply_filters ( 'woocommerce_default_order_status' , 'pending' );
}
return $status ;
2014-07-28 03:28:25 +00:00
}
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get discount_total .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_discount_total ( $context = 'view' ) {
return $this -> get_prop ( 'discount_total' , $context );
2014-07-03 11:38:55 +00:00
}
2014-07-28 03:28:25 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get discount_tax .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
2014-07-28 03:28:25 +00:00
*/
2016-11-17 11:22:24 +00:00
public function get_discount_tax ( $context = 'view' ) {
return $this -> get_prop ( 'discount_tax' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Get shipping_total .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_shipping_total ( $context = 'view' ) {
return $this -> get_prop ( 'shipping_total' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
* Get shipping_tax .
2016-11-17 11:22:24 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return string
*/
2016-11-17 11:22:24 +00:00
public function get_shipping_tax ( $context = 'view' ) {
return $this -> get_prop ( 'shipping_tax' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
* Gets cart tax amount .
2016-11-17 11:22:24 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return float
*/
2016-11-17 11:22:24 +00:00
public function get_cart_tax ( $context = 'view' ) {
return $this -> get_prop ( 'cart_tax' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:28:25 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:22:24 +00:00
* Gets order grand total . incl . taxes . Used in gateways .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return float
*/
2016-11-17 11:22:24 +00:00
public function get_total ( $context = 'view' ) {
return $this -> get_prop ( 'total' , $context );
2014-07-28 03:28:25 +00:00
}
2014-07-03 11:38:55 +00:00
/**
2016-06-21 19:03:56 +00:00
* Get total tax amount . Alias for get_order_tax () .
2014-07-28 03:23:59 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2016-06-21 19:03:56 +00:00
* @ return float
2014-07-03 11:38:55 +00:00
*/
2016-11-17 11:22:24 +00:00
public function get_total_tax ( $context = 'view' ) {
return $this -> get_prop ( 'total_tax' , $context );
2016-06-21 19:03:56 +00:00
}
2014-07-03 11:38:55 +00:00
2016-11-17 11:22:24 +00:00
/*
|--------------------------------------------------------------------------
| Non - CRUD Getters
|--------------------------------------------------------------------------
*/
2016-06-21 19:03:56 +00:00
/**
* Gets the total discount amount .
2016-11-17 11:22:24 +00:00
*
2016-11-17 11:26:24 +00:00
* @ param bool $ex_tax Show discount excl any tax .
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_total_discount ( $ex_tax = true ) {
if ( $ex_tax ) {
$total_discount = $this -> get_discount_total ();
} else {
$total_discount = $this -> get_discount_total () + $this -> get_discount_tax ();
2014-07-03 11:38:55 +00:00
}
2016-11-17 11:22:24 +00:00
return apply_filters ( 'woocommerce_order_get_total_discount' , round ( $total_discount , WC_ROUNDING_PRECISION ), $this );
2016-06-21 19:03:56 +00:00
}
2014-07-28 03:23:59 +00:00
2016-06-21 19:03:56 +00:00
/**
* Gets order subtotal .
2018-01-19 15:42:48 +00:00
*
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_subtotal () {
2020-05-27 18:18:47 +00:00
$subtotal = round ( $this -> get_cart_subtotal_for_order (), wc_get_price_decimals () );
2019-01-27 15:38:08 +00:00
return apply_filters ( 'woocommerce_order_get_subtotal' , ( float ) $subtotal , $this );
2014-07-03 11:38:55 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Get taxes , merged by code , formatted ready for output .
2014-08-31 05:49:58 +00:00
*
2016-06-21 19:03:56 +00:00
* @ return array
2014-07-03 11:38:55 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_tax_totals () {
$tax_totals = array ();
2014-07-03 11:38:55 +00:00
2016-06-21 19:03:56 +00:00
foreach ( $this -> get_items ( 'tax' ) as $key => $ tax ) {
$code = $tax -> get_rate_code ();
2014-07-03 11:38:55 +00:00
2016-06-21 19:03:56 +00:00
if ( ! isset ( $tax_totals [ $code ] ) ) {
2018-01-19 15:42:48 +00:00
$tax_totals [ $code ] = new stdClass ();
2016-06-21 19:03:56 +00:00
$tax_totals [ $code ] -> amount = 0 ;
2016-01-08 11:42:32 +00:00
}
2016-06-21 19:03:56 +00:00
2018-01-19 15:42:48 +00:00
$tax_totals [ $code ] -> id = $key ;
$tax_totals [ $code ] -> rate_id = $tax -> get_rate_id ();
$tax_totals [ $code ] -> is_compound = $tax -> is_compound ();
$tax_totals [ $code ] -> label = $tax -> get_label ();
$tax_totals [ $code ] -> amount += ( float ) $tax -> get_tax_total () + ( float ) $tax -> get_shipping_tax_total ();
2020-06-23 14:26:20 +00:00
$tax_totals [ $code ] -> formatted_amount = wc_price ( $tax_totals [ $code ] -> amount , array ( 'currency' => $this -> get_currency () ) );
2016-01-08 11:42:32 +00:00
}
2016-06-21 19:03:56 +00:00
if ( apply_filters ( 'woocommerce_order_hide_zero_taxes' , true ) ) {
$amounts = array_filter ( wp_list_pluck ( $tax_totals , 'amount' ) );
$tax_totals = array_intersect_key ( $tax_totals , $amounts );
2014-07-28 03:28:25 +00:00
}
2016-11-17 11:22:24 +00:00
return apply_filters ( 'woocommerce_order_get_tax_totals' , $tax_totals , $this );
2014-07-28 03:28:25 +00:00
}
2017-01-31 22:55:35 +00:00
/**
* Get all valid statuses for this order
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-01-31 22:55:35 +00:00
* @ return array Internal status keys e . g . 'wc-processing'
*/
protected function get_valid_statuses () {
return array_keys ( wc_get_order_statuses () );
}
2017-04-20 15:02:10 +00:00
/**
* Get user ID . Used by orders , not other order types like refunds .
*
2018-01-19 15:42:48 +00:00
* @ param string $context View or edit context .
2017-04-20 15:02:10 +00:00
* @ return int
*/
public function get_user_id ( $context = 'view' ) {
return 0 ;
}
/**
* Get user . Used by orders , not other order types like refunds .
*
2017-04-20 15:03:40 +00:00
* @ return WP_User | false
2017-04-20 15:02:10 +00:00
*/
public function get_user () {
return false ;
}
2016-06-21 19:03:56 +00:00
/*
|--------------------------------------------------------------------------
| Setters
|--------------------------------------------------------------------------
|
| Functions for setting order data . These should not update anything in the
| database itself and should only change what is stored in the class
2017-03-15 16:36:53 +00:00
| object . However , for backwards compatibility pre 3.0 . 0 some of these
2016-06-21 19:03:56 +00:00
| setters may handle both .
*/
2014-07-03 11:38:55 +00:00
/**
2016-06-21 19:03:56 +00:00
* Set parent order ID .
2016-11-17 11:26:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-19 15:42:48 +00:00
* @ param int $value Value to set .
* @ throws WC_Data_Exception Exception thrown if parent ID does not exist or is invalid .
2014-07-03 11:38:55 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_parent_id ( $value ) {
2017-04-25 13:18:27 +00:00
if ( $value && ( $value === $this -> get_id () || ! wc_get_order ( $value ) ) ) {
2016-08-25 12:05:27 +00:00
$this -> error ( 'order_invalid_parent_id' , __ ( 'Invalid parent ID' , 'woocommerce' ) );
2016-08-23 14:25:50 +00:00
}
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'parent_id' , absint ( $value ) );
2014-07-03 11:38:55 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Set order status .
2016-11-17 11:26:24 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-06-21 19:03:56 +00:00
* @ param string $new_status Status to change the order to . No internal wc - prefix is required .
2016-08-23 14:25:50 +00:00
* @ return array details of change
2014-07-03 11:38:55 +00:00
*/
2017-02-15 12:10:31 +00:00
public function set_status ( $new_status ) {
2016-08-08 16:03:43 +00:00
$old_status = $this -> get_status ();
$new_status = 'wc-' === substr ( $new_status , 0 , 3 ) ? substr ( $new_status , 3 ) : $new_status ;
2016-05-11 15:32:51 +00:00
2017-04-04 11:39:35 +00:00
// If setting the status, ensure it's set to a valid status.
if ( true === $this -> object_read ) {
2018-01-19 15:42:48 +00:00
// Only allow valid new status.
if ( ! in_array ( 'wc-' . $new_status , $this -> get_valid_statuses (), true ) && 'trash' !== $new_status ) {
2017-04-04 11:39:35 +00:00
$new_status = 'pending' ;
}
// If the old status is set but unknown (e.g. draft) assume its pending for action usage.
2018-01-19 15:42:48 +00:00
if ( $old_status && ! in_array ( 'wc-' . $old_status , $this -> get_valid_statuses (), true ) && 'trash' !== $old_status ) {
2017-04-04 11:39:35 +00:00
$old_status = 'pending' ;
}
2015-12-23 12:50:28 +00:00
}
2014-07-03 11:38:55 +00:00
2016-11-17 12:34:39 +00:00
$this -> set_prop ( 'status' , $new_status );
2016-08-08 16:03:43 +00:00
2016-06-21 19:03:56 +00:00
return array (
'from' => $old_status ,
2016-08-27 01:46:45 +00:00
'to' => $new_status ,
2016-06-21 19:03:56 +00:00
);
2017-02-15 12:10:31 +00:00
}
2014-07-28 03:29:40 +00:00
2014-07-03 11:38:55 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set order_version .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-07-03 11:38:55 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_version ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'version' , $value );
2014-07-03 11:38:55 +00:00
}
2014-06-13 13:35:53 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set order_currency .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_currency ( $value ) {
2018-01-19 15:42:48 +00:00
if ( $value && ! in_array ( $value , array_keys ( get_woocommerce_currencies () ), true ) ) {
2016-08-25 12:05:27 +00:00
$this -> error ( 'order_invalid_currency' , __ ( 'Invalid currency code' , 'woocommerce' ) );
2016-08-17 10:44:56 +00:00
}
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'currency' , $value ? $value : get_woocommerce_currency () );
2014-06-13 13:35:53 +00:00
}
/**
2016-11-17 11:26:24 +00:00
* Set prices_include_tax .
*
2018-01-19 15:42:48 +00:00
* @ param bool $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_prices_include_tax ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'prices_include_tax' , ( bool ) $value );
2014-06-13 13:35:53 +00:00
}
/**
2016-11-17 11:26:24 +00:00
* Set date_created .
*
2017-06-06 21:30:20 +00:00
* @ 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 there is no date .
2018-01-19 15:42:48 +00:00
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2017-03-08 16:51:10 +00:00
public function set_date_created ( $date = null ) {
$this -> set_date_prop ( 'date_created' , $date );
2014-06-13 13:35:53 +00:00
}
/**
2016-11-17 11:26:24 +00:00
* Set date_modified .
*
2017-06-06 21:30:20 +00:00
* @ 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 there is no date .
2018-01-19 15:42:48 +00:00
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2017-03-08 16:51:10 +00:00
public function set_date_modified ( $date = null ) {
$this -> set_date_prop ( 'date_modified' , $date );
2014-06-13 13:35:53 +00:00
}
/**
2016-11-17 11:26:24 +00:00
* Set discount_total .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_discount_total ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'discount_total' , wc_format_decimal ( $value ) );
2014-06-13 13:35:53 +00:00
}
/**
2016-11-17 11:26:24 +00:00
* Set discount_tax .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_discount_tax ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'discount_tax' , wc_format_decimal ( $value ) );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set shipping_total .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2016-06-21 19:03:56 +00:00
*/
public function set_shipping_total ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'shipping_total' , wc_format_decimal ( $value ) );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set shipping_tax .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2016-06-21 19:03:56 +00:00
*/
public function set_shipping_tax ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'shipping_tax' , wc_format_decimal ( $value ) );
2017-01-04 18:45:45 +00:00
$this -> set_total_tax ( ( float ) $this -> get_cart_tax () + ( float ) $this -> get_shipping_tax () );
2016-06-21 19:03:56 +00:00
}
2014-06-13 13:35:53 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set cart tax .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function set_cart_tax ( $value ) {
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'cart_tax' , wc_format_decimal ( $value ) );
2017-01-04 18:45:45 +00:00
$this -> set_total_tax ( ( float ) $this -> get_cart_tax () + ( float ) $this -> get_shipping_tax () );
2016-06-21 19:03:56 +00:00
}
/**
2017-07-17 10:10:52 +00:00
* Sets order tax ( sum of cart and shipping tax ) . Used internally only .
2016-11-17 11:26:24 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2016-06-21 19:03:56 +00:00
*/
protected function set_total_tax ( $value ) {
2020-03-02 08:46:12 +00:00
// We round here because this is a total entry, as opposed to line items in other setters.
2020-06-23 14:26:20 +00:00
$this -> set_prop ( 'total_tax' , wc_format_decimal ( round ( $value , wc_get_price_decimals () ) ) );
2016-06-21 19:03:56 +00:00
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-11-17 11:26:24 +00:00
* Set total .
*
2018-01-19 15:42:48 +00:00
* @ param string $value Value to set .
2016-06-21 19:03:56 +00:00
* @ param string $deprecated Function used to set different totals based on this .
2017-05-12 08:48:46 +00:00
*
* @ return bool | void
2018-01-19 15:42:48 +00:00
* @ throws WC_Data_Exception Exception may be thrown if value is invalid .
2016-06-21 19:03:56 +00:00
*/
public function set_total ( $value , $deprecated = '' ) {
if ( $deprecated ) {
2017-03-15 16:36:53 +00:00
wc_deprecated_argument ( 'total_type' , '3.0' , 'Use dedicated total setter methods instead.' );
2016-06-21 19:03:56 +00:00
return $this -> legacy_set_total ( $value , $deprecated );
2015-11-30 15:16:23 +00:00
}
2016-11-17 11:26:24 +00:00
$this -> set_prop ( 'total' , wc_format_decimal ( $value , wc_get_price_decimals () ) );
2016-06-21 19:03:56 +00:00
}
2015-11-30 15:16:23 +00:00
2016-06-21 19:03:56 +00:00
/*
|--------------------------------------------------------------------------
| Order Item Handling
|--------------------------------------------------------------------------
|
| Order items are used for products , taxes , shipping , and fees within
| each order .
*/
2015-11-30 15:16:23 +00:00
2016-06-21 19:03:56 +00:00
/**
* Remove all line items ( products , coupons , shipping , taxes ) from the order .
2017-02-01 13:54:18 +00:00
*
2016-06-21 19:03:56 +00:00
* @ param string $type Order item type . Default null .
*/
public function remove_order_items ( $type = null ) {
if ( ! empty ( $type ) ) {
2016-11-17 12:34:39 +00:00
$this -> data_store -> delete_items ( $this , $type );
2018-01-19 15:42:48 +00:00
$group = $this -> type_to_group ( $type );
if ( $group ) {
2017-02-01 13:54:18 +00:00
unset ( $this -> items [ $group ] );
2016-08-12 15:48:48 +00:00
}
2016-06-21 19:03:56 +00:00
} else {
2016-11-17 12:34:39 +00:00
$this -> data_store -> delete_items ( $this );
2017-02-01 13:54:18 +00:00
$this -> items = array ();
2016-06-21 19:03:56 +00:00
}
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
2016-08-12 15:48:48 +00:00
* Convert a type to a types group .
2017-02-01 13:54:18 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $type type to lookup .
* @ return string
2016-06-21 19:03:56 +00:00
*/
2016-08-12 15:48:48 +00:00
protected function type_to_group ( $type ) {
2019-01-27 15:38:08 +00:00
$type_to_group = apply_filters (
'woocommerce_order_type_to_group' ,
array (
'line_item' => 'line_items' ,
'tax' => 'tax_lines' ,
'shipping' => 'shipping_lines' ,
'fee' => 'fee_lines' ,
'coupon' => 'coupon_lines' ,
)
);
2016-08-12 15:48:48 +00:00
return isset ( $type_to_group [ $type ] ) ? $type_to_group [ $type ] : '' ;
}
2016-08-09 13:15:04 +00:00
2016-08-12 15:48:48 +00:00
/**
* Return an array of items / products within this order .
2017-02-01 13:54:18 +00:00
*
2016-08-12 15:48:48 +00:00
* @ param string | array $types Types of line items to get ( array or string ) .
2018-04-12 07:10:14 +00:00
* @ return WC_Order_Item []
2016-08-12 15:48:48 +00:00
*/
public function get_items ( $types = 'line_item' ) {
2016-08-09 13:02:40 +00:00
$items = array ();
2016-08-09 13:21:54 +00:00
$types = array_filter ( ( array ) $types );
2016-08-09 13:02:40 +00:00
foreach ( $types as $type ) {
2018-01-19 15:42:48 +00:00
$group = $this -> type_to_group ( $type );
if ( $group ) {
2017-02-01 13:54:18 +00:00
if ( ! isset ( $this -> items [ $group ] ) ) {
2017-08-10 14:33:01 +00:00
$this -> items [ $group ] = array_filter ( $this -> data_store -> read_items ( $this , $type ) );
2016-08-09 13:21:54 +00:00
}
2018-01-19 15:42:48 +00:00
// Don't use array_merge here because keys are numeric.
2017-08-10 14:33:01 +00:00
$items = $items + $this -> items [ $group ];
2016-08-09 13:15:04 +00:00
}
2016-08-09 13:02:40 +00:00
}
2017-11-23 10:00:03 +00:00
return apply_filters ( 'woocommerce_order_get_items' , $items , $this , $types );
2016-08-09 13:02:40 +00:00
}
2019-10-14 18:53:16 +00:00
/**
* Return array of values for calculations .
*
* @ param string $field Field name to return .
*
* @ return array Array of values .
*/
protected function get_values_for_total ( $field ) {
$items = array_map (
function ( $item ) use ( $field ) {
2019-11-25 20:00:45 +00:00
return wc_add_number_precision ( $item [ $field ], false );
2019-10-14 18:53:16 +00:00
},
array_values ( $this -> get_items () )
);
return $items ;
}
2019-05-14 06:16:48 +00:00
/**
* Return an array of coupons within this order .
*
* @ since 3.7 . 0
* @ return WC_Order_Item_Coupon []
*/
public function get_coupons () {
return $this -> get_items ( 'coupon' );
}
2015-11-30 15:16:23 +00:00
/**
2016-06-21 19:03:56 +00:00
* Return an array of fees within this order .
2017-02-01 13:54:18 +00:00
*
2018-04-12 07:10:14 +00:00
* @ return WC_Order_item_Fee []
2015-11-30 15:16:23 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_fees () {
return $this -> get_items ( 'fee' );
2015-11-30 15:16:23 +00:00
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Return an array of taxes within this order .
2017-02-01 13:54:18 +00:00
*
2018-04-12 07:10:14 +00:00
* @ return WC_Order_Item_Tax []
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_taxes () {
return $this -> get_items ( 'tax' );
2014-06-13 13:35:53 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Return an array of shipping costs within this order .
2017-02-01 13:54:18 +00:00
*
2018-04-12 07:10:14 +00:00
* @ return WC_Order_Item_Shipping []
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_shipping_methods () {
return $this -> get_items ( 'shipping' );
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
* Gets formatted shipping method title .
2017-02-01 13:54:18 +00:00
*
2016-06-21 19:03:56 +00:00
* @ return string
*/
public function get_shipping_method () {
$names = array ();
foreach ( $this -> get_shipping_methods () as $shipping_method ) {
$names [] = $shipping_method -> get_name ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_shipping_method' , implode ( ', ' , $names ), $this );
2015-10-29 15:34:33 +00:00
}
2014-06-13 13:35:53 +00:00
2015-10-29 15:34:33 +00:00
/**
2019-05-13 12:40:54 +00:00
* Get used coupon codes only .
2017-02-01 13:54:18 +00:00
*
2019-05-13 12:40:54 +00:00
* @ since 3.7 . 0
2016-06-21 19:03:56 +00:00
* @ return array
2015-10-29 15:34:33 +00:00
*/
2019-05-14 09:19:48 +00:00
public function get_coupon_codes () {
2016-06-23 14:52:12 +00:00
$coupon_codes = array ();
2018-01-19 15:42:48 +00:00
$coupons = $this -> get_items ( 'coupon' );
if ( $coupons ) {
2016-06-23 14:52:12 +00:00
foreach ( $coupons as $coupon ) {
$coupon_codes [] = $coupon -> get_code ();
}
}
return $coupon_codes ;
2014-06-13 13:35:53 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Gets the count of order items of a certain type .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $item_type Item type to lookup .
2018-04-12 07:10:14 +00:00
* @ return int | string
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_item_count ( $item_type = '' ) {
$items = $this -> get_items ( empty ( $item_type ) ? 'line_item' : $item_type );
$count = 0 ;
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
foreach ( $items as $item ) {
2016-08-16 11:36:38 +00:00
$count += $item -> get_quantity ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_get_item_count' , $count , $item_type , $this );
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
2018-04-06 02:45:14 +00:00
* Get an order item object , based on its type .
2017-02-01 13:54:18 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-08-10 13:38:02 +00:00
* @ param int $item_id ID of item to get .
* @ param bool $load_from_db Prior to 3.2 this item was loaded direct from WC_Order_Factory , not this object . This param is here for backwards compatility with that . If false , uses the local items variable instead .
* @ return WC_Order_Item | false
2016-06-21 19:03:56 +00:00
*/
2017-08-10 13:38:02 +00:00
public function get_item ( $item_id , $load_from_db = true ) {
if ( $load_from_db ) {
return WC_Order_Factory :: get_order_item ( $item_id );
}
2017-08-10 14:33:01 +00:00
// Search for item id.
if ( $this -> items ) {
foreach ( $this -> items as $group => $items ) {
if ( isset ( $items [ $item_id ] ) ) {
return $items [ $item_id ];
}
}
}
// Load all items of type and cache.
2017-08-10 13:38:02 +00:00
$type = $this -> data_store -> get_order_item_type ( $this , $item_id );
if ( ! $type ) {
return false ;
}
$items = $this -> get_items ( $type );
return ! empty ( $items [ $item_id ] ) ? $items [ $item_id ] : false ;
2014-06-13 13:35:53 +00:00
}
2016-08-09 13:02:40 +00:00
/**
2016-08-17 15:53:01 +00:00
* Get key for where a certain item type is stored in _items .
2017-02-01 13:54:18 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-19 15:42:48 +00:00
* @ param string $item object Order item ( product , shipping , fee , coupon , tax ) .
2016-08-17 15:53:01 +00:00
* @ return string
2016-08-09 13:02:40 +00:00
*/
2016-08-17 15:53:01 +00:00
protected function get_items_key ( $item ) {
2016-08-09 13:02:40 +00:00
if ( is_a ( $item , 'WC_Order_Item_Product' ) ) {
2016-08-17 15:53:01 +00:00
return 'line_items' ;
2016-08-09 13:02:40 +00:00
} elseif ( is_a ( $item , 'WC_Order_Item_Fee' ) ) {
2016-08-17 15:53:01 +00:00
return 'fee_lines' ;
2016-08-09 13:02:40 +00:00
} elseif ( is_a ( $item , 'WC_Order_Item_Shipping' ) ) {
2016-08-17 15:53:01 +00:00
return 'shipping_lines' ;
2016-08-09 13:02:40 +00:00
} elseif ( is_a ( $item , 'WC_Order_Item_Tax' ) ) {
2016-08-17 15:53:01 +00:00
return 'tax_lines' ;
2016-08-09 13:02:40 +00:00
} elseif ( is_a ( $item , 'WC_Order_Item_Coupon' ) ) {
2016-08-17 15:53:01 +00:00
return 'coupon_lines' ;
}
2017-05-23 18:16:30 +00:00
return apply_filters ( 'woocommerce_get_items_key' , '' , $item );
2016-08-17 15:53:01 +00:00
}
/**
* Remove item from the order .
2017-02-01 13:54:18 +00:00
*
2017-11-08 12:20:49 +00:00
* @ param int $item_id Item ID to delete .
2017-05-12 08:48:46 +00:00
* @ return false | void
2016-08-17 15:53:01 +00:00
*/
public function remove_item ( $item_id ) {
2018-01-19 15:42:48 +00:00
$item = $this -> get_item ( $item_id , false );
$items_key = $item ? $this -> get_items_key ( $item ) : false ;
2016-08-17 15:53:01 +00:00
2018-01-19 15:42:48 +00:00
if ( ! $items_key ) {
2016-08-17 15:53:01 +00:00
return false ;
}
2017-11-08 12:20:49 +00:00
// Unset and remove later.
2016-09-09 12:34:49 +00:00
$this -> items_to_delete [] = $item ;
unset ( $this -> items [ $items_key ][ $item -> get_id () ] );
2016-08-17 15:53:01 +00:00
}
/**
* Adds an order item to this order . The order item will not persist until save .
2017-02-01 13:54:18 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-19 15:42:48 +00:00
* @ param WC_Order_Item $item Order item object ( product , shipping , fee , coupon , tax ) .
2017-08-10 14:33:01 +00:00
* @ return false | void
2016-08-17 15:53:01 +00:00
*/
public function add_item ( $item ) {
2018-01-19 15:42:48 +00:00
$items_key = $this -> get_items_key ( $item );
if ( ! $items_key ) {
2016-08-09 13:02:40 +00:00
return false ;
}
// Make sure existing items are loaded so we can append this new one.
2017-02-01 13:54:18 +00:00
if ( ! isset ( $this -> items [ $items_key ] ) ) {
2016-09-09 12:34:49 +00:00
$this -> items [ $items_key ] = $this -> get_items ( $item -> get_type () );
2016-08-09 13:02:40 +00:00
}
2016-11-23 13:11:51 +00:00
// Set parent.
$item -> set_order_id ( $this -> get_id () );
2017-08-10 14:33:01 +00:00
// Append new row with generated temporary ID.
2018-01-19 15:42:48 +00:00
$item_id = $item -> get_id ();
if ( $item_id ) {
2017-02-01 09:53:53 +00:00
$this -> items [ $items_key ][ $item_id ] = $item ;
2016-08-15 10:02:25 +00:00
} else {
2018-01-19 15:42:48 +00:00
$this -> items [ $items_key ][ 'new:' . $items_key . count ( $this -> items [ $items_key ] ) ] = $item ;
2016-08-15 10:02:25 +00:00
}
2016-08-09 13:02:40 +00:00
}
2019-07-09 21:07:44 +00:00
/**
* Check and records coupon usage tentatively so that counts validation is correct . Display an error if coupon usage limit has been reached .
*
* If you are using this method , make sure to `release_held_coupons` in case an Exception is thrown .
*
* @ throws Exception When not able to apply coupon .
*
* @ param string $billing_email Billing email of order .
*/
public function hold_applied_coupons ( $billing_email ) {
$held_keys = array ();
$held_keys_for_user = array ();
$error = null ;
try {
foreach ( WC () -> cart -> get_applied_coupons () as $code ) {
$coupon = new WC_Coupon ( $code );
if ( ! $coupon -> get_data_store () ) {
continue ;
}
// Hold coupon for when global coupon usage limit is present.
if ( 0 < $coupon -> get_usage_limit () ) {
$held_key = $this -> hold_coupon ( $coupon );
if ( $held_key ) {
$held_keys [ $coupon -> get_id () ] = $held_key ;
}
}
// Hold coupon for when usage limit per customer is enabled.
if ( 0 < $coupon -> get_usage_limit_per_user () ) {
if ( ! isset ( $user_ids_and_emails ) ) {
$user_alias = get_current_user_id () ? wp_get_current_user () -> ID : sanitize_email ( $billing_email );
$user_ids_and_emails = $this -> get_billing_and_current_user_aliases ( $billing_email );
}
$held_key_for_user = $this -> hold_coupon_for_users ( $coupon , $user_ids_and_emails , $user_alias );
if ( $held_key_for_user ) {
$held_keys_for_user [ $coupon -> get_id () ] = $held_key_for_user ;
}
}
}
} catch ( Exception $e ) {
$error = $e ;
} finally {
// Even in case of error, we will save keys for whatever coupons that were held so our data remains accurate.
// We save them in bulk instead of one by one for performance reasons.
if ( 0 < count ( $held_keys_for_user ) || 0 < count ( $held_keys ) ) {
$this -> get_data_store () -> set_coupon_held_keys ( $this , $held_keys , $held_keys_for_user );
}
if ( $error instanceof Exception ) {
throw $error ;
}
}
}
/**
* Hold coupon if a global usage limit is defined .
*
* @ param WC_Coupon $coupon Coupon object .
*
* @ return string Meta key which indicates held coupon .
* @ throws Exception When can ' t be held .
*/
private function hold_coupon ( $coupon ) {
$result = $coupon -> get_data_store () -> check_and_hold_coupon ( $coupon );
if ( false === $result ) {
// translators: Actual coupon code.
2020-02-26 11:24:04 +00:00
throw new Exception ( sprintf ( __ ( 'An unexpected error happened while applying the Coupon %s.' , 'woocommerce' ), esc_html ( $coupon -> get_code () ) ) );
2019-07-09 21:07:44 +00:00
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
2020-02-26 11:24:04 +00:00
throw new Exception ( sprintf ( __ ( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.' , 'woocommerce' ), esc_html ( $coupon -> get_code () ) ) );
2019-07-09 21:07:44 +00:00
}
return $result ;
}
/**
* Hold coupon if usage limit per customer is defined .
*
* @ param WC_Coupon $coupon Coupon object .
* @ param array $user_ids_and_emails Array of user Id and emails to check for usage limit .
* @ param string $user_alias User ID or email to use to record current usage .
*
* @ return string Meta key which indicates held coupon .
* @ throws Exception When coupon can ' t be held .
*/
private function hold_coupon_for_users ( $coupon , $user_ids_and_emails , $user_alias ) {
$result = $coupon -> get_data_store () -> check_and_hold_coupon_for_user ( $coupon , $user_ids_and_emails , $user_alias );
if ( false === $result ) {
// translators: Actual coupon code.
2020-02-26 11:24:04 +00:00
throw new Exception ( sprintf ( __ ( 'An unexpected error happened while applying the Coupon %s.' , 'woocommerce' ), esc_html ( $coupon -> get_code () ) ) );
2019-07-09 21:07:44 +00:00
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
2020-02-26 11:24:04 +00:00
throw new Exception ( sprintf ( __ ( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.' , 'woocommerce' ), esc_html ( $coupon -> get_code () ) ) );
2019-07-09 21:07:44 +00:00
}
return $result ;
}
/**
* Helper method to get all aliases for current user and provide billing email .
*
* @ param string $billing_email Billing email provided in form .
*
* @ return array Array of all aliases .
* @ throws Exception When validation fails .
*/
private function get_billing_and_current_user_aliases ( $billing_email ) {
$emails = array ( $billing_email );
if ( get_current_user_id () ) {
2020-04-07 10:28:30 +00:00
$emails [] = wp_get_current_user () -> user_email ;
2019-07-09 21:07:44 +00:00
}
2020-04-07 10:28:30 +00:00
$emails = array_unique (
2019-07-09 21:07:44 +00:00
array_map ( 'strtolower' , array_map ( 'sanitize_email' , $emails ) )
);
$customer_data_store = WC_Data_Store :: load ( 'customer' );
2020-04-07 10:28:30 +00:00
$user_ids = $customer_data_store -> get_user_ids_for_billing_email ( $emails );
2019-07-09 21:07:44 +00:00
return array_merge ( $user_ids , $emails );
}
2017-08-10 13:22:27 +00:00
/**
* Apply a coupon to the order and recalculate totals .
*
* @ since 3.2 . 0
2017-08-23 13:25:41 +00:00
* @ param string | WC_Coupon $raw_coupon Coupon code or object .
2017-08-10 13:22:27 +00:00
* @ return true | WP_Error True if applied , error if not .
*/
2017-08-23 13:25:41 +00:00
public function apply_coupon ( $raw_coupon ) {
if ( is_a ( $raw_coupon , 'WC_Coupon' ) ) {
$coupon = $raw_coupon ;
} elseif ( is_string ( $raw_coupon ) ) {
2018-01-19 15:42:48 +00:00
$code = wc_format_coupon_code ( $raw_coupon );
$coupon = new WC_Coupon ( $code );
2017-08-10 13:22:27 +00:00
2017-09-27 11:54:04 +00:00
if ( $coupon -> get_code () !== $code ) {
2017-08-10 13:22:27 +00:00
return new WP_Error ( 'invalid_coupon' , __ ( 'Invalid coupon code' , 'woocommerce' ) );
}
2017-08-23 13:25:41 +00:00
} else {
return new WP_Error ( 'invalid_coupon' , __ ( 'Invalid coupon' , 'woocommerce' ) );
2017-08-10 13:22:27 +00:00
}
2017-08-10 18:14:01 +00:00
// Check to make sure coupon is not already applied.
$applied_coupons = $this -> get_items ( 'coupon' );
foreach ( $applied_coupons as $applied_coupon ) {
if ( $applied_coupon -> get_code () === $coupon -> get_code () ) {
return new WP_Error ( 'invalid_coupon' , __ ( 'Coupon code already applied!' , 'woocommerce' ) );
}
}
2017-08-10 13:22:27 +00:00
$discounts = new WC_Discounts ( $this );
2017-08-18 09:36:10 +00:00
$applied = $discounts -> apply_coupon ( $coupon );
2017-08-10 13:22:27 +00:00
if ( is_wp_error ( $applied ) ) {
return $applied ;
}
2019-06-06 20:37:08 +00:00
$data_store = $coupon -> get_data_store ();
2019-05-27 14:45:29 +00:00
// Check specific for guest checkouts here as well since WC_Cart handles that seperately in check_customer_coupons.
2019-06-06 20:37:08 +00:00
if ( $data_store && 0 === $this -> get_customer_id () ) {
2019-05-27 14:45:29 +00:00
$usage_count = $data_store -> get_usage_by_email ( $coupon , $this -> get_billing_email () );
2019-06-03 13:47:32 +00:00
if ( 0 < $coupon -> get_usage_limit_per_user () && $usage_count >= $coupon -> get_usage_limit_per_user () ) {
2019-05-27 14:45:29 +00:00
return new WP_Error (
'invalid_coupon' ,
$coupon -> get_coupon_error ( 106 ),
array (
'status' => 400 ,
)
);
}
}
2017-08-14 14:57:51 +00:00
$this -> set_coupon_discount_amounts ( $discounts );
2019-01-27 03:57:32 +00:00
$this -> save ();
2017-08-10 13:22:27 +00:00
2017-08-14 14:57:51 +00:00
// Recalculate totals and taxes.
2019-01-27 03:57:32 +00:00
$this -> recalculate_coupons ();
2017-08-14 14:57:51 +00:00
// Record usage so counts and validation is correct.
2018-01-19 15:42:48 +00:00
$used_by = $this -> get_user_id ();
if ( ! $used_by ) {
2017-08-14 14:57:51 +00:00
$used_by = $this -> get_billing_email ();
2017-08-09 15:19:10 +00:00
}
2017-08-09 17:53:06 +00:00
2017-08-14 14:57:51 +00:00
$coupon -> increase_usage_count ( $used_by );
2017-08-10 13:22:27 +00:00
return true ;
2017-08-09 15:19:10 +00:00
}
2017-08-09 21:55:35 +00:00
/**
* Remove a coupon from the order and recalculate totals .
*
2017-08-10 10:38:09 +00:00
* Coupons affect line item totals , but there is no relationship between
* coupon and line total , so to remove a coupon we need to work from the
* line subtotal ( price before discount ) and re - apply all coupons in this
* order .
*
* Manual discounts are not affected ; those are separate and do not affect
* stored line totals .
*
2017-08-10 13:38:02 +00:00
* @ since 3.2 . 0
* @ param string $code Coupon code .
2017-08-09 21:55:35 +00:00
* @ return void
*/
public function remove_coupon ( $code ) {
2017-08-10 10:38:09 +00:00
$coupons = $this -> get_items ( 'coupon' );
// Remove the coupon line.
foreach ( $coupons as $item_id => $coupon ) {
if ( $coupon -> get_code () === $code ) {
$this -> remove_item ( $item_id );
2017-08-10 18:27:13 +00:00
$coupon_object = new WC_Coupon ( $code );
$coupon_object -> decrease_usage_count ( $this -> get_user_id () );
2017-08-25 15:05:01 +00:00
$this -> recalculate_coupons ();
2017-08-10 10:38:09 +00:00
break ;
}
}
}
/**
* Apply all coupons in this order again to all line items .
2019-10-01 20:19:33 +00:00
* This method is public since WooCommerce 3.8 . 0.
2017-08-10 10:38:09 +00:00
*
2019-10-01 20:19:33 +00:00
* @ since 3.2 . 0
2017-08-10 10:38:09 +00:00
*/
2019-10-01 20:19:33 +00:00
public function recalculate_coupons () {
2017-08-14 14:57:51 +00:00
// Reset line item totals.
foreach ( $this -> get_items () as $item ) {
$item -> set_total ( $item -> get_subtotal () );
$item -> set_total_tax ( $item -> get_subtotal_tax () );
}
2017-08-10 10:38:09 +00:00
2017-08-24 16:08:45 +00:00
$discounts = new WC_Discounts ( $this );
foreach ( $this -> get_items ( 'coupon' ) as $coupon_item ) {
2018-01-19 15:42:48 +00:00
$coupon_code = $coupon_item -> get_code ();
$coupon_id = wc_get_coupon_id_by_code ( $coupon_code );
2017-08-24 16:08:45 +00:00
2017-08-25 13:42:56 +00:00
// If we have a coupon ID (loaded via wc_get_coupon_id_by_code) we can simply load the new coupon object using the ID.
2017-08-24 16:08:45 +00:00
if ( $coupon_id ) {
$coupon_object = new WC_Coupon ( $coupon_id );
2017-08-25 13:42:56 +00:00
2017-08-25 17:21:27 +00:00
} else {
2017-08-25 15:05:01 +00:00
// If we do not have a coupon ID (was it virtual? has it been deleted?) we must create a temporary coupon using what data we have stored during checkout.
2017-08-24 16:08:45 +00:00
$coupon_object = new WC_Coupon ();
2017-08-25 17:21:27 +00:00
$coupon_object -> set_props ( ( array ) $coupon_item -> get_meta ( 'coupon_data' , true ) );
2017-08-24 16:08:45 +00:00
$coupon_object -> set_code ( $coupon_code );
2017-08-25 15:07:07 +00:00
$coupon_object -> set_virtual ( true );
2017-08-24 16:08:45 +00:00
2017-08-25 13:42:56 +00:00
// If there is no coupon amount (maybe dynamic?), set it to the given **discount** amount so the coupon's same value is applied.
2017-08-24 16:08:45 +00:00
if ( ! $coupon_object -> get_amount () ) {
2017-08-25 17:21:27 +00:00
// If the order originally had prices including tax, remove the discount + discount tax.
if ( $this -> get_prices_include_tax () ) {
$coupon_object -> set_amount ( $coupon_item -> get_discount () + $coupon_item -> get_discount_tax () );
} else {
$coupon_object -> set_amount ( $coupon_item -> get_discount () );
}
2017-08-24 16:08:45 +00:00
$coupon_object -> set_discount_type ( 'fixed_cart' );
}
}
/**
* Allow developers to filter this coupon before it get ' s re - applied to the order .
*
* @ since 3.2 . 0
*/
$coupon_object = apply_filters ( 'woocommerce_order_recalculate_coupons_coupon_object' , $coupon_object , $coupon_code , $coupon_item , $this );
if ( $coupon_object ) {
2017-09-06 10:26:34 +00:00
$discounts -> apply_coupon ( $coupon_object , false );
2017-08-24 16:08:45 +00:00
}
}
$this -> set_coupon_discount_amounts ( $discounts );
2017-08-25 17:21:27 +00:00
$this -> set_item_discount_amounts ( $discounts );
// Recalculate totals and taxes.
$this -> calculate_totals ( true );
}
2017-08-24 16:08:45 +00:00
2017-08-25 17:21:27 +00:00
/**
2018-04-12 07:10:14 +00:00
* After applying coupons via the WC_Discounts class , update line items .
2017-08-25 17:21:27 +00:00
*
* @ since 3.2 . 0
* @ param WC_Discounts $discounts Discounts class .
*/
protected function set_item_discount_amounts ( $discounts ) {
2018-01-19 15:42:48 +00:00
$item_discounts = $discounts -> get_discounts_by_item ();
if ( $item_discounts ) {
2017-08-10 11:06:03 +00:00
foreach ( $item_discounts as $item_id => $amount ) {
2017-08-10 13:38:02 +00:00
$item = $this -> get_item ( $item_id , false );
2017-08-25 17:21:27 +00:00
// If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart.
2019-11-21 10:28:22 +00:00
if ( $this -> get_prices_include_tax () && wc_tax_enabled () && 'taxable' === $item -> get_tax_status () ) {
2019-01-11 13:06:26 +00:00
$taxes = WC_Tax :: calc_tax ( $amount , WC_Tax :: get_rates ( $item -> get_tax_class () ), true );
2019-11-21 10:28:22 +00:00
// Use unrounded taxes so totals will be re-calculated accurately, like in cart.
2019-01-11 13:06:26 +00:00
$amount = $amount - array_sum ( $taxes );
2017-08-25 17:21:27 +00:00
}
2019-11-21 10:28:22 +00:00
$item -> set_total ( max ( 0 , $item -> get_total () - $amount ) );
2017-08-10 11:06:03 +00:00
}
}
2017-08-14 14:57:51 +00:00
}
/**
2018-04-12 07:10:14 +00:00
* After applying coupons via the WC_Discounts class , update or create coupon items .
2017-08-14 14:57:51 +00:00
*
* @ since 3.2 . 0
* @ param WC_Discounts $discounts Discounts class .
*/
protected function set_coupon_discount_amounts ( $discounts ) {
$coupons = $this -> get_items ( 'coupon' );
$coupon_code_to_id = wc_list_pluck ( $coupons , 'get_id' , 'get_code' );
$all_discounts = $discounts -> get_discounts ();
$coupon_discounts = $discounts -> get_discounts_by_coupon ();
2017-08-10 11:06:03 +00:00
if ( $coupon_discounts ) {
foreach ( $coupon_discounts as $coupon_code => $amount ) {
2017-08-14 14:57:51 +00:00
$item_id = isset ( $coupon_code_to_id [ $coupon_code ] ) ? $coupon_code_to_id [ $coupon_code ] : 0 ;
if ( ! $item_id ) {
$coupon_item = new WC_Order_Item_Coupon ();
$coupon_item -> set_code ( $coupon_code );
} else {
$coupon_item = $this -> get_item ( $item_id , false );
}
2017-08-25 17:21:27 +00:00
$discount_tax = 0 ;
2017-08-14 14:57:51 +00:00
// Work out how much tax has been removed as a result of the discount from this coupon.
2017-08-25 17:21:27 +00:00
foreach ( $all_discounts [ $coupon_code ] as $item_id => $item_discount_amount ) {
$item = $this -> get_item ( $item_id , false );
2020-04-07 10:28:30 +00:00
if ( 'taxable' !== $item -> get_tax_status () || ! wc_tax_enabled () ) {
2019-11-21 10:28:22 +00:00
continue ;
}
2019-01-11 13:09:20 +00:00
2019-11-21 10:28:22 +00:00
$taxes = array_sum ( WC_Tax :: calc_tax ( $item_discount_amount , WC_Tax :: get_rates ( $item -> get_tax_class () ), $this -> get_prices_include_tax () ) );
if ( 'yes' !== get_option ( 'woocommerce_tax_round_at_subtotal' ) ) {
$taxes = wc_round_tax_total ( $taxes );
}
2019-01-11 13:09:20 +00:00
2019-11-21 10:28:22 +00:00
$discount_tax += $taxes ;
2019-01-11 13:09:20 +00:00
2019-11-21 10:28:22 +00:00
if ( $this -> get_prices_include_tax () ) {
$amount = $amount - $taxes ;
2017-08-14 14:57:51 +00:00
}
}
2017-08-25 17:21:27 +00:00
$coupon_item -> set_discount ( $amount );
$coupon_item -> set_discount_tax ( $discount_tax );
2017-08-14 14:57:51 +00:00
$this -> add_item ( $coupon_item );
2017-08-09 21:55:35 +00:00
}
}
}
2016-08-22 17:00:52 +00:00
/**
* Add a product line item to the order . This is the only line item type with
2018-04-06 02:45:14 +00:00
* its own method because it saves looking up order amounts ( costs are added up for you ) .
2017-02-01 13:54:18 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param WC_Product $product Product object .
* @ param int $qty Quantity to add .
* @ param array $args Args for the added product .
* @ return int
* @ throws WC_Data_Exception Exception thrown if the item cannot be added to the cart .
2016-08-22 17:00:52 +00:00
*/
public function add_product ( $product , $qty = 1 , $args = array () ) {
if ( $product ) {
$default_args = array (
2016-11-16 12:17:00 +00:00
'name' => $product -> get_name (),
2016-08-22 17:00:52 +00:00
'tax_class' => $product -> get_tax_class (),
2016-11-09 12:26:46 +00:00
'product_id' => $product -> is_type ( 'variation' ) ? $product -> get_parent_id () : $product -> get_id (),
'variation_id' => $product -> is_type ( 'variation' ) ? $product -> get_id () : 0 ,
'variation' => $product -> is_type ( 'variation' ) ? $product -> get_attributes () : array (),
2016-11-11 18:30:18 +00:00
'subtotal' => wc_get_price_excluding_tax ( $product , array ( 'qty' => $qty ) ),
'total' => wc_get_price_excluding_tax ( $product , array ( 'qty' => $qty ) ),
2016-08-22 17:00:52 +00:00
'quantity' => $qty ,
);
} else {
$default_args = array (
2018-01-19 15:42:48 +00:00
'quantity' => $qty ,
2016-08-22 17:00:52 +00:00
);
}
$args = wp_parse_args ( $args , $default_args );
2018-01-19 15:42:48 +00:00
// BW compatibility with old args.
2016-08-22 17:00:52 +00:00
if ( isset ( $args [ 'totals' ] ) ) {
foreach ( $args [ 'totals' ] as $key => $value ) {
if ( 'tax' === $key ) {
$args [ 'total_tax' ] = $value ;
} elseif ( 'tax_data' === $key ) {
$args [ 'taxes' ] = $value ;
} else {
$args [ $key ] = $value ;
}
}
}
2016-11-17 21:30:34 +00:00
$item = new WC_Order_Item_Product ();
$item -> set_props ( $args );
2016-08-22 17:00:52 +00:00
$item -> set_backorder_meta ();
$item -> set_order_id ( $this -> get_id () );
$item -> save ();
$this -> add_item ( $item );
2017-03-15 16:36:53 +00:00
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' );
2018-06-05 10:28:45 +00:00
delete_transient ( 'wc_order_' . $this -> get_id () . '_needs_processing' );
2016-08-22 17:00:52 +00:00
return $item -> get_id ();
}
2016-08-15 10:02:25 +00:00
/*
|--------------------------------------------------------------------------
| Payment Token Handling
|--------------------------------------------------------------------------
|
| Payment tokens are hashes used to take payments by certain gateways .
|
*/
2015-05-01 11:42:29 +00:00
/**
2016-06-21 19:03:56 +00:00
* Add a payment token to an order
*
* @ since 2.6
2018-01-19 15:42:48 +00:00
* @ param WC_Payment_Token $token Payment token object .
2016-06-21 19:03:56 +00:00
* @ return boolean | int The new token ID or false if it failed .
2015-05-01 11:42:29 +00:00
*/
2016-06-21 19:03:56 +00:00
public function add_payment_token ( $token ) {
if ( empty ( $token ) || ! ( $token instanceof WC_Payment_Token ) ) {
return false ;
}
2015-05-01 11:42:29 +00:00
2016-11-17 12:34:39 +00:00
$token_ids = $this -> data_store -> get_payment_token_ids ( $this );
2016-06-21 19:03:56 +00:00
$token_ids [] = $token -> get_id ();
2016-11-17 12:34:39 +00:00
$this -> data_store -> update_payment_token_ids ( $this , $token_ids );
2016-06-21 19:03:56 +00:00
do_action ( 'woocommerce_payment_token_added_to_order' , $this -> get_id (), $token -> get_id (), $token , $token_ids );
return $token -> get_id ();
2015-05-01 11:42:29 +00:00
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Returns a list of all payment tokens associated with the current order
2014-08-31 05:49:58 +00:00
*
2016-06-21 19:03:56 +00:00
* @ since 2.6
* @ return array An array of payment token objects
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_payment_tokens () {
2016-11-18 20:27:59 +00:00
return $this -> data_store -> get_payment_token_ids ( $this );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/*
|--------------------------------------------------------------------------
| Calculations .
|--------------------------------------------------------------------------
|
| These methods calculate order totals and taxes based on the current data .
|
*/
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Calculate shipping total .
2014-06-13 13:35:53 +00:00
*
2016-06-21 19:03:56 +00:00
* @ since 2.2
* @ return float
*/
public function calculate_shipping () {
$shipping_total = 0 ;
2015-04-23 10:50:09 +00:00
2016-06-21 19:03:56 +00:00
foreach ( $this -> get_shipping_methods () as $shipping ) {
$shipping_total += $shipping -> get_total ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
$this -> set_shipping_total ( $shipping_total );
$this -> save ();
return $this -> get_shipping_total ();
2014-06-13 13:35:53 +00:00
}
2016-08-03 11:32:27 +00:00
/**
* Get all tax classes for items in the order .
*
* @ since 2.6 . 3
* @ return array
*/
public function get_items_tax_classes () {
$found_tax_classes = array ();
2015-04-23 10:50:09 +00:00
2016-08-03 11:32:27 +00:00
foreach ( $this -> get_items () as $item ) {
2017-10-30 13:23:15 +00:00
if ( is_callable ( array ( $item , 'get_tax_status' ) ) && in_array ( $item -> get_tax_status (), array ( 'taxable' , 'shipping' ), true ) ) {
$found_tax_classes [] = $item -> get_tax_class ();
2016-08-03 11:32:27 +00:00
}
2014-06-13 13:35:53 +00:00
}
2017-08-10 13:22:27 +00:00
return array_unique ( $found_tax_classes );
}
2014-06-13 13:35:53 +00:00
/**
2017-07-27 09:49:47 +00:00
* Get tax location for this order .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ since 3.2 . 0
* @ param array $args array Override the location .
2017-07-27 09:49:47 +00:00
* @ return array
2016-06-21 19:03:56 +00:00
*/
2017-07-27 09:49:47 +00:00
protected function get_tax_location ( $args = array () ) {
2016-11-17 12:34:39 +00:00
$tax_based_on = get_option ( 'woocommerce_tax_based_on' );
2017-06-01 19:51:55 +00:00
if ( 'shipping' === $tax_based_on && ! $this -> get_shipping_country () ) {
$tax_based_on = 'billing' ;
}
2019-01-27 15:38:08 +00:00
$args = wp_parse_args (
$args ,
array (
'country' => 'billing' === $tax_based_on ? $this -> get_billing_country () : $this -> get_shipping_country (),
'state' => 'billing' === $tax_based_on ? $this -> get_billing_state () : $this -> get_shipping_state (),
'postcode' => 'billing' === $tax_based_on ? $this -> get_billing_postcode () : $this -> get_shipping_postcode (),
'city' => 'billing' === $tax_based_on ? $this -> get_billing_city () : $this -> get_shipping_city (),
)
);
2016-06-21 19:03:56 +00:00
2018-01-19 15:42:48 +00:00
// Default to base.
2016-06-21 19:03:56 +00:00
if ( 'base' === $tax_based_on || empty ( $args [ 'country' ] ) ) {
2019-05-15 05:43:15 +00:00
$args [ 'country' ] = WC () -> countries -> get_base_country ();
$args [ 'state' ] = WC () -> countries -> get_base_state ();
2019-05-14 03:43:57 +00:00
$args [ 'postcode' ] = WC () -> countries -> get_base_postcode ();
$args [ 'city' ] = WC () -> countries -> get_base_city ();
2014-09-17 11:24:05 +00:00
}
2020-02-21 10:45:06 +00:00
return apply_filters ( 'woocommerce_order_get_tax_location' , $args , $this );
2017-07-27 09:49:47 +00:00
}
/**
* Calculate taxes for all line items and shipping , and store the totals and tax rows .
*
* If by default the taxes are based on the shipping address and the current order doesn ' t
* have any , it would use the billing address rather than using the Shopping base location .
*
* Will use the base country unless customer addresses are set .
*
* @ param array $args Added in 3.0 . 0 to pass things like location .
*/
public function calculate_taxes ( $args = array () ) {
2017-09-27 15:07:02 +00:00
do_action ( 'woocommerce_order_before_calculate_taxes' , $args , $this );
2017-07-27 09:49:47 +00:00
$calculate_tax_for = $this -> get_tax_location ( $args );
$shipping_tax_class = get_option ( 'woocommerce_shipping_tax_class' );
if ( 'inherit' === $shipping_tax_class ) {
2018-01-19 15:42:48 +00:00
$found_classes = array_intersect ( array_merge ( array ( '' ), WC_Tax :: get_tax_class_slugs () ), $this -> get_items_tax_classes () );
$shipping_tax_class = count ( $found_classes ) ? current ( $found_classes ) : false ;
2017-07-27 09:49:47 +00:00
}
2018-06-04 10:01:15 +00:00
$is_vat_exempt = apply_filters ( 'woocommerce_order_is_vat_exempt' , 'yes' === $this -> get_meta ( 'is_vat_exempt' ), $this );
2017-09-27 17:56:11 +00:00
2017-07-27 09:49:47 +00:00
// Trigger tax recalculation for all items.
2017-08-18 09:36:10 +00:00
foreach ( $this -> get_items ( array ( 'line_item' , 'fee' ) ) as $item_id => $item ) {
2017-10-31 13:37:57 +00:00
if ( ! $is_vat_exempt ) {
$item -> calculate_taxes ( $calculate_tax_for );
} else {
$item -> set_taxes ( false );
}
2015-12-07 10:12:17 +00:00
}
2014-07-28 03:23:59 +00:00
2017-08-10 15:33:21 +00:00
foreach ( $this -> get_shipping_methods () as $item_id => $item ) {
2017-10-31 13:37:57 +00:00
if ( false !== $shipping_tax_class && ! $is_vat_exempt ) {
2017-10-30 13:23:15 +00:00
$item -> calculate_taxes ( array_merge ( $calculate_tax_for , array ( 'tax_class' => $shipping_tax_class ) ) );
} else {
$item -> set_taxes ( false );
}
2016-02-29 12:33:09 +00:00
}
2017-07-27 09:49:47 +00:00
2016-06-21 19:03:56 +00:00
$this -> update_taxes ();
}
2014-09-09 17:00:37 +00:00
2019-12-19 04:58:39 +00:00
/**
* Calculate fees for all line items .
2019-12-19 06:08:39 +00:00
*
* @ return float Fee total .
2019-12-19 04:58:39 +00:00
*/
2019-12-19 06:19:34 +00:00
public function get_total_fees () {
2020-01-30 16:09:33 +00:00
return array_reduce (
$this -> get_fees (),
function ( $carry , $item ) {
return $carry + $item -> get_total ();
}
);
2019-12-19 04:58:39 +00:00
}
2016-06-21 19:03:56 +00:00
/**
* Update tax lines for the order based on the line item taxes themselves .
*/
public function update_taxes () {
$cart_taxes = array ();
$shipping_taxes = array ();
2017-02-23 14:14:50 +00:00
$existing_taxes = $this -> get_taxes ();
$saved_rate_ids = array ();
2014-06-13 13:35:53 +00:00
2017-08-18 09:36:10 +00:00
foreach ( $this -> get_items ( array ( 'line_item' , 'fee' ) ) as $item_id => $item ) {
2016-06-21 19:03:56 +00:00
$taxes = $item -> get_taxes ();
foreach ( $taxes [ 'total' ] as $tax_rate_id => $tax ) {
2020-02-28 10:54:41 +00:00
$tax_amount = $this -> round_line_tax ( $tax , false );
2018-05-15 22:18:39 +00:00
$cart_taxes [ $tax_rate_id ] = isset ( $cart_taxes [ $tax_rate_id ] ) ? $cart_taxes [ $tax_rate_id ] + $tax_amount : $tax_amount ;
2016-06-21 19:03:56 +00:00
}
2016-03-03 17:04:20 +00:00
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
foreach ( $this -> get_shipping_methods () as $item_id => $item ) {
$taxes = $item -> get_taxes ();
foreach ( $taxes [ 'total' ] as $tax_rate_id => $tax ) {
2018-06-14 11:52:13 +00:00
$tax_amount = ( float ) $tax ;
if ( 'yes' !== get_option ( 'woocommerce_tax_round_at_subtotal' ) ) {
$tax_amount = wc_round_tax_total ( $tax_amount );
}
$shipping_taxes [ $tax_rate_id ] = isset ( $shipping_taxes [ $tax_rate_id ] ) ? $shipping_taxes [ $tax_rate_id ] + $tax_amount : $tax_amount ;
2016-06-21 19:03:56 +00:00
}
}
2014-06-13 13:35:53 +00:00
2017-02-23 14:14:50 +00:00
foreach ( $existing_taxes as $tax ) {
// Remove taxes which no longer exist for cart/shipping.
2018-01-19 15:42:48 +00:00
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 , true ) ) {
2017-02-23 14:14:50 +00:00
$this -> remove_item ( $tax -> get_id () );
continue ;
}
2017-02-23 15:25:49 +00:00
$saved_rate_ids [] = $tax -> get_rate_id ();
$tax -> set_tax_total ( isset ( $cart_taxes [ $tax -> get_rate_id () ] ) ? $cart_taxes [ $tax -> get_rate_id () ] : 0 );
$tax -> set_shipping_tax_total ( ! empty ( $shipping_taxes [ $tax -> get_rate_id () ] ) ? $shipping_taxes [ $tax -> get_rate_id () ] : 0 );
2017-02-23 14:14:50 +00:00
$tax -> save ();
}
$new_rate_ids = wp_parse_id_list ( array_diff ( array_keys ( $cart_taxes + $shipping_taxes ), $saved_rate_ids ) );
2014-06-13 13:35:53 +00:00
2017-02-23 14:14:50 +00:00
// New taxes.
foreach ( $new_rate_ids as $tax_rate_id ) {
2016-08-22 15:48:19 +00:00
$item = new WC_Order_Item_Tax ();
$item -> set_rate ( $tax_rate_id );
$item -> set_tax_total ( isset ( $cart_taxes [ $tax_rate_id ] ) ? $cart_taxes [ $tax_rate_id ] : 0 );
2017-02-15 12:10:31 +00:00
$item -> set_shipping_tax_total ( ! empty ( $shipping_taxes [ $tax_rate_id ] ) ? $shipping_taxes [ $tax_rate_id ] : 0 );
2016-08-22 15:48:19 +00:00
$this -> add_item ( $item );
2016-06-21 19:03:56 +00:00
}
2014-07-03 11:38:55 +00:00
2020-03-02 08:55:00 +00:00
$this -> set_shipping_tax ( array_sum ( $shipping_taxes ) );
$this -> set_cart_tax ( array_sum ( $cart_taxes ) );
2016-06-21 19:03:56 +00:00
$this -> save ();
}
2014-08-31 05:49:58 +00:00
2019-11-26 23:13:23 +00:00
/**
* Helper function .
* If you add all items in this order in cart again , this would be the cart subtotal ( assuming all other settings are same ) .
*
* @ return float Cart subtotal .
*/
protected function get_cart_subtotal_for_order () {
return wc_remove_number_precision (
$this -> get_rounded_items_total (
$this -> get_values_for_total ( 'subtotal' )
)
);
}
/**
* Helper function .
* If you add all items in this order in cart again , this would be the cart total ( assuming all other settings are same ) .
*
* @ return float Cart total .
*/
protected function get_cart_total_for_order () {
return wc_remove_number_precision (
$this -> get_rounded_items_total (
$this -> get_values_for_total ( 'total' )
)
);
}
2016-06-21 19:03:56 +00:00
/**
* Calculate totals by looking at the contents of the order . Stores the totals and returns the orders final total .
*
* @ since 2.2
* @ param bool $and_taxes Calc taxes if true .
* @ return float calculated grand total .
*/
public function calculate_totals ( $and_taxes = true ) {
2017-09-27 15:07:02 +00:00
do_action ( 'woocommerce_order_before_calculate_totals' , $and_taxes , $this );
2019-12-19 06:08:39 +00:00
$fees_total = 0 ;
2018-01-19 15:42:48 +00:00
$shipping_total = 0 ;
$cart_subtotal_tax = 0 ;
$cart_total_tax = 0 ;
2017-08-09 17:53:06 +00:00
2020-05-22 16:38:42 +00:00
$cart_subtotal = $this -> get_cart_subtotal_for_order ();
$cart_total = $this -> get_cart_total_for_order ();
2020-04-07 10:28:30 +00:00
2017-08-11 15:16:09 +00:00
// Sum shipping costs.
foreach ( $this -> get_shipping_methods () as $shipping ) {
2018-06-14 11:52:13 +00:00
$shipping_total += round ( $shipping -> get_total (), wc_get_price_decimals () );
2017-08-11 15:16:09 +00:00
}
$this -> set_shipping_total ( $shipping_total );
2017-08-23 11:15:06 +00:00
// Sum fee costs.
2019-12-19 06:08:39 +00:00
foreach ( $this -> get_fees () as $item ) {
$fee_total = $item -> get_total ();
if ( 0 > $fee_total ) {
$max_discount = round ( $cart_total + $fees_total + $shipping_total , wc_get_price_decimals () ) * - 1 ;
if ( $fee_total < $max_discount && 0 > $max_discount ) {
$item -> set_total ( $max_discount );
}
}
2020-02-04 20:43:29 +00:00
$fees_total += $item -> get_total ();
2019-12-19 06:08:39 +00:00
}
2017-08-23 11:15:06 +00:00
2018-02-12 11:56:48 +00:00
// Calculate taxes for items, shipping, discounts. Note; this also triggers save().
2017-05-15 13:55:39 +00:00
if ( $and_taxes ) {
2016-06-21 19:03:56 +00:00
$this -> calculate_taxes ();
}
2014-07-03 11:38:55 +00:00
2019-01-11 13:17:06 +00:00
// Sum taxes again so we can work out how much tax was discounted. This uses original values, not those possibly rounded to 2dp.
2016-06-21 19:03:56 +00:00
foreach ( $this -> get_items () as $item ) {
2019-01-11 13:17:06 +00:00
$taxes = $item -> get_taxes ();
foreach ( $taxes [ 'total' ] as $tax_rate_id => $tax ) {
$cart_total_tax += ( float ) $tax ;
}
foreach ( $taxes [ 'subtotal' ] as $tax_rate_id => $tax ) {
$cart_subtotal_tax += ( float ) $tax ;
}
2016-06-21 19:03:56 +00:00
}
2014-08-31 05:49:58 +00:00
2019-11-21 10:28:22 +00:00
$this -> set_discount_total ( round ( $cart_subtotal - $cart_total , wc_get_price_decimals () ) );
2019-01-11 13:17:06 +00:00
$this -> set_discount_tax ( wc_round_tax_total ( $cart_subtotal_tax - $cart_total_tax ) );
2019-02-20 16:22:20 +00:00
$this -> set_total ( round ( $cart_total + $fees_total + $this -> get_shipping_total () + $this -> get_cart_tax () + $this -> get_shipping_tax (), wc_get_price_decimals () ) );
2018-01-22 14:53:42 +00:00
2018-01-24 07:46:26 +00:00
do_action ( 'woocommerce_order_after_calculate_totals' , $and_taxes , $this );
2018-01-22 14:53:42 +00:00
2016-06-21 19:03:56 +00:00
$this -> save ();
2014-06-13 13:35:53 +00:00
2017-08-11 15:16:09 +00:00
return $this -> get_total ();
2014-06-13 13:35:53 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Get item subtotal - this is the cost before discount .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param object $item Item to get total from .
* @ param bool $inc_tax ( default : false ) .
* @ param bool $round ( default : true ) .
2016-06-21 19:03:56 +00:00
* @ return float
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_item_subtotal ( $item , $inc_tax = false , $round = true ) {
$subtotal = 0 ;
2014-06-13 13:35:53 +00:00
2018-01-05 12:48:24 +00:00
if ( is_callable ( array ( $item , 'get_subtotal' ) ) && $item -> get_quantity () ) {
2016-06-21 19:03:56 +00:00
if ( $inc_tax ) {
2018-01-05 12:48:24 +00:00
$subtotal = ( $item -> get_subtotal () + $item -> get_subtotal_tax () ) / $item -> get_quantity ();
2016-06-21 19:03:56 +00:00
} else {
2018-01-05 12:48:24 +00:00
$subtotal = floatval ( $item -> get_subtotal () ) / $item -> get_quantity ();
2016-06-21 19:03:56 +00:00
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
$subtotal = $round ? number_format ( ( float ) $subtotal , wc_get_price_decimals (), '.' , '' ) : $subtotal ;
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_amount_item_subtotal' , $subtotal , $this , $item , $inc_tax , $round );
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
* Get line subtotal - this is the cost before discount .
*
2018-01-19 15:42:48 +00:00
* @ param object $item Item to get total from .
* @ param bool $inc_tax ( default : false ) .
* @ param bool $round ( default : true ) .
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_line_subtotal ( $item , $inc_tax = false , $round = true ) {
$subtotal = 0 ;
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
if ( is_callable ( array ( $item , 'get_subtotal' ) ) ) {
if ( $inc_tax ) {
$subtotal = $item -> get_subtotal () + $item -> get_subtotal_tax ();
} else {
$subtotal = $item -> get_subtotal ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
$subtotal = $round ? round ( $subtotal , wc_get_price_decimals () ) : $subtotal ;
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_amount_line_subtotal' , $subtotal , $this , $item , $inc_tax , $round );
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
/**
* Calculate item cost - useful for gateways .
*
2018-01-19 15:42:48 +00:00
* @ param object $item Item to get total from .
* @ param bool $inc_tax ( default : false ) .
* @ param bool $round ( default : true ) .
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_item_total ( $item , $inc_tax = false , $round = true ) {
$total = 0 ;
2014-06-13 13:35:53 +00:00
2018-01-05 12:48:24 +00:00
if ( is_callable ( array ( $item , 'get_total' ) ) && $item -> get_quantity () ) {
2016-06-21 19:03:56 +00:00
if ( $inc_tax ) {
2018-01-05 12:48:24 +00:00
$total = ( $item -> get_total () + $item -> get_total_tax () ) / $item -> get_quantity ();
2016-06-21 19:03:56 +00:00
} else {
2018-01-05 12:48:24 +00:00
$total = floatval ( $item -> get_total () ) / $item -> get_quantity ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
$total = $round ? round ( $total , wc_get_price_decimals () ) : $total ;
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_amount_item_total' , $total , $this , $item , $inc_tax , $round );
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Calculate line total - useful for gateways .
*
2018-01-19 15:42:48 +00:00
* @ param object $item Item to get total from .
* @ param bool $inc_tax ( default : false ) .
* @ param bool $round ( default : true ) .
2016-06-21 19:03:56 +00:00
* @ return float
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_line_total ( $item , $inc_tax = false , $round = true ) {
$total = 0 ;
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
if ( is_callable ( array ( $item , 'get_total' ) ) ) {
// Check if we need to add line tax to the line total.
$total = $inc_tax ? $item -> get_total () + $item -> get_total_tax () : $item -> get_total ();
2014-08-31 05:49:58 +00:00
2016-06-21 19:03:56 +00:00
// Check if we need to round.
$total = $round ? round ( $total , wc_get_price_decimals () ) : $total ;
}
2014-08-31 05:49:58 +00:00
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_amount_line_total' , $total , $this , $item , $inc_tax , $round );
}
2014-08-31 05:49:58 +00:00
2016-06-21 19:03:56 +00:00
/**
* Get item tax - useful for gateways .
*
2018-01-19 15:42:48 +00:00
* @ param mixed $item Item to get total from .
* @ param bool $round ( default : true ) .
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_item_tax ( $item , $round = true ) {
$tax = 0 ;
2014-06-13 13:35:53 +00:00
2018-01-05 12:48:24 +00:00
if ( is_callable ( array ( $item , 'get_total_tax' ) ) && $item -> get_quantity () ) {
$tax = $item -> get_total_tax () / $item -> get_quantity ();
2016-06-21 19:03:56 +00:00
$tax = $round ? wc_round_tax_total ( $tax ) : $tax ;
}
2015-11-19 16:07:04 +00:00
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_amount_item_tax' , $tax , $item , $round , $this );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/**
* Get line tax - useful for gateways .
*
2018-01-19 15:42:48 +00:00
* @ param mixed $item Item to get total from .
2016-06-21 19:03:56 +00:00
* @ return float
*/
public function get_line_tax ( $item ) {
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 );
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Gets line subtotal - formatted for display .
2014-06-13 13:35:53 +00:00
*
2019-05-16 10:04:34 +00:00
* @ param object $item Item to get total from .
2018-01-19 15:42:48 +00:00
* @ param string $tax_display Incl or excl tax display mode .
2016-06-21 19:03:56 +00:00
* @ return string
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_formatted_line_subtotal ( $item , $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option ( 'woocommerce_tax_display_cart' );
2014-06-13 13:35:53 +00:00
2018-01-19 15:42:48 +00:00
if ( 'excl' === $tax_display ) {
2016-06-21 19:03:56 +00:00
$ex_tax_label = $this -> get_prices_include_tax () ? 1 : 0 ;
2014-06-13 13:35:53 +00:00
2019-01-27 15:38:08 +00:00
$subtotal = wc_price (
$this -> get_line_subtotal ( $item ),
array (
'ex_tax_label' => $ex_tax_label ,
'currency' => $this -> get_currency (),
)
);
2016-06-21 19:03:56 +00:00
} else {
2016-08-27 02:23:54 +00:00
$subtotal = wc_price ( $this -> get_line_subtotal ( $item , true ), array ( 'currency' => $this -> get_currency () ) );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_formatted_line_subtotal' , $subtotal , $item , $this );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/**
* Gets order total - formatted for display .
2018-01-19 15:42:48 +00:00
*
2016-06-21 19:03:56 +00:00
* @ return string
*/
public function get_formatted_order_total () {
$formatted_total = wc_price ( $this -> get_total (), array ( 'currency' => $this -> get_currency () ) );
return apply_filters ( 'woocommerce_get_formatted_order_total' , $formatted_total , $this );
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Gets subtotal - subtotal is shown before discounts , but with localised taxes .
*
2018-01-19 15:42:48 +00:00
* @ param bool $compound ( default : false ) .
2016-06-21 19:03:56 +00:00
* @ param string $tax_display ( default : the tax_display_cart value ) .
* @ return string
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_subtotal_to_display ( $compound = false , $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option ( 'woocommerce_tax_display_cart' );
2019-11-26 23:13:23 +00:00
$subtotal = $this -> get_cart_subtotal_for_order ();
2017-12-04 21:40:12 +00:00
2016-06-21 19:03:56 +00:00
if ( ! $compound ) {
2014-08-31 05:49:58 +00:00
2019-11-26 23:13:23 +00:00
if ( 'incl' === $tax_display ) {
$subtotal_taxes = 0 ;
foreach ( $this -> get_items () as $item ) {
$subtotal_taxes += self :: round_line_tax ( $item -> get_subtotal_tax (), false );
2014-06-13 13:35:53 +00:00
}
2019-11-26 23:13:23 +00:00
$subtotal += wc_round_tax_total ( $subtotal_taxes );
2016-06-21 19:03:56 +00:00
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
$subtotal = wc_price ( $subtotal , array ( 'currency' => $this -> get_currency () ) );
2014-06-13 13:35:53 +00:00
2018-03-02 16:22:54 +00:00
if ( 'excl' === $tax_display && $this -> get_prices_include_tax () && wc_tax_enabled () ) {
2016-06-21 19:03:56 +00:00
$subtotal .= ' <small class="tax_label">' . WC () -> countries -> ex_tax_or_vat () . '</small>' ;
}
} else {
if ( 'incl' === $tax_display ) {
return '' ;
}
// Add Shipping Costs.
$subtotal += $this -> get_shipping_total ();
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
// Remove non-compound taxes.
foreach ( $this -> get_taxes () as $tax ) {
2017-04-18 11:00:21 +00:00
if ( $tax -> is_compound () ) {
2016-06-21 19:03:56 +00:00
continue ;
}
$subtotal = $subtotal + $tax -> get_tax_total () + $tax -> get_shipping_tax_total ();
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
// Remove discounts.
$subtotal = $subtotal - $this -> get_total_discount ();
$subtotal = wc_price ( $subtotal , array ( 'currency' => $this -> get_currency () ) );
2015-01-30 14:14:34 +00:00
}
2014-06-13 13:35:53 +00:00
2016-06-21 19:03:56 +00:00
return apply_filters ( 'woocommerce_order_subtotal_to_display' , $subtotal , $compound , $this );
}
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Gets shipping ( formatted ) .
*
2018-01-19 15:42:48 +00:00
* @ param string $tax_display Excl or incl tax display mode .
2016-06-21 19:03:56 +00:00
* @ return string
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_shipping_to_display ( $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option ( 'woocommerce_tax_display_cart' );
2014-06-13 13:35:53 +00:00
2018-09-13 13:23:00 +00:00
if ( 0 < abs ( ( float ) $this -> get_shipping_total () ) ) {
2014-06-13 13:35:53 +00:00
2016-09-07 22:32:24 +00:00
if ( 'excl' === $tax_display ) {
2014-08-31 05:49:58 +00:00
2016-06-21 19:03:56 +00:00
// Show shipping excluding tax.
2016-08-27 02:23:54 +00:00
$shipping = wc_price ( $this -> get_shipping_total (), array ( 'currency' => $this -> get_currency () ) );
2014-08-31 05:49:58 +00:00
2018-06-14 10:49:45 +00:00
if ( ( float ) $this -> get_shipping_tax () > 0 && $this -> get_prices_include_tax () ) {
2016-06-21 19:03:56 +00:00
$shipping .= apply_filters ( 'woocommerce_order_shipping_to_display_tax_label' , ' <small class="tax_label">' . WC () -> countries -> ex_tax_or_vat () . '</small>' , $this , $tax_display );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
} else {
// Show shipping including tax.
2016-08-27 02:23:54 +00:00
$shipping = wc_price ( $this -> get_shipping_total () + $this -> get_shipping_tax (), array ( 'currency' => $this -> get_currency () ) );
2014-06-13 13:35:53 +00:00
2018-06-14 10:49:45 +00:00
if ( ( float ) $this -> get_shipping_tax () > 0 && ! $this -> get_prices_include_tax () ) {
2016-06-21 19:03:56 +00:00
$shipping .= apply_filters ( 'woocommerce_order_shipping_to_display_tax_label' , ' <small class="tax_label">' . WC () -> countries -> inc_tax_or_vat () . '</small>' , $this , $tax_display );
2014-06-13 13:35:53 +00:00
}
}
2017-09-04 16:42:31 +00:00
/* translators: %s: method */
2016-06-21 19:03:56 +00:00
$shipping .= apply_filters ( 'woocommerce_order_shipping_to_display_shipped_via' , ' <small class="shipped_via">' . sprintf ( __ ( 'via %s' , 'woocommerce' ), $this -> get_shipping_method () ) . '</small>' , $this );
} elseif ( $this -> get_shipping_method () ) {
$shipping = $this -> get_shipping_method ();
} else {
$shipping = __ ( 'Free!' , 'woocommerce' );
2015-01-30 14:14:34 +00:00
}
2016-06-21 19:03:56 +00:00
2018-10-02 15:03:17 +00:00
return apply_filters ( 'woocommerce_order_shipping_to_display' , $shipping , $this , $tax_display );
2014-06-13 13:35:53 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Get the discount amount ( formatted ) .
2017-05-15 11:50:52 +00:00
*
2018-01-19 15:42:48 +00:00
* @ since 2.3 . 0
* @ param string $tax_display Excl or incl tax display mode .
2016-06-21 19:03:56 +00:00
* @ return string
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function get_discount_to_display ( $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option ( 'woocommerce_tax_display_cart' );
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 );
2014-06-13 13:35:53 +00:00
}
/**
2017-03-06 13:44:52 +00:00
* Add total row for subtotal .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2014-06-13 13:35:53 +00:00
*/
2017-03-06 13:44:52 +00:00
protected function add_order_item_totals_subtotal_row ( & $total_rows , $tax_display ) {
2018-01-19 15:42:48 +00:00
$subtotal = $this -> get_subtotal_to_display ( false , $tax_display );
if ( $subtotal ) {
2016-06-21 19:03:56 +00:00
$total_rows [ 'cart_subtotal' ] = array (
'label' => __ ( 'Subtotal:' , 'woocommerce' ),
2018-01-19 15:42:48 +00:00
'value' => $subtotal ,
2016-06-21 19:03:56 +00:00
);
2014-06-13 13:35:53 +00:00
}
2017-03-06 13:44:52 +00:00
}
2014-06-13 13:35:53 +00:00
2017-03-06 13:44:52 +00:00
/**
* Add total row for discounts .
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
*/
protected function add_order_item_totals_discount_row ( & $total_rows , $tax_display ) {
2016-06-21 19:03:56 +00:00
if ( $this -> get_total_discount () > 0 ) {
$total_rows [ 'discount' ] = array (
'label' => __ ( 'Discount:' , 'woocommerce' ),
2018-01-19 15:42:48 +00:00
'value' => '-' . $this -> get_discount_to_display ( $tax_display ),
2016-06-21 19:03:56 +00:00
);
}
2017-03-06 13:44:52 +00:00
}
2014-06-13 13:35:53 +00:00
2017-03-06 13:44:52 +00:00
/**
* Add total row for shipping .
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
*/
protected function add_order_item_totals_shipping_row ( & $total_rows , $tax_display ) {
2016-06-21 19:03:56 +00:00
if ( $this -> get_shipping_method () ) {
$total_rows [ 'shipping' ] = array (
'label' => __ ( 'Shipping:' , 'woocommerce' ),
2018-01-19 15:42:48 +00:00
'value' => $this -> get_shipping_to_display ( $tax_display ),
2016-06-21 19:03:56 +00:00
);
2014-06-13 13:35:53 +00:00
}
2017-03-06 13:44:52 +00:00
}
2014-08-31 05:49:58 +00:00
2017-03-06 13:44:52 +00:00
/**
* Add total row for fees .
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
*/
protected function add_order_item_totals_fee_rows ( & $total_rows , $tax_display ) {
2018-01-19 15:42:48 +00:00
$fees = $this -> get_fees ();
if ( $fees ) {
2016-06-21 19:03:56 +00:00
foreach ( $fees as $id => $fee ) {
if ( apply_filters ( 'woocommerce_get_order_item_totals_excl_free_fees' , empty ( $fee [ 'line_total' ] ) && empty ( $fee [ 'line_tax' ] ), $id ) ) {
continue ;
}
2016-08-03 11:57:32 +00:00
$total_rows [ 'fee_' . $fee -> get_id () ] = array (
2016-06-21 19:03:56 +00:00
'label' => $fee -> get_name () . ':' ,
2016-08-27 02:23:54 +00:00
'value' => wc_price ( 'excl' === $tax_display ? $fee -> get_total () : $fee -> get_total () + $fee -> get_total_tax (), array ( 'currency' => $this -> get_currency () ) ),
2016-06-21 19:03:56 +00:00
);
}
2014-06-13 13:35:53 +00:00
}
2017-03-06 13:44:52 +00:00
}
2015-09-21 21:28:03 +00:00
2017-03-06 13:44:52 +00:00
/**
* Add total row for taxes .
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
*/
protected function add_order_item_totals_tax_rows ( & $total_rows , $tax_display ) {
2016-06-21 19:03:56 +00:00
// Tax for tax exclusive prices.
2019-05-01 21:05:46 +00:00
if ( 'excl' === $tax_display && wc_tax_enabled () ) {
2017-03-06 13:44:52 +00:00
if ( 'itemized' === get_option ( 'woocommerce_tax_total_display' ) ) {
2016-06-21 19:03:56 +00:00
foreach ( $this -> get_tax_totals () as $code => $tax ) {
$total_rows [ sanitize_title ( $code ) ] = array (
'label' => $tax -> label . ':' ,
2018-01-19 15:42:48 +00:00
'value' => $tax -> formatted_amount ,
2016-06-21 19:03:56 +00:00
);
}
} else {
$total_rows [ 'tax' ] = array (
'label' => WC () -> countries -> tax_or_vat () . ':' ,
2018-01-19 15:42:48 +00:00
'value' => wc_price ( $this -> get_total_tax (), array ( 'currency' => $this -> get_currency () ) ),
2016-06-21 19:03:56 +00:00
);
}
}
2017-03-06 13:44:52 +00:00
}
2014-06-13 13:35:53 +00:00
2017-03-06 13:44:52 +00:00
/**
* Add total row for grand total .
*
2018-01-19 15:42:48 +00:00
* @ param array $total_rows Reference to total rows array .
* @ param string $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
*/
protected function add_order_item_totals_total_row ( & $total_rows , $tax_display ) {
2016-06-21 19:03:56 +00:00
$total_rows [ 'order_total' ] = array (
'label' => __ ( 'Total:' , 'woocommerce' ),
2018-01-19 15:42:48 +00:00
'value' => $this -> get_formatted_order_total ( $tax_display ),
2016-06-21 19:03:56 +00:00
);
2017-03-06 13:44:52 +00:00
}
/**
* Get totals for display on pages and in emails .
*
2018-01-19 15:42:48 +00:00
* @ param mixed $tax_display Excl or incl tax display mode .
2017-03-06 13:44:52 +00:00
* @ return array
*/
public function get_order_item_totals ( $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option ( 'woocommerce_tax_display_cart' );
$total_rows = array ();
$this -> add_order_item_totals_subtotal_row ( $total_rows , $tax_display );
$this -> add_order_item_totals_discount_row ( $total_rows , $tax_display );
$this -> add_order_item_totals_shipping_row ( $total_rows , $tax_display );
$this -> add_order_item_totals_fee_rows ( $total_rows , $tax_display );
$this -> add_order_item_totals_tax_rows ( $total_rows , $tax_display );
$this -> add_order_item_totals_total_row ( $total_rows , $tax_display );
2014-06-13 13:35:53 +00:00
2017-03-06 13:44:52 +00:00
return apply_filters ( 'woocommerce_get_order_item_totals' , $total_rows , $this , $tax_display );
2014-06-13 13:35:53 +00:00
}
2016-06-21 19:03:56 +00:00
/*
|--------------------------------------------------------------------------
| Conditionals
|--------------------------------------------------------------------------
|
| Checks if a condition is true or false .
|
*/
2014-06-13 13:35:53 +00:00
/**
2016-06-21 19:03:56 +00:00
* Checks the order status against a passed in status .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param array | string $status Status to check .
2014-06-13 13:35:53 +00:00
* @ return bool
*/
2016-06-21 19:03:56 +00:00
public function has_status ( $status ) {
2018-03-21 22:57:10 +00:00
return apply_filters ( 'woocommerce_order_has_status' , ( is_array ( $status ) && in_array ( $this -> get_status (), $status , true ) ) || $this -> get_status () === $status , $this , $status );
2014-06-13 13:35:53 +00:00
}
/**
2016-06-21 19:03:56 +00:00
* Check whether this order has a specific shipping method or not .
2014-06-13 13:35:53 +00:00
*
2018-01-19 15:42:48 +00:00
* @ param string $method_id Method ID to check .
2016-06-21 19:03:56 +00:00
* @ return bool
2014-06-13 13:35:53 +00:00
*/
2016-06-21 19:03:56 +00:00
public function has_shipping_method ( $method_id ) {
2014-06-13 13:35:53 +00:00
foreach ( $this -> get_shipping_methods () as $shipping_method ) {
2016-08-25 09:58:04 +00:00
if ( strpos ( $shipping_method -> get_method_id (), $method_id ) === 0 ) {
2016-06-21 19:03:56 +00:00
return true ;
2014-06-13 13:35:53 +00:00
}
}
2016-06-21 19:03:56 +00:00
return false ;
2014-06-13 13:35:53 +00:00
}
2014-09-02 18:47:56 +00:00
2016-06-21 19:03:56 +00:00
/**
* Returns true if the order contains a free product .
2018-01-19 15:42:48 +00:00
*
2016-06-21 19:03:56 +00:00
* @ since 2.5 . 0
2014-09-02 18:47:56 +00:00
* @ return bool
*/
2016-06-21 19:03:56 +00:00
public function has_free_item () {
foreach ( $this -> get_items () as $item ) {
if ( ! $item -> get_total () ) {
return true ;
}
}
return false ;
2014-09-02 18:47:56 +00:00
}
2014-07-28 03:31:26 +00:00
}