initial commit
This commit is contained in:
764
includes/admin/reports/class-wc-admin-report.php
Normal file
764
includes/admin/reports/class-wc-admin-report.php
Normal file
@ -0,0 +1,764 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Report.
|
||||
*
|
||||
* Extended by reports to show charts and stats in admin.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* @var array List of transients name that have been updated and need persisting.
|
||||
*/
|
||||
protected static $transients_to_update = array();
|
||||
|
||||
/**
|
||||
* @var array The list of transients.
|
||||
*/
|
||||
protected static $cached_results = array();
|
||||
|
||||
/**
|
||||
* The chart interval.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $chart_interval;
|
||||
|
||||
/**
|
||||
* Group by SQL query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $group_by_query;
|
||||
|
||||
/**
|
||||
* The bar width.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $barwidth;
|
||||
|
||||
/**
|
||||
* Group chart item by day or month.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $chart_groupby;
|
||||
|
||||
/**
|
||||
* The start date of the report.
|
||||
*
|
||||
* @var int timestamp
|
||||
*/
|
||||
public $start_date;
|
||||
|
||||
/**
|
||||
* The end date of the report.
|
||||
*
|
||||
* @var int timestamp
|
||||
*/
|
||||
public $end_date;
|
||||
|
||||
/**
|
||||
* Get report totals such as order totals and discount amounts.
|
||||
*
|
||||
* Data example:
|
||||
*
|
||||
* '_order_total' => array(
|
||||
* 'type' => 'meta',
|
||||
* 'function' => 'SUM',
|
||||
* 'name' => 'total_sales'
|
||||
* )
|
||||
*
|
||||
* @param array $args
|
||||
* @return mixed depending on query_type
|
||||
*/
|
||||
public function get_order_report_data( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$default_args = array(
|
||||
'data' => array(),
|
||||
'where' => array(),
|
||||
'where_meta' => array(),
|
||||
'query_type' => 'get_row',
|
||||
'group_by' => '',
|
||||
'order_by' => '',
|
||||
'limit' => '',
|
||||
'filter_range' => false,
|
||||
'nocache' => false,
|
||||
'debug' => false,
|
||||
'order_types' => wc_get_order_types( 'reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold' ),
|
||||
'parent_order_status' => false,
|
||||
);
|
||||
$args = apply_filters( 'woocommerce_reports_get_order_report_data_args', $args );
|
||||
$args = wp_parse_args( $args, $default_args );
|
||||
|
||||
extract( $args );
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$order_status = apply_filters( 'woocommerce_reports_order_statuses', $order_status );
|
||||
|
||||
$query = array();
|
||||
$select = array();
|
||||
|
||||
foreach ( $data as $raw_key => $value ) {
|
||||
$key = sanitize_key( $raw_key );
|
||||
$distinct = '';
|
||||
|
||||
if ( isset( $value['distinct'] ) ) {
|
||||
$distinct = 'DISTINCT';
|
||||
}
|
||||
|
||||
switch ( $value['type'] ) {
|
||||
case 'meta':
|
||||
$get_key = "meta_{$key}.meta_value";
|
||||
break;
|
||||
case 'parent_meta':
|
||||
$get_key = "parent_meta_{$key}.meta_value";
|
||||
break;
|
||||
case 'post_data':
|
||||
$get_key = "posts.{$key}";
|
||||
break;
|
||||
case 'order_item_meta':
|
||||
$get_key = "order_item_meta_{$key}.meta_value";
|
||||
break;
|
||||
case 'order_item':
|
||||
$get_key = "order_items.{$key}";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( empty( $get_key ) ) {
|
||||
// Skip to the next foreach iteration else the query will be invalid.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $value['function'] ) {
|
||||
$get = "{$value['function']}({$distinct} {$get_key})";
|
||||
} else {
|
||||
$get = "{$distinct} {$get_key}";
|
||||
}
|
||||
|
||||
$select[] = "{$get} as {$value['name']}";
|
||||
}
|
||||
|
||||
$query['select'] = 'SELECT ' . implode( ',', $select );
|
||||
$query['from'] = "FROM {$wpdb->posts} AS posts";
|
||||
|
||||
// Joins
|
||||
$joins = array();
|
||||
|
||||
foreach ( ( $data + $where ) as $raw_key => $value ) {
|
||||
$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
|
||||
$type = isset( $value['type'] ) ? $value['type'] : false;
|
||||
$key = sanitize_key( $raw_key );
|
||||
|
||||
switch ( $type ) {
|
||||
case 'meta':
|
||||
$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON ( posts.ID = meta_{$key}.post_id AND meta_{$key}.meta_key = '{$raw_key}' )";
|
||||
break;
|
||||
case 'parent_meta':
|
||||
$joins[ "parent_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS parent_meta_{$key} ON (posts.post_parent = parent_meta_{$key}.post_id) AND (parent_meta_{$key}.meta_key = '{$raw_key}')";
|
||||
break;
|
||||
case 'order_item_meta':
|
||||
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON (posts.ID = order_items.order_id)";
|
||||
|
||||
if ( ! empty( $value['order_item_type'] ) ) {
|
||||
$joins['order_items'] .= " AND (order_items.order_item_type = '{$value['order_item_type']}')";
|
||||
}
|
||||
|
||||
$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON " .
|
||||
"(order_items.order_item_id = order_item_meta_{$key}.order_item_id) " .
|
||||
" AND (order_item_meta_{$key}.meta_key = '{$raw_key}')";
|
||||
break;
|
||||
case 'order_item':
|
||||
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $where_meta ) ) {
|
||||
foreach ( $where_meta as $value ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
|
||||
$type = isset( $value['type'] ) ? $value['type'] : false;
|
||||
$key = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
|
||||
|
||||
if ( 'order_item_meta' === $type ) {
|
||||
|
||||
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
|
||||
$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON order_items.order_item_id = order_item_meta_{$key}.order_item_id";
|
||||
|
||||
} else {
|
||||
// If we have a where clause for meta, join the postmeta table
|
||||
$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON posts.ID = meta_{$key}.post_id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $parent_order_status ) ) {
|
||||
$joins['parent'] = "LEFT JOIN {$wpdb->posts} AS parent ON posts.post_parent = parent.ID";
|
||||
}
|
||||
|
||||
$query['join'] = implode( ' ', $joins );
|
||||
|
||||
$query['where'] = "
|
||||
WHERE posts.post_type IN ( '" . implode( "','", $order_types ) . "' )
|
||||
";
|
||||
|
||||
if ( ! empty( $order_status ) ) {
|
||||
$query['where'] .= "
|
||||
AND posts.post_status IN ( 'wc-" . implode( "','wc-", $order_status ) . "')
|
||||
";
|
||||
}
|
||||
|
||||
if ( ! empty( $parent_order_status ) ) {
|
||||
if ( ! empty( $order_status ) ) {
|
||||
$query['where'] .= " AND ( parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') OR parent.ID IS NULL ) ";
|
||||
} else {
|
||||
$query['where'] .= " AND parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') ";
|
||||
}
|
||||
}
|
||||
|
||||
if ( $filter_range ) {
|
||||
$query['where'] .= "
|
||||
AND posts.post_date >= '" . date( 'Y-m-d H:i:s', $this->start_date ) . "'
|
||||
AND posts.post_date < '" . date( 'Y-m-d H:i:s', strtotime( '+1 DAY', $this->end_date ) ) . "'
|
||||
";
|
||||
}
|
||||
|
||||
if ( ! empty( $where_meta ) ) {
|
||||
|
||||
$relation = isset( $where_meta['relation'] ) ? $where_meta['relation'] : 'AND';
|
||||
|
||||
$query['where'] .= ' AND (';
|
||||
|
||||
foreach ( $where_meta as $index => $value ) {
|
||||
|
||||
if ( ! is_array( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
|
||||
|
||||
if ( strtolower( $value['operator'] ) == 'in' || strtolower( $value['operator'] ) == 'not in' ) {
|
||||
|
||||
if ( is_array( $value['meta_value'] ) ) {
|
||||
$value['meta_value'] = implode( "','", $value['meta_value'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $value['meta_value'] ) ) {
|
||||
$where_value = "{$value['operator']} ('{$value['meta_value']}')";
|
||||
}
|
||||
} else {
|
||||
$where_value = "{$value['operator']} '{$value['meta_value']}'";
|
||||
}
|
||||
|
||||
if ( ! empty( $where_value ) ) {
|
||||
if ( $index > 0 ) {
|
||||
$query['where'] .= ' ' . $relation;
|
||||
}
|
||||
|
||||
if ( isset( $value['type'] ) && 'order_item_meta' === $value['type'] ) {
|
||||
|
||||
if ( is_array( $value['meta_key'] ) ) {
|
||||
$query['where'] .= " ( order_item_meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
|
||||
} else {
|
||||
$query['where'] .= " ( order_item_meta_{$key}.meta_key = '{$value['meta_key']}'";
|
||||
}
|
||||
|
||||
$query['where'] .= " AND order_item_meta_{$key}.meta_value {$where_value} )";
|
||||
} else {
|
||||
|
||||
if ( is_array( $value['meta_key'] ) ) {
|
||||
$query['where'] .= " ( meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
|
||||
} else {
|
||||
$query['where'] .= " ( meta_{$key}.meta_key = '{$value['meta_key']}'";
|
||||
}
|
||||
|
||||
$query['where'] .= " AND meta_{$key}.meta_value {$where_value} )";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query['where'] .= ')';
|
||||
}
|
||||
|
||||
if ( ! empty( $where ) ) {
|
||||
|
||||
foreach ( $where as $value ) {
|
||||
|
||||
if ( strtolower( $value['operator'] ) == 'in' || strtolower( $value['operator'] ) == 'not in' ) {
|
||||
|
||||
if ( is_array( $value['value'] ) ) {
|
||||
$value['value'] = implode( "','", $value['value'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $value['value'] ) ) {
|
||||
$where_value = "{$value['operator']} ('{$value['value']}')";
|
||||
}
|
||||
} else {
|
||||
$where_value = "{$value['operator']} '{$value['value']}'";
|
||||
}
|
||||
|
||||
if ( ! empty( $where_value ) ) {
|
||||
$query['where'] .= " AND {$value['key']} {$where_value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $group_by ) {
|
||||
$query['group_by'] = "GROUP BY {$group_by}";
|
||||
}
|
||||
|
||||
if ( $order_by ) {
|
||||
$query['order_by'] = "ORDER BY {$order_by}";
|
||||
}
|
||||
|
||||
if ( $limit ) {
|
||||
$query['limit'] = "LIMIT {$limit}";
|
||||
}
|
||||
|
||||
$query = apply_filters( 'woocommerce_reports_get_order_report_query', $query );
|
||||
$query = implode( ' ', $query );
|
||||
|
||||
if ( $debug ) {
|
||||
echo '<pre>';
|
||||
wc_print_r( $query );
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
if ( $debug || $nocache ) {
|
||||
self::enable_big_selects();
|
||||
|
||||
$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
|
||||
} else {
|
||||
$query_hash = md5( $query_type . $query );
|
||||
$result = $this->get_cached_query( $query_hash );
|
||||
if ( $result === null ) {
|
||||
self::enable_big_selects();
|
||||
|
||||
$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
|
||||
}
|
||||
$this->set_cached_query( $query_hash, $result );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the static hooks of the class.
|
||||
*/
|
||||
protected static function add_update_transients_hook() {
|
||||
if ( ! has_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) ) ) {
|
||||
add_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables big mysql selects for reports, just once for this session.
|
||||
*/
|
||||
protected static function enable_big_selects() {
|
||||
static $big_selects = false;
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( ! $big_selects ) {
|
||||
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
|
||||
$big_selects = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached query result or null if it's not in the cache.
|
||||
*
|
||||
* @param string $query_hash The query hash.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_cached_query( $query_hash ) {
|
||||
$class = strtolower( get_class( $this ) );
|
||||
|
||||
if ( ! isset( self::$cached_results[ $class ] ) ) {
|
||||
self::$cached_results[ $class ] = get_transient( strtolower( get_class( $this ) ) );
|
||||
}
|
||||
|
||||
if ( isset( self::$cached_results[ $class ][ $query_hash ] ) ) {
|
||||
return self::$cached_results[ $class ][ $query_hash ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cached query result.
|
||||
*
|
||||
* @param string $query_hash The query hash.
|
||||
* @param mixed $data The data to cache.
|
||||
*/
|
||||
protected function set_cached_query( $query_hash, $data ) {
|
||||
$class = strtolower( get_class( $this ) );
|
||||
|
||||
if ( ! isset( self::$cached_results[ $class ] ) ) {
|
||||
self::$cached_results[ $class ] = get_transient( strtolower( get_class( $this ) ) );
|
||||
}
|
||||
|
||||
self::add_update_transients_hook();
|
||||
|
||||
self::$transients_to_update[ $class ] = $class;
|
||||
self::$cached_results[ $class ][ $query_hash ] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to update the modified transients at the end of the request.
|
||||
*/
|
||||
public static function maybe_update_transients() {
|
||||
foreach ( self::$transients_to_update as $key => $transient_name ) {
|
||||
set_transient( $transient_name, self::$cached_results[ $transient_name ], DAY_IN_SECONDS );
|
||||
}
|
||||
// Transients have been updated reset the list.
|
||||
self::$transients_to_update = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put data with post_date's into an array of times.
|
||||
*
|
||||
* @param array $data array of your data
|
||||
* @param string $date_key key for the 'date' field. e.g. 'post_date'
|
||||
* @param string $data_key key for the data you are charting
|
||||
* @param int $interval
|
||||
* @param string $start_date
|
||||
* @param string $group_by
|
||||
* @return array
|
||||
*/
|
||||
public function prepare_chart_data( $data, $date_key, $data_key, $interval, $start_date, $group_by ) {
|
||||
$prepared_data = array();
|
||||
|
||||
// Ensure all days (or months) have values in this range.
|
||||
if ( 'day' === $group_by ) {
|
||||
for ( $i = 0; $i <= $interval; $i ++ ) {
|
||||
$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $start_date ) ) ) . '000';
|
||||
|
||||
if ( ! isset( $prepared_data[ $time ] ) ) {
|
||||
$prepared_data[ $time ] = array( esc_js( $time ), 0 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$current_yearnum = date( 'Y', $start_date );
|
||||
$current_monthnum = date( 'm', $start_date );
|
||||
|
||||
for ( $i = 0; $i <= $interval; $i ++ ) {
|
||||
$time = strtotime( $current_yearnum . str_pad( $current_monthnum, 2, '0', STR_PAD_LEFT ) . '01' ) . '000';
|
||||
|
||||
if ( ! isset( $prepared_data[ $time ] ) ) {
|
||||
$prepared_data[ $time ] = array( esc_js( $time ), 0 );
|
||||
}
|
||||
|
||||
$current_monthnum ++;
|
||||
|
||||
if ( $current_monthnum > 12 ) {
|
||||
$current_monthnum = 1;
|
||||
$current_yearnum ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $data as $d ) {
|
||||
switch ( $group_by ) {
|
||||
case 'day':
|
||||
$time = strtotime( date( 'Ymd', strtotime( $d->$date_key ) ) ) . '000';
|
||||
break;
|
||||
case 'month':
|
||||
default:
|
||||
$time = strtotime( date( 'Ym', strtotime( $d->$date_key ) ) . '01' ) . '000';
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! isset( $prepared_data[ $time ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $data_key ) {
|
||||
$prepared_data[ $time ][1] += $d->$data_key;
|
||||
} else {
|
||||
$prepared_data[ $time ][1] ++;
|
||||
}
|
||||
}
|
||||
|
||||
return $prepared_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a sparkline to show sales in the last X days.
|
||||
*
|
||||
* @param int $id ID of the product to show. Blank to get all orders.
|
||||
* @param int $days Days of stats to get.
|
||||
* @param string $type Type of sparkline to get. Ignored if ID is not set.
|
||||
* @return string
|
||||
*/
|
||||
public function sales_sparkline( $id = '', $days = 7, $type = 'sales' ) {
|
||||
|
||||
if ( $id ) {
|
||||
$meta_key = ( 'sales' === $type ) ? '_line_total' : '_qty';
|
||||
|
||||
$data = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
$meta_key => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'sparkline_value',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'post_date',
|
||||
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
|
||||
'operator' => '>',
|
||||
),
|
||||
array(
|
||||
'key' => 'order_item_meta__product_id.meta_value',
|
||||
'value' => $id,
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'group_by' => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => false,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
||||
$data = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_order_total' => array(
|
||||
'type' => 'meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'sparkline_value',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'post_date',
|
||||
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
|
||||
'operator' => '>',
|
||||
),
|
||||
),
|
||||
'group_by' => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
foreach ( $data as $d ) {
|
||||
$total += $d->sparkline_value;
|
||||
}
|
||||
|
||||
if ( 'sales' === $type ) {
|
||||
/* translators: 1: total income 2: days */
|
||||
$tooltip = sprintf( __( 'Sold %1$s worth in the last %2$d days', 'woocommerce' ), strip_tags( wc_price( $total ) ), $days );
|
||||
} else {
|
||||
/* translators: 1: total items sold 2: days */
|
||||
$tooltip = sprintf( _n( 'Sold %1$d item in the last %2$d days', 'Sold %1$d items in the last %2$d days', $total, 'woocommerce' ), $total, $days );
|
||||
}
|
||||
|
||||
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
|
||||
|
||||
return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . wc_esc_json( wp_json_encode( $sparkline_data ) ) . '"></span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current range and calculate the start and end dates.
|
||||
*
|
||||
* @param string $current_range
|
||||
*/
|
||||
public function calculate_current_range( $current_range ) {
|
||||
|
||||
switch ( $current_range ) {
|
||||
|
||||
case 'custom':
|
||||
$this->start_date = max( strtotime( '-20 years' ), strtotime( sanitize_text_field( $_GET['start_date'] ) ) );
|
||||
|
||||
if ( empty( $_GET['end_date'] ) ) {
|
||||
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
|
||||
} else {
|
||||
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
|
||||
}
|
||||
|
||||
$interval = 0;
|
||||
$min_date = $this->start_date;
|
||||
|
||||
while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
|
||||
$interval ++;
|
||||
}
|
||||
|
||||
// 3 months max for day view
|
||||
if ( $interval > 3 ) {
|
||||
$this->chart_groupby = 'month';
|
||||
} else {
|
||||
$this->chart_groupby = 'day';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
$this->start_date = strtotime( date( 'Y-01-01', current_time( 'timestamp' ) ) );
|
||||
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
|
||||
$this->chart_groupby = 'month';
|
||||
break;
|
||||
|
||||
case 'last_month':
|
||||
$first_day_current_month = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
|
||||
$this->start_date = strtotime( date( 'Y-m-01', strtotime( '-1 DAY', $first_day_current_month ) ) );
|
||||
$this->end_date = strtotime( date( 'Y-m-t', strtotime( '-1 DAY', $first_day_current_month ) ) );
|
||||
$this->chart_groupby = 'day';
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$this->start_date = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
|
||||
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
|
||||
$this->chart_groupby = 'day';
|
||||
break;
|
||||
|
||||
case '7day':
|
||||
$this->start_date = strtotime( '-6 days', strtotime( 'midnight', current_time( 'timestamp' ) ) );
|
||||
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
|
||||
$this->chart_groupby = 'day';
|
||||
break;
|
||||
}
|
||||
|
||||
// Group by
|
||||
switch ( $this->chart_groupby ) {
|
||||
|
||||
case 'day':
|
||||
$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
|
||||
$this->chart_interval = absint( ceil( max( 0, ( $this->end_date - $this->start_date ) / ( 60 * 60 * 24 ) ) ) );
|
||||
$this->barwidth = 60 * 60 * 24 * 1000;
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date)';
|
||||
$this->chart_interval = 0;
|
||||
$min_date = strtotime( date( 'Y-m-01', $this->start_date ) );
|
||||
|
||||
while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
|
||||
$this->chart_interval ++;
|
||||
}
|
||||
|
||||
$this->barwidth = 60 * 60 * 24 * 7 * 4 * 1000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return currency tooltip JS based on WooCommerce currency position settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_currency_tooltip() {
|
||||
switch ( get_option( 'woocommerce_currency_pos' ) ) {
|
||||
case 'right':
|
||||
$currency_tooltip = 'append_tooltip: "' . get_woocommerce_currency_symbol() . '"';
|
||||
break;
|
||||
case 'right_space':
|
||||
$currency_tooltip = 'append_tooltip: " ' . get_woocommerce_currency_symbol() . '"';
|
||||
break;
|
||||
case 'left':
|
||||
$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . '"';
|
||||
break;
|
||||
case 'left_space':
|
||||
default:
|
||||
$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . ' "';
|
||||
break;
|
||||
}
|
||||
|
||||
return $currency_tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart widgets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an export link if needed.
|
||||
*/
|
||||
public function get_export_button() {}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {}
|
||||
|
||||
/**
|
||||
* Check nonce for current range.
|
||||
*
|
||||
* @since 3.0.4
|
||||
* @param string $current_range Current range.
|
||||
*/
|
||||
public function check_current_range_nonce( $current_range ) {
|
||||
if ( 'custom' !== $current_range ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_GET['wc_reports_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['wc_reports_nonce'] ), 'custom_range' ) ) { // WPCS: input var ok, CSRF ok.
|
||||
wp_die(
|
||||
/* translators: %1$s: open link, %2$s: close link */
|
||||
sprintf( esc_html__( 'This report link has expired. %1$sClick here to view the filtered report%2$s.', 'woocommerce' ), '<a href="' . esc_url( wp_nonce_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'custom_range', 'wc_reports_nonce' ) ) . '">', '</a>' ), // @codingStandardsIgnoreLine.
|
||||
esc_attr__( 'Confirm navigation', 'woocommerce' )
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
571
includes/admin/reports/class-wc-report-coupon-usage.php
Normal file
571
includes/admin/reports/class-wc-report-coupon-usage.php
Normal file
@ -0,0 +1,571 @@
|
||||
<?php
|
||||
/**
|
||||
* Coupon usage report functionality
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Coupon_Usage
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Coupon_Usage extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Chart colors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chart_colours = array();
|
||||
|
||||
/**
|
||||
* Coupon codes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $coupon_codes = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( isset( $_GET['coupon_codes'] ) && is_array( $_GET['coupon_codes'] ) ) {
|
||||
$this->coupon_codes = array_filter( array_map( 'sanitize_text_field', wp_unslash( $_GET['coupon_codes'] ) ) );
|
||||
} elseif ( isset( $_GET['coupon_codes'] ) ) {
|
||||
$this->coupon_codes = array_filter( array( sanitize_text_field( wp_unslash( $_GET['coupon_codes'] ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
$legend = array();
|
||||
|
||||
$total_discount_query = 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,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
);
|
||||
|
||||
$total_coupons_query = array(
|
||||
'data' => array(
|
||||
'order_item_id' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'order_coupon_count',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
);
|
||||
|
||||
if ( ! empty( $this->coupon_codes ) ) {
|
||||
$coupon_code_query = array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN',
|
||||
);
|
||||
|
||||
$total_discount_query['where'][] = $coupon_code_query;
|
||||
$total_coupons_query['where'][] = $coupon_code_query;
|
||||
}
|
||||
|
||||
$total_discount = $this->get_order_report_data( $total_discount_query );
|
||||
$total_coupons = absint( $this->get_order_report_data( $total_coupons_query ) );
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: %s: discount amount */
|
||||
'title' => sprintf( __( '%s discounts in total', 'woocommerce' ), '<strong>' . wc_price( $total_discount ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['discount_amount'],
|
||||
'highlight_series' => 1,
|
||||
);
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: %s: coupons amount */
|
||||
'title' => sprintf( __( '%s coupons used in total', 'woocommerce' ), '<strong>' . $total_coupons . '</strong>' ),
|
||||
'color' => $this->chart_colours['coupon_count'],
|
||||
'highlight_series' => 0,
|
||||
);
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
'7day' => __( 'Last 7 days', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$this->chart_colours = array(
|
||||
'discount_amount' => '#3498db',
|
||||
'coupon_count' => '#d4d9dc',
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
|
||||
$current_range = '7day';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart widgets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
$widgets = array();
|
||||
|
||||
$widgets[] = array(
|
||||
'title' => '',
|
||||
'callback' => array( $this, 'coupons_widget' ),
|
||||
);
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output coupons widget.
|
||||
*/
|
||||
public function coupons_widget() {
|
||||
?>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Filter by coupon', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<form method="GET">
|
||||
<div>
|
||||
<?php
|
||||
$used_coupons = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => '',
|
||||
'distinct' => true,
|
||||
'name' => 'order_item_name',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_col',
|
||||
'filter_range' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $used_coupons ) && is_array( $used_coupons ) ) :
|
||||
?>
|
||||
<select id="coupon_codes" name="coupon_codes" class="wc-enhanced-select" data-placeholder="<?php esc_attr_e( 'Choose coupons…', 'woocommerce' ); ?>" style="width:100%;">
|
||||
<option value=""><?php esc_html_e( 'All coupons', 'woocommerce' ); ?></option>
|
||||
<?php
|
||||
foreach ( $used_coupons as $coupon ) {
|
||||
echo '<option value="' . esc_attr( $coupon ) . '"' . wc_selected( $coupon, $this->coupon_codes ) . '>' . esc_html( $coupon ) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php // @codingStandardsIgnoreStart ?>
|
||||
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
|
||||
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( wp_unslash( $_GET['range'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( wp_unslash( $_GET['start_date'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( wp_unslash( $_GET['end_date'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( wp_unslash( $_GET['page'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( wp_unslash( $_GET['tab'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( wp_unslash( $_GET['report'] ) ) : ''; ?>" />
|
||||
<?php // @codingStandardsIgnoreEnd ?>
|
||||
<?php else : ?>
|
||||
<span><?php esc_html_e( 'No used coupons found', 'woocommerce' ); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Most popular', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<table cellspacing="0">
|
||||
<?php
|
||||
$most_popular = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => '',
|
||||
'name' => 'coupon_code',
|
||||
),
|
||||
'order_item_id' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'coupon_count',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'order_by' => 'coupon_count DESC',
|
||||
'group_by' => 'order_item_name',
|
||||
'limit' => 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $most_popular ) && is_array( $most_popular ) ) {
|
||||
foreach ( $most_popular as $coupon ) {
|
||||
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
|
||||
<td class="count" width="1%">' . esc_html( $coupon->coupon_count ) . '</td>
|
||||
<td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
|
||||
</tr>';
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="2">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Most discount', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<table cellspacing="0">
|
||||
<?php
|
||||
$most_discount = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => '',
|
||||
'name' => 'coupon_code',
|
||||
),
|
||||
'discount_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'SUM',
|
||||
'name' => 'discount_amount',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'order_by' => 'discount_amount DESC',
|
||||
'group_by' => 'order_item_name',
|
||||
'limit' => 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $most_discount ) && is_array( $most_discount ) ) {
|
||||
foreach ( $most_discount as $coupon ) {
|
||||
// @codingStandardsIgnoreStart
|
||||
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
|
||||
<td class="count" width="1%">' . wc_price( $coupon->discount_amount ) . '</td>
|
||||
<td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
|
||||
</tr>';
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery( '.section_title' ).on( 'click', function() {
|
||||
var next_section = jQuery( this ).next( '.section' );
|
||||
|
||||
if ( jQuery( next_section ).is( ':visible' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jQuery( '.section:visible' ).slideUp();
|
||||
jQuery( '.section_title' ).removeClass( 'open' );
|
||||
jQuery( this ).addClass( 'open' ).next( '.section' ).slideDown();
|
||||
|
||||
return false;
|
||||
} );
|
||||
jQuery( '.section' ).slideUp( 100, function() {
|
||||
<?php if ( empty( $this->coupon_codes ) ) : ?>
|
||||
jQuery( '.section_title:eq(1)' ).trigger( 'click' );
|
||||
<?php else : ?>
|
||||
jQuery( '.section_title:eq(0)' ).trigger( 'click' );
|
||||
<?php endif; ?>
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
// Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
|
||||
$order_coupon_counts_query = array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'order_coupon_count',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
);
|
||||
|
||||
$order_discount_amounts_query = 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->group_by_query . ', order_item_name',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
);
|
||||
|
||||
if ( ! empty( $this->coupon_codes ) ) {
|
||||
$coupon_code_query = array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN',
|
||||
);
|
||||
|
||||
$order_coupon_counts_query['where'][] = $coupon_code_query;
|
||||
$order_discount_amounts_query['where'][] = $coupon_code_query;
|
||||
}
|
||||
|
||||
$order_coupon_counts = $this->get_order_report_data( $order_coupon_counts_query );
|
||||
$order_discount_amounts = $this->get_order_report_data( $order_discount_amounts_query );
|
||||
|
||||
// Prepare data for report.
|
||||
$order_coupon_counts = $this->prepare_chart_data( $order_coupon_counts, 'post_date', 'order_coupon_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$order_discount_amounts = $this->prepare_chart_data( $order_discount_amounts, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
|
||||
// Encode in json format.
|
||||
$chart_data = wp_json_encode(
|
||||
array(
|
||||
'order_coupon_counts' => array_values( $order_coupon_counts ),
|
||||
'order_discount_amounts' => array_values( $order_discount_amounts ),
|
||||
)
|
||||
);
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
|
||||
|
||||
var drawGraph = function( highlight ) {
|
||||
var series = [
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Number of coupons used', 'woocommerce' ) ); ?>",
|
||||
data: order_data.order_coupon_counts,
|
||||
color: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>',
|
||||
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Discount amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.order_discount_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['discount_amount'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 4, fill: false },
|
||||
shadowSize: 0,
|
||||
<?php echo $this->get_currency_tooltip(); ?><?php // @codingStandardsIgnoreLine ?>
|
||||
}
|
||||
];
|
||||
|
||||
if ( highlight !== 'undefined' && series[ highlight ] ) {
|
||||
highlight_series = series[ highlight ];
|
||||
|
||||
highlight_series.color = '#9c5d90';
|
||||
|
||||
if ( highlight_series.bars )
|
||||
highlight_series.bars.fillColor = '#9c5d90';
|
||||
|
||||
if ( highlight_series.lines ) {
|
||||
highlight_series.lines.lineWidth = 5;
|
||||
}
|
||||
}
|
||||
|
||||
main_chart = jQuery.plot(
|
||||
jQuery('.chart-placeholder.main'),
|
||||
series,
|
||||
{
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
color: '#aaa',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
hoverable: true
|
||||
},
|
||||
xaxes: [ {
|
||||
color: '#aaa',
|
||||
position: "bottom",
|
||||
tickColor: 'transparent',
|
||||
mode: "time",
|
||||
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
|
||||
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
|
||||
font: {
|
||||
color: "#aaa"
|
||||
}
|
||||
} ],
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
minTickSize: 1,
|
||||
tickDecimals: 0,
|
||||
color: '#ecf0f1',
|
||||
font: { color: "#aaa" }
|
||||
},
|
||||
{
|
||||
position: "right",
|
||||
min: 0,
|
||||
tickDecimals: 2,
|
||||
alignTicksWithAxis: 1,
|
||||
color: 'transparent',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder').trigger( 'resize' );
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').on( 'mouseenter',
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
} ).on( 'mouseleave',
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
309
includes/admin/reports/class-wc-report-customer-list.php
Normal file
309
includes/admin/reports/class-wc-report-customer-list.php
Normal file
@ -0,0 +1,309 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WC_Report_Customer_List file.
|
||||
*
|
||||
* @package WooCommerce\Reports
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Customer_List.
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Customer_List extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
parent::__construct(
|
||||
array(
|
||||
'singular' => 'customer',
|
||||
'plural' => 'customers',
|
||||
'ajax' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No customers found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
$this->prepare_items();
|
||||
|
||||
echo '<div id="poststuff" class="woocommerce-reports-wide">';
|
||||
|
||||
if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$linked = wc_update_new_customer_past_orders( absint( $_GET['link_orders'] ) );
|
||||
/* translators: single or plural number of orders */
|
||||
echo '<div class="updated"><p>' . sprintf( esc_html( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) ) . '</p></div>';
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['refresh'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'refresh' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$user_id = absint( $_GET['refresh'] );
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
|
||||
delete_user_meta( $user_id, '_money_spent' );
|
||||
delete_user_meta( $user_id, '_order_count' );
|
||||
delete_user_meta( $user_id, '_last_order' );
|
||||
/* translators: User display name */
|
||||
echo '<div class="updated"><p>' . sprintf( esc_html__( 'Refreshed stats for %s', 'woocommerce' ), esc_html( $user->display_name ) ) . '</p></div>';
|
||||
}
|
||||
|
||||
echo '<form method="post" id="woocommerce_customers">';
|
||||
|
||||
$this->search_box( __( 'Search customers', 'woocommerce' ), 'customer_search' );
|
||||
$this->display();
|
||||
|
||||
echo '</form>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column value.
|
||||
*
|
||||
* @param WP_User $user WP User object.
|
||||
* @param string $column_name Column name.
|
||||
* @return string
|
||||
*/
|
||||
public function column_default( $user, $column_name ) {
|
||||
switch ( $column_name ) {
|
||||
|
||||
case 'customer_name':
|
||||
if ( $user->last_name && $user->first_name ) {
|
||||
return $user->last_name . ', ' . $user->first_name;
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
|
||||
case 'username':
|
||||
return $user->user_login;
|
||||
|
||||
case 'location':
|
||||
$state_code = get_user_meta( $user->ID, 'billing_state', true );
|
||||
$country_code = get_user_meta( $user->ID, 'billing_country', true );
|
||||
|
||||
$state = isset( WC()->countries->states[ $country_code ][ $state_code ] ) ? WC()->countries->states[ $country_code ][ $state_code ] : $state_code;
|
||||
$country = isset( WC()->countries->countries[ $country_code ] ) ? WC()->countries->countries[ $country_code ] : $country_code;
|
||||
|
||||
$value = '';
|
||||
|
||||
if ( $state ) {
|
||||
$value .= $state . ', ';
|
||||
}
|
||||
|
||||
$value .= $country;
|
||||
|
||||
if ( $value ) {
|
||||
return $value;
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
|
||||
case 'email':
|
||||
return '<a href="mailto:' . $user->user_email . '">' . $user->user_email . '</a>';
|
||||
|
||||
case 'spent':
|
||||
return wc_price( wc_get_customer_total_spent( $user->ID ) );
|
||||
|
||||
case 'orders':
|
||||
return wc_get_customer_order_count( $user->ID );
|
||||
|
||||
case 'last_order':
|
||||
$orders = wc_get_orders(
|
||||
array(
|
||||
'limit' => 1,
|
||||
'status' => array_map( 'wc_get_order_status_name', wc_get_is_paid_statuses() ),
|
||||
'customer' => $user->ID,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $orders ) ) {
|
||||
$order = $orders[0];
|
||||
return '<a href="' . admin_url( 'post.php?post=' . $order->get_id() . '&action=edit' ) . '">' . _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number() . '</a> – ' . wc_format_datetime( $order->get_date_created() );
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'wc_actions':
|
||||
ob_start();
|
||||
?><p>
|
||||
<?php
|
||||
do_action( 'woocommerce_admin_user_actions_start', $user );
|
||||
|
||||
$actions = array();
|
||||
|
||||
$actions['refresh'] = array(
|
||||
'url' => wp_nonce_url( add_query_arg( 'refresh', $user->ID ), 'refresh' ),
|
||||
'name' => __( 'Refresh stats', 'woocommerce' ),
|
||||
'action' => 'refresh',
|
||||
);
|
||||
|
||||
$actions['edit'] = array(
|
||||
'url' => admin_url( 'user-edit.php?user_id=' . $user->ID ),
|
||||
'name' => __( 'Edit', 'woocommerce' ),
|
||||
'action' => 'edit',
|
||||
);
|
||||
|
||||
$actions['view'] = array(
|
||||
'url' => admin_url( 'edit.php?post_type=shop_order&_customer_user=' . $user->ID ),
|
||||
'name' => __( 'View orders', 'woocommerce' ),
|
||||
'action' => 'view',
|
||||
);
|
||||
|
||||
$orders = wc_get_orders(
|
||||
array(
|
||||
'limit' => 1,
|
||||
'status' => array_map( 'wc_get_order_status_name', wc_get_is_paid_statuses() ),
|
||||
'customer' => array( array( 0, $user->user_email ) ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( $orders ) {
|
||||
$actions['link'] = array(
|
||||
'url' => wp_nonce_url( add_query_arg( 'link_orders', $user->ID ), 'link_orders' ),
|
||||
'name' => __( 'Link previous orders', 'woocommerce' ),
|
||||
'action' => 'link',
|
||||
);
|
||||
}
|
||||
|
||||
$actions = apply_filters( 'woocommerce_admin_user_actions', $actions, $user );
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
printf( '<a class="button tips %s" href="%s" data-tip="%s">%s</a>', esc_attr( $action['action'] ), esc_url( $action['url'] ), esc_attr( $action['name'] ), esc_attr( $action['name'] ) );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_admin_user_actions_end', $user );
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
$user_actions = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $user_actions;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
$columns = array(
|
||||
'customer_name' => __( 'Name (Last, First)', 'woocommerce' ),
|
||||
'username' => __( 'Username', 'woocommerce' ),
|
||||
'email' => __( 'Email', 'woocommerce' ),
|
||||
'location' => __( 'Location', 'woocommerce' ),
|
||||
'orders' => __( 'Orders', 'woocommerce' ),
|
||||
'spent' => __( 'Money spent', 'woocommerce' ),
|
||||
'last_order' => __( 'Last order', 'woocommerce' ),
|
||||
'wc_actions' => __( 'Actions', 'woocommerce' ),
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order users by name.
|
||||
*
|
||||
* @param WP_User_Query $query Query that gets passed through.
|
||||
* @return WP_User_Query
|
||||
*/
|
||||
public function order_by_last_name( $query ) {
|
||||
global $wpdb;
|
||||
|
||||
$s = ! empty( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
$query->query_from .= " LEFT JOIN {$wpdb->usermeta} as meta2 ON ({$wpdb->users}.ID = meta2.user_id) ";
|
||||
$query->query_where .= " AND meta2.meta_key = 'last_name' ";
|
||||
$query->query_orderby = ' ORDER BY meta2.meta_value, user_login ASC ';
|
||||
|
||||
if ( $s ) {
|
||||
$query->query_from .= " LEFT JOIN {$wpdb->usermeta} as meta3 ON ({$wpdb->users}.ID = meta3.user_id)";
|
||||
$query->query_where .= " AND ( user_login LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' OR user_nicename LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' OR meta3.meta_value LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' ) ";
|
||||
$query->query_orderby = ' GROUP BY ID ' . $query->query_orderby;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare customer list items.
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$current_page = absint( $this->get_pagenum() );
|
||||
$per_page = 20;
|
||||
|
||||
/**
|
||||
* Init column headers.
|
||||
*/
|
||||
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
|
||||
|
||||
add_action( 'pre_user_query', array( $this, 'order_by_last_name' ) );
|
||||
|
||||
/**
|
||||
* Get users.
|
||||
*/
|
||||
$admin_users = new WP_User_Query(
|
||||
array(
|
||||
'role' => 'administrator',
|
||||
'fields' => 'ID',
|
||||
)
|
||||
);
|
||||
|
||||
$manager_users = new WP_User_Query(
|
||||
array(
|
||||
'role' => 'shop_manager',
|
||||
'fields' => 'ID',
|
||||
)
|
||||
);
|
||||
|
||||
$query = new WP_User_Query(
|
||||
apply_filters(
|
||||
'woocommerce_admin_report_customer_list_user_query_args',
|
||||
array(
|
||||
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ),
|
||||
'number' => $per_page,
|
||||
'offset' => ( $current_page - 1 ) * $per_page,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->items = $query->get_results();
|
||||
|
||||
remove_action( 'pre_user_query', array( $this, 'order_by_last_name' ) );
|
||||
|
||||
/**
|
||||
* Pagination.
|
||||
*/
|
||||
$this->set_pagination_args(
|
||||
array(
|
||||
'total_items' => $query->total_users,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => ceil( $query->total_users / $per_page ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
432
includes/admin/reports/class-wc-report-customers.php
Normal file
432
includes/admin/reports/class-wc-report-customers.php
Normal file
@ -0,0 +1,432 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WC_Report_Customers file.
|
||||
*
|
||||
* @package WooCommerce\Reports
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Customers
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Customers extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Chart colors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chart_colours = array();
|
||||
|
||||
/**
|
||||
* Customers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $customers = array();
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
$legend = array();
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: %s: signups amount */
|
||||
'title' => sprintf( __( '%s signups in this period', 'woocommerce' ), '<strong>' . count( $this->customers ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['signups'],
|
||||
'highlight_series' => 2,
|
||||
);
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart widgets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
$widgets = array();
|
||||
|
||||
$widgets[] = array(
|
||||
'title' => '',
|
||||
'callback' => array( $this, 'customers_vs_guests' ),
|
||||
);
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output customers vs guests chart.
|
||||
*/
|
||||
public function customers_vs_guests() {
|
||||
|
||||
$customer_order_totals = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
array(
|
||||
'meta_key' => '_customer_user',
|
||||
'meta_value' => '0',
|
||||
'operator' => '>',
|
||||
),
|
||||
),
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$guest_order_totals = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
array(
|
||||
'meta_key' => '_customer_user',
|
||||
'meta_value' => '0',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder customers_vs_guests pie-chart" style="height:200px"></div>
|
||||
<ul class="pie-chart-legend">
|
||||
<li style="border-color: <?php echo esc_attr( $this->chart_colours['customers'] ); ?>"><?php esc_html_e( 'Customer sales', 'woocommerce' ); ?></li>
|
||||
<li style="border-color: <?php echo esc_attr( $this->chart_colours['guests'] ); ?>"><?php esc_html_e( 'Guest sales', 'woocommerce' ); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function(){
|
||||
jQuery.plot(
|
||||
jQuery('.chart-placeholder.customers_vs_guests'),
|
||||
[
|
||||
{
|
||||
label: '<?php esc_html_e( 'Customer orders', 'woocommerce' ); ?>',
|
||||
data: "<?php echo esc_html( $customer_order_totals->total_orders ); ?>",
|
||||
color: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>'
|
||||
},
|
||||
{
|
||||
label: '<?php esc_html_e( 'Guest orders', 'woocommerce' ); ?>',
|
||||
data: "<?php echo esc_html( $guest_order_totals->total_orders ); ?>",
|
||||
color: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>'
|
||||
}
|
||||
],
|
||||
{
|
||||
grid: {
|
||||
hoverable: true
|
||||
},
|
||||
series: {
|
||||
pie: {
|
||||
show: true,
|
||||
radius: 1,
|
||||
innerRadius: 0.6,
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
enable_tooltip: true,
|
||||
append_tooltip: "<?php echo esc_html( ' ' . __( 'orders', 'woocommerce' ) ); ?>",
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder.customers_vs_guests').trigger( 'resize' );
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
'7day' => __( 'Last 7 days', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$this->chart_colours = array(
|
||||
'signups' => '#3498db',
|
||||
'customers' => '#1abc9c',
|
||||
'guests' => '#8fdece',
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
|
||||
$current_range = '7day';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
$admin_users = new WP_User_Query(
|
||||
array(
|
||||
'role' => 'administrator',
|
||||
'fields' => 'ID',
|
||||
)
|
||||
);
|
||||
|
||||
$manager_users = new WP_User_Query(
|
||||
array(
|
||||
'role' => 'shop_manager',
|
||||
'fields' => 'ID',
|
||||
)
|
||||
);
|
||||
|
||||
$users_query = new WP_User_Query(
|
||||
apply_filters(
|
||||
'woocommerce_admin_report_customers_user_query_args',
|
||||
array(
|
||||
'fields' => array( 'user_registered' ),
|
||||
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->customers = $users_query->get_results();
|
||||
|
||||
foreach ( $this->customers as $key => $customer ) {
|
||||
if ( strtotime( $customer->user_registered ) < $this->start_date || strtotime( $customer->user_registered ) > $this->end_date ) {
|
||||
unset( $this->customers[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
$customer_orders = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
array(
|
||||
'meta_key' => '_customer_user',
|
||||
'meta_value' => '0',
|
||||
'operator' => '>',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$guest_orders = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
array(
|
||||
'meta_key' => '_customer_user',
|
||||
'meta_value' => '0',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$signups = $this->prepare_chart_data( $this->customers, 'user_registered', '', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$guest_orders = $this->prepare_chart_data( $guest_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
|
||||
$chart_data = wp_json_encode(
|
||||
array(
|
||||
'signups' => array_values( $signups ),
|
||||
'customer_orders' => array_values( $customer_orders ),
|
||||
'guest_orders' => array_values( $guest_orders ),
|
||||
)
|
||||
);
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var chart_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
|
||||
|
||||
var drawGraph = function( highlight ) {
|
||||
var series = [
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Customer orders', 'woocommerce' ) ); ?>",
|
||||
data: chart_data.customer_orders,
|
||||
color: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>',
|
||||
bars: { fillColor: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_html( $this->barwidth ); ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
enable_tooltip: true,
|
||||
append_tooltip: "<?php echo esc_html( ' ' . __( 'customer orders', 'woocommerce' ) ); ?>",
|
||||
stack: true,
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Guest orders', 'woocommerce' ) ); ?>",
|
||||
data: chart_data.guest_orders,
|
||||
color: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>',
|
||||
bars: { fillColor: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_html( $this->barwidth ); ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
enable_tooltip: true,
|
||||
append_tooltip: "<?php echo esc_html( ' ' . __( 'guest orders', 'woocommerce' ) ); ?>",
|
||||
stack: true,
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Signups', 'woocommerce' ) ); ?>",
|
||||
data: chart_data.signups,
|
||||
color: '<?php echo esc_html( $this->chart_colours['signups'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 4, fill: false },
|
||||
shadowSize: 0,
|
||||
enable_tooltip: true,
|
||||
append_tooltip: "<?php echo esc_html( ' ' . __( 'new users', 'woocommerce' ) ); ?>",
|
||||
stack: false
|
||||
},
|
||||
];
|
||||
|
||||
if ( highlight !== 'undefined' && series[ highlight ] ) {
|
||||
highlight_series = series[ highlight ];
|
||||
|
||||
highlight_series.color = '#9c5d90';
|
||||
|
||||
if ( highlight_series.bars )
|
||||
highlight_series.bars.fillColor = '#9c5d90';
|
||||
|
||||
if ( highlight_series.lines ) {
|
||||
highlight_series.lines.lineWidth = 5;
|
||||
}
|
||||
}
|
||||
|
||||
main_chart = jQuery.plot(
|
||||
jQuery('.chart-placeholder.main'),
|
||||
series,
|
||||
{
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
color: '#aaa',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
hoverable: true
|
||||
},
|
||||
xaxes: [ {
|
||||
color: '#aaa',
|
||||
position: "bottom",
|
||||
tickColor: 'transparent',
|
||||
mode: "time",
|
||||
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
|
||||
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],
|
||||
tickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],
|
||||
font: {
|
||||
color: "#aaa"
|
||||
}
|
||||
} ],
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
minTickSize: 1,
|
||||
tickDecimals: 0,
|
||||
color: '#ecf0f1',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
jQuery('.chart-placeholder').trigger( 'resize' );
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').on( 'mouseenter',
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
} ).on( 'mouseleave',
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
336
includes/admin/reports/class-wc-report-downloads.php
Normal file
336
includes/admin/reports/class-wc-report-downloads.php
Normal file
@ -0,0 +1,336 @@
|
||||
<?php
|
||||
/**
|
||||
* Download report.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 3.3.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Downloads.
|
||||
*/
|
||||
class WC_Report_Downloads extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* Max items.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $max_items;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
parent::__construct(
|
||||
array(
|
||||
'singular' => 'download',
|
||||
'plural' => 'downloads',
|
||||
'ajax' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't need this.
|
||||
*
|
||||
* @param string $position Top or bottom.
|
||||
*/
|
||||
public function display_tablenav( $position ) {
|
||||
if ( 'top' !== $position ) {
|
||||
parent::display_tablenav( $position );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$this->prepare_items();
|
||||
|
||||
// Subtitle for permission if set.
|
||||
if ( ! empty( $_GET['permission_id'] ) ) { // WPCS: input var ok.
|
||||
$permission_id = absint( $_GET['permission_id'] ); // WPCS: input var ok.
|
||||
|
||||
// Load the permission, order, etc. so we can render more information.
|
||||
$permission = null;
|
||||
$product = null;
|
||||
|
||||
try {
|
||||
$permission = new WC_Customer_Download( $permission_id );
|
||||
$product = wc_get_product( $permission->product_id );
|
||||
} catch ( Exception $e ) {
|
||||
wp_die( sprintf( esc_html__( 'Permission #%d not found.', 'woocommerce' ), esc_html( $permission_id ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
echo '<h1>' . esc_html__( 'Customer downloads', 'woocommerce' );
|
||||
|
||||
$filters = $this->get_filter_vars();
|
||||
$filter_list = array();
|
||||
$filter_names = array(
|
||||
'product_id' => __( 'Product', 'woocommerce' ),
|
||||
'download_id' => __( 'File ID', 'woocommerce' ),
|
||||
'permission_id' => __( 'Permission ID', 'woocommerce' ),
|
||||
'order_id' => __( 'Order', 'woocommerce' ),
|
||||
'user_id' => __( 'User', 'woocommerce' ),
|
||||
'user_ip_address' => __( 'IP address', 'woocommerce' ),
|
||||
);
|
||||
|
||||
foreach ( $filters as $key => $value ) {
|
||||
if ( is_null( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
switch ( $key ) {
|
||||
case 'order_id':
|
||||
$order = wc_get_order( $value );
|
||||
if ( $order ) {
|
||||
$display_value = _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number();
|
||||
} else {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
case 'product_id':
|
||||
$product = wc_get_product( $value );
|
||||
if ( $product ) {
|
||||
$display_value = $product->get_formatted_name();
|
||||
} else {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$display_value = $value;
|
||||
break;
|
||||
}
|
||||
$filter_list[] = $filter_names[ $key ] . ' ' . $display_value . ' <a href="' . esc_url( remove_query_arg( $key ) ) . '" class="woocommerce-reports-remove-filter">×</a>';
|
||||
}
|
||||
|
||||
echo '</h1>';
|
||||
|
||||
echo '<div id="active-filters" class="woocommerce-reports-wide"><h2>';
|
||||
echo esc_html__( 'Active filters', 'woocommerce' ) . ': ';
|
||||
echo $filter_list ? wp_kses_post( implode( ', ', $filter_list ) ) : '';
|
||||
echo '</h2></div>';
|
||||
|
||||
echo '<div id="poststuff" class="woocommerce-reports-wide">';
|
||||
$this->display();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column value.
|
||||
*
|
||||
* @param mixed $item Item being displayed.
|
||||
* @param string $column_name Column name.
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
$permission = null;
|
||||
$product = null;
|
||||
try {
|
||||
$permission = new WC_Customer_Download( $item->permission_id );
|
||||
$product = wc_get_product( $permission->product_id );
|
||||
} catch ( Exception $e ) {
|
||||
// Ok to continue rendering other information even if permission and/or product is not found.
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $column_name ) {
|
||||
case 'timestamp':
|
||||
echo esc_html( $item->timestamp );
|
||||
break;
|
||||
case 'product':
|
||||
if ( ! empty( $product ) ) {
|
||||
edit_post_link( esc_html( $product->get_formatted_name() ), '', '', $product->get_id(), 'view-link' );
|
||||
|
||||
echo '<div class="row-actions">';
|
||||
echo '<a href="' . esc_url( add_query_arg( 'product_id', $product->get_id() ) ) . '">' . esc_html__( 'Filter by product', 'woocommerce' ) . '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
break;
|
||||
case 'file':
|
||||
if ( ! empty( $permission ) && ! empty( $product ) ) {
|
||||
// File information.
|
||||
$file = $product->get_file( $permission->get_download_id() );
|
||||
|
||||
if ( false === $file ) {
|
||||
echo esc_html__( 'File does not exist', 'woocommerce' );
|
||||
} else {
|
||||
echo esc_html( $file->get_name() . ' - ' . basename( $file->get_file() ) );
|
||||
|
||||
echo '<div class="row-actions">';
|
||||
echo '<a href="' . esc_url( add_query_arg( 'download_id', $permission->get_download_id() ) ) . '">' . esc_html__( 'Filter by file', 'woocommerce' ) . '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'order':
|
||||
if ( ! empty( $permission ) && ( $order = wc_get_order( $permission->order_id ) ) ) {
|
||||
edit_post_link( esc_html( _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number() ), '', '', $permission->order_id, 'view-link' );
|
||||
|
||||
echo '<div class="row-actions">';
|
||||
echo '<a href="' . esc_url( add_query_arg( 'order_id', $order->get_id() ) ) . '">' . esc_html__( 'Filter by order', 'woocommerce' ) . '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
if ( $item->user_id > 0 ) {
|
||||
$user = get_user_by( 'id', $item->user_id );
|
||||
|
||||
if ( ! empty( $user ) ) {
|
||||
echo '<a href="' . esc_url( get_edit_user_link( $item->user_id ) ) . '">' . esc_html( $user->display_name ) . '</a>';
|
||||
echo '<div class="row-actions">';
|
||||
echo '<a href="' . esc_url( add_query_arg( 'user_id', $item->user_id ) ) . '">' . esc_html__( 'Filter by user', 'woocommerce' ) . '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
} else {
|
||||
esc_html_e( 'Guest', 'woocommerce' );
|
||||
}
|
||||
break;
|
||||
case 'user_ip_address':
|
||||
echo esc_html( $item->user_ip_address );
|
||||
|
||||
echo '<div class="row-actions">';
|
||||
echo '<a href="' . esc_url( add_query_arg( 'user_ip_address', $item->user_ip_address ) ) . '">' . esc_html__( 'Filter by IP address', 'woocommerce' ) . '</a>';
|
||||
echo '</div>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
$columns = array(
|
||||
'timestamp' => __( 'Timestamp', 'woocommerce' ),
|
||||
'product' => __( 'Product', 'woocommerce' ),
|
||||
'file' => __( 'File', 'woocommerce' ),
|
||||
'order' => __( 'Order', 'woocommerce' ),
|
||||
'user' => __( 'User', 'woocommerce' ),
|
||||
'user_ip_address' => __( 'IP address', 'woocommerce' ),
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare download list items.
|
||||
*/
|
||||
public function prepare_items() {
|
||||
|
||||
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
|
||||
$current_page = absint( $this->get_pagenum() );
|
||||
// Allow filtering per_page value, but ensure it's at least 1.
|
||||
$per_page = max( 1, apply_filters( 'woocommerce_admin_downloads_report_downloads_per_page', 20 ) );
|
||||
|
||||
$this->get_items( $current_page, $per_page );
|
||||
|
||||
/**
|
||||
* Pagination.
|
||||
*/
|
||||
$this->set_pagination_args(
|
||||
array(
|
||||
'total_items' => $this->max_items,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => ceil( $this->max_items / $per_page ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No customer downloads found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filters from querystring.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function get_filter_vars() {
|
||||
$product_id = ! empty( $_GET['product_id'] ) ? absint( wp_unslash( $_GET['product_id'] ) ) : null; // WPCS: input var ok.
|
||||
$download_id = ! empty( $_GET['download_id'] ) ? wc_clean( wp_unslash( $_GET['download_id'] ) ) : null; // WPCS: input var ok.
|
||||
$permission_id = ! empty( $_GET['permission_id'] ) ? absint( wp_unslash( $_GET['permission_id'] ) ) : null; // WPCS: input var ok.
|
||||
$order_id = ! empty( $_GET['order_id'] ) ? absint( wp_unslash( $_GET['order_id'] ) ) : null; // WPCS: input var ok.
|
||||
$user_id = ! empty( $_GET['user_id'] ) ? absint( wp_unslash( $_GET['user_id'] ) ) : null; // WPCS: input var ok.
|
||||
$user_ip_address = ! empty( $_GET['user_ip_address'] ) ? wc_clean( wp_unslash( $_GET['user_ip_address'] ) ) : null; // WPCS: input var ok.
|
||||
|
||||
return (object) array(
|
||||
'product_id' => $product_id,
|
||||
'download_id' => $download_id,
|
||||
'permission_id' => $permission_id,
|
||||
'order_id' => $order_id,
|
||||
'user_id' => $user_id,
|
||||
'user_ip_address' => $user_ip_address,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get downloads matching criteria.
|
||||
*
|
||||
* @param int $current_page Current viewed page.
|
||||
* @param int $per_page How many results to show per page.
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
$filters = $this->get_filter_vars();
|
||||
|
||||
// Get downloads from database.
|
||||
$table = $wpdb->prefix . WC_Customer_Download_Log_Data_Store::get_table_name();
|
||||
$query_from = " FROM {$table} as downloads ";
|
||||
|
||||
if ( ! is_null( $filters->product_id ) || ! is_null( $filters->download_id ) || ! is_null( $filters->order_id ) ) {
|
||||
$query_from .= " LEFT JOIN {$wpdb->prefix}woocommerce_downloadable_product_permissions as permissions on downloads.permission_id = permissions.permission_id ";
|
||||
}
|
||||
|
||||
$query_from .= ' WHERE 1=1 ';
|
||||
|
||||
if ( ! is_null( $filters->product_id ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND product_id = %d ', $filters->product_id );
|
||||
}
|
||||
|
||||
if ( ! is_null( $filters->download_id ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND download_id = %s ', $filters->download_id );
|
||||
}
|
||||
|
||||
if ( ! is_null( $filters->order_id ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND order_id = %d ', $filters->order_id );
|
||||
}
|
||||
|
||||
if ( ! is_null( $filters->permission_id ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND downloads.permission_id = %d ', $filters->permission_id );
|
||||
}
|
||||
|
||||
if ( ! is_null( $filters->user_id ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND downloads.user_id = %d ', $filters->user_id );
|
||||
}
|
||||
|
||||
if ( ! is_null( $filters->user_ip_address ) ) {
|
||||
$query_from .= $wpdb->prepare( ' AND user_ip_address = %s ', $filters->user_ip_address );
|
||||
}
|
||||
|
||||
$query_from = apply_filters( 'woocommerce_report_downloads_query_from', $query_from );
|
||||
$query_order = $wpdb->prepare( 'ORDER BY timestamp DESC LIMIT %d, %d;', ( $current_page - 1 ) * $per_page, $per_page );
|
||||
|
||||
$this->items = $wpdb->get_results( "SELECT * {$query_from} {$query_order}" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
|
||||
$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT download_log_id ) {$query_from};" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
|
||||
}
|
||||
}
|
61
includes/admin/reports/class-wc-report-low-in-stock.php
Normal file
61
includes/admin/reports/class-wc-report-low-in-stock.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_Report_Low_In_Stock.
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) ) {
|
||||
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Low stock report class.
|
||||
*/
|
||||
class WC_Report_Low_In_Stock extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No low in stock products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria.
|
||||
*
|
||||
* @param int $current_page Current page number.
|
||||
* @param int $per_page How many results to show per page.
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
|
||||
$nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = apply_filters(
|
||||
'woocommerce_report_low_in_stock_query_from',
|
||||
$wpdb->prepare(
|
||||
"
|
||||
FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ( 'product', 'product_variation' )
|
||||
AND posts.post_status = 'publish'
|
||||
AND lookup.stock_quantity <= %d
|
||||
AND lookup.stock_quantity > %d
|
||||
",
|
||||
$stock,
|
||||
$nostock
|
||||
)
|
||||
);
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
}
|
51
includes/admin/reports/class-wc-report-most-stocked.php
Normal file
51
includes/admin/reports/class-wc-report-most-stocked.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_Report_Most_Stocked.
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) ) {
|
||||
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Most_Stocked.
|
||||
*/
|
||||
class WC_Report_Most_Stocked extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria.
|
||||
*
|
||||
* @param int $current_page Current page number.
|
||||
* @param int $per_page How many results to show per page.
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = apply_filters(
|
||||
'woocommerce_report_most_stocked_query_from',
|
||||
$wpdb->prepare(
|
||||
"
|
||||
FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ( 'product', 'product_variation' )
|
||||
AND posts.post_status = 'publish'
|
||||
AND lookup.stock_quantity > %d
|
||||
",
|
||||
$stock
|
||||
)
|
||||
);
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY lookup.stock_quantity DESC, id ASC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
}
|
58
includes/admin/reports/class-wc-report-out-of-stock.php
Normal file
58
includes/admin/reports/class-wc-report-out-of-stock.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_Report_Out_Of_Stock.
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) ) {
|
||||
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Out_Of_Stock class.
|
||||
*/
|
||||
class WC_Report_Out_Of_Stock extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No out of stock products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria.
|
||||
*
|
||||
* @param int $current_page Current page number.
|
||||
* @param int $per_page How many results to show per page.
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = apply_filters(
|
||||
'woocommerce_report_out_of_stock_query_from',
|
||||
$wpdb->prepare(
|
||||
"
|
||||
FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ( 'product', 'product_variation' )
|
||||
AND posts.post_status = 'publish'
|
||||
AND lookup.stock_quantity <= %d
|
||||
",
|
||||
$stock
|
||||
)
|
||||
);
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
}
|
451
includes/admin/reports/class-wc-report-sales-by-category.php
Normal file
451
includes/admin/reports/class-wc-report-sales-by-category.php
Normal file
@ -0,0 +1,451 @@
|
||||
<?php
|
||||
/**
|
||||
* Sales by category report functionality
|
||||
*
|
||||
* @package WooCommerce\Admin\Reporting
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Sales_By_Category
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Sales_By_Category extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Chart colors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chart_colours = array();
|
||||
|
||||
/**
|
||||
* Categories ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $show_categories = array();
|
||||
|
||||
/**
|
||||
* Item sales.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $item_sales = array();
|
||||
|
||||
/**
|
||||
* Item sales and times.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $item_sales_and_times = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( isset( $_GET['show_categories'] ) ) {
|
||||
$this->show_categories = is_array( $_GET['show_categories'] ) ? array_map( 'absint', $_GET['show_categories'] ) : array( absint( $_GET['show_categories'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all product ids in a category (and its children).
|
||||
*
|
||||
* @param int $category_id Category ID.
|
||||
* @return array
|
||||
*/
|
||||
public function get_products_in_category( $category_id ) {
|
||||
$term_ids = get_term_children( $category_id, 'product_cat' );
|
||||
$term_ids[] = $category_id;
|
||||
$product_ids = get_objects_in_term( $term_ids, 'product_cat' );
|
||||
|
||||
return array_unique( apply_filters( 'woocommerce_report_sales_by_category_get_products_in_category', $product_ids, $category_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
|
||||
if ( empty( $this->show_categories ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$legend = array();
|
||||
$index = 0;
|
||||
|
||||
foreach ( $this->show_categories as $category ) {
|
||||
|
||||
$category = get_term( $category, 'product_cat' );
|
||||
$total = 0;
|
||||
$product_ids = $this->get_products_in_category( $category->term_id );
|
||||
|
||||
foreach ( $product_ids as $id ) {
|
||||
|
||||
if ( isset( $this->item_sales[ $id ] ) ) {
|
||||
$total += $this->item_sales[ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: 1: total items sold 2: category name */
|
||||
'title' => sprintf( __( '%1$s sales in %2$s', 'woocommerce' ), '<strong>' . wc_price( $total ) . '</strong>', $category->name ),
|
||||
'color' => isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0],
|
||||
'highlight_series' => $index,
|
||||
);
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
'7day' => __( 'Last 7 days', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$this->chart_colours = array( '#3498db', '#34495e', '#1abc9c', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c', '#2980b9', '#8e44ad', '#2c3e50', '#16a085', '#27ae60', '#f39c12', '#d35400', '#c0392b' );
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
|
||||
$current_range = '7day';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
// Get item sales data.
|
||||
if ( ! empty( $this->show_categories ) ) {
|
||||
$order_items = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
'_line_total' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_amount',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'group_by' => 'ID, product_id, post_date',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
$this->item_sales = array();
|
||||
$this->item_sales_and_times = array();
|
||||
|
||||
if ( is_array( $order_items ) ) {
|
||||
|
||||
foreach ( $order_items as $order_item ) {
|
||||
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day':
|
||||
$time = strtotime( gmdate( 'Ymd', strtotime( $order_item->post_date ) ) ) * 1000;
|
||||
break;
|
||||
case 'month':
|
||||
default:
|
||||
$time = strtotime( gmdate( 'Ym', strtotime( $order_item->post_date ) ) . '01' ) * 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->item_sales_and_times[ $time ][ $order_item->product_id ] = isset( $this->item_sales_and_times[ $time ][ $order_item->product_id ] ) ? $this->item_sales_and_times[ $time ][ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
|
||||
|
||||
$this->item_sales[ $order_item->product_id ] = isset( $this->item_sales[ $order_item->product_id ] ) ? $this->item_sales[ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart widgets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'title' => __( 'Categories', 'woocommerce' ),
|
||||
'callback' => array( $this, 'category_widget' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output category widget.
|
||||
*/
|
||||
public function category_widget() {
|
||||
|
||||
$categories = get_terms( 'product_cat', array( 'orderby' => 'name' ) );
|
||||
?>
|
||||
<form method="GET">
|
||||
<div>
|
||||
<select multiple="multiple" data-placeholder="<?php esc_attr_e( 'Select categories…', 'woocommerce' ); ?>" class="wc-enhanced-select" id="show_categories" name="show_categories[]" style="width: 205px;">
|
||||
<?php
|
||||
$r = array();
|
||||
$r['pad_counts'] = 1;
|
||||
$r['hierarchical'] = 1;
|
||||
$r['hide_empty'] = 1;
|
||||
$r['value'] = 'id';
|
||||
$r['selected'] = $this->show_categories;
|
||||
|
||||
include_once WC()->plugin_path() . '/includes/walkers/class-wc-product-cat-dropdown-walker.php';
|
||||
|
||||
echo wc_walk_category_dropdown_tree( $categories, 0, $r ); // @codingStandardsIgnoreLine
|
||||
?>
|
||||
</select>
|
||||
<?php // @codingStandardsIgnoreStart ?>
|
||||
<a href="#" class="select_none"><?php esc_html_e( 'None', 'woocommerce' ); ?></a>
|
||||
<a href="#" class="select_all"><?php esc_html_e( 'All', 'woocommerce' ); ?></a>
|
||||
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
|
||||
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( wp_unslash( $_GET['range'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( wp_unslash( $_GET['start_date'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( wp_unslash( $_GET['end_date'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( wp_unslash( $_GET['page'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( wp_unslash( $_GET['tab'] ) ) : ''; ?>" />
|
||||
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( wp_unslash( $_GET['report'] ) ) : ''; ?>" />
|
||||
<?php // @codingStandardsIgnoreEnd ?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function(){
|
||||
// Select all/None
|
||||
jQuery( '.chart-widget' ).on( 'click', '.select_all', function() {
|
||||
jQuery(this).closest( 'div' ).find( 'select option' ).attr( 'selected', 'selected' );
|
||||
jQuery(this).closest( 'div' ).find('select').trigger( 'change' );
|
||||
return false;
|
||||
});
|
||||
|
||||
jQuery( '.chart-widget').on( 'click', '.select_none', function() {
|
||||
jQuery(this).closest( 'div' ).find( 'select option' ).prop( 'selected', false );
|
||||
jQuery(this).closest( 'div' ).find('select').trigger( 'change' );
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
if ( empty( $this->show_categories ) ) {
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<p class="chart-prompt"><?php esc_html_e( 'Choose a category to view stats', 'woocommerce' ); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
$chart_data = array();
|
||||
$index = 0;
|
||||
|
||||
foreach ( $this->show_categories as $category ) {
|
||||
|
||||
$category = get_term( $category, 'product_cat' );
|
||||
$product_ids = $this->get_products_in_category( $category->term_id );
|
||||
$category_chart_data = array();
|
||||
|
||||
for ( $i = 0; $i <= $this->chart_interval; $i ++ ) {
|
||||
|
||||
$interval_total = 0;
|
||||
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day':
|
||||
$time = strtotime( gmdate( 'Ymd', strtotime( "+{$i} DAY", $this->start_date ) ) ) * 1000;
|
||||
break;
|
||||
case 'month':
|
||||
default:
|
||||
$time = strtotime( gmdate( 'Ym', strtotime( "+{$i} MONTH", $this->start_date ) ) . '01' ) * 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ( $product_ids as $id ) {
|
||||
|
||||
if ( isset( $this->item_sales_and_times[ $time ][ $id ] ) ) {
|
||||
$interval_total += $this->item_sales_and_times[ $time ][ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
$category_chart_data[] = array( $time, (float) wc_format_decimal( $interval_total, wc_get_price_decimals() ) );
|
||||
}
|
||||
|
||||
$chart_data[ $category->term_id ]['category'] = $category->name;
|
||||
$chart_data[ $category->term_id ]['data'] = $category_chart_data;
|
||||
|
||||
$index++;
|
||||
}
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<?php // @codingStandardsIgnoreStart ?>
|
||||
<script type="text/javascript">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var drawGraph = function( highlight ) {
|
||||
var series = [
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ( $chart_data as $data ) {
|
||||
$color = isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0];
|
||||
$width = $this->barwidth / sizeof( $chart_data );
|
||||
$offset = ( $width * $index );
|
||||
$series = $data['data'];
|
||||
|
||||
foreach ( $series as $key => $series_data ) {
|
||||
$series[ $key ][0] = $series_data[0] + $offset;
|
||||
}
|
||||
|
||||
$series = wp_json_encode( $series );
|
||||
|
||||
echo '{
|
||||
label: "' . esc_js( $data['category'] ) . '",
|
||||
data: JSON.parse( decodeURIComponent( "' . rawurlencode( $series ) . '" ) ),
|
||||
color: "' . $color . '",
|
||||
bars: {
|
||||
fillColor: "' . $color . '",
|
||||
fill: true,
|
||||
show: true,
|
||||
lineWidth: 1,
|
||||
align: "center",
|
||||
barWidth: ' . $width * 0.75 . ',
|
||||
stack: false
|
||||
},
|
||||
' . $this->get_currency_tooltip() . ',
|
||||
enable_tooltip: true,
|
||||
prepend_label: true
|
||||
},';
|
||||
$index++;
|
||||
}
|
||||
?>
|
||||
];
|
||||
|
||||
if ( highlight !== 'undefined' && series[ highlight ] ) {
|
||||
highlight_series = series[ highlight ];
|
||||
|
||||
highlight_series.color = '#9c5d90';
|
||||
|
||||
if ( highlight_series.bars ) {
|
||||
highlight_series.bars.fillColor = '#9c5d90';
|
||||
}
|
||||
|
||||
if ( highlight_series.lines ) {
|
||||
highlight_series.lines.lineWidth = 5;
|
||||
}
|
||||
}
|
||||
|
||||
main_chart = jQuery.plot(
|
||||
jQuery('.chart-placeholder.main'),
|
||||
series,
|
||||
{
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
color: '#aaa',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
hoverable: true
|
||||
},
|
||||
xaxes: [ {
|
||||
color: '#aaa',
|
||||
reserveSpace: true,
|
||||
position: "bottom",
|
||||
tickColor: 'transparent',
|
||||
mode: "time",
|
||||
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
|
||||
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
|
||||
tickSize: [1, "<?php echo $this->chart_groupby; ?>"],
|
||||
font: {
|
||||
color: "#aaa"
|
||||
}
|
||||
} ],
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
tickDecimals: 2,
|
||||
color: 'transparent',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder').trigger( 'resize' );
|
||||
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').on( 'mouseenter',
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
} ).on( 'mouseleave',
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php // @codingStandardsIgnoreEnd ?>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
869
includes/admin/reports/class-wc-report-sales-by-date.php
Normal file
869
includes/admin/reports/class-wc-report-sales-by-date.php
Normal file
@ -0,0 +1,869 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_Report_Sales_By_Date
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Sales_By_Date
|
||||
*/
|
||||
class WC_Report_Sales_By_Date extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Chart colors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chart_colours = array();
|
||||
|
||||
/**
|
||||
* The report data.
|
||||
*
|
||||
* @var stdClass
|
||||
*/
|
||||
private $report_data;
|
||||
|
||||
/**
|
||||
* Get report data.
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function get_report_data() {
|
||||
if ( empty( $this->report_data ) ) {
|
||||
$this->query_report_data();
|
||||
}
|
||||
return $this->report_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data needed for this report and store in the class.
|
||||
*/
|
||||
private function query_report_data() {
|
||||
$this->report_data = new stdClass();
|
||||
|
||||
$this->report_data->order_counts = (array) $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'count',
|
||||
'distinct' => true,
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$this->report_data->coupons = (array) $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'function' => '',
|
||||
'name' => 'order_item_name',
|
||||
),
|
||||
'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_items.order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query . ', order_item_name',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
// All items from orders - even those refunded.
|
||||
$this->report_data->order_items = (array) $this->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_items.order_item_type',
|
||||
'value' => 'line_item',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Get total of fully refunded items.
|
||||
*/
|
||||
$this->report_data->refunded_order_items = absint(
|
||||
$this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_count',
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_items.order_item_type',
|
||||
'value' => 'line_item',
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'order-count' ),
|
||||
'order_status' => array( 'refunded' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Order totals by date. Charts should show GROSS amounts to avoid going -ve.
|
||||
*/
|
||||
$this->report_data->orders = (array) $this->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',
|
||||
),
|
||||
'_order_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'total_tax',
|
||||
),
|
||||
'_order_shipping_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'total_shipping_tax',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'sales-reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* If an order is 100% refunded we should look at the parent's totals, but the refunds dates.
|
||||
* We also need to ensure each parent order's values are only counted/summed once.
|
||||
*/
|
||||
$this->report_data->full_refunds = (array) $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_order_total' => array(
|
||||
'type' => 'parent_meta',
|
||||
'function' => '',
|
||||
'name' => 'total_refund',
|
||||
),
|
||||
'_order_shipping' => array(
|
||||
'type' => 'parent_meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping',
|
||||
),
|
||||
'_order_tax' => array(
|
||||
'type' => 'parent_meta',
|
||||
'function' => '',
|
||||
'name' => 'total_tax',
|
||||
),
|
||||
'_order_shipping_tax' => array(
|
||||
'type' => 'parent_meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping_tax',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'group_by' => 'posts.post_parent',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => false,
|
||||
'parent_order_status' => array( 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $this->report_data->full_refunds as $key => $order ) {
|
||||
$total_refund = is_numeric( $order->total_refund ) ? $order->total_refund : 0;
|
||||
$total_shipping = is_numeric( $order->total_shipping ) ? $order->total_shipping : 0;
|
||||
$total_tax = is_numeric( $order->total_tax ) ? $order->total_tax : 0;
|
||||
$total_shipping_tax = is_numeric( $order->total_shipping_tax ) ? $order->total_shipping_tax : 0;
|
||||
|
||||
$this->report_data->full_refunds[ $key ]->net_refund = $total_refund - ( $total_shipping + $total_tax + $total_shipping_tax );
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial refunds. This includes line items, shipping and taxes. Not grouped by date.
|
||||
*/
|
||||
$this->report_data->partial_refunds = (array) $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'refund_id',
|
||||
),
|
||||
'_refund_amount' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_refund',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
'order_item_type' => array(
|
||||
'type' => 'order_item',
|
||||
'function' => '',
|
||||
'name' => 'item_type',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_total' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_sales',
|
||||
),
|
||||
'_order_shipping' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_tax',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_shipping_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping_tax',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_count',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
),
|
||||
'group_by' => 'refund_id',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => false,
|
||||
'parent_order_status' => array( 'completed', 'processing', 'on-hold' ),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $this->report_data->partial_refunds as $key => $order ) {
|
||||
$this->report_data->partial_refunds[ $key ]->net_refund = $order->total_refund - ( $order->total_shipping + $order->total_tax + $order->total_shipping_tax );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund lines - all partial refunds on all order types so we can plot full AND partial refunds on the chart.
|
||||
*/
|
||||
$this->report_data->refund_lines = (array) $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'refund_id',
|
||||
),
|
||||
'_refund_amount' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_refund',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
'order_item_type' => array(
|
||||
'type' => 'order_item',
|
||||
'function' => '',
|
||||
'name' => 'item_type',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_total' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_sales',
|
||||
),
|
||||
'_order_shipping' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_tax',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_order_shipping_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => '',
|
||||
'name' => 'total_shipping_tax',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_count',
|
||||
'join_type' => 'LEFT',
|
||||
),
|
||||
),
|
||||
'group_by' => 'refund_id',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => false,
|
||||
'parent_order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Total up refunds. Note: when an order is fully refunded, a refund line will be added.
|
||||
*/
|
||||
$this->report_data->total_tax_refunded = 0;
|
||||
$this->report_data->total_shipping_refunded = 0;
|
||||
$this->report_data->total_shipping_tax_refunded = 0;
|
||||
$this->report_data->total_refunds = 0;
|
||||
|
||||
$this->report_data->refunded_orders = array_merge( $this->report_data->partial_refunds, $this->report_data->full_refunds );
|
||||
|
||||
foreach ( $this->report_data->refunded_orders as $key => $value ) {
|
||||
$this->report_data->total_tax_refunded += floatval( $value->total_tax < 0 ? $value->total_tax * -1 : $value->total_tax );
|
||||
$this->report_data->total_refunds += floatval( $value->total_refund );
|
||||
$this->report_data->total_shipping_tax_refunded += floatval( $value->total_shipping_tax < 0 ? $value->total_shipping_tax * -1 : $value->total_shipping_tax );
|
||||
$this->report_data->total_shipping_refunded += floatval( $value->total_shipping < 0 ? $value->total_shipping * -1 : $value->total_shipping );
|
||||
|
||||
// Only applies to parial.
|
||||
if ( isset( $value->order_item_count ) ) {
|
||||
$this->report_data->refunded_order_items += floatval( $value->order_item_count < 0 ? $value->order_item_count * -1 : $value->order_item_count );
|
||||
}
|
||||
}
|
||||
|
||||
// Totals from all orders - including those refunded. Subtract refunded amounts.
|
||||
$this->report_data->total_tax = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_tax' ) ) - $this->report_data->total_tax_refunded, 2 );
|
||||
$this->report_data->total_shipping = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_shipping' ) ) - $this->report_data->total_shipping_refunded, 2 );
|
||||
$this->report_data->total_shipping_tax = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_shipping_tax' ) ) - $this->report_data->total_shipping_tax_refunded, 2 );
|
||||
|
||||
// Total the refunds and sales amounts. Sales subract refunds. Note - total_sales also includes shipping costs.
|
||||
$this->report_data->total_sales = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_sales' ) ) - $this->report_data->total_refunds, 2 );
|
||||
$this->report_data->net_sales = wc_format_decimal( $this->report_data->total_sales - $this->report_data->total_shipping - max( 0, $this->report_data->total_tax ) - max( 0, $this->report_data->total_shipping_tax ), 2 );
|
||||
|
||||
// Calculate average based on net.
|
||||
$this->report_data->average_sales = wc_format_decimal( $this->report_data->net_sales / ( $this->chart_interval + 1 ), 2 );
|
||||
$this->report_data->average_total_sales = wc_format_decimal( $this->report_data->total_sales / ( $this->chart_interval + 1 ), 2 );
|
||||
|
||||
// Total orders and discounts also includes those which have been refunded at some point.
|
||||
$this->report_data->total_coupons = number_format( array_sum( wp_list_pluck( $this->report_data->coupons, 'discount_amount' ) ), 2, '.', '' );
|
||||
$this->report_data->total_refunded_orders = absint( count( $this->report_data->full_refunds ) );
|
||||
|
||||
// Total orders in this period, even if refunded.
|
||||
$this->report_data->total_orders = absint( array_sum( wp_list_pluck( $this->report_data->order_counts, 'count' ) ) );
|
||||
|
||||
// Item items ordered in this period, even if refunded.
|
||||
$this->report_data->total_items = absint( array_sum( wp_list_pluck( $this->report_data->order_items, 'order_item_count' ) ) );
|
||||
|
||||
// 3rd party filtering of report data
|
||||
$this->report_data = apply_filters( 'woocommerce_admin_report_data', $this->report_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
$legend = array();
|
||||
$data = $this->get_report_data();
|
||||
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day':
|
||||
$average_total_sales_title = sprintf(
|
||||
/* translators: %s: average total sales */
|
||||
__( '%s average gross daily sales', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->average_total_sales ) . '</strong>'
|
||||
);
|
||||
$average_sales_title = sprintf(
|
||||
/* translators: %s: average sales */
|
||||
__( '%s average net daily sales', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->average_sales ) . '</strong>'
|
||||
);
|
||||
break;
|
||||
case 'month':
|
||||
default:
|
||||
$average_total_sales_title = sprintf(
|
||||
/* translators: %s: average total sales */
|
||||
__( '%s average gross monthly sales', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->average_total_sales ) . '</strong>'
|
||||
);
|
||||
$average_sales_title = sprintf(
|
||||
/* translators: %s: average sales */
|
||||
__( '%s average net monthly sales', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->average_sales ) . '</strong>'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: total sales */
|
||||
__( '%s gross sales in this period', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->total_sales ) . '</strong>'
|
||||
),
|
||||
'placeholder' => __( 'This is the sum of the order totals after any refunds and including shipping and taxes.', 'woocommerce' ),
|
||||
'color' => $this->chart_colours['sales_amount'],
|
||||
'highlight_series' => 6,
|
||||
);
|
||||
if ( $data->average_total_sales > 0 ) {
|
||||
$legend[] = array(
|
||||
'title' => $average_total_sales_title,
|
||||
'color' => $this->chart_colours['average'],
|
||||
'highlight_series' => 2,
|
||||
);
|
||||
}
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: net sales */
|
||||
__( '%s net sales in this period', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->net_sales ) . '</strong>'
|
||||
),
|
||||
'placeholder' => __( 'This is the sum of the order totals after any refunds and excluding shipping and taxes.', 'woocommerce' ),
|
||||
'color' => $this->chart_colours['net_sales_amount'],
|
||||
'highlight_series' => 7,
|
||||
);
|
||||
if ( $data->average_sales > 0 ) {
|
||||
$legend[] = array(
|
||||
'title' => $average_sales_title,
|
||||
'color' => $this->chart_colours['net_average'],
|
||||
'highlight_series' => 3,
|
||||
);
|
||||
}
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: total orders */
|
||||
__( '%s orders placed', 'woocommerce' ),
|
||||
'<strong>' . $data->total_orders . '</strong>'
|
||||
),
|
||||
'color' => $this->chart_colours['order_count'],
|
||||
'highlight_series' => 1,
|
||||
);
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: total items */
|
||||
__( '%s items purchased', 'woocommerce' ),
|
||||
'<strong>' . $data->total_items . '</strong>'
|
||||
),
|
||||
'color' => $this->chart_colours['item_count'],
|
||||
'highlight_series' => 0,
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: 1: total refunds 2: total refunded orders 3: refunded items */
|
||||
_n( '%1$s refunded %2$d order (%3$d item)', '%1$s refunded %2$d orders (%3$d items)', $this->report_data->total_refunded_orders, 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->total_refunds ) . '</strong>',
|
||||
$this->report_data->total_refunded_orders,
|
||||
$this->report_data->refunded_order_items
|
||||
),
|
||||
'color' => $this->chart_colours['refund_amount'],
|
||||
'highlight_series' => 8,
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: total shipping */
|
||||
__( '%s charged for shipping', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->total_shipping ) . '</strong>'
|
||||
),
|
||||
'color' => $this->chart_colours['shipping_amount'],
|
||||
'highlight_series' => 5,
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %s: total coupons */
|
||||
__( '%s worth of coupons used', 'woocommerce' ),
|
||||
'<strong>' . wc_price( $data->total_coupons ) . '</strong>'
|
||||
),
|
||||
'color' => $this->chart_colours['coupon_amount'],
|
||||
'highlight_series' => 4,
|
||||
);
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
'7day' => __( 'Last 7 days', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$this->chart_colours = array(
|
||||
'sales_amount' => '#b1d4ea',
|
||||
'net_sales_amount' => '#3498db',
|
||||
'average' => '#b1d4ea',
|
||||
'net_average' => '#3498db',
|
||||
'order_count' => '#dbe1e3',
|
||||
'item_count' => '#ecf0f1',
|
||||
'shipping_amount' => '#5cc488',
|
||||
'coupon_amount' => '#f1c40f',
|
||||
'refund_amount' => '#e74c3c',
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
|
||||
$current_range = '7day';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
|
||||
data-exclude_series="2"
|
||||
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Round our totals correctly.
|
||||
*
|
||||
* @param array|string $amount Chart total.
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
private function round_chart_totals( $amount ) {
|
||||
if ( is_array( $amount ) ) {
|
||||
return array( $amount[0], wc_format_decimal( $amount[1], wc_get_price_decimals() ) );
|
||||
} else {
|
||||
return wc_format_decimal( $amount, wc_get_price_decimals() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
// Prepare data for report.
|
||||
$data = array(
|
||||
'order_counts' => $this->prepare_chart_data( $this->report_data->order_counts, 'post_date', 'count', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'order_item_counts' => $this->prepare_chart_data( $this->report_data->order_items, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'order_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_sales', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'coupon_amounts' => $this->prepare_chart_data( $this->report_data->coupons, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'shipping_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_shipping', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'refund_amounts' => $this->prepare_chart_data( $this->report_data->refund_lines, 'post_date', 'total_refund', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'net_refund_amounts' => $this->prepare_chart_data( $this->report_data->refunded_orders, 'post_date', 'net_refund', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'shipping_tax_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_shipping_tax', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'tax_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_tax', $this->chart_interval, $this->start_date, $this->chart_groupby ),
|
||||
'net_order_amounts' => array(),
|
||||
'gross_order_amounts' => array(),
|
||||
);
|
||||
|
||||
foreach ( $data['order_amounts'] as $order_amount_key => $order_amount_value ) {
|
||||
$data['gross_order_amounts'][ $order_amount_key ] = $order_amount_value;
|
||||
$data['gross_order_amounts'][ $order_amount_key ][1] -= $data['refund_amounts'][ $order_amount_key ][1];
|
||||
|
||||
$data['net_order_amounts'][ $order_amount_key ] = $order_amount_value;
|
||||
// Subtract the sum of the values from net order amounts.
|
||||
$data['net_order_amounts'][ $order_amount_key ][1] -=
|
||||
$data['net_refund_amounts'][ $order_amount_key ][1] +
|
||||
$data['shipping_amounts'][ $order_amount_key ][1] +
|
||||
$data['shipping_tax_amounts'][ $order_amount_key ][1] +
|
||||
$data['tax_amounts'][ $order_amount_key ][1];
|
||||
}
|
||||
|
||||
// 3rd party filtering of report data.
|
||||
$data = apply_filters( 'woocommerce_admin_report_chart_data', $data );
|
||||
|
||||
// Encode in json format.
|
||||
$chart_data = wp_json_encode(
|
||||
array(
|
||||
'order_counts' => array_values( $data['order_counts'] ),
|
||||
'order_item_counts' => array_values( $data['order_item_counts'] ),
|
||||
'order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['order_amounts'] ) ),
|
||||
'gross_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['gross_order_amounts'] ) ),
|
||||
'net_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['net_order_amounts'] ) ),
|
||||
'shipping_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['shipping_amounts'] ) ),
|
||||
'coupon_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['coupon_amounts'] ) ),
|
||||
'refund_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['refund_amounts'] ) ),
|
||||
)
|
||||
);
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
|
||||
var drawGraph = function( highlight ) {
|
||||
var series = [
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ); ?>",
|
||||
data: order_data.order_item_counts,
|
||||
color: '<?php echo esc_js( $this->chart_colours['item_count'] ); ?>',
|
||||
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['item_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Number of orders', 'woocommerce' ) ); ?>",
|
||||
data: order_data.order_counts,
|
||||
color: '<?php echo esc_js( $this->chart_colours['order_count'] ); ?>',
|
||||
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['order_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Average gross sales amount', 'woocommerce' ) ); ?>",
|
||||
data: [ [ <?php echo esc_js( min( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_total_sales ); ?> ], [ <?php echo esc_js( max( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_total_sales ); ?> ] ],
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['average'] ); ?>',
|
||||
points: { show: false },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Average net sales amount', 'woocommerce' ) ); ?>",
|
||||
data: [ [ <?php echo esc_js( min( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_sales ); ?> ], [ <?php echo esc_js( max( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_sales ); ?> ] ],
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['net_average'] ); ?>',
|
||||
points: { show: false },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Coupon amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.coupon_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['coupon_amount'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Shipping amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.shipping_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['shipping_amount'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>"
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Gross sales amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.gross_order_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['sales_amount'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Net sales amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.net_order_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['net_sales_amount'] ); ?>',
|
||||
points: { show: true, radius: 6, lineWidth: 4, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 5, fill: false },
|
||||
shadowSize: 0,
|
||||
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Refund amount', 'woocommerce' ) ); ?>",
|
||||
data: order_data.refund_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo esc_js( $this->chart_colours['refund_amount'] ); ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 2, fill: false },
|
||||
shadowSize: 0,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>"
|
||||
},
|
||||
];
|
||||
|
||||
if ( highlight !== 'undefined' && series[ highlight ] ) {
|
||||
highlight_series = series[ highlight ];
|
||||
|
||||
highlight_series.color = '#9c5d90';
|
||||
|
||||
if ( highlight_series.bars ) {
|
||||
highlight_series.bars.fillColor = '#9c5d90';
|
||||
}
|
||||
|
||||
if ( highlight_series.lines ) {
|
||||
highlight_series.lines.lineWidth = 5;
|
||||
}
|
||||
}
|
||||
|
||||
main_chart = jQuery.plot(
|
||||
jQuery('.chart-placeholder.main'),
|
||||
series,
|
||||
{
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
color: '#aaa',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
hoverable: true
|
||||
},
|
||||
xaxes: [ {
|
||||
color: '#aaa',
|
||||
position: "bottom",
|
||||
tickColor: 'transparent',
|
||||
mode: "time",
|
||||
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
|
||||
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
|
||||
font: {
|
||||
color: "#aaa"
|
||||
}
|
||||
} ],
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
minTickSize: 1,
|
||||
tickDecimals: 0,
|
||||
color: '#d4d9dc',
|
||||
font: { color: "#aaa" }
|
||||
},
|
||||
{
|
||||
position: "right",
|
||||
min: 0,
|
||||
tickDecimals: 2,
|
||||
alignTicksWithAxis: 1,
|
||||
color: 'transparent',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder').trigger( 'resize' );
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').on( 'mouseenter',
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
} ).on( 'mouseleave',
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
632
includes/admin/reports/class-wc-report-sales-by-product.php
Normal file
632
includes/admin/reports/class-wc-report-sales-by-product.php
Normal file
@ -0,0 +1,632 @@
|
||||
<?php
|
||||
/**
|
||||
* Sales By Product Reporting
|
||||
*
|
||||
* @package WooCommerce\Admin\Reporting
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Sales_By_Product
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Sales_By_Product extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Chart colors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chart_colours = array();
|
||||
|
||||
/**
|
||||
* Product ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $product_ids = array();
|
||||
|
||||
/**
|
||||
* Product ids with titles.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $product_ids_titles = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
// @codingStandardsIgnoreStart
|
||||
if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) ) {
|
||||
$this->product_ids = array_filter( array_map( 'absint', $_GET['product_ids'] ) );
|
||||
} elseif ( isset( $_GET['product_ids'] ) ) {
|
||||
$this->product_ids = array_filter( array( absint( $_GET['product_ids'] ) ) );
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
|
||||
if ( empty( $this->product_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$legend = array();
|
||||
|
||||
$total_sales = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_line_total' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_amount',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'operator' => 'IN',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$total_items = absint(
|
||||
$this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_count',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'operator' => 'IN',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: %s: total items sold */
|
||||
'title' => sprintf( __( '%s sales for the selected items', 'woocommerce' ), '<strong>' . wc_price( $total_sales ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['sales_amount'],
|
||||
'highlight_series' => 1,
|
||||
);
|
||||
|
||||
$legend[] = array(
|
||||
/* translators: %s: total items purchased */
|
||||
'title' => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . ( $total_items ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['item_count'],
|
||||
'highlight_series' => 0,
|
||||
);
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
'7day' => __( 'Last 7 days', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$this->chart_colours = array(
|
||||
'sales_amount' => '#3498db',
|
||||
'item_count' => '#d4d9dc',
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
|
||||
$current_range = '7day';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart widgets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
|
||||
$widgets = array();
|
||||
|
||||
if ( ! empty( $this->product_ids ) ) {
|
||||
$widgets[] = array(
|
||||
'title' => __( 'Showing reports for:', 'woocommerce' ),
|
||||
'callback' => array( $this, 'current_filters' ),
|
||||
);
|
||||
}
|
||||
|
||||
$widgets[] = array(
|
||||
'title' => '',
|
||||
'callback' => array( $this, 'products_widget' ),
|
||||
);
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output current filters.
|
||||
*/
|
||||
public function current_filters() {
|
||||
|
||||
$this->product_ids_titles = array();
|
||||
|
||||
foreach ( $this->product_ids as $product_id ) {
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( $product ) {
|
||||
$this->product_ids_titles[] = $product->get_formatted_name();
|
||||
} else {
|
||||
$this->product_ids_titles[] = '#' . $product_id;
|
||||
}
|
||||
}
|
||||
|
||||
echo '<p><strong>' . wp_kses_post( implode( ', ', $this->product_ids_titles ) ) . '</strong></p>';
|
||||
echo '<p><a class="button" href="' . esc_url( remove_query_arg( 'product_ids' ) ) . '">' . esc_html__( 'Reset', 'woocommerce' ) . '</a></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output products widget.
|
||||
*/
|
||||
public function products_widget() {
|
||||
?>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Product search', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<form method="GET">
|
||||
<div>
|
||||
<?php // @codingStandardsIgnoreStart ?>
|
||||
<select class="wc-product-search" style="width:203px;" multiple="multiple" id="product_ids" name="product_ids[]" data-placeholder="<?php esc_attr_e( 'Search for a product…', 'woocommerce' ); ?>" data-action="woocommerce_json_search_products_and_variations"></select>
|
||||
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
|
||||
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( $_GET['range'] ) : ''; ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( $_GET['start_date'] ) : ''; ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( $_GET['end_date'] ) : ''; ?>" />
|
||||
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( $_GET['page'] ) : ''; ?>" />
|
||||
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( $_GET['tab'] ) : ''; ?>" />
|
||||
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( $_GET['report'] ) : ''; ?>" />
|
||||
<?php wp_nonce_field( 'custom_range', 'wc_reports_nonce', false ); ?>
|
||||
<?php // @codingStandardsIgnoreEnd ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Top sellers', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<table cellspacing="0">
|
||||
<?php
|
||||
$top_sellers = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_qty',
|
||||
),
|
||||
),
|
||||
'order_by' => 'order_item_qty DESC',
|
||||
'group_by' => 'product_id',
|
||||
'limit' => 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( $top_sellers ) {
|
||||
// @codingStandardsIgnoreStart
|
||||
foreach ( $top_sellers as $product ) {
|
||||
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
|
||||
<td class="count">' . esc_html( $product->order_item_qty ) . '</td>
|
||||
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
|
||||
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Top freebies', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<table cellspacing="0">
|
||||
<?php
|
||||
$top_freebies = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_qty',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => '_line_subtotal', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => '0', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'operator' => '=',
|
||||
),
|
||||
),
|
||||
'order_by' => 'order_item_qty DESC',
|
||||
'group_by' => 'product_id',
|
||||
'limit' => 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( $top_freebies ) {
|
||||
// @codingStandardsIgnoreStart
|
||||
foreach ( $top_freebies as $product ) {
|
||||
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
|
||||
<td class="count">' . esc_html( $product->order_item_qty ) . '</td>
|
||||
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
|
||||
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php esc_html_e( 'Top earners', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<table cellspacing="0">
|
||||
<?php
|
||||
$top_earners = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
'_line_total' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_total',
|
||||
),
|
||||
),
|
||||
'order_by' => 'order_item_total DESC',
|
||||
'group_by' => 'product_id',
|
||||
'limit' => 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( $top_earners ) {
|
||||
// @codingStandardsIgnoreStart
|
||||
foreach ( $top_earners as $product ) {
|
||||
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
|
||||
<td class="count">' . wc_price( $product->order_item_total ) . '</td>
|
||||
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
|
||||
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'sales' ) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery( '.section_title' ).on( 'click', function() {
|
||||
var next_section = jQuery( this ).next( '.section' );
|
||||
|
||||
if ( jQuery( next_section ).is( ':visible' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jQuery( '.section:visible' ).slideUp();
|
||||
jQuery( '.section_title' ).removeClass( 'open' );
|
||||
jQuery( this ).addClass( 'open' ).next( '.section' ).slideDown();
|
||||
|
||||
return false;
|
||||
} );
|
||||
jQuery( '.section' ).slideUp( 100, function() {
|
||||
<?php if ( empty( $this->product_ids ) ) : ?>
|
||||
jQuery( '.section_title:eq(1)' ).trigger( 'click' );
|
||||
<?php endif; ?>
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_html( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"<?php // @codingStandardsIgnoreLine ?>
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
if ( empty( $this->product_ids ) ) {
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<p class="chart-prompt"><?php esc_html_e( 'Choose a product to view stats', 'woocommerce' ); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
// Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
|
||||
$order_item_counts = $this->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',
|
||||
),
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'operator' => 'IN',
|
||||
),
|
||||
),
|
||||
'group_by' => 'product_id,' . $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$order_item_amounts = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'_line_total' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_amount',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
),
|
||||
'where_meta' => array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'operator' => 'IN',
|
||||
),
|
||||
),
|
||||
'group_by' => 'product_id, ' . $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Prepare data for report.
|
||||
$order_item_counts = $this->prepare_chart_data( $order_item_counts, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$order_item_amounts = $this->prepare_chart_data( $order_item_amounts, 'post_date', 'order_item_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
|
||||
// Encode in json format.
|
||||
$chart_data = wp_json_encode(
|
||||
array(
|
||||
'order_item_counts' => array_values( $order_item_counts ),
|
||||
'order_item_amounts' => array_values( $order_item_amounts ),
|
||||
)
|
||||
);
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<?php // @codingStandardsIgnoreStart ?>
|
||||
<script type="text/javascript">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
|
||||
|
||||
var drawGraph = function( highlight ) {
|
||||
|
||||
var series = [
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ) ?>",
|
||||
data: order_data.order_item_counts,
|
||||
color: '<?php echo $this->chart_colours['item_count']; ?>',
|
||||
bars: { fillColor: '<?php echo $this->chart_colours['item_count']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
|
||||
shadowSize: 0,
|
||||
hoverable: false
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>",
|
||||
data: order_data.order_item_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo $this->chart_colours['sales_amount']; ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 4, fill: false },
|
||||
shadowSize: 0,
|
||||
<?php echo $this->get_currency_tooltip(); ?>
|
||||
}
|
||||
];
|
||||
|
||||
if ( highlight !== 'undefined' && series[ highlight ] ) {
|
||||
highlight_series = series[ highlight ];
|
||||
|
||||
highlight_series.color = '#9c5d90';
|
||||
|
||||
if ( highlight_series.bars )
|
||||
highlight_series.bars.fillColor = '#9c5d90';
|
||||
|
||||
if ( highlight_series.lines ) {
|
||||
highlight_series.lines.lineWidth = 5;
|
||||
}
|
||||
}
|
||||
|
||||
main_chart = jQuery.plot(
|
||||
jQuery('.chart-placeholder.main'),
|
||||
series,
|
||||
{
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
color: '#aaa',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
hoverable: true
|
||||
},
|
||||
xaxes: [ {
|
||||
color: '#aaa',
|
||||
position: "bottom",
|
||||
tickColor: 'transparent',
|
||||
mode: "time",
|
||||
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
|
||||
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
|
||||
font: {
|
||||
color: "#aaa"
|
||||
}
|
||||
} ],
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
minTickSize: 1,
|
||||
tickDecimals: 0,
|
||||
color: '#ecf0f1',
|
||||
font: { color: "#aaa" }
|
||||
},
|
||||
{
|
||||
position: "right",
|
||||
min: 0,
|
||||
tickDecimals: 2,
|
||||
alignTicksWithAxis: 1,
|
||||
color: 'transparent',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder').trigger( 'resize' );
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').on( 'mouseenter',
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
} ).on( 'mouseleave',
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
205
includes/admin/reports/class-wc-report-stock.php
Normal file
205
includes/admin/reports/class-wc-report-stock.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Stock.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Stock extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* Max items.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $max_items;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
parent::__construct(
|
||||
array(
|
||||
'singular' => 'stock',
|
||||
'plural' => 'stock',
|
||||
'ajax' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text.
|
||||
*/
|
||||
public function no_items() {
|
||||
_e( 'No products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't need this.
|
||||
*
|
||||
* @param string $position
|
||||
*/
|
||||
public function display_tablenav( $position ) {
|
||||
|
||||
if ( 'top' !== $position ) {
|
||||
parent::display_tablenav( $position );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$this->prepare_items();
|
||||
echo '<div id="poststuff" class="woocommerce-reports-wide">';
|
||||
$this->display();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column value.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @param string $column_name
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
global $product;
|
||||
|
||||
if ( ! $product || $product->get_id() !== $item->id ) {
|
||||
$product = wc_get_product( $item->id );
|
||||
}
|
||||
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $column_name ) {
|
||||
|
||||
case 'product':
|
||||
if ( $sku = $product->get_sku() ) {
|
||||
echo esc_html( $sku ) . ' - ';
|
||||
}
|
||||
|
||||
echo esc_html( $product->get_name() );
|
||||
|
||||
// Get variation data.
|
||||
if ( $product->is_type( 'variation' ) ) {
|
||||
echo '<div class="description">' . wp_kses_post( wc_get_formatted_variation( $product, true ) ) . '</div>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if ( $item->parent ) {
|
||||
echo esc_html( get_the_title( $item->parent ) );
|
||||
} else {
|
||||
echo '-';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'stock_status':
|
||||
if ( $product->is_on_backorder() ) {
|
||||
$stock_html = '<mark class="onbackorder">' . __( 'On backorder', 'woocommerce' ) . '</mark>';
|
||||
} elseif ( $product->is_in_stock() ) {
|
||||
$stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>';
|
||||
} else {
|
||||
$stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>';
|
||||
}
|
||||
echo apply_filters( 'woocommerce_admin_stock_html', $stock_html, $product );
|
||||
break;
|
||||
|
||||
case 'stock_level':
|
||||
echo esc_html( $product->get_stock_quantity() );
|
||||
break;
|
||||
|
||||
case 'wc_actions':
|
||||
?><p>
|
||||
<?php
|
||||
$actions = array();
|
||||
$action_id = $product->is_type( 'variation' ) ? $item->parent : $item->id;
|
||||
|
||||
$actions['edit'] = array(
|
||||
'url' => admin_url( 'post.php?post=' . $action_id . '&action=edit' ),
|
||||
'name' => __( 'Edit', 'woocommerce' ),
|
||||
'action' => 'edit',
|
||||
);
|
||||
|
||||
if ( $product->is_visible() ) {
|
||||
$actions['view'] = array(
|
||||
'url' => get_permalink( $action_id ),
|
||||
'name' => __( 'View', 'woocommerce' ),
|
||||
'action' => 'view',
|
||||
);
|
||||
}
|
||||
|
||||
$actions = apply_filters( 'woocommerce_admin_stock_report_product_actions', $actions, $product );
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
printf(
|
||||
'<a class="button tips %1$s" href="%2$s" data-tip="%3$s">%4$s</a>',
|
||||
esc_attr( $action['action'] ),
|
||||
esc_url( $action['url'] ),
|
||||
sprintf( esc_attr__( '%s product', 'woocommerce' ), $action['name'] ),
|
||||
esc_html( $action['name'] )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_columns() {
|
||||
|
||||
$columns = array(
|
||||
'product' => __( 'Product', 'woocommerce' ),
|
||||
'parent' => __( 'Parent', 'woocommerce' ),
|
||||
'stock_level' => __( 'Units in stock', 'woocommerce' ),
|
||||
'stock_status' => __( 'Stock status', 'woocommerce' ),
|
||||
'wc_actions' => __( 'Actions', 'woocommerce' ),
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare customer list items.
|
||||
*/
|
||||
public function prepare_items() {
|
||||
|
||||
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
|
||||
$current_page = absint( $this->get_pagenum() );
|
||||
$per_page = apply_filters( 'woocommerce_admin_stock_report_products_per_page', 20 );
|
||||
|
||||
$this->get_items( $current_page, $per_page );
|
||||
|
||||
/**
|
||||
* Pagination.
|
||||
*/
|
||||
$this->set_pagination_args(
|
||||
array(
|
||||
'total_items' => $this->max_items,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => ceil( $this->max_items / $per_page ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
234
includes/admin/reports/class-wc-report-taxes-by-code.php
Normal file
234
includes/admin/reports/class-wc-report-taxes-by-code.php
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
/**
|
||||
* Taxes by tax code report.
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Taxes_By_Code
|
||||
*
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Taxes_By_Code extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : 'last_month';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="table"
|
||||
>
|
||||
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : 'last_month';
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
|
||||
$current_range = 'last_month';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
$hide_sidebar = true;
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wpdb;
|
||||
|
||||
$query_data = array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'function' => '',
|
||||
'name' => 'tax_rate',
|
||||
),
|
||||
'tax_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'tax',
|
||||
'function' => '',
|
||||
'name' => 'tax_amount',
|
||||
),
|
||||
'shipping_tax_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'tax',
|
||||
'function' => '',
|
||||
'name' => 'shipping_tax_amount',
|
||||
),
|
||||
'rate_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'tax',
|
||||
'function' => '',
|
||||
'name' => 'rate_id',
|
||||
),
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_id',
|
||||
),
|
||||
);
|
||||
|
||||
$query_where = array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'tax',
|
||||
'operator' => '=',
|
||||
),
|
||||
array(
|
||||
'key' => 'order_item_name',
|
||||
'value' => '',
|
||||
'operator' => '!=',
|
||||
),
|
||||
);
|
||||
|
||||
// We exclude on-hold orders as they are still pending payment.
|
||||
$tax_rows_orders = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'where' => $query_where,
|
||||
'order_by' => 'posts.post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'sales-reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$tax_rows_partial_refunds = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'where' => $query_where,
|
||||
'order_by' => 'posts.post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => array( 'shop_order_refund' ),
|
||||
'parent_order_status' => array( 'completed', 'processing' ), // Partial refunds inside refunded orders should be ignored.
|
||||
)
|
||||
);
|
||||
|
||||
$tax_rows_full_refunds = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'where' => $query_where,
|
||||
'order_by' => 'posts.post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => array( 'shop_order_refund' ),
|
||||
'parent_order_status' => array( 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Merge.
|
||||
$tax_rows = array();
|
||||
|
||||
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_orders' => 0,
|
||||
);
|
||||
$tax_rows[ $key ]->total_orders += 1;
|
||||
$tax_rows[ $key ]->tax_rate = $tax_row->tax_rate;
|
||||
$tax_rows[ $key ]->tax_amount += wc_round_tax_total( $tax_row->tax_amount );
|
||||
$tax_rows[ $key ]->shipping_tax_amount += wc_round_tax_total( $tax_row->shipping_tax_amount );
|
||||
}
|
||||
|
||||
foreach ( $tax_rows_full_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_orders' => 0,
|
||||
);
|
||||
$tax_rows[ $key ]->tax_rate = $tax_row->tax_rate;
|
||||
$tax_rows[ $key ]->tax_amount += wc_round_tax_total( $tax_row->tax_amount );
|
||||
$tax_rows[ $key ]->shipping_tax_amount += wc_round_tax_total( $tax_row->shipping_tax_amount );
|
||||
}
|
||||
?>
|
||||
<table class="widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Tax', 'woocommerce' ); ?></th>
|
||||
<th><?php esc_html_e( 'Rate', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php esc_html_e( 'Number of orders', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php esc_html_e( 'Tax amount', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the sum of the "Tax rows" tax amount within your orders.', 'woocommerce' ) ); ?></th>
|
||||
<th class="total_row"><?php esc_html_e( 'Shipping tax amount', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the sum of the "Tax rows" shipping tax amount within your orders.', 'woocommerce' ) ); ?></th>
|
||||
<th class="total_row"><?php esc_html_e( 'Total tax', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ) ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<?php if ( ! empty( $tax_rows ) ) : ?>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $tax_rows as $rate_id => $tax_row ) {
|
||||
$rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row"><?php echo wp_kses_post( apply_filters( 'woocommerce_reports_taxes_tax_rate', $tax_row->tax_rate, $rate_id, $tax_row ) ); ?></th>
|
||||
<td><?php echo wp_kses_post( apply_filters( 'woocommerce_reports_taxes_rate', $rate, $rate_id, $tax_row ) ); ?>%</td>
|
||||
<td class="total_row"><?php echo esc_html( $tax_row->total_orders ); ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $tax_row->tax_amount ); // phpcs:ignore ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $tax_row->shipping_tax_amount ); // phpcs:ignore ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $tax_row->tax_amount + $tax_row->shipping_tax_amount ); // phpcs:ignore ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row" colspan="3"><?php esc_html_e( 'Total', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) ) ); // phpcs:ignore ?></th>
|
||||
<th class="total_row"><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ) ); // phpcs:ignore ?></th>
|
||||
<th class="total_row"><strong><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) + array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ) ); // phpcs:ignore ?></strong></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php else : ?>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?php esc_html_e( 'No taxes found in this period', 'woocommerce' ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
264
includes/admin/reports/class-wc-report-taxes-by-date.php
Normal file
264
includes/admin/reports/class-wc-report-taxes-by-date.php
Normal file
@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Report_Taxes_By_Date
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce\Admin\Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
class WC_Report_Taxes_By_Date extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link.
|
||||
*/
|
||||
public function get_export_button() {
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : 'last_month';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo date_i18n( 'Y-m-d', current_time( 'timestamp' ) ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="table"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the report.
|
||||
*/
|
||||
public function output_report() {
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last month', 'woocommerce' ),
|
||||
'month' => __( 'This month', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : 'last_month';
|
||||
|
||||
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
|
||||
$current_range = 'last_month';
|
||||
}
|
||||
|
||||
$this->check_current_range_nonce( $current_range );
|
||||
$this->calculate_current_range( $current_range );
|
||||
|
||||
$hide_sidebar = true;
|
||||
|
||||
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart.
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
$query_data = array(
|
||||
'_order_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'tax_amount',
|
||||
),
|
||||
'_order_shipping_tax' => array(
|
||||
'type' => 'meta',
|
||||
'function' => 'SUM',
|
||||
'name' => 'shipping_tax_amount',
|
||||
),
|
||||
'_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',
|
||||
),
|
||||
);
|
||||
|
||||
// We exlude on-hold orders are they are still pending payment.
|
||||
$tax_rows_orders = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'sales-reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$tax_rows_full_refunds = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => array(
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'distinct' => true,
|
||||
'function' => '',
|
||||
'name' => 'ID',
|
||||
),
|
||||
'post_parent' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_parent',
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date',
|
||||
),
|
||||
),
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => array( 'shop_order_refund' ),
|
||||
'parent_order_status' => array( 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
$tax_rows_partial_refunds = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => array( 'shop_order_refund' ),
|
||||
'parent_order_status' => array( 'completed', 'processing' ), // Partial refunds inside refunded orders should be ignored.
|
||||
)
|
||||
);
|
||||
|
||||
$tax_rows = array();
|
||||
|
||||
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
|
||||
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_sales' => 0,
|
||||
'total_shipping' => 0,
|
||||
'total_orders' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $tax_rows_orders as $tax_row ) {
|
||||
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
|
||||
$tax_rows[ $key ]->total_orders += $tax_row->total_orders;
|
||||
$tax_rows[ $key ]->tax_amount += $tax_row->tax_amount;
|
||||
$tax_rows[ $key ]->shipping_tax_amount += $tax_row->shipping_tax_amount;
|
||||
$tax_rows[ $key ]->total_sales += $tax_row->total_sales;
|
||||
$tax_rows[ $key ]->total_shipping += $tax_row->total_shipping;
|
||||
}
|
||||
|
||||
foreach ( $tax_rows_partial_refunds as $tax_row ) {
|
||||
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
|
||||
$tax_rows[ $key ]->tax_amount += $tax_row->tax_amount;
|
||||
$tax_rows[ $key ]->shipping_tax_amount += $tax_row->shipping_tax_amount;
|
||||
$tax_rows[ $key ]->total_sales += $tax_row->total_sales;
|
||||
$tax_rows[ $key ]->total_shipping += $tax_row->total_shipping;
|
||||
}
|
||||
|
||||
foreach ( $tax_rows_full_refunds as $tax_row ) {
|
||||
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_sales' => 0,
|
||||
'total_shipping' => 0,
|
||||
'total_orders' => 0,
|
||||
);
|
||||
$parent_order = wc_get_order( $tax_row->post_parent );
|
||||
|
||||
if ( $parent_order ) {
|
||||
$tax_rows[ $key ]->tax_amount += $parent_order->get_cart_tax() * -1;
|
||||
$tax_rows[ $key ]->shipping_tax_amount += $parent_order->get_shipping_tax() * -1;
|
||||
$tax_rows[ $key ]->total_sales += $parent_order->get_total() * -1;
|
||||
$tax_rows[ $key ]->total_shipping += $parent_order->get_shipping_total() * -1;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<table class="widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e( 'Period', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Number of orders', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Total sales', 'woocommerce' ); ?> <?php echo wc_help_tip( __( "This is the sum of the 'Order total' field within your orders.", 'woocommerce' ) ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Total shipping', 'woocommerce' ); ?> <?php echo wc_help_tip( __( "This is the sum of the 'Shipping total' field within your orders.", 'woocommerce' ) ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Total tax', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ) ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Net profit', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'Total sales minus shipping and tax.', 'woocommerce' ) ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<?php if ( ! empty( $tax_rows ) ) : ?>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $tax_rows as $date => $tax_row ) {
|
||||
$gross = $tax_row->total_sales - $tax_row->total_shipping;
|
||||
$total_tax = $tax_row->tax_amount + $tax_row->shipping_tax_amount;
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<?php echo ( 'month' === $this->chart_groupby ) ? date_i18n( 'F', strtotime( $date . '01' ) ) : date_i18n( get_option( 'date_format' ), strtotime( $date ) ); ?>
|
||||
</th>
|
||||
<td class="total_row"><?php echo $tax_row->total_orders; ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $gross ); ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $tax_row->total_shipping ); ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $total_tax ); ?></td>
|
||||
<td class="total_row"><?php echo wc_price( $gross - $total_tax ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<?php
|
||||
$gross = array_sum( wp_list_pluck( (array) $tax_rows, 'total_sales' ) ) - array_sum( wp_list_pluck( (array) $tax_rows, 'total_shipping' ) );
|
||||
$total_tax = array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) + array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) );
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row"><?php _e( 'Totals', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php echo array_sum( wp_list_pluck( (array) $tax_rows, 'total_orders' ) ); ?></th>
|
||||
<th class="total_row"><?php echo wc_price( $gross ); ?></th>
|
||||
<th class="total_row"><?php echo wc_price( array_sum( wp_list_pluck( (array) $tax_rows, 'total_shipping' ) ) ); ?></th>
|
||||
<th class="total_row"><?php echo wc_price( $total_tax ); ?></th>
|
||||
<th class="total_row"><?php echo wc_price( $gross - $total_tax ); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php else : ?>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?php _e( 'No taxes found in this period', 'woocommerce' ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user