base ] = array( array( array( $this, 'get_reports' ), WC_API_Server::READABLE ), ); # GET /reports/sales $routes[ $this->base . '/sales'] = array( array( array( $this, 'get_sales_report' ), WC_API_Server::READABLE ), ); return $routes; } /** * Get a simple listing of available reports * * @since 2.1 * @return array */ public function get_reports() { return array( 'reports' => array( 'sales' ) ); } /** * Get the sales report * * @since 2.1 * @param string $fields fields to include in response * @param array $filter date filtering * @return array */ public function get_sales_report( $fields = null, $filter = array() ) { // check user permissions $check = $this->validate_request(); if ( is_wp_error( $check ) ) return $check; // set date filtering $this->setup_report( $filter ); // total sales, taxes, shipping, and order count $totals = $this->report->get_order_report_data( array( 'data' => array( '_order_total' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'sales' ), '_order_tax' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'tax' ), '_order_shipping_tax' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'shipping_tax' ), '_order_shipping' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'shipping' ), 'ID' => array( 'type' => 'post_data', 'function' => 'COUNT', 'name' => 'order_count' ) ), 'filter_range' => true, ) ); // total items ordered $total_items = absint( $this->report->get_order_report_data( array( 'data' => array( '_qty' => array( 'type' => 'order_item_meta', 'order_item_type' => 'line_item', 'function' => 'SUM', 'name' => 'order_item_qty' ) ), 'query_type' => 'get_var', 'filter_range' => true, ) ) ); // total discount used $total_discount = $this->report->get_order_report_data( array( 'data' => array( 'discount_amount' => array( 'type' => 'order_item_meta', 'order_item_type' => 'coupon', 'function' => 'SUM', 'name' => 'discount_amount' ) ), 'where' => array( array( 'key' => 'order_item_type', 'value' => 'coupon', 'operator' => '=' ) ), 'query_type' => 'get_var', 'filter_range' => true, ) ); // get order totals grouped by period $orders = $this->report->get_order_report_data( array( 'data' => array( '_order_total' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'total_sales' ), '_order_shipping' => array( 'type' => 'meta', 'function' => 'SUM', 'name' => 'total_shipping' ), 'ID' => array( 'type' => 'post_data', 'function' => 'COUNT', 'name' => 'total_orders', 'distinct' => true, ), 'post_date' => array( 'type' => 'post_data', 'function' => '', 'name' => 'post_date' ), ), 'group_by' => $this->report->group_by_query, 'order_by' => 'post_date ASC', 'query_type' => 'get_results', 'filter_range' => true, ) ); // get order item totals grouped by period $order_items = $this->report->get_order_report_data( array( 'data' => array( '_qty' => array( 'type' => 'order_item_meta', 'order_item_type' => 'line_item', 'function' => 'SUM', 'name' => 'order_item_count' ), 'post_date' => array( 'type' => 'post_data', 'function' => '', 'name' => 'post_date' ), ), 'where' => array( array( 'key' => 'order_item_type', 'value' => 'line_item', 'operator' => '=' ) ), 'group_by' => $this->report->group_by_query, 'order_by' => 'post_date ASC', 'query_type' => 'get_results', 'filter_range' => true, ) ); // get discount totals grouped by period $discounts = $this->report->get_order_report_data( array( 'data' => array( 'discount_amount' => array( 'type' => 'order_item_meta', 'order_item_type' => 'coupon', 'function' => 'SUM', 'name' => 'discount_amount' ), 'post_date' => array( 'type' => 'post_data', 'function' => '', 'name' => 'post_date' ), ), 'where' => array( array( 'key' => 'order_item_type', 'value' => 'coupon', 'operator' => '=' ) ), 'group_by' => $this->report->group_by_query . ', order_item_name', 'order_by' => 'post_date ASC', 'query_type' => 'get_results', 'filter_range' => true, ) ); $period_totals = array(); // setup period totals by ensuring each period in the interval has data for ( $i = 0; $i <= $this->report->chart_interval; $i ++ ) { switch ( $this->report->chart_groupby ) { case 'day' : $time = date( 'Y-m-d', strtotime( "+{$i} DAY", $this->report->start_date ) ); break; case 'month' : $time = date( 'Y-m', strtotime( "+{$i} MONTH", $this->report->start_date ) ); break; } $period_totals[ $time ] = array( 'sales' => 0, 'orders' => 0, 'items' => 0, 'tax' => 0, 'shipping' => 0, 'discount' => 0, ); } // add total sales, total order count, total tax and total shipping for each period foreach ( $orders as $order ) { $time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date) ) : date( 'Y-m', strtotime( $order->post_date ) ); if ( ! isset( $period_totals[ $time ] ) ) continue; $period_totals[ $time ]['sales'] = $order->total_sales; $period_totals[ $time ]['orders'] = $order->total_orders; $period_totals[ $time ]['tax'] = 1; $period_totals[ $time ]['shipping'] = $order->total_shipping; } // add total order items for each period foreach ( $order_items as $order_item ) { $time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $order_item->post_date) ) : date( 'Y-m', strtotime( $order_item->post_date ) ); if ( ! isset( $period_totals[ $time ] ) ) continue; $period_totals[ $time ]['items'] = $order_item->order_item_count; } // add total discount for each period foreach ( $discounts as $discount ) { $time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $discount->post_date) ) : date( 'Y-m', strtotime( $discount->post_date ) ); if ( ! isset( $period_totals[ $time ] ) ) continue; $period_totals[ $time ]['discount'] = $discount->discount_amount; } $sales_data = array( 'sales' => $totals->sales, 'average' => (string) number_format( $totals->sales / ( $this->report->chart_interval + 1 ), 2 ), 'orders' => absint( $totals->order_count ), 'items' => $total_items, 'tax' => (string) number_format( $totals->tax + $totals->shipping_tax, 2 ), 'shipping' => $totals->shipping, 'discount' => is_null( $total_discount ) ? 0 : $total_discount, 'totals' => $period_totals, 'totals_grouped_by' => $this->report->chart_groupby, ); return apply_filters( 'woocommerce_api_sales_report_response', array( 'sales' => $sales_data ), 'sales', $fields, $this->report, $this->server ); } /** * Setup the report object and parse any date filtering * * @since 2.1 * @param array $filter date filtering */ private function setup_report( $filter ) { include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php' ); $this->report = new WC_Admin_Report(); if ( empty( $filter['period'] ) ) { // custom date range $filter['period'] = 'custom'; if ( ! empty( $filter['date_min'] ) || ! empty( $filter['date_max'] ) ) { // overwrite _GET to make use of WC_Admin_Report::calculate_current_range() for custom date ranges $_GET['start_date'] = $filter['date_min']; // TODO: date formatting? $_GET['end_date'] = $filter['date_max']; // TODO: date formatting } else { // default custom range to today $_GET['start_date'] = $_GET['end_date'] = date( 'Y-m-d', current_time( 'timestamp' ) ); } } // TODO: handle invalid periods (e.g. `decade`) $this->report->calculate_current_range( $filter['period'] ); } /** * Verify that the current user has permission to view reports * * @since 2.1 * @see WC_API_Resource::validate_request() * @param null $id unused * @param null $type unused * @param null $context unused * @return bool true if the request is valid and should be processed, false otherwise */ protected function validate_request( $id = null, $type = null, $context = null ) { if ( ! current_user_can( 'view_woocommerce_reports' ) ) { return new WP_Error( 'woocommerce_api_user_cannot_read_report', __( 'You do not have permission to read this report', 'woocommerce' ), array( 'status' => 401 ) ); } else { return true; } } }