From b20b3590c83d4b58bd993092ad49a9af0ba5005d Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Thu, 17 Nov 2016 13:30:34 -0800 Subject: [PATCH] First pass at order items --- .../abstracts/abstract-wc-legacy-order.php | 12 +- includes/abstracts/abstract-wc-order.php | 3 +- .../api/legacy/v2/class-wc-api-orders.php | 3 +- .../api/legacy/v3/class-wc-api-orders.php | 3 +- includes/class-wc-checkout.php | 19 +- includes/class-wc-data-store.php | 29 +- includes/class-wc-order-factory.php | 22 +- includes/class-wc-order-item-coupon.php | 231 ++++--- includes/class-wc-order-item-fee.php | 328 +++++----- includes/class-wc-order-item-product.php | 609 +++++++++--------- includes/class-wc-order-item-shipping.php | 357 +++++----- includes/class-wc-order-item-tax.php | 139 ++-- includes/class-wc-order-item.php | 200 ++---- includes/class-wc-order.php | 1 + .../class-wc-order-item-coupon-data-store.php | 34 + .../class-wc-order-item-data-store.php | 99 +++ .../class-wc-order-item-fee-data-store.php | 39 ++ .../class-wc-order-item-product-store.php | 63 ++ ...lass-wc-order-item-shipping-data-store.php | 39 ++ .../class-wc-order-item-tax-data-store.php | 43 ++ .../helpers/class-wc-helper-order.php | 3 +- tests/unit-tests/order/crud.php | 68 +- woocommerce.php | 6 + 23 files changed, 1287 insertions(+), 1063 deletions(-) create mode 100644 includes/data-stores/class-wc-order-item-coupon-data-store.php create mode 100644 includes/data-stores/class-wc-order-item-data-store.php create mode 100644 includes/data-stores/class-wc-order-item-fee-data-store.php create mode 100644 includes/data-stores/class-wc-order-item-product-store.php create mode 100644 includes/data-stores/class-wc-order-item-shipping-data-store.php create mode 100644 includes/data-stores/class-wc-order-item-tax-data-store.php diff --git a/includes/abstracts/abstract-wc-legacy-order.php b/includes/abstracts/abstract-wc-legacy-order.php index b523fdf9048..efea8da45d6 100644 --- a/includes/abstracts/abstract-wc-legacy-order.php +++ b/includes/abstracts/abstract-wc-legacy-order.php @@ -27,7 +27,8 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data { public function add_coupon( $code = array(), $discount = 0, $discount_tax = 0 ) { _deprecated_function( 'WC_Order::add_coupon', '2.7', 'Create new WC_Order_Item_Coupon object and add to order with WC_Order::add_item()' ); - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => $code, 'discount' => $discount, 'discount_tax' => $discount_tax, @@ -50,7 +51,8 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data { public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) { _deprecated_function( 'WC_Order::add_tax', '2.7', 'Create new WC_Order_Item_Tax object and add to order with WC_Order::add_item()' ); - $item = new WC_Order_Item_Tax( array( + $item = new WC_Order_Item_Tax(); + $item->set_props( array( 'rate_id' => $tax_rate_id, 'tax_total' => $tax_amount, 'shipping_tax_total' => $shipping_tax_amount, @@ -72,7 +74,8 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data { public function add_shipping( $shipping_rate ) { _deprecated_function( 'WC_Order::add_shipping', '2.7', 'Create new WC_Order_Item_Shipping object and add to order with WC_Order::add_item()' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $shipping_rate->label, 'method_id' => $shipping_rate->id, 'total' => wc_format_decimal( $shipping_rate->cost ), @@ -96,7 +99,8 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data { public function add_fee( $fee ) { _deprecated_function( 'WC_Order::add_fee', '2.7', 'Create new WC_Order_Item_Fee object and add to order with WC_Order::add_item()' ); - $item = new WC_Order_Item_Fee( array( + $item = new WC_Order_Item_Fee(); + $item->set_props( array( 'name' => $fee->name, 'tax_class' => $fee->taxable ? $fee->tax_class : 0, 'total' => $fee->amount, diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 3eba303f2f1..1f4d7ce5d44 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -905,7 +905,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { } } - $item = new WC_Order_Item_Product( $args ); + $item = new WC_Order_Item_Product(); + $item->set_props( $args ); $item->set_backorder_meta(); $item->set_order_id( $this->get_id() ); $item->save(); diff --git a/includes/api/legacy/v2/class-wc-api-orders.php b/includes/api/legacy/v2/class-wc-api-orders.php index b2b7c35122d..03d3455db92 100644 --- a/includes/api/legacy/v2/class-wc-api-orders.php +++ b/includes/api/legacy/v2/class-wc-api-orders.php @@ -1142,7 +1142,8 @@ class WC_API_Orders extends WC_API_Resource { throw new WC_API_Exception( 'woocommerce_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); } - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => $coupon['code'], 'discount' => isset( $coupon['amount'] ) ? floatval( $coupon['amount'] ) : 0, 'discount_tax' => 0, diff --git a/includes/api/legacy/v3/class-wc-api-orders.php b/includes/api/legacy/v3/class-wc-api-orders.php index bbcb23f9591..0299df381e4 100644 --- a/includes/api/legacy/v3/class-wc-api-orders.php +++ b/includes/api/legacy/v3/class-wc-api-orders.php @@ -1188,7 +1188,8 @@ class WC_API_Orders extends WC_API_Resource { throw new WC_API_Exception( 'woocommerce_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); } - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => $coupon['code'], 'discount' => isset( $coupon['amount'] ) ? floatval( $coupon['amount'] ) : 0, 'discount_tax' => 0, diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index fd52da48331..2b760e1140b 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -230,7 +230,8 @@ class WC_Checkout { // Add line items. foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { $product = $values['data']; - $item = new WC_Order_Item_Product( array( + $item = new WC_Order_Item_Product(); + $item->set_props( array( 'quantity' => $values['quantity'], 'name' => $product ? $product->get_name() : '', 'tax_class' => $product ? $product->get_tax_class() : '', @@ -243,7 +244,6 @@ class WC_Checkout { 'total_tax' => $values['line_tax'], 'taxes' => $values['line_tax_data'], ) ); - $item->set_backorder_meta(); // Set this to pass to legacy actions. $item->legacy_values = $values; @@ -254,7 +254,8 @@ class WC_Checkout { // Add fees foreach ( WC()->cart->get_fees() as $fee_key => $fee ) { - $item = new WC_Order_Item_Fee( array( + $item = new WC_Order_Item_Fee(); + $item->set_props( array( 'name' => $fee->name, 'tax_class' => $fee->taxable ? $fee->tax_class : 0, 'total' => $fee->amount, @@ -275,7 +276,8 @@ class WC_Checkout { foreach ( WC()->shipping->get_packages() as $package_key => $package ) { if ( isset( $package['rates'][ $this->shipping_methods[ $package_key ] ] ) ) { $shipping_rate = $package['rates'][ $this->shipping_methods[ $package_key ] ]; - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $shipping_rate->label, 'method_id' => $shipping_rate->id, 'total' => wc_format_decimal( $shipping_rate->cost ), @@ -293,20 +295,23 @@ class WC_Checkout { // Store tax rows foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) { if ( $tax_rate_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) { - $order->add_item( new WC_Order_Item_Tax( array( + $item = new WC_Order_Item_Tax(); + $item->set_props( array( 'rate_id' => $tax_rate_id, 'tax_total' => WC()->cart->get_tax_amount( $tax_rate_id ), 'shipping_tax_total' => WC()->cart->get_shipping_tax_amount( $tax_rate_id ), 'rate_code' => WC_Tax::get_rate_code( $tax_rate_id ), 'label' => WC_Tax::get_rate_label( $tax_rate_id ), 'compound' => WC_Tax::is_compound( $tax_rate_id ), - ) ) ); + ) ); + $order->add_item( $item ); } } // Store coupons foreach ( WC()->cart->get_coupons() as $code => $coupon ) { - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => $code, 'discount' => WC()->cart->get_coupon_discount_amount( $code ), 'discount_tax' => WC()->cart->get_coupon_discount_tax_amount( $code ), diff --git a/includes/class-wc-data-store.php b/includes/class-wc-data-store.php index 7753d3ba951..b8d35fa6932 100644 --- a/includes/class-wc-data-store.php +++ b/includes/class-wc-data-store.php @@ -28,18 +28,23 @@ class WC_Data_Store { * Ran through `woocommerce_data_stores`. */ private $stores = array( - 'coupon' => 'WC_Coupon_Data_Store_CPT', - 'customer' => 'WC_Customer_Data_Store', - 'customer-download' => 'WC_Customer_Download_Data_Store', - 'customer-session' => 'WC_Customer_Data_Store_Session', - 'order' => 'WC_Order_Data_Store_CPT', - 'order-refund' => 'WC_Order_Refund_Data_Store_CPT', - 'payment-token' => 'WC_Payment_Token_Data_Store', - 'product' => 'WC_Product_Data_Store_CPT', - 'product_grouped' => 'WC_Product_Grouped_Data_Store_CPT', - 'product_variable' => 'WC_Product_Variable_Data_Store_CPT', - 'product_variation' => 'WC_Product_Variation_Data_Store_CPT', - 'shipping-zone' => 'WC_Shipping_Zone_Data_Store', + 'coupon' => 'WC_Coupon_Data_Store_CPT', + 'customer' => 'WC_Customer_Data_Store', + 'customer-download' => 'WC_Customer_Download_Data_Store', + 'customer-session' => 'WC_Customer_Data_Store_Session', + 'order' => 'WC_Order_Data_Store_CPT', + 'order-refund' => 'WC_Order_Refund_Data_Store_CPT', + 'order-item-coupon' => 'WC_Order_Item_Coupon_Data_Store', + 'order-item-fee' => 'WC_Order_Item_Fee_Data_Store', + 'order-item-product' => 'WC_Order_Item_Product_Data_Store', + 'order-item-shipping' => 'WC_Order_Item_Shipping_Data_Store', + 'order-item-tax' => 'WC_Order_Item_Tax_Data_Store', + 'payment-token' => 'WC_Payment_Token_Data_Store', + 'product' => 'WC_Product_Data_Store_CPT', + 'product_grouped' => 'WC_Product_Grouped_Data_Store_CPT', + 'product_variable' => 'WC_Product_Variable_Data_Store_CPT', + 'product_variation' => 'WC_Product_Variation_Data_Store_CPT', + 'shipping-zone' => 'WC_Shipping_Zone_Data_Store', ); /** diff --git a/includes/class-wc-order-factory.php b/includes/class-wc-order-factory.php index 4805d5b5131..5aa9361c51e 100644 --- a/includes/class-wc-order-factory.php +++ b/includes/class-wc-order-factory.php @@ -57,36 +57,38 @@ class WC_Order_Factory { global $wpdb; if ( is_numeric( $item_id ) ) { - $item_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d LIMIT 1;", $item_id ) ); + $item_data = $wpdb->get_row( $wpdb->prepare( "SELECT order_item_type FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d LIMIT 1;", $item_id ) ); $item_type = $item_data->order_item_type; + $id = $item_id; } elseif ( $item_id instanceof WC_Order_Item ) { - $item_data = $item_id->get_data(); - $item_type = $item_data->get_type(); + $item_type = $item_id->get_type(); + $id = $item_id->get_id(); } elseif ( is_object( $item_id ) && ! empty( $item_id->order_item_type ) ) { - $item_data = $item_id; + $id = $item_id; $item_type = $item_id->order_item_type; } else { $item_data = false; $item_type = false; + $id = false; } - if ( $item_data && $item_type ) { + if ( $id && $item_type ) { switch ( $item_type ) { case 'line_item' : case 'product' : - return new WC_Order_Item_Product( $item_data ); + return new WC_Order_Item_Product( $id ); break; case 'coupon' : - return new WC_Order_Item_Coupon( $item_data ); + return new WC_Order_Item_Coupon( $id ); break; case 'fee' : - return new WC_Order_Item_Fee( $item_data ); + return new WC_Order_Item_Fee( $id ); break; case 'shipping' : - return new WC_Order_Item_Shipping( $item_data ); + return new WC_Order_Item_Shipping( $id ); break; case 'tax' : - return new WC_Order_Item_Tax( $item_data ); + return new WC_Order_Item_Tax( $id ); break; } } diff --git a/includes/class-wc-order-item-coupon.php b/includes/class-wc-order-item-coupon.php index 204404f9ab8..47bbd4f97a1 100644 --- a/includes/class-wc-order-item-coupon.php +++ b/includes/class-wc-order-item-coupon.php @@ -18,14 +18,121 @@ class WC_Order_Item_Coupon extends WC_Order_Item { * @since 2.7.0 * @var array */ - protected $_data = array( - 'order_id' => 0, - 'id' => 0, + protected $extra_data = array( 'code' => '', 'discount' => 0, 'discount_tax' => 0, ); + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + */ + + /** + * Set order item name. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_name( $value ) { + return $this->set_code( $value ); + } + + /** + * Set code. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_code( $value ) { + $this->set_prop( 'code', wc_clean( $value ) ); + } + + /** + * Set discount amount. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_discount( $value ) { + $this->set_prop( 'discount', wc_format_decimal( $value ) ); + } + + /** + * Set discounted tax amount. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_discount_tax( $value ) { + $this->set_prop( 'discount_tax', wc_format_decimal( $value ) ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + */ + + /** + * Get order item type. + * + * @param string $context + * @return string + */ + public function get_type( $context = 'view' ) { + return 'coupon'; + } + + /** + * Get order item name. + * + * @param string $context + * @return string + */ + public function get_name( $context = 'view' ) { + return $this->get_code( $context ); + } + + /** + * Get coupon code. + * + * @param string $context + * @return string + */ + public function get_code( $context = 'view' ) { + return $this->get_prop( 'code', $context ); + } + + /** + * Get discount amount. + * + * @param string $context + * @return string + */ + public function get_discount( $context = 'view' ) { + return $this->get_prop( 'discount', $context ); + } + + /** + * Get discounted tax amount. + * @return string + */ + public function get_discount_tax( $context = 'view' ) { + return $this->get_prop( 'discount_tax', $context ); + } + + /* + |-------------------------------------------------------------------------- + | Array Access Methods + |-------------------------------------------------------------------------- + | + | For backwards compat with legacy arrays. + | + */ + /** * offsetGet for ArrayAccess/Backwards compatibility. * @deprecated Add deprecation notices in future release. @@ -68,36 +175,6 @@ class WC_Order_Item_Coupon extends WC_Order_Item { return parent::offsetExists( $offset ); } - /** - * Read/populate data properties specific to this order item. - */ - public function read( $id ) { - parent::read( $id ); - - if ( ! $this->get_id() ) { - return; - } - - $this->set_props( array( - 'discount' => get_metadata( 'order_item', $this->get_id(), 'discount_amount', true ), - 'discount_tax' => get_metadata( 'order_item', $this->get_id(), 'discount_amount_tax', true ), - ) ); - } - - /** - * Save properties specific to this order item. - * @return int Item ID - */ - public function save() { - parent::save(); - if ( $this->get_id() ) { - wc_update_order_item_meta( $this->get_id(), 'discount_amount', $this->get_discount() ); - wc_update_order_item_meta( $this->get_id(), 'discount_amount_tax', $this->get_discount_tax() ); - } - - return $this->get_id(); - } - /** * Internal meta keys we don't want exposed as part of meta_data. * @return array() @@ -105,92 +182,4 @@ class WC_Order_Item_Coupon extends WC_Order_Item { protected function get_internal_meta_keys() { return array( 'discount_amount', 'discount_amount_tax' ); } - - /* - |-------------------------------------------------------------------------- - | Setters - |-------------------------------------------------------------------------- - */ - - /** - * Set order item name. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_name( $value ) { - return $this->set_code( $value ); - } - - /** - * Set code. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_code( $value ) { - $this->_data['code'] = wc_clean( $value ); - } - - /** - * Set discount amount. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_discount( $value ) { - $this->_data['discount'] = wc_format_decimal( $value ); - } - - /** - * Set discounted tax amount. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_discount_tax( $value ) { - $this->_data['discount_tax'] = wc_format_decimal( $value ); - } - - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - */ - - /** - * Get order item type. - * @return string - */ - public function get_type() { - return 'coupon'; - } - - /** - * Get order item name. - * @return string - */ - public function get_name() { - return $this->get_code(); - } - - /** - * Get coupon code. - * @return string - */ - public function get_code() { - return $this->_data['code']; - } - - /** - * Get discount amount. - * @return string - */ - public function get_discount() { - return wc_format_decimal( $this->_data['discount'] ); - } - - /** - * Get discounted tax amount. - * @return string - */ - public function get_discount_tax() { - return wc_format_decimal( $this->_data['discount_tax'] ); - } } diff --git a/includes/class-wc-order-item-fee.php b/includes/class-wc-order-item-fee.php index 5ef45e36683..dad947ec3b8 100644 --- a/includes/class-wc-order-item-fee.php +++ b/includes/class-wc-order-item-fee.php @@ -18,10 +18,7 @@ class WC_Order_Item_Fee extends WC_Order_Item { * @since 2.7.0 * @var array */ - protected $_data = array( - 'order_id' => 0, - 'id' => 0, - 'name' => '', + protected $extra_data = array( 'tax_class' => '', 'tax_status' => 'taxable', 'total' => '', @@ -31,6 +28,164 @@ class WC_Order_Item_Fee extends WC_Order_Item { ), ); + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + */ + + /** + * Set tax class. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_tax_class( $value ) { + if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { + $this->error( 'order_item_fee_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); + } + $this->set_prop( 'tax_class', $value ); + } + + /** + * Set tax_status. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_tax_status( $value ) { + if ( in_array( $value, array( 'taxable', 'none' ) ) ) { + $this->set_prop( 'tax_status', $value ); + } else { + $this->set_prop( 'tax_status', 'taxable' ); + } + } + + /** + * Set total. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_total( $value ) { + $this->set_prop( 'total', wc_format_decimal( $value ) ); + } + + /** + * Set total tax. + * + * @param string $value + * @throws WC_Data_Exception + */ + protected function set_total_tax( $value ) { + $this->set_prop( 'total_tax', wc_format_decimal( $value ) ); + } + + /** + * Set taxes. + * + * This is an array of tax ID keys with total amount values. + * @param array $raw_tax_data + * @throws WC_Data_Exception + */ + public function set_taxes( $raw_tax_data ) { + $raw_tax_data = maybe_unserialize( $raw_tax_data ); + $tax_data = array( + 'total' => array(), + ); + if ( ! empty( $raw_tax_data['total'] ) ) { + $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); + } + $this->set_prop( 'taxes', $tax_data ); + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + */ + + /** + * Get order item name. + * + * @param string $context + * @return string + */ + public function get_name( $context = 'view' ) { + $name = $this->get_prop( 'name', $context ); + return $name ? $name : __( 'Fee', 'woocommerce' ); + } + + /** + * Get order item type. + * + * @param string $context + * @return string + */ + public function get_type( $context = 'view' ) { + return 'fee'; + } + + /** + * Get tax class. + * + * @param string $context + * @return string + */ + public function get_tax_class( $context = 'view' ) { + return $this->get_prop( 'tax_class', $context ); + } + + /** + * Get tax status. + * + * @param string $context + * @return string + */ + public function get_tax_status( $context = 'view' ) { + return $this->get_prop( 'tax_status', $context ); + } + + /** + * Get total fee. + * + * @param string $context + * @return string + */ + public function get_total( $context = 'view' ) { + return $this->get_prop( 'total', $context ); + } + + /** + * Get total tax. + * + * @param string $context + * @return string + */ + public function get_total_tax( $context = 'view' ) { + return $this->get_prop( 'total_tax', $context ); + } + + /** + * Get fee taxes. + * + * @param string $context + * @return array + */ + public function get_taxes( $context = 'view' ) { + return $this->get_prop( 'taxes', $context ); + } + + /* + |-------------------------------------------------------------------------- + | Array Access Methods + |-------------------------------------------------------------------------- + | + | For backwards compat with legacy arrays. + | + */ + /** * offsetGet for ArrayAccess/Backwards compatibility. * @deprecated Add deprecation notices in future release. @@ -77,41 +232,6 @@ class WC_Order_Item_Fee extends WC_Order_Item { return parent::offsetExists( $offset ); } - /** - * Read/populate data properties specific to this order item. - */ - public function read( $id ) { - parent::read( $id ); - - if ( ! $this->get_id() ) { - return; - } - - $this->set_props( array( - 'tax_class' => get_metadata( 'order_item', $this->get_id(), '_tax_class', true ), - 'tax_status' => get_metadata( 'order_item', $this->get_id(), '_tax_status', true ), - 'total' => get_metadata( 'order_item', $this->get_id(), '_line_total', true ), - 'taxes' => get_metadata( 'order_item', $this->get_id(), '_line_tax_data', true ), - ) ); - } - - /** - * Save properties specific to this order item. - * @return int Item ID - */ - public function save() { - parent::save(); - if ( $this->get_id() ) { - wc_update_order_item_meta( $this->get_id(), '_tax_class', $this->get_tax_class() ); - wc_update_order_item_meta( $this->get_id(), '_tax_status', $this->get_tax_status() ); - wc_update_order_item_meta( $this->get_id(), '_line_total', $this->get_total() ); - wc_update_order_item_meta( $this->get_id(), '_line_tax', $this->get_total_tax() ); - wc_update_order_item_meta( $this->get_id(), '_line_tax_data', $this->get_taxes() ); - } - - return $this->get_id(); - } - /** * Internal meta keys we don't want exposed as part of meta_data. * @return array() @@ -119,134 +239,4 @@ class WC_Order_Item_Fee extends WC_Order_Item { protected function get_internal_meta_keys() { return array( '_tax_class', '_tax_status', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' ); } - - /* - |-------------------------------------------------------------------------- - | Setters - |-------------------------------------------------------------------------- - */ - - /** - * Set tax class. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_tax_class( $value ) { - if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { - $this->error( 'order_item_fee_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); - } - $this->_data['tax_class'] = $value; - } - - /** - * Set tax_status. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_tax_status( $value ) { - if ( in_array( $value, array( 'taxable', 'none' ) ) ) { - $this->_data['tax_status'] = $value; - } else { - $this->_data['tax_status'] = 'taxable'; - } - } - - /** - * Set total. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_total( $value ) { - $this->_data['total'] = wc_format_decimal( $value ); - } - - /** - * Set total tax. - * @param string $value - * @throws WC_Data_Exception - */ - protected function set_total_tax( $value ) { - $this->_data['total_tax'] = wc_format_decimal( $value ); - } - - /** - * Set taxes. - * - * This is an array of tax ID keys with total amount values. - * @param array $raw_tax_data - * @throws WC_Data_Exception - */ - public function set_taxes( $raw_tax_data ) { - $raw_tax_data = maybe_unserialize( $raw_tax_data ); - $tax_data = array( - 'total' => array(), - ); - if ( ! empty( $raw_tax_data['total'] ) ) { - $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); - } - $this->_data['taxes'] = $tax_data; - $this->set_total_tax( array_sum( $tax_data['total'] ) ); - } - - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - */ - - /** - * Get order item name. - * @return string - */ - public function get_name() { - return $this->_data['name'] ? $this->_data['name'] : __( 'Fee', 'woocommerce' ); - } - - /** - * Get order item type. - * @return string - */ - public function get_type() { - return 'fee'; - } - - /** - * Get tax class. - * @return string - */ - public function get_tax_class() { - return $this->_data['tax_class']; - } - - /** - * Get tax status. - * @return string - */ - public function get_tax_status() { - return $this->_data['tax_status']; - } - - /** - * Get total fee. - * @return string - */ - public function get_total() { - return wc_format_decimal( $this->_data['total'] ); - } - - /** - * Get total tax. - * @return string - */ - public function get_total_tax() { - return wc_format_decimal( $this->_data['total_tax'] ); - } - - /** - * Get fee taxes. - * @return array - */ - public function get_taxes() { - return $this->_data['taxes']; - } } diff --git a/includes/class-wc-order-item-product.php b/includes/class-wc-order-item-product.php index 8abb1bc78b5..0ed434c7397 100644 --- a/includes/class-wc-order-item-product.php +++ b/includes/class-wc-order-item-product.php @@ -19,10 +19,7 @@ class WC_Order_Item_Product extends WC_Order_Item { * @since 2.7.0 * @var array */ - protected $_data = array( - 'order_id' => 0, - 'id' => 0, - 'name' => '', + protected $extra_data = array( 'product_id' => 0, 'variation_id' => 0, 'quantity' => 1, @@ -37,118 +34,286 @@ class WC_Order_Item_Product extends WC_Order_Item { ), ); + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + */ + /** - * offsetGet for ArrayAccess/Backwards compatibility. + * Set quantity. * - * @deprecated Add deprecation notices in future release. - * @param string $offset - * @return mixed + * @param int $value + * @throws WC_Data_Exception */ - public function offsetGet( $offset ) { - if ( 'line_subtotal' === $offset ) { - $offset = 'subtotal'; - } elseif ( 'line_subtotal_tax' === $offset ) { - $offset = 'subtotal_tax'; - } elseif ( 'line_total' === $offset ) { - $offset = 'total'; - } elseif ( 'line_tax' === $offset ) { - $offset = 'total_tax'; - } elseif ( 'line_tax_data' === $offset ) { - $offset = 'taxes'; + public function set_quantity( $value ) { + if ( 0 > $value ) { + $this->error( 'order_item_product_invalid_quantity', __( 'Quantity must be positive', 'woocommerce' ) ); } - return parent::offsetGet( $offset ); + $this->set_prop( 'quantity', wc_stock_amount( $value ) ); } /** - * offsetSet for ArrayAccess/Backwards compatibility. + * Set tax class. * - * @deprecated Add deprecation notices in future release. - * @param string $offset - * @param mixed $value + * @param string $value + * @throws WC_Data_Exception */ - public function offsetSet( $offset, $value ) { - if ( 'line_subtotal' === $offset ) { - $offset = 'subtotal'; - } elseif ( 'line_subtotal_tax' === $offset ) { - $offset = 'subtotal_tax'; - } elseif ( 'line_total' === $offset ) { - $offset = 'total'; - } elseif ( 'line_tax' === $offset ) { - $offset = 'total_tax'; - } elseif ( 'line_tax_data' === $offset ) { - $offset = 'taxes'; + public function set_tax_class( $value ) { + if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { + $this->error( 'order_item_product_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); } - parent::offsetSet( $offset, $value ); + $this->set_prop( 'tax_class', $value ); } /** - * offsetExists for ArrayAccess. + * Set Product ID * - * @param string $offset - * @return bool + * @param int $value + * @throws WC_Data_Exception */ - public function offsetExists( $offset ) { - if ( in_array( $offset, array( 'line_subtotal', 'line_subtotal_tax', 'line_total', 'line_tax', 'line_tax_data', 'item_meta_array', 'item_meta' ) ) ) { - return true; + public function set_product_id( $value ) { + if ( $value > 0 && 'product' !== get_post_type( absint( $value ) ) ) { + $this->error( 'order_item_product_invalid_product_id', __( 'Invalid product ID', 'woocommerce' ) ); } - return parent::offsetExists( $offset ); + $this->set_prop( 'product_id', absint( $value ) ); } /** - * Read/populate data properties specific to this order item. + * Set variation ID. + * + * @param int $value + * @throws WC_Data_Exception */ - public function read( $id ) { - parent::read( $id ); - - if ( ! $this->get_id() ) { - return; + public function set_variation_id( $value ) { + if ( $value > 0 && 'product_variation' !== get_post_type( $value ) ) { + $this->error( 'order_item_product_invalid_variation_id', __( 'Invalid variation ID', 'woocommerce' ) ); } - - $this->set_props( array( - 'product_id' => get_metadata( 'order_item', $this->get_id(), '_product_id', true ), - 'variation_id' => get_metadata( 'order_item', $this->get_id(), '_variation_id', true ), - 'quantity' => get_metadata( 'order_item', $this->get_id(), '_qty', true ), - 'tax_class' => get_metadata( 'order_item', $this->get_id(), '_tax_class', true ), - 'subtotal' => get_metadata( 'order_item', $this->get_id(), '_line_subtotal', true ), - 'total' => get_metadata( 'order_item', $this->get_id(), '_line_total', true ), - 'taxes' => get_metadata( 'order_item', $this->get_id(), '_line_tax_data', true ), - ) ); + $this->set_prop( 'variation_id', absint( $value ) ); } /** - * Save properties specific to this order item. + * Line subtotal (before discounts). * - * @return int Item ID + * @param string $value + * @throws WC_Data_Exception */ - public function save() { - parent::save(); - if ( $this->get_id() ) { - wc_update_order_item_meta( $this->get_id(), '_product_id', $this->get_product_id() ); - wc_update_order_item_meta( $this->get_id(), '_variation_id', $this->get_variation_id() ); - wc_update_order_item_meta( $this->get_id(), '_qty', $this->get_quantity() ); - wc_update_order_item_meta( $this->get_id(), '_tax_class', $this->get_tax_class() ); - wc_update_order_item_meta( $this->get_id(), '_line_subtotal', $this->get_subtotal() ); - wc_update_order_item_meta( $this->get_id(), '_line_subtotal_tax', $this->get_subtotal_tax() ); - wc_update_order_item_meta( $this->get_id(), '_line_total', $this->get_total() ); - wc_update_order_item_meta( $this->get_id(), '_line_tax', $this->get_total_tax() ); - wc_update_order_item_meta( $this->get_id(), '_line_tax_data', $this->get_taxes() ); - } - - return $this->get_id(); + public function set_subtotal( $value ) { + $this->set_prop( 'subtotal', wc_format_decimal( $value ) ); } /** - * Internal meta keys we don't want exposed as part of meta_data. + * Line total (after discounts). * - * @return array() + * @param string $value + * @throws WC_Data_Exception */ - protected function get_internal_meta_keys() { - return array( '_product_id', '_variation_id', '_qty', '_tax_class', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' ); + public function set_total( $value ) { + $this->set_prop( 'total', wc_format_decimal( $value ) ); + + // Subtotal cannot be less than total + if ( ! $this->get_subtotal() || $this->get_subtotal() < $this->get_total() ) { + $this->set_subtotal( $value ); + } + } + + /** + * Line subtotal tax (before discounts). + * + * @param string $value + * @throws WC_Data_Exception + */ + protected function set_subtotal_tax( $value ) { + $this->set_prop( 'subtotal_tax', wc_format_decimal( $value ) ); + } + + /** + * Line total tax (after discounts). + * + * @param string $value + * @throws WC_Data_Exception + */ + protected function set_total_tax( $value ) { + $this->set_prop( 'total_tax', wc_format_decimal( $value ) ); + } + + /** + * Set line taxes and totals for passed in taxes. + * + * @param array $raw_tax_data + * @throws WC_Data_Exception + */ + public function set_taxes( $raw_tax_data ) { + $raw_tax_data = maybe_unserialize( $raw_tax_data ); + $tax_data = array( + 'total' => array(), + 'subtotal' => array(), + ); + if ( ! empty( $raw_tax_data['total'] ) && ! empty( $raw_tax_data['subtotal'] ) ) { + $tax_data['subtotal'] = array_map( 'wc_format_decimal', $raw_tax_data['subtotal'] ); + $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); + + // Subtotal cannot be less than total! + if ( array_sum( $tax_data['subtotal'] ) < array_sum( $tax_data['total'] ) ) { + $tax_data['subtotal'] = $tax_data['total']; + } + } + $this->set_prop( 'taxes', $tax_data ); + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + $this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) ); + } + + /** + * Set variation data (stored as meta data - write only). + * + * @param array $data Key/Value pairs + */ + public function set_variation( $data ) { + foreach ( $data as $key => $value ) { + $this->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true ); + } + } + + /** + * Set properties based on passed in product object. + * + * @param WC_Product $product + * @throws WC_Data_Exception + */ + public function set_product( $product ) { + if ( ! is_a( $product, 'WC_Product' ) ) { + $this->error( 'order_item_product_invalid_product', __( 'Invalid product', 'woocommerce' ) ); + } + if ( $product->is_type( 'variation' ) ) { + $this->set_product_id( $product->get_parent_id() ); + $this->set_variation_id( $product->get_id() ); + $this->set_variation( is_callable( array( $product, 'get_variation_attributes' ) ) ? $product->get_variation_attributes() : array() ); + } else { + $this->set_product_id( $product->get_id() ); + } + $this->set_name( $product->get_name() ); + $this->set_tax_class( $product->get_tax_class() ); + } + + /** + * Set meta data for backordered products. + */ + public function set_backorder_meta() { + if ( $this->get_product()->backorders_require_notification() && $this->get_product()->is_on_backorder( $this->get_quantity() ) ) { + $this->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $this->get_quantity() - max( 0, $this->get_product()->get_stock_quantity() ), true ); + } + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + */ + + /** + * Get order item type. + * + * @param string $context + * @return string + */ + public function get_type( $context = 'view' ) { + return 'line_item'; + } + + /** + * Get product ID. + * + * @param string $context + * @return int + */ + public function get_product_id( $context = 'view' ) { + return $this->get_prop( 'product_id', $context ); + } + + /** + * Get variation ID. + * + * @param string $context + * @return int + */ + public function get_variation_id( $context = 'view' ) { + return $this->get_prop( 'variation_id', $context ); + } + + /** + * Get quantity. + * + * @param string $context + * @return int + */ + public function get_quantity( $context = 'view' ) { + return $this->get_prop( 'quantity', $context ); + } + + /** + * Get tax class. + * + * @param string $context + * @return string + */ + public function get_tax_class( $context = 'view' ) { + return $this->get_prop( 'tax_class', $context ); + } + + /** + * Get subtotal. + * + * @param string $context + * @return string + */ + public function get_subtotal( $context = 'view' ) { + return $this->get_prop( 'subtotal', $context ); + } + + /** + * Get subtotal tax. + * + * @param string $context + * @return string + */ + public function get_subtotal_tax( $context = 'view' ) { + return $this->get_prop( 'subtotal_tax', $context ); + } + + /** + * Get total. + * + * @param string $context + * @return string + */ + public function get_total( $context = 'view' ) { + return $this->get_prop( 'total', $context ); + } + + /** + * Get total tax. + * + * @param string $context + * @return string + */ + public function get_total_tax( $context = 'view' ) { + return $this->get_prop( 'total_tax', $context ); + } + + /** + * Get fee taxes. + * + * @param string $context + * @return array + */ + public function get_taxes( $context = 'view' ) { + return $this->get_prop( 'taxes', $context ); } /** * Get the associated product. * + * @param string $context * @return WC_Product|bool */ public function get_product() { @@ -217,7 +382,6 @@ class WC_Order_Item_Product extends WC_Order_Item { /** * Get tax status. - * * @return string */ public function get_tax_status() { @@ -225,269 +389,78 @@ class WC_Order_Item_Product extends WC_Order_Item { return $product ? $product->get_tax_status() : 'taxable'; } - /** - * Set meta data for backordered products. - */ - public function set_backorder_meta() { - if ( $this->get_product()->backorders_require_notification() && $this->get_product()->is_on_backorder( $this->get_quantity() ) ) { - $this->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $this->get_quantity() - max( 0, $this->get_product()->get_stock_quantity() ), true ); - } - } - /* |-------------------------------------------------------------------------- - | Setters + | Array Access Methods |-------------------------------------------------------------------------- + | + | For backwards compat with legacy arrays. + | */ /** - * Set quantity. + * offsetGet for ArrayAccess/Backwards compatibility. * - * @param int $value - * @throws WC_Data_Exception + * @deprecated Add deprecation notices in future release. + * @param string $offset + * @return mixed */ - public function set_quantity( $value ) { - if ( 0 > $value ) { - $this->error( 'order_item_product_invalid_quantity', __( 'Quantity must be positive', 'woocommerce' ) ); + public function offsetGet( $offset ) { + if ( 'line_subtotal' === $offset ) { + $offset = 'subtotal'; + } elseif ( 'line_subtotal_tax' === $offset ) { + $offset = 'subtotal_tax'; + } elseif ( 'line_total' === $offset ) { + $offset = 'total'; + } elseif ( 'line_tax' === $offset ) { + $offset = 'total_tax'; + } elseif ( 'line_tax_data' === $offset ) { + $offset = 'taxes'; } - $this->_data['quantity'] = wc_stock_amount( $value ); + return parent::offsetGet( $offset ); } /** - * Set tax class. + * offsetSet for ArrayAccess/Backwards compatibility. * - * @param string $value - * @throws WC_Data_Exception + * @deprecated Add deprecation notices in future release. + * @param string $offset + * @param mixed $value */ - public function set_tax_class( $value ) { - if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { - $this->error( 'order_item_product_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); + public function offsetSet( $offset, $value ) { + if ( 'line_subtotal' === $offset ) { + $offset = 'subtotal'; + } elseif ( 'line_subtotal_tax' === $offset ) { + $offset = 'subtotal_tax'; + } elseif ( 'line_total' === $offset ) { + $offset = 'total'; + } elseif ( 'line_tax' === $offset ) { + $offset = 'total_tax'; + } elseif ( 'line_tax_data' === $offset ) { + $offset = 'taxes'; } - $this->_data['tax_class'] = $value; + parent::offsetSet( $offset, $value ); } /** - * Set Product ID. + * offsetExists for ArrayAccess * - * @param int $value - * @throws WC_Data_Exception + * @param string $offset + * @return bool */ - public function set_product_id( $value ) { - if ( $value > 0 && 'product' !== get_post_type( absint( $value ) ) ) { - $this->error( 'order_item_product_invalid_product_id', __( 'Invalid product ID', 'woocommerce' ) ); + public function offsetExists( $offset ) { + if ( in_array( $offset, array( 'line_subtotal', 'line_subtotal_tax', 'line_total', 'line_tax', 'line_tax_data', 'item_meta_array', 'item_meta' ) ) ) { + return true; } - $this->_data['product_id'] = absint( $value ); + return parent::offsetExists( $offset ); } /** - * Set variation ID. - * - * @param int $value - * @throws WC_Data_Exception - */ - public function set_variation_id( $value ) { - if ( $value > 0 && 'product_variation' !== get_post_type( $value ) ) { - $this->error( 'order_item_product_invalid_variation_id', __( 'Invalid variation ID', 'woocommerce' ) ); - } - $this->_data['variation_id'] = absint( $value ); - } - - /** - * Line subtotal (before discounts). - * - * @param string $value - * @throws WC_Data_Exception - */ - public function set_subtotal( $value ) { - $this->_data['subtotal'] = wc_format_decimal( $value ); - } - - /** - * Line total (after discounts). - * - * @param string $value - * @throws WC_Data_Exception - */ - public function set_total( $value ) { - $this->_data['total'] = wc_format_decimal( $value ); - - // Subtotal cannot be less than total - if ( ! $this->get_subtotal() || $this->get_subtotal() < $this->get_total() ) { - $this->set_subtotal( $value ); - } - } - - /** - * Line subtotal tax (before discounts). - * - * @param string $value - * @throws WC_Data_Exception - */ - protected function set_subtotal_tax( $value ) { - $this->_data['subtotal_tax'] = wc_format_decimal( $value ); - } - - /** - * Line total tax (after discounts). - * - * @param string $value - * @throws WC_Data_Exception - */ - protected function set_total_tax( $value ) { - $this->_data['total_tax'] = wc_format_decimal( $value ); - } - - /** - * Set line taxes and totals for passed in taxes. - * - * @param array $raw_tax_data - * @throws WC_Data_Exception - */ - public function set_taxes( $raw_tax_data ) { - $raw_tax_data = maybe_unserialize( $raw_tax_data ); - $tax_data = array( - 'total' => array(), - 'subtotal' => array(), - ); - if ( ! empty( $raw_tax_data['total'] ) && ! empty( $raw_tax_data['subtotal'] ) ) { - $tax_data['subtotal'] = array_map( 'wc_format_decimal', $raw_tax_data['subtotal'] ); - $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); - - // Subtotal cannot be less than total! - if ( array_sum( $tax_data['subtotal'] ) < array_sum( $tax_data['total'] ) ) { - $tax_data['subtotal'] = $tax_data['total']; - } - } - $this->_data['taxes'] = $tax_data; - $this->set_total_tax( array_sum( $tax_data['total'] ) ); - $this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) ); - } - - /** - * Set variation data (stored as meta data - write only). - * - * @param array $data Key/Value pairs - */ - public function set_variation( $data ) { - foreach ( $data as $key => $value ) { - $this->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true ); - } - } - - /** - * Set properties based on passed in product object. - * - * @param WC_Product $product - * @throws WC_Data_Exception - */ - public function set_product( $product ) { - if ( ! is_a( $product, 'WC_Product' ) ) { - $this->error( 'order_item_product_invalid_product', __( 'Invalid product', 'woocommerce' ) ); - } - if ( $product->is_type( 'variation' ) ) { - $this->set_product_id( $product->get_parent_id() ); - $this->set_variation_id( $product->get_id() ); - $this->set_variation( is_callable( array( $product, 'get_variation_attributes' ) ) ? $product->get_variation_attributes() : array() ); - } else { - $this->set_product_id( $product->get_id() ); - } - $this->set_name( $product->get_name() ); - $this->set_tax_class( $product->get_tax_class() ); - } - - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - */ - - /** - * Get order item type. - * - * @return string - */ - public function get_type() { - return 'line_item'; - } - - /** - * Get product ID. - * - * @return int - */ - public function get_product_id() { - return absint( $this->_data['product_id'] ); - } - - /** - * Get variation ID. - * - * @return int - */ - public function get_variation_id() { - return absint( $this->_data['variation_id'] ); - } - - /** - * Get quantity. - * - * @return int - */ - public function get_quantity() { - return wc_stock_amount( $this->_data['quantity'] ); - } - - /** - * Get tax class. - * - * @return string - */ - public function get_tax_class() { - return $this->_data['tax_class']; - } - - /** - * Get subtotal. - * - * @return string - */ - public function get_subtotal() { - return wc_format_decimal( $this->_data['subtotal'] ); - } - - /** - * Get subtotal tax. - * - * @return string - */ - public function get_subtotal_tax() { - return wc_format_decimal( $this->_data['subtotal_tax'] ); - } - - /** - * Get total. - * - * @return string - */ - public function get_total() { - return wc_format_decimal( $this->_data['total'] ); - } - - /** - * Get total tax. - * - * @return string - */ - public function get_total_tax() { - return wc_format_decimal( $this->_data['total_tax'] ); - } - - /** - * Get fee taxes. + * Internal meta keys we don't want exposed as part of meta_data. * * @return array */ - public function get_taxes() { - return $this->_data['taxes']; + protected function get_internal_meta_keys() { + return array( '_product_id', '_variation_id', '_qty', '_tax_class', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' ); } } diff --git a/includes/class-wc-order-item-shipping.php b/includes/class-wc-order-item-shipping.php index f33a106480b..8c96bf3406e 100644 --- a/includes/class-wc-order-item-shipping.php +++ b/includes/class-wc-order-item-shipping.php @@ -18,9 +18,7 @@ class WC_Order_Item_Shipping extends WC_Order_Item { * @since 2.7.0 * @var array */ - protected $_data = array( - 'order_id' => 0, - 'id' => 0, + protected $extra_data = array( 'method_title' => '', 'method_id' => '', 'total' => 0, @@ -30,6 +28,181 @@ class WC_Order_Item_Shipping extends WC_Order_Item { ), ); + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + */ + + /** + * Set order item name. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_name( $value ) { + $this->set_method_title( $value ); + } + + /** + * Set method title. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_method_title( $value ) { + $this->set_prop( 'method_title', wc_clean( $value ) ); + } + + /** + * Set shipping method id. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_method_id( $value ) { + $this->set_prop( 'method_id', wc_clean( $value ) ); + } + + /** + * Set total. + * + * @param string $value + * @throws WC_Data_Exception + */ + public function set_total( $value ) { + $this->set_prop( 'total', wc_format_decimal( $value ) ); + } + + /** + * Set total tax. + * + * @param string $value + * @throws WC_Data_Exception + */ + protected function set_total_tax( $value ) { + $this->set_prop( 'total_tax', wc_format_decimal( $value ) ); + } + + /** + * Set taxes. + * + * This is an array of tax ID keys with total amount values. + * @param array $raw_tax_data + * @throws WC_Data_Exception + */ + public function set_taxes( $raw_tax_data ) { + $raw_tax_data = maybe_unserialize( $raw_tax_data ); + $tax_data = array( + 'total' => array(), + ); + if ( ! empty( $raw_tax_data['total'] ) ) { + $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); + } + $this->set_prop( 'taxes', $tax_data ); + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + } + + /** + * Set properties based on passed in shipping rate object. + * + * @param WC_Shipping_Rate $tax_rate_id + * @throws WC_Data_Exception + */ + public function set_shipping_rate( $shipping_rate ) { + $this->set_method_title( $shipping_rate->label ); + $this->set_method_id( $shipping_rate->id ); + $this->set_total( $shipping_rate->cost ); + $this->set_taxes( $shipping_rate->taxes ); + $this->set_meta_data( $shipping_rate->get_meta_data() ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + */ + + /** + * Get order item type. + * + * @param string $context + * @return string + */ + public function get_type( $context = 'view' ) { + return 'shipping'; + } + + /** + * Get order item name. + * + * @param string $context + * @return string + */ + public function get_name( $context = 'view' ) { + return $this->get_method_title( $context ); + } + + /** + * Get title. + * + * @param string $context + * @return string + */ + public function get_method_title( $context = 'view' ) { + $method_title = $this->get_prop( 'method_title', $context ); + return $method_title ? $method_title : __( 'Shipping', 'woocommerce' ); + } + + /** + * Get method ID. + * + * @param string $context + * @return string + */ + public function get_method_id( $context = 'view' ) { + return $this->get_prop( 'method_id', $context ); + } + + /** + * Get total cost. + * + * @param string $context + * @return string + */ + public function get_total( $context = 'view' ) { + return $this->get_prop( 'total', $context ); + } + + /** + * Get total tax. + * + * @param string $context + * @return string + */ + public function get_total_tax( $context = 'view' ) { + return $this->get_prop( 'total_tax', $context ); + } + + /** + * Get taxes. + * + * @param string $context + * @return array + */ + public function get_taxes( $context = 'view' ) { + return $this->get_prop( 'taxes', $context ); + } + + /* + |-------------------------------------------------------------------------- + | Array Access Methods + |-------------------------------------------------------------------------- + | + | For backwards compat with legacy arrays. + | + */ + /** * offsetGet for ArrayAccess/Backwards compatibility. * @deprecated Add deprecation notices in future release. @@ -68,39 +241,6 @@ class WC_Order_Item_Shipping extends WC_Order_Item { return parent::offsetExists( $offset ); } - /** - * Read/populate data properties specific to this order item. - */ - public function read( $id ) { - parent::read( $id ); - - if ( ! $this->get_id() ) { - return; - } - - $this->set_props( array( - 'method_id' => get_metadata( 'order_item', $this->get_id(), 'method_id', true ), - 'total' => get_metadata( 'order_item', $this->get_id(), 'cost', true ), - 'taxes' => get_metadata( 'order_item', $this->get_id(), 'taxes', true ), - ) ); - } - - /** - * Save properties specific to this order item. - * @return int Item ID - */ - public function save() { - parent::save(); - if ( $this->get_id() ) { - wc_update_order_item_meta( $this->get_id(), 'method_id', $this->get_method_id() ); - wc_update_order_item_meta( $this->get_id(), 'cost', $this->get_total() ); - wc_update_order_item_meta( $this->get_id(), 'total_tax', $this->get_total_tax() ); - wc_update_order_item_meta( $this->get_id(), 'taxes', $this->get_taxes() ); - } - - return $this->get_id(); - } - /** * Internal meta keys we don't want exposed as part of meta_data. * @return array() @@ -108,149 +248,4 @@ class WC_Order_Item_Shipping extends WC_Order_Item { protected function get_internal_meta_keys() { return array( 'method_id', 'cost', 'total_tax', 'taxes' ); } - - /* - |-------------------------------------------------------------------------- - | Setters - |-------------------------------------------------------------------------- - */ - - /** - * Set order item name. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_name( $value ) { - $this->set_method_title( $value ); - } - - /** - * Set code. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_method_title( $value ) { - $this->_data['method_title'] = wc_clean( $value ); - } - - /** - * Set shipping method id. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_method_id( $value ) { - $this->_data['method_id'] = wc_clean( $value ); - } - - /** - * Set total. - * @param string $value - * @throws WC_Data_Exception - */ - public function set_total( $value ) { - $this->_data['total'] = wc_format_decimal( $value ); - } - - /** - * Set total tax. - * @param string $value - * @throws WC_Data_Exception - */ - protected function set_total_tax( $value ) { - $this->_data['total_tax'] = wc_format_decimal( $value ); - } - - /** - * Set taxes. - * - * This is an array of tax ID keys with total amount values. - * @param array $raw_tax_data - * @throws WC_Data_Exception - */ - public function set_taxes( $raw_tax_data ) { - $raw_tax_data = maybe_unserialize( $raw_tax_data ); - $tax_data = array( - 'total' => array(), - ); - if ( ! empty( $raw_tax_data['total'] ) ) { - $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); - } - $this->_data['taxes'] = $tax_data; - $this->set_total_tax( array_sum( $tax_data['total'] ) ); - } - - /** - * Set properties based on passed in shipping rate object. - * @param WC_Shipping_Rate $tax_rate_id - * @throws WC_Data_Exception - */ - public function set_shipping_rate( $shipping_rate ) { - $this->set_method_title( $shipping_rate->label ); - $this->set_method_id( $shipping_rate->id ); - $this->set_total( $shipping_rate->cost ); - $this->set_taxes( $shipping_rate->taxes ); - $this->set_meta_data( $shipping_rate->get_meta_data() ); - } - - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - */ - - /** - * Get order item type. - * @return string - */ - public function get_type() { - return 'shipping'; - } - - /** - * Get order item name. - * @return string - */ - public function get_name() { - return $this->get_method_title(); - } - - /** - * Get title. - * @return string - */ - public function get_method_title() { - return $this->_data['method_title'] ? $this->_data['method_title'] : __( 'Shipping', 'woocommerce' ); - } - - /** - * Get method ID. - * @return string - */ - public function get_method_id() { - return $this->_data['method_id']; - } - - /** - * Get total cost. - * @return string - */ - public function get_total() { - return wc_format_decimal( $this->_data['total'] ); - } - - /** - * Get total tax. - * @return string - */ - public function get_total_tax() { - return wc_format_decimal( $this->_data['total_tax'] ); - } - - /** - * Get taxes. - * @return array - */ - public function get_taxes() { - return $this->_data['taxes']; - } } diff --git a/includes/class-wc-order-item-tax.php b/includes/class-wc-order-item-tax.php index bbcf65d9c8f..32dd57b711d 100644 --- a/includes/class-wc-order-item-tax.php +++ b/includes/class-wc-order-item-tax.php @@ -18,9 +18,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @since 2.7.0 * @var array */ - protected $_data = array( - 'order_id' => 0, - 'id' => 0, + protected $extra_data = array( 'rate_code' => '', 'rate_id' => 0, 'label' => '', @@ -29,58 +27,6 @@ class WC_Order_Item_Tax extends WC_Order_Item { 'shipping_tax_total' => 0, ); - /** - * Read/populate data properties specific to this order item. - */ - public function read( $id ) { - parent::read( $id ); - - if ( ! $this->get_id() ) { - return; - } - - $this->set_props( array( - 'rate_id' => get_metadata( 'order_item', $this->get_id(), 'rate_id', true ), - 'label' => get_metadata( 'order_item', $this->get_id(), 'label', true ), - 'compound' => get_metadata( 'order_item', $this->get_id(), 'compound', true ), - 'tax_total' => get_metadata( 'order_item', $this->get_id(), 'tax_amount', true ), - 'shipping_tax_total' => get_metadata( 'order_item', $this->get_id(), 'shipping_tax_amount', true ), - ) ); - } - - /** - * Save properties specific to this order item. - * @return int Item ID - */ - public function save() { - parent::save(); - if ( $this->get_id() ) { - wc_update_order_item_meta( $this->get_id(), 'rate_id', $this->get_rate_id() ); - wc_update_order_item_meta( $this->get_id(), 'label', $this->get_label() ); - wc_update_order_item_meta( $this->get_id(), 'compound', $this->get_compound() ); - wc_update_order_item_meta( $this->get_id(), 'tax_amount', $this->get_tax_total() ); - wc_update_order_item_meta( $this->get_id(), 'shipping_tax_amount', $this->get_shipping_tax_total() ); - } - - return $this->get_id(); - } - - /** - * Internal meta keys we don't want exposed as part of meta_data. - * @return array() - */ - protected function get_internal_meta_keys() { - return array( 'rate_id', 'label', 'compound', 'tax_amount', 'shipping_tax_amount' ); - } - - /** - * Is this a compound tax rate? - * @return boolean - */ - public function is_compound() { - return $this->get_compound(); - } - /* |-------------------------------------------------------------------------- | Setters @@ -89,6 +35,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { /** * Set order item name. + * * @param string $value * @throws WC_Data_Exception */ @@ -98,11 +45,12 @@ class WC_Order_Item_Tax extends WC_Order_Item { /** * Set item name. + * * @param string $value * @throws WC_Data_Exception */ public function set_rate_code( $value ) { - $this->_data['rate_code'] = wc_clean( $value ); + $this->set_prop( 'rate_code', wc_clean( $value ) ); } /** @@ -111,7 +59,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_label( $value ) { - $this->_data['label'] = wc_clean( $value ); + $this->set_prop( 'label', wc_clean( $value ) ); } /** @@ -120,7 +68,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_rate_id( $value ) { - $this->_data['rate_id'] = absint( $value ); + $this->set_prop( 'rate_id', absint( $value ) ); } /** @@ -129,7 +77,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_tax_total( $value ) { - $this->_data['tax_total'] = wc_format_decimal( $value ); + $this->set_prop( 'tax_total', wc_format_decimal( $value ) ); } /** @@ -138,7 +86,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_shipping_tax_total( $value ) { - $this->_data['shipping_tax_total'] = wc_format_decimal( $value ); + $this->set_prop( 'shipping_tax_total', wc_format_decimal( $value ) ); } /** @@ -147,7 +95,7 @@ class WC_Order_Item_Tax extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_compound( $value ) { - $this->_data['compound'] = (bool) $value; + $this->set_prop( 'compound', (bool) $value ); } /** @@ -170,65 +118,104 @@ class WC_Order_Item_Tax extends WC_Order_Item { /** * Get order item type. + * + * @param string $context * @return string */ - public function get_type() { + public function get_type( $context = 'view' ) { return 'tax'; } /** * Get rate code/name. + * + * @param string $context * @return string */ - public function get_name() { - return $this->get_rate_code(); + public function get_name( $context = 'view' ) { + return $this->get_rate_code( $context ); } /** * Get rate code/name. + * + * @param string $context * @return string */ - public function get_rate_code() { - return $this->_data['rate_code']; + public function get_rate_code( $context = 'view' ) { + return $this->get_prop( 'rate_code', $context ); } /** * Get label. + * + * @param string $context * @return string */ - public function get_label() { - return $this->_data['label'] ? $this->_data['label'] : __( 'Tax', 'woocommerce' ); + public function get_label( $context = 'view' ) { + $label = $this->get_prop( 'label', $context ); + return $label ? $label : __( 'Tax', 'woocommerce' ); } /** * Get tax rate ID. + * + * @param string $context * @return int */ - public function get_rate_id() { - return absint( $this->_data['rate_id'] ); + public function get_rate_id( $context = 'view' ) { + return $this->get_prop( 'rate_id', $context ); } /** * Get tax_total + * + * @param string $context * @return string */ - public function get_tax_total() { - return wc_format_decimal( $this->_data['tax_total'] ); + public function get_tax_total( $context = 'view' ) { + return $this->get_prop( 'tax_total', $context ); } /** * Get shipping_tax_total + * + * @param string $context * @return string */ - public function get_shipping_tax_total() { - return wc_format_decimal( $this->_data['shipping_tax_total'] ); + public function get_shipping_tax_total( $context = 'view' ) { + return $this->get_prop( 'shipping_tax_total', $context ); } /** * Get compound. + * + * @param string $context * @return bool */ - public function get_compound() { - return (bool) $this->_data['compound']; + public function get_compound( $context = 'view' ) { + return $this->get_prop( 'compound', $context ); + } + + /* + |-------------------------------------------------------------------------- + | Other + |-------------------------------------------------------------------------- + */ + + /** + * Internal meta keys we don't want exposed as part of meta_data. + * @return array() + */ + protected function get_internal_meta_keys() { + return array( 'rate_id', 'label', 'compound', 'tax_amount', 'shipping_tax_amount' ); + } + + /** + * Is this a compound tax rate? + * @return boolean + */ + public function is_compound() { + return $this->get_compound(); } } diff --git a/includes/class-wc-order-item.php b/includes/class-wc-order-item.php index 38c728a411c..4bc29493b06 100644 --- a/includes/class-wc-order-item.php +++ b/includes/class-wc-order-item.php @@ -49,29 +49,61 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { /** * Constructor. - * @param int|object|array $read ID to load from the DB (optional) or already queried data. + * @param int|object|array $item ID to load from the DB, or WC_Order_Item Object */ - public function __construct( $read = 0 ) { - parent::__construct( $read ); + public function __construct( $item = 0 ) { + $this->data = array_merge( $this->data, $this->extra_data ); + parent::__construct( $item ); - if ( $read instanceof WC_Order_Item ) { - if ( $this->is_type( $read->get_type() ) ) { - $this->set_props( $read->get_data() ); - } - } elseif ( is_array( $read ) ) { - $this->set_props( $read ); + if ( $item instanceof WC_Order_Item ) { + $this->set_id( $item->get_id() ); + } elseif ( is_numeric( $item ) && $item > 0 ) { + $this->set_id( $item ); } else { - $this->read( $read ); + $this->set_object_read( true ); + } + + $type = 'line_item' === $this->get_type() ? 'product' : $this->get_type(); + $this->data_store = WC_Data_Store::load( 'order-item-' . $type ); + if ( $this->get_id() > 0 ) { + $this->data_store->read( $this ); } } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + */ + /** - * Type checking - * @param string|array $Type - * @return boolean + * Get order ID this meta belongs to. + * + * @param string $context + * @return int */ - public function is_type( $type ) { - return is_array( $type ) ? in_array( $this->get_type(), $type ) : $type === $this->get_type(); + public function get_order_id( $context = 'view' ) { + return $this->get_prop( 'order_id', $context ); + } + + /** + * Get order item name. + * + * @param string $context + * @return string + */ + public function get_name( $context = 'view' ) { + return $this->get_prop( 'name', $context ); + } + + /** + * Get order item type. + * + * @param string $context + * @return string + */ + public function get_type( $context = 'view' ) { + return $this->get_prop( 'type', $context ); } /** @@ -93,36 +125,6 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { return $this->order; } - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - */ - - /** - * Get order ID this meta belongs to. - * @return int - */ - public function get_order_id() { - return $this->data['order_id']; - } - - /** - * Get order item name. - * @return string - */ - public function get_name() { - return $this->data['name']; - } - - /** - * Get order item type. - * @return string - */ - public function get_type() { - return $this->data['type']; - } - /* |-------------------------------------------------------------------------- | Setters @@ -131,20 +133,22 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { /** * Set order ID. - * @param int $value + * + * @param int $value * @throws WC_Data_Exception */ public function set_order_id( $value ) { - $this->data['order_id'] = absint( $value ); + $this->set_prop( 'order_id', absint( $value ) ); } /** * Set order item name. - * @param string $value + * + * @param string $value * @throws WC_Data_Exception */ public function set_name( $value ) { - $this->data['name'] = wc_clean( $value ); + $this->set_prop( 'name', wc_clean( $value ) ); } /** @@ -153,112 +157,38 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { * @throws WC_Data_Exception */ protected function set_type( $value ) { - $this->data['type'] = wc_clean( $value ); + $this->set_prop( 'type', wc_clean( $value ) ); } /* |-------------------------------------------------------------------------- - | CRUD methods + | Other Methods |-------------------------------------------------------------------------- - | - | Methods which create, read, update and delete data from the database. - | */ /** - * Insert data into the database. - * @since 2.7.0 + * Type checking + * @param string|array $Type + * @return boolean */ - public function create() { - global $wpdb; - - $wpdb->insert( $wpdb->prefix . 'woocommerce_order_items', array( - 'order_item_name' => $this->get_name(), - 'order_item_type' => $this->get_type(), - 'order_id' => $this->get_order_id(), - ) ); - $this->set_id( $wpdb->insert_id ); - - do_action( 'woocommerce_new_order_item', $this->get_id(), $this, $this->get_order_id() ); + public function is_type( $type ) { + return is_array( $type ) ? in_array( $this->get_type(), $type ) : $type === $this->get_type(); } /** - * Update data in the database. - * @since 2.7.0 - */ - public function update() { - global $wpdb; - - $wpdb->update( $wpdb->prefix . 'woocommerce_order_items', array( - 'order_item_name' => $this->get_name(), - 'order_item_type' => $this->get_type(), - 'order_id' => $this->get_order_id(), - ), array( 'order_item_id' => $this->get_id() ) ); - - do_action( 'woocommerce_update_order_item', $this->get_id(), $this, $this->get_order_id() ); - } - - /** - * Read from the database. - * @since 2.7.0 - * @param int|object $item ID of object to read, or already queried object. - */ - public function read( $item ) { - global $wpdb; - - $this->set_defaults(); - - if ( is_numeric( $item ) && ! empty( $item ) ) { - $data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d LIMIT 1;", $item ) ); - } elseif ( ! empty( $item->order_item_id ) ) { - $data = $item; - } else { - $data = false; - } - - if ( ! $data ) { - return; - } - - $this->set_id( $data->order_item_id ); - $this->set_props( array( - 'order_id' => $data->order_id, - 'name' => $data->order_item_name, - 'type' => $data->order_item_type, - ) ); - $this->read_meta_data(); - } - - /** - * Save data to the database. - * @since 2.7.0 + * Save properties specific to this order item. * @return int Item ID */ public function save() { - if ( ! $this->get_id() ) { - $this->create(); - } else { - $this->update(); + parent::save(); + + if ( $this->data_store ) { + $this->data_store->save_item_data( $this ); } - $this->save_meta_data(); return $this->get_id(); } - /** - * Delete data from the database. - * @since 2.7.0 - */ - public function delete( $force_delete = false ) { - if ( $this->get_id() ) { - global $wpdb; - do_action( 'woocommerce_before_delete_order_item', $this->get_id() ); - $wpdb->delete( $wpdb->prefix . 'woocommerce_order_items', array( 'order_item_id' => $this->get_id() ) ); - $wpdb->delete( $wpdb->prefix . 'woocommerce_order_itemmeta', array( 'order_item_id' => $this->get_id() ) ); - do_action( 'woocommerce_delete_order_item', $this->get_id() ); - } - } - /* |-------------------------------------------------------------------------- | Meta Data Handling diff --git a/includes/class-wc-order.php b/includes/class-wc-order.php index 148f7baa92a..83bca81ee3c 100644 --- a/includes/class-wc-order.php +++ b/includes/class-wc-order.php @@ -167,6 +167,7 @@ class WC_Order extends WC_Abstract_Order { if ( $this->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_payment_complete', array( 'on-hold', 'pending', 'failed', 'cancelled' ), $this ) ) ) { $order_needs_processing = false; + error_log( print_r ( $this->get_items(), 1 ) ); if ( sizeof( $this->get_items() ) > 0 ) { foreach ( $this->get_items() as $item ) { if ( $item->is_type( 'line_item' ) && ( $product = $item->get_product() ) ) { diff --git a/includes/data-stores/class-wc-order-item-coupon-data-store.php b/includes/data-stores/class-wc-order-item-coupon-data-store.php new file mode 100644 index 00000000000..430a125c94c --- /dev/null +++ b/includes/data-stores/class-wc-order-item-coupon-data-store.php @@ -0,0 +1,34 @@ +set_props( array( + 'discount' => get_metadata( 'order_item', $item->get_id(), 'discount_amount', true ), + 'discount_tax' => get_metadata( 'order_item', $item->get_id(), 'discount_amount_tax', true ), + ) ); + } + + public function save_item_data( &$item ) { + wc_update_order_item_meta( $item->get_id(), 'discount_amount', $item->get_discount() ); + wc_update_order_item_meta( $item->get_id(), 'discount_amount_tax', $item->get_discount_tax() ); + } + +} diff --git a/includes/data-stores/class-wc-order-item-data-store.php b/includes/data-stores/class-wc-order-item-data-store.php new file mode 100644 index 00000000000..11983769fb9 --- /dev/null +++ b/includes/data-stores/class-wc-order-item-data-store.php @@ -0,0 +1,99 @@ +insert( $wpdb->prefix . 'woocommerce_order_items', array( + 'order_item_name' => $item->get_name(), + 'order_item_type' => $item->get_type(), + 'order_id' => $item->get_order_id(), + ) ); + $item->set_id( $wpdb->insert_id ); + + do_action( 'woocommerce_new_order_item', $item->get_id(), $item, $item->get_order_id() ); + + $item->apply_changes(); + } + + /** + * Update a order item in the database. + * + * @since 2.7.0 + * @param WC_Order_Item $item + */ + public function update( &$item ) { + global $wpdb; + + $wpdb->update( $wpdb->prefix . 'woocommerce_order_items', array( + 'order_item_name' => $item->get_name(), + 'order_item_type' => $item->get_type(), + 'order_id' => $item->get_order_id(), + ), array( 'order_item_id' => $item->get_id() ) ); + + do_action( 'woocommerce_update_order_item', $item->get_id(), $item, $item->get_order_id() ); + + $item->apply_changes(); + } + + /** + * Remove an order item from the database. + * + * @since 2.7.0 + * @param WC_Order_Item $item + * @param array $args Array of args to pass to the delete method. + */ + public function delete( &$item, $args = array() ) { + if ( $item->get_id() ) { + global $wpdb; + do_action( 'woocommerce_before_delete_order_item', $item->get_id() ); + $wpdb->delete( $wpdb->prefix . 'woocommerce_order_items', array( 'order_item_id' => $item->get_id() ) ); + $wpdb->delete( $wpdb->prefix . 'woocommerce_order_itemmeta', array( 'order_item_id' => $item->get_id() ) ); + do_action( 'woocommerce_delete_order_item', $item->get_id() ); + } + } + + /** + * Read a order item from the database. + * + * @since 2.7.0 + * @param WC_Order_Item $item + */ + public function read( &$item ) { + global $wpdb; + + $item->set_defaults(); + + $data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d LIMIT 1;", $item->get_id() ) ); + + if ( ! $data ) { + throw new Exception( __( 'Invalid order item.', 'woocommerce' ) ); + } + + $item->set_props( array( + 'order_id' => $data->order_id, + 'name' => $data->order_item_name, + 'type' => $data->order_item_type, + ) ); + $item->read_meta_data(); + $item->set_object_read( true ); + } + +} diff --git a/includes/data-stores/class-wc-order-item-fee-data-store.php b/includes/data-stores/class-wc-order-item-fee-data-store.php new file mode 100644 index 00000000000..3b45b2f99cc --- /dev/null +++ b/includes/data-stores/class-wc-order-item-fee-data-store.php @@ -0,0 +1,39 @@ +set_props( array( + 'tax_class' => get_metadata( 'order_item', $item->get_id(), '_tax_class', true ), + 'tax_status' => get_metadata( 'order_item', $item->get_id(), '_tax_status', true ), + 'total' => get_metadata( 'order_item', $item->get_id(), '_line_total', true ), + 'taxes' => get_metadata( 'order_item', $item->get_id(), '_line_tax_data', true ), + ) ); + } + + public function save_item_data( &$item ) { + wc_update_order_item_meta( $item->get_id(), '_tax_class', $item->get_tax_class() ); + wc_update_order_item_meta( $item->get_id(), '_tax_status', $item->get_tax_status() ); + wc_update_order_item_meta( $item->get_id(), '_line_total', $item->get_total() ); + wc_update_order_item_meta( $item->get_id(), '_line_tax', $item->get_total_tax() ); + wc_update_order_item_meta( $item->get_id(), '_line_tax_data', $item->get_taxes() ); + } + +} diff --git a/includes/data-stores/class-wc-order-item-product-store.php b/includes/data-stores/class-wc-order-item-product-store.php new file mode 100644 index 00000000000..fba3e4717f9 --- /dev/null +++ b/includes/data-stores/class-wc-order-item-product-store.php @@ -0,0 +1,63 @@ +set_props( array( + 'product_id' => get_metadata( 'order_item', $item->get_id(), '_product_id', true ), + 'variation_id' => get_metadata( 'order_item', $item->get_id(), '_variation_id', true ), + 'quantity' => get_metadata( 'order_item', $item->get_id(), '_qty', true ), + 'tax_class' => get_metadata( 'order_item', $item->get_id(), '_tax_class', true ), + 'subtotal' => get_metadata( 'order_item', $item->get_id(), '_line_subtotal', true ), + 'total' => get_metadata( 'order_item', $item->get_id(), '_line_total', true ), + 'taxes' => get_metadata( 'order_item', $item->get_id(), '_line_tax_data', true ), + ) ); + } + + /** + * Save properties specific to this order item. + * + * @return int Item ID + */ + public function save_item_data( &$item ) { + wc_update_order_item_meta( $item->get_id(), '_product_id', $item->get_product_id() ); + wc_update_order_item_meta( $item->get_id(), '_variation_id', $item->get_variation_id() ); + wc_update_order_item_meta( $item->get_id(), '_qty', $item->get_quantity() ); + wc_update_order_item_meta( $item->get_id(), '_tax_class', $item->get_tax_class() ); + wc_update_order_item_meta( $item->get_id(), '_line_subtotal', $item->get_subtotal() ); + wc_update_order_item_meta( $item->get_id(), '_line_subtotal_tax', $item->get_subtotal_tax() ); + wc_update_order_item_meta( $item->get_id(), '_line_total', $item->get_total() ); + wc_update_order_item_meta( $item->get_id(), '_line_tax', $item->get_total_tax() ); + wc_update_order_item_meta( $item->get_id(), '_line_tax_data', $item->get_taxes() ); + } + + public function get_download_ids( $item, $order ) { + global $wpdb; + return $wpdb->get_col( + $wpdb->prepare( + "SELECT download_id FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE user_email = %s AND order_key = %s AND product_id = %d ORDER BY permission_id", + $order->get_billing_email(), + $order->get_order_key(), + $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id() + ) + ); + } + +} diff --git a/includes/data-stores/class-wc-order-item-shipping-data-store.php b/includes/data-stores/class-wc-order-item-shipping-data-store.php new file mode 100644 index 00000000000..f175f67cbe9 --- /dev/null +++ b/includes/data-stores/class-wc-order-item-shipping-data-store.php @@ -0,0 +1,39 @@ +set_props( array( + 'method_id' => get_metadata( 'order_item', $item->get_id(), 'method_id', true ), + 'total' => get_metadata( 'order_item', $item->get_id(), 'cost', true ), + 'taxes' => get_metadata( 'order_item', $item->get_id(), 'taxes', true ), + ) ); + } + + /** + * Save properties specific to this order item. + */ + public function save_item_data( &$item ) { + wc_update_order_item_meta( $item->get_id(), 'method_id', $item->get_method_id() ); + wc_update_order_item_meta( $item->get_id(), 'cost', $item->get_total() ); + wc_update_order_item_meta( $item->get_id(), 'total_tax', $item->get_total_tax() ); + wc_update_order_item_meta( $item->get_id(), 'taxes', $item->get_taxes() ); + } +} diff --git a/includes/data-stores/class-wc-order-item-tax-data-store.php b/includes/data-stores/class-wc-order-item-tax-data-store.php new file mode 100644 index 00000000000..56153c86567 --- /dev/null +++ b/includes/data-stores/class-wc-order-item-tax-data-store.php @@ -0,0 +1,43 @@ +set_props( array( + 'rate_id' => get_metadata( 'order_item', $item->get_id(), 'rate_id', true ), + 'label' => get_metadata( 'order_item', $item->get_id(), 'label', true ), + 'compound' => get_metadata( 'order_item', $item->get_id(), 'compound', true ), + 'tax_total' => get_metadata( 'order_item', $item->get_id(), 'tax_amount', true ), + 'shipping_tax_total' => get_metadata( 'order_item', $item->get_id(), 'shipping_tax_amount', true ), + ) ); + } + + /** + * Save properties specific to this order item. + */ + public function save_item_data( &$item ) { + wc_update_order_item_meta( $item->get_id(), 'rate_id', $item->get_rate_id() ); + wc_update_order_item_meta( $item->get_id(), 'label', $item->get_label() ); + wc_update_order_item_meta( $item->get_id(), 'compound', $item->get_compound() ); + wc_update_order_item_meta( $item->get_id(), 'tax_amount', $item->get_tax_total() ); + wc_update_order_item_meta( $item->get_id(), 'shipping_tax_amount', $item->get_shipping_tax_total() ); + } + +} diff --git a/tests/framework/helpers/class-wc-helper-order.php b/tests/framework/helpers/class-wc-helper-order.php index dd4f6962dd0..d40872f70de 100644 --- a/tests/framework/helpers/class-wc-helper-order.php +++ b/tests/framework/helpers/class-wc-helper-order.php @@ -69,7 +69,8 @@ class WC_Helper_Order { // Add shipping costs $shipping_taxes = WC_Tax::calc_shipping_tax( '10', WC_Tax::get_shipping_tax_rates() ); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', $shipping_taxes, 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), diff --git a/tests/unit-tests/order/crud.php b/tests/unit-tests/order/crud.php index 1f1b3972e97..31a5c3927d0 100644 --- a/tests/unit-tests/order/crud.php +++ b/tests/unit-tests/order/crud.php @@ -233,15 +233,16 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { function test_remove_order_items() { $product = WC_Helper_Product::create_simple_product(); $object = new WC_Order(); - $item_1 = new WC_Order_Item_Product( array( + $item_1 = new WC_Order_Item_Product(); + $item_1->set_props( array( 'product' => $product, 'quantity' => 4, ) ); - $item_2 = new WC_Order_Item_Product( array( + $item_2 = new WC_Order_Item_Product(); + $item_2->set_props( array( 'product' => $product, 'quantity' => 2, ) ); - $object->add_item( $item_1 ); $object->add_item( $item_2 ); $object->save(); @@ -255,11 +256,13 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_get_items() { $object = new WC_Order(); - $item_1 = new WC_Order_Item_Product( array( + $item_1 = new WC_Order_Item_Product(); + $item_1->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 4, ) ); - $item_2 = new WC_Order_Item_Product( array( + $item_2 = new WC_Order_Item_Product(); + $item_2->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 2, ) ); @@ -275,7 +278,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_get_fees() { $object = new WC_Order(); - $item = new WC_Order_Item_Fee( array( + $item = new WC_Order_Item_Fee(); + $item->set_props( array( 'name' => 'Some Fee', 'tax_status' => 'taxable', 'total' => '100', @@ -306,7 +310,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { WC_Tax::_insert_tax_rate( $tax_rate ); $object = new WC_Order(); - $item_1 = new WC_Order_Item_Product( array( + $item_1 = new WC_Order_Item_Product(); + $item_1->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 4, ) ); @@ -335,7 +340,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { function test_get_shipping_methods() { $object = new WC_Order(); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -353,7 +359,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { function test_get_shipping_method() { $object = new WC_Order(); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -365,7 +372,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $this->assertEquals( 'Flat rate shipping', $object->get_shipping_method() ); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping 2', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -382,7 +390,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_get_used_coupons() { $object = new WC_Order(); - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => '12345', 'discount' => 10, 'discount_tax' => 5, @@ -397,15 +406,16 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_get_item_count() { $object = new WC_Order(); - $item_1 = new WC_Order_Item_Product( array( + $item_1 = new WC_Order_Item_Product(); + $item_1->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 4, ) ); - $item_2 = new WC_Order_Item_Product( array( + $item_2 = new WC_Order_Item_Product(); + $item_2->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 2, ) ); - $object->add_item( $item_1 ); $object->add_item( $item_2 ); $object->save(); @@ -417,17 +427,19 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_get_item() { $object = new WC_Order(); - $item = new WC_Order_Item_Product( array( + $item = new WC_Order_Item_Product(); + $item->set_props( array( 'product' => WC_Helper_Product::create_simple_product(), 'quantity' => 4, ) ); - $item_id = $item->save(); - $object->add_item( $item_id ); + $item->save(); + $object->add_item( $item->get_id() ); $object->save(); - $this->assertTrue( $object->get_item( $item_id ) instanceOf WC_Order_Item_Product ); + $this->assertTrue( $object->get_item( $item->get_id() ) instanceOf WC_Order_Item_Product ); $object = new WC_Order(); - $item = new WC_Order_Item_Coupon( array( + $item = new WC_Order_Item_Coupon(); + $item->set_props( array( 'code' => '12345', 'discount' => 10, 'discount_tax' => 5, @@ -472,14 +484,16 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { function test_calculate_shipping() { $object = new WC_Order(); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item_1 = new WC_Order_Item_Shipping( array( + $item_1 = new WC_Order_Item_Shipping(); + $item_1->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), 'taxes' => $rate->taxes, 'meta_data' => $rate->get_meta_data(), ) ); - $item_2 = new WC_Order_Item_Shipping( array( + $item_2 = new WC_Order_Item_Shipping(); + $item_2->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -516,13 +530,14 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $object->add_product( WC_Helper_Product::create_simple_product(), 4 ); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), 'taxes' => $rate->taxes, 'meta_data' => $rate->get_meta_data(), - ) ); + ) ); $object->add_item( $item ); $object->calculate_taxes(); @@ -557,7 +572,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $object->add_product( WC_Helper_Product::create_simple_product(), 4 ); $rate = new WC_Shipping_Rate( 'flat_rate_shipping', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -596,7 +612,8 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $this->assertFalse( $object->has_shipping_method( 'flat_rate_shipping' ) ); $rate = new WC_Shipping_Rate( 'flat_rate_shipping:1', 'Flat rate shipping', '10', array(), 'flat_rate' ); - $item = new WC_Order_Item_Shipping( array( + $item = new WC_Order_Item_Shipping(); + $item->set_props( array( 'method_title' => $rate->label, 'method_id' => $rate->id, 'total' => wc_format_decimal( $rate->cost ), @@ -661,7 +678,6 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { */ function test_payment_complete() { $object = new WC_Order(); - $this->assertFalse( $object->payment_complete() ); $object->save(); $this->assertTrue( $object->payment_complete( '12345' ) ); diff --git a/woocommerce.php b/woocommerce.php index 51012477291..50462959237 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -305,6 +305,12 @@ final class WooCommerce { include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-grouped-data-store-cpt.php' ); include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-variable-data-store-cpt.php' ); include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-variation-data-store-cpt.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-data-store.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-coupon-data-store.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-fee-data-store.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-product-store.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-shipping-data-store.php' ); + include_once( WC_ABSPATH . 'includes/data-stores/class-wc-order-item-tax-data-store.php' ); include_once( WC_ABSPATH . 'includes/data-stores/class-wc-payment-token-data-store.php' ); include_once( WC_ABSPATH . 'includes/data-stores/class-wc-customer-data-store.php' ); include_once( WC_ABSPATH . 'includes/data-stores/class-wc-customer-data-store-session.php' );