From 6efef57cbad1fa26f07d423b5caedb29280b7e7e Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Tue, 29 Jan 2019 19:57:04 +0100 Subject: [PATCH] Factored out order-related product-level calculations to WC_Admin_Order and plugged it into the WC framework. --- .../includes/class-wc-admin-api-init.php | 6 + .../includes/class-wc-admin-order.php | 189 ++++++++++++++++++ ...s-wc-admin-reports-products-data-store.php | 74 +------ 3 files changed, 202 insertions(+), 67 deletions(-) create mode 100644 plugins/woocommerce-admin/includes/class-wc-admin-order.php diff --git a/plugins/woocommerce-admin/includes/class-wc-admin-api-init.php b/plugins/woocommerce-admin/includes/class-wc-admin-api-init.php index c47a38bc03a..25da857b9c6 100644 --- a/plugins/woocommerce-admin/includes/class-wc-admin-api-init.php +++ b/plugins/woocommerce-admin/includes/class-wc-admin-api-init.php @@ -113,6 +113,9 @@ class WC_Admin_Api_Init { // Exceptions. require_once dirname( __FILE__ ) . '/class-wc-admin-reports-parameter-exception.php'; + // WC Class extensions. + require_once dirname( __FILE__ ) . '/class-wc-admin-order.php'; + // Segmentation. require_once dirname( __FILE__ ) . '/class-wc-admin-reports-segmenting.php'; require_once dirname( __FILE__ ) . '/class-wc-admin-reports-orders-stats-segmenting.php'; @@ -414,6 +417,9 @@ class WC_Admin_Api_Init { * Init orders data store. */ public static function orders_data_store_init() { + // Activate WC_Order extension. + WC_Admin_Order::add_filters(); + // Initialize data stores. WC_Admin_Reports_Orders_Stats_Data_Store::init(); WC_Admin_Reports_Products_Data_Store::init(); WC_Admin_Reports_Taxes_Data_Store::init(); diff --git a/plugins/woocommerce-admin/includes/class-wc-admin-order.php b/plugins/woocommerce-admin/includes/class-wc-admin-order.php new file mode 100644 index 00000000000..772878cbd99 --- /dev/null +++ b/plugins/woocommerce-admin/includes/class-wc-admin-order.php @@ -0,0 +1,189 @@ +get_item_quantity_refunded( $item ); + $product_qty = $item->get_quantity( 'edit' ) - $quantity_refunded; + + $order_items = $this->get_item_count(); + if ( 0 === $order_items ) { + return 0; + } + + $refunded = $this->get_total_shipping_refunded(); + if ( $refunded > 0 ) { + $total_shipping_amount = $this->get_shipping_total() - $refunded; + } else { + $total_shipping_amount = $this->get_shipping_total(); + } + + return $total_shipping_amount / $order_items * $product_qty; + } + + /** + * Save refund amounts and quantities for the order in an array for later use in calculations. + */ + protected function set_order_refund_items() { + if ( ! isset( $this->refunded_line_items ) ) { + $refunds = $this->get_refunds(); + $refunded_line_items = array(); + foreach ( $refunds as $refund ) { + foreach ( $refund->get_items() as $refunded_item ) { + $line_item_id = wc_get_order_item_meta( $refunded_item->get_id(), '_refunded_item_id', true ); + if ( ! isset( $refunded_line_items[ $line_item_id ] ) ) { + $refunded_line_items[ $line_item_id ]['quantity'] = 0; + $refunded_line_items[ $line_item_id ]['subtotal'] = 0; + } + $refunded_line_items[ $line_item_id ]['quantity'] += absint( $refunded_item['quantity'] ); + $refunded_line_items[ $line_item_id ]['subtotal'] += abs( $refunded_item['subtotal'] ); + } + } + $this->refunded_line_items = $refunded_line_items; + } + } + + /** + * Get quantity refunded for the line item. + * + * @param WC_Order_Item $item Line item from order. + * + * @return int + */ + public function get_item_quantity_refunded( $item ) { + $this->set_order_refund_items(); + $order_item_id = $item->get_id(); + + return isset( $this->refunded_line_items[ $order_item_id ] ) ? $this->refunded_line_items[ $order_item_id ]['quantity'] : 0; + } + + /** + * Get amount refunded for the line item. + * + * @param WC_Order_Item $item Line item from order. + * + * @return int + */ + public function get_item_amount_refunded( $item ) { + $this->set_order_refund_items(); + $order_item_id = $item->get_id(); + + return isset( $this->refunded_line_items[ $order_item_id ] ) ? $this->refunded_line_items[ $order_item_id ]['subtotal'] : 0; + } + + /** + * Get item quantity minus refunded quantity for the line item. + * + * @param WC_Order_Item $item Line item from order. + * + * @return int + */ + public function get_item_quantity_minus_refunded( $item ) { + return $item->get_quantity( 'edit' ) - $this->get_item_quantity_refunded( $item ); + } + + /** + * Calculate shipping tax amount for line item/product as a total shipping tax amount ratio based on quantity. + * + * Loosely based on code in includes/admin/meta-boxes/views/html-order-item(s).php. + * + * @todo: if WC is currently not tax enabled, but it was before (or vice versa), would this work correctly? + * + * @param WC_Order_Item $item Line item from order. + * + * @return float|int + */ + public function get_item_shipping_tax_amount( $item ) { + $order_items = $this->get_item_count(); + if ( 0 === $order_items ) { + return 0; + } + + $quantity_refunded = $this->get_item_quantity_refunded( $item ); + $product_qty = $item->get_quantity( 'edit' ) - $quantity_refunded; + $order_taxes = $this->get_taxes(); + $line_items_shipping = $this->get_items( 'shipping' ); + $total_shipping_tax_amount = 0; + foreach ( $line_items_shipping as $item_id => $shipping_item ) { + $tax_data = $shipping_item->get_taxes(); + if ( $tax_data ) { + foreach ( $order_taxes as $tax_item ) { + $tax_item_id = $tax_item->get_rate_id(); + $tax_item_total = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : ''; + $refunded = $this->get_tax_refunded_for_item( $item_id, $tax_item_id, 'shipping' ); + if ( $refunded ) { + $total_shipping_tax_amount += $tax_item_total - $refunded; + } else { + $total_shipping_tax_amount += $tax_item_total; + } + } + } + } + return $total_shipping_tax_amount / $order_items * $product_qty; + } + + /** + * Calculates coupon amount for specified line item/product. + * + * Coupon calculation based on woocommerce code in includes/admin/meta-boxes/views/html-order-item.php. + * + * @param WC_Order_Item $item Line item from order. + * + * @return float + */ + public function get_item_coupon_amount( $item ) { + return floatval( $item->get_subtotal( 'edit' ) - $item->get_total( 'edit' ) ); + } +} diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php index 4f11c458e77..e1c0ce9d89e 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php @@ -331,48 +331,14 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i return; } - $refunds = self::get_order_refund_items( $order ); - foreach ( $order->get_items() as $order_item ) { - $order_item_id = $order_item->get_id(); - $quantity_refunded = isset( $refunds[ $order_item_id ] ) ? $refunds[ $order_item_id ]['quantity'] : 0; - $amount_refunded = isset( $refunds[ $order_item_id ] ) ? $refunds[ $order_item_id ]['subtotal'] : 0; - $product_qty = $order_item->get_quantity( 'edit' ) - $quantity_refunded; - - // Shipping amount based on woocommerce code in includes/admin/meta-boxes/views/html-order-item(s).php - // distributed simply based on number of line items. - $order_items = $order->get_item_count(); - $refunded = $order->get_total_shipping_refunded(); - if ( $refunded > 0 ) { - $total_shipping_amount = $order->get_shipping_total() - $refunded; - } else { - $total_shipping_amount = $order->get_shipping_total(); - } - $shipping_amount = $total_shipping_amount / $order_items * $product_qty; - - // Shipping amount tax based on woocommerce code in includes/admin/meta-boxes/views/html-order-item(s).php - // distribute simply based on number of line items. - $shipping_tax_amount = 0; - // @todo: if WC is currently not tax enabled, but it was before (or vice versa), would this work correctly? - $order_taxes = $order->get_taxes(); - $line_items_shipping = $order->get_items( 'shipping' ); - $total_shipping_tax_amount = 0; - foreach ( $line_items_shipping as $item_id => $item ) { - $tax_data = $item->get_taxes(); - if ( $tax_data ) { - foreach ( $order_taxes as $tax_item ) { - $tax_item_id = $tax_item->get_rate_id(); - $tax_item_total = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : ''; - $refunded = $order->get_tax_refunded_for_item( $item_id, $tax_item_id, 'shipping' ); - if ( $refunded ) { - $total_shipping_tax_amount += $tax_item_total - $refunded; - } else { - $total_shipping_tax_amount += $tax_item_total; - } - } - } - } - $shipping_tax_amount = $total_shipping_tax_amount / $order_items * $product_qty; + $order_item_id = $order_item->get_id(); + $quantity_refunded = $order->get_item_quantity_refunded( $order_item ); + $amount_refunded = $order->get_item_amount_refunded( $order_item ); + $product_qty = $order->get_item_quantity_minus_refunded( $order_item ); + $shipping_amount = $order->get_item_shipping_amount( $order_item ); + $shipping_tax_amount = $order->get_item_shipping_tax_amount( $order_item ); + $coupon_amount = $order->get_item_coupon_amount( $order_item ); // Tax amount. // @todo: check if this calculates tax correctly with refunds. @@ -388,9 +354,6 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i // @todo: should net revenue be affected by refunds, as refunds are tracked separately? $net_revenue = $order_item->get_subtotal( 'edit' ) - $amount_refunded; - // Coupon calculation based on woocommerce code in includes/admin/meta-boxes/views/html-order-item.php. - $coupon_amount = $order_item->get_subtotal( 'edit' ) - $order_item->get_total( 'edit' ); - if ( $quantity_refunded >= $order_item->get_quantity( 'edit' ) ) { $wpdb->delete( $wpdb->prefix . self::TABLE_NAME, @@ -438,27 +401,4 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i } } - /** - * Get order refund items quantity and subtotal - * - * @param object $order WC Order object. - * @return array - */ - public static function get_order_refund_items( $order ) { - $refunds = $order->get_refunds(); - $refunded_line_items = array(); - foreach ( $refunds as $refund ) { - foreach ( $refund->get_items() as $refunded_item ) { - $line_item_id = wc_get_order_item_meta( $refunded_item->get_id(), '_refunded_item_id', true ); - if ( ! isset( $refunded_line_items[ $line_item_id ] ) ) { - $refunded_line_items[ $line_item_id ]['quantity'] = 0; - $refunded_line_items[ $line_item_id ]['subtotal'] = 0; - } - $refunded_line_items[ $line_item_id ]['quantity'] += absint( $refunded_item['quantity'] ); - $refunded_line_items[ $line_item_id ]['subtotal'] += abs( $refunded_item['subtotal'] ); - } - } - return $refunded_line_items; - } - }