Customer reports

This commit is contained in:
Mike Jolley 2013-07-09 15:31:22 +01:00
parent aa41255596
commit 1c1d60b375
13 changed files with 681 additions and 223 deletions

View File

@ -60,6 +60,7 @@ class WC_Admin_Reports {
wp_enqueue_script( 'flot-resize', WC()->plugin_url() . '/assets/js/admin/jquery.flot.resize' . $suffix . '.js', array('jquery', 'flot'), '1.0' );
wp_enqueue_script( 'flot-time', WC()->plugin_url() . '/assets/js/admin/jquery.flot.time' . $suffix . '.js', array( 'jquery', 'flot' ), '1.0' );
wp_enqueue_script( 'flot-pie', WC()->plugin_url() . '/assets/js/admin/jquery.flot.pie' . $suffix . '.js', array( 'jquery', 'flot' ), '1.0' );
wp_enqueue_script( 'flot-stack', WC()->plugin_url() . '/assets/js/admin/jquery.flot.stack' . $suffix . '.js', array( 'jquery', 'flot' ), '1.0' );
}
}
@ -102,11 +103,11 @@ class WC_Admin_Reports {
'customers' => array(
'title' => __( 'Customers', 'woocommerce' ),
'reports' => array(
"overview" => array(
"customers" => array(
'title' => __( 'Overview', 'woocommerce' ),
'description' => '',
'hide_title' => true,
'callback' => 'woocommerce_customer_overview'
'callback' => array( $this, 'get_report' )
),
)
),
@ -554,206 +555,24 @@ function woocommerce_customer_overview() {
}
/**
* Output the stock overview stats.
*
* @access public
* @return void
*/
function woocommerce_stock_overview() {
global $start_date, $end_date, $woocommerce, $wpdb;
// Low/No stock lists
$lowstockamount = get_option('woocommerce_notify_low_stock_amount');
if (!is_numeric($lowstockamount)) $lowstockamount = 1;
$nostockamount = get_option('woocommerce_notify_no_stock_amount');
if (!is_numeric($nostockamount)) $nostockamount = 0;
// Get low in stock simple/downloadable/virtual products. Grouped don't have stock. Variations need a separate query.
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_manage_stock',
'value' => 'yes'
),
array(
'key' => '_stock',
'value' => $lowstockamount,
'compare' => '<=',
'type' => 'NUMERIC'
)
),
'tax_query' => array(
array(
'taxonomy' => 'product_type',
'field' => 'name',
'terms' => array('simple'),
'operator' => 'IN'
)
),
'fields' => 'id=>parent'
);
$low_stock_products = (array) get_posts($args);
// Get low stock product variations
$args = array(
'post_type' => 'product_variation',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_stock',
'value' => $lowstockamount,
'compare' => '<=',
'type' => 'NUMERIC'
),
array(
'key' => '_stock',
'value' => array( '', false, null ),
'compare' => 'NOT IN'
)
),
'fields' => 'id=>parent'
);
$low_stock_variations = (array) get_posts($args);
// Get low stock variable products (where stock is set for the parent)
$args = array(
'post_type' => array('product'),
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_manage_stock',
'value' => 'yes'
),
array(
'key' => '_stock',
'value' => $lowstockamount,
'compare' => '<=',
'type' => 'NUMERIC'
)
),
'tax_query' => array(
array(
'taxonomy' => 'product_type',
'field' => 'name',
'terms' => array('variable'),
'operator' => 'IN'
)
),
'fields' => 'id=>parent'
);
$low_stock_variable_products = (array) get_posts($args);
// Get products marked out of stock
$args = array(
'post_type' => array( 'product' ),
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_stock_status',
'value' => 'outofstock'
)
),
'fields' => 'id=>parent'
);
$out_of_stock_status_products = (array) get_posts($args);
// Merge results
$low_in_stock = apply_filters( 'woocommerce_reports_stock_overview_products', $low_stock_products + $low_stock_variations + $low_stock_variable_products + $out_of_stock_status_products );
?>
<div id="poststuff" class="woocommerce-reports-wrap halved">
<div class="woocommerce-reports-left">
<div class="postbox">
<h3><span><?php _e( 'Low stock', 'woocommerce' ); ?></span></h3>
<div class="inside">
<?php
if ( $low_in_stock ) {
echo '<ul class="stock_list">';
foreach ( $low_in_stock as $product_id => $parent ) {
$stock = (int) get_post_meta( $product_id, '_stock', true );
$sku = get_post_meta( $product_id, '_sku', true );
if ( $stock <= $nostockamount || in_array( $product_id, array_keys( $out_of_stock_status_products ) ) )
continue;
$title = esc_html__( get_the_title( $product_id ) );
if ( $sku )
$title .= ' (' . __( 'SKU', 'woocommerce' ) . ': ' . esc_html( $sku ) . ')';
if ( get_post_type( $product_id ) == 'product' )
$product_url = admin_url( 'post.php?post=' . $product_id . '&action=edit' );
else
$product_url = admin_url( 'post.php?post=' . $parent . '&action=edit' );
printf( '<li><a href="%s"><small>' . _n('%d in stock', '%d in stock', $stock, 'woocommerce') . '</small> %s</a></li>', $product_url, $stock, $title );
}
echo '</ul>';
} else {
echo '<p>'.__( 'No products are low in stock.', 'woocommerce' ).'</p>';
}
?>
</div>
</div>
</div>
<div class="woocommerce-reports-right">
<div class="postbox">
<h3><span><?php _e( 'Out of stock', 'woocommerce' ); ?></span></h3>
<div class="inside">
<?php
if ( $low_in_stock ) {
echo '<ul class="stock_list">';
foreach ( $low_in_stock as $product_id => $parent ) {
$stock = get_post_meta( $product_id, '_stock', true );
$sku = get_post_meta( $product_id, '_sku', true );
if ( $stock > $nostockamount && ! in_array( $product_id, array_keys( $out_of_stock_status_products ) ) )
continue;
$title = esc_html__( get_the_title( $product_id ) );
if ( $sku )
$title .= ' (' . __( 'SKU', 'woocommerce' ) . ': ' . esc_html( $sku ) . ')';
if ( get_post_type( $product_id ) == 'product' )
$product_url = admin_url( 'post.php?post=' . $product_id . '&action=edit' );
else
$product_url = admin_url( 'post.php?post=' . $parent . '&action=edit' );
if ( $stock == '' )
printf( '<li><a href="%s"><small>' . __('Marked out of stock', 'woocommerce') . '</small> %s</a></li>', $product_url, $title );
else
printf( '<li><a href="%s"><small>' . _n('%d in stock', '%d in stock', $stock, 'woocommerce') . '</small> %s</a></li>', $product_url, $stock, $title );
}
echo '</ul>';
} else {
echo '<p>'.__( 'No products are out in stock.', 'woocommerce' ).'</p>';
}
?>
</div>
</div>
</div>
</div>
<?php
}
/**

View File

@ -269,7 +269,10 @@ class WC_Admin_Report {
if ( ! isset( $prepared_data[ $time ] ) )
continue;
$prepared_data[ $time ][1] += $d->$data_key;
if ( $data_key )
$prepared_data[ $time ][1] += $d->$data_key;
else
$prepared_data[ $time ][1] ++;
}
return $prepared_data;

View File

@ -101,7 +101,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
switch ( $current_range ) {
case 'custom' :
$this->start_date = strtotime( sanitize_text_field( $_GET['start_date'] ) );
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
$this->end_date = strtotime( '12am + 1 day', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
if ( ! $this->end_date )
$this->end_date = current_time('timestamp');
@ -120,7 +120,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
break;
case 'year' :
$this->start_date = strtotime( 'first day of january', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month' :
@ -130,13 +130,13 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
break;
case 'month' :
$this->start_date = strtotime( 'first day of this month', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day' :
default :
$this->start_date = strtotime( 'midnight -6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->start_date = strtotime( '-6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}
@ -460,9 +460,6 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
legend: {
show: false
},
series: {
stack: true
},
grid: {
color: '#aaa',
borderColor: 'transparent',

View File

@ -0,0 +1,391 @@
<?php
/**
* WC_Report_Customers class
*/
class WC_Report_Customers extends WC_Admin_Report {
/**
* Get the legend for the main chart sidebar
* @return array
*/
public function get_chart_legend() {
$legend = array();
$legend[] = array(
'title' => sprintf( __( '%s signups in this period', 'woocommerce' ), '<strong>' . sizeof( $this->customers ) . '</strong>' ),
'color' => $this->chart_colours['signups']
);
return $legend;
}
/**
* [get_chart_widgets description]
* @return array
*/
public function get_chart_widgets() {
$widgets = array();
$widgets[] = array(
'title' => '',
'callback' => array( $this, 'customers_vs_guests' )
);
return $widgets;
}
/**
* customers_vs_guests
* @return void
*/
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 $this->chart_colours['customers']; ?>"><?php _e( 'Customer Sales', 'woocommerce' ); ?></li>
<li style="border-color: <?php echo $this->chart_colours['guests']; ?>"><?php _e( 'Guest Sales', 'woocommerce' ); ?></li>
</ul>
</div>
<script type="text/javascript">
jQuery(function(){
jQuery.plot(
jQuery('.chart-placeholder.customers_vs_guests'),
[
{
label: '<?php _e( 'Customer Orders', 'woocommerce' ); ?>',
data: "<?php echo $customer_order_totals->total_orders ?>",
color: '<?php echo $this->chart_colours['customers']; ?>'
},
{
label: '<?php _e( 'Guest Orders', 'woocommerce' ); ?>',
data: "<?php echo $guest_order_totals->total_orders ?>",
color: '<?php echo $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 ' ' . __( 'orders', 'woocommerce' ); ?>",
},
legend: {
show: false
}
}
);
jQuery('.chart-placeholder.customers_vs_guests').resize();
});
</script>
<?php
}
/**
* Output the report
*/
public function output_report() {
global $woocommerce, $wpdb, $wp_locale;
$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'] ) ? $_GET['range'] : '7day';
switch ( $current_range ) {
case 'custom' :
$this->start_date = strtotime( sanitize_text_field( $_GET['start_date'] ) );
$this->end_date = strtotime( '12am + 1 day', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
if ( ! $this->end_date )
$this->end_date = current_time('timestamp');
$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( 'first day of january', current_time('timestamp') );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month' :
$this->start_date = strtotime( 'first day of last month', current_time('timestamp') );
$this->end_date = strtotime( 'last day of last month', current_time('timestamp') );
$this->chart_groupby = 'day';
break;
case 'month' :
$this->start_date = strtotime( 'first day of this month', current_time('timestamp') );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day' :
default :
$this->start_date = strtotime( '-6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}
// Group by
switch ( $this->chart_groupby ) {
case 'day' :
$this->group_by_query = 'YEAR(post_date), MONTH(post_date), DAY(post_date)';
$this->chart_interval = 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(post_date), MONTH(post_date)';
$this->chart_interval = 0;
$min_date = $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;
}
$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(
array(
'fields' => array( 'user_registered' ),
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() )
)
);
$this->customers = $users_query->get_results();
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php' );
}
/**
* Get the main chart
* @return string
*/
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 );
// Encode in json format
$chart_data = 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">
jQuery(function(){
var chart_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
jQuery.plot(
jQuery('.chart-placeholder.main'),
[
{
label: "<?php echo esc_js( __( 'Customer Orders', 'woocommerce' ) ) ?>",
data: chart_data.customer_orders,
color: '<?php echo $this->chart_colours['customers']; ?>',
bars: { fillColor: '<?php echo $this->chart_colours['customers']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
shadowSize: 0,
enable_tooltip: true,
append_tooltip: "<?php echo ' ' . __( 'customer orders', 'woocommerce' ); ?>",
stack: true,
},
{
label: "<?php echo esc_js( __( 'Guest Orders', 'woocommerce' ) ) ?>",
data: chart_data.guest_orders,
color: '<?php echo $this->chart_colours['guests']; ?>',
bars: { fillColor: '<?php echo $this->chart_colours['guests']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
shadowSize: 0,
enable_tooltip: true,
append_tooltip: "<?php echo ' ' . __( 'guest orders', 'woocommerce' ); ?>",
stack: true,
},
{
label: "<?php echo esc_js( __( 'Signups', 'woocommerce' ) ) ?>",
data: chart_data.signups,
color: '<?php echo $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 ' ' . __( 'new users', 'woocommerce' ); ?>",
stack: false
},
],
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php if ( $this->chart_groupby == 'day' ) echo '%d %b'; else echo '%b'; ?>",
monthNames: <?php echo 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,
minTickSize: 1,
tickDecimals: 0,
color: '#ecf0f1',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder.main').resize();
});
</script>
<?php
}
}

View File

@ -74,7 +74,7 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
switch ( $current_range ) {
case 'custom' :
$this->start_date = strtotime( sanitize_text_field( $_GET['start_date'] ) );
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
$this->end_date = strtotime( '12am + 1 day', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
if ( ! $this->end_date )
$this->end_date = current_time('timestamp');
@ -93,7 +93,7 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
break;
case 'year' :
$this->start_date = strtotime( 'first day of january', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month' :
@ -103,13 +103,13 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
break;
case 'month' :
$this->start_date = strtotime( 'first day of this month', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day' :
default :
$this->start_date = strtotime( 'midnight -6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->start_date = strtotime( '-6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}

View File

@ -133,7 +133,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
switch ( $current_range ) {
case 'custom' :
$this->start_date = strtotime( sanitize_text_field( $_GET['start_date'] ) );
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
$this->end_date = strtotime( '12am + 1 day', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
if ( ! $this->end_date )
$this->end_date = current_time('timestamp');
@ -152,7 +152,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
break;
case 'year' :
$this->start_date = strtotime( 'first day of january', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month' :
@ -162,13 +162,13 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
break;
case 'month' :
$this->start_date = strtotime( 'first day of this month', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day' :
default :
$this->start_date = strtotime( 'midnight -6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->start_date = strtotime( '-6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}
@ -177,7 +177,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
switch ( $this->chart_groupby ) {
case 'day' :
$this->group_by_query = 'YEAR(post_date), MONTH(post_date), DAY(post_date)';
$this->chart_interval = max( 0, ( $this->end_date - $this->start_date ) / ( 60 * 60 * 24 ) );
$this->chart_interval = max( 0, ( $this->end_date - $this->start_date ) / ( 60 * 60 * 24 ) );
$this->barwidth = 60 * 60 * 24 * 1000;
break;
case 'month' :
@ -380,9 +380,6 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
legend: {
show: false
},
series: {
stack: true
},
grid: {
color: '#aaa',
borderColor: 'transparent',

View File

@ -102,7 +102,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
switch ( $current_range ) {
case 'custom' :
$this->start_date = strtotime( sanitize_text_field( $_GET['start_date'] ) );
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
$this->end_date = strtotime( '12am + 1 day', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
if ( ! $this->end_date )
$this->end_date = current_time('timestamp');
@ -121,7 +121,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
break;
case 'year' :
$this->start_date = strtotime( 'first day of january', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month' :
@ -131,13 +131,13 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
break;
case 'month' :
$this->start_date = strtotime( 'first day of this month', current_time('timestamp') );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day' :
default :
$this->start_date = strtotime( 'midnight -6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->start_date = strtotime( '-6 days', current_time( 'timestamp' ) );
$this->end_date = strtotime( '12am + 1 day', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}
@ -460,9 +460,6 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
legend: {
show: false
},
series: {
stack: true
},
grid: {
color: '#aaa',
borderColor: 'transparent',

File diff suppressed because one or more lines are too long

View File

@ -2765,6 +2765,21 @@ img.ui-datepicker-trigger { vertical-align: middle; margin-top: -1px; cursor: po
box-shadow: 0px 1px 2px rgba(0,0,0,.1);
}
}
.pie-chart-legend {
margin: 12px 0 0 0;
overflow: hidden;
li {
float: left;
margin: 0;
padding: 6px 0 0 0;
border-top: 4px solid #999;
text-align: center;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
width: 50%;
}
}
.stat {
font-size: 1.5em !important;
font-weight: bold;

View File

@ -0,0 +1,188 @@
/* Flot plugin for stacking data sets rather than overlyaing them.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
The plugin assumes the data is sorted on x (or y if stacking horizontally).
For line charts, it is assumed that if a line has an undefined gap (from a
null point), then the line above it should have the same gap - insert zeros
instead of "null" if you want another behaviour. This also holds for the start
and end of the chart. Note that stacking a mix of positive and negative values
in most instances doesn't make sense (so it looks weird).
Two or more series are stacked when their "stack" attribute is set to the same
key (which can be any number or string or just "true"). To specify the default
stack, you can set the stack option like this:
series: {
stack: null/false, true, or a key (number/string)
}
You can also specify it for a single series, like this:
$.plot( $("#placeholder"), [{
data: [ ... ],
stack: true
}])
The stacking order is determined by the order of the data series in the array
(later series end up on top of the previous).
Internally, the plugin modifies the datapoints in each series, adding an
offset to the y value. For line series, extra data points are inserted through
interpolation. If there's a second y value, it's also adjusted (e.g for bar
charts or filled areas).
*/
(function ($) {
var options = {
series: { stack: null } // or number/string
};
function init(plot) {
function findMatchingSeries(s, allseries) {
var res = null;
for (var i = 0; i < allseries.length; ++i) {
if (s == allseries[i])
break;
if (allseries[i].stack == s.stack)
res = allseries[i];
}
return res;
}
function stackData(plot, s, datapoints) {
if (s.stack == null || s.stack === false)
return;
var other = findMatchingSeries(s, plot.getData());
if (!other)
return;
var ps = datapoints.pointsize,
points = datapoints.points,
otherps = other.datapoints.pointsize,
otherpoints = other.datapoints.points,
newpoints = [],
px, py, intery, qx, qy, bottom,
withlines = s.lines.show,
horizontal = s.bars.horizontal,
withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y),
withsteps = withlines && s.lines.steps,
fromgap = true,
keyOffset = horizontal ? 1 : 0,
accumulateOffset = horizontal ? 0 : 1,
i = 0, j = 0, l, m;
while (true) {
if (i >= points.length)
break;
l = newpoints.length;
if (points[i] == null) {
// copy gaps
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
i += ps;
}
else if (j >= otherpoints.length) {
// for lines, we can't use the rest of the points
if (!withlines) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
}
i += ps;
}
else if (otherpoints[j] == null) {
// oops, got a gap
for (m = 0; m < ps; ++m)
newpoints.push(null);
fromgap = true;
j += otherps;
}
else {
// cases where we actually got two points
px = points[i + keyOffset];
py = points[i + accumulateOffset];
qx = otherpoints[j + keyOffset];
qy = otherpoints[j + accumulateOffset];
bottom = 0;
if (px == qx) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
newpoints[l + accumulateOffset] += qy;
bottom = qy;
i += ps;
j += otherps;
}
else if (px > qx) {
// we got past point below, might need to
// insert interpolated extra point
if (withlines && i > 0 && points[i - ps] != null) {
intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px);
newpoints.push(qx);
newpoints.push(intery + qy);
for (m = 2; m < ps; ++m)
newpoints.push(points[i + m]);
bottom = qy;
}
j += otherps;
}
else { // px < qx
if (fromgap && withlines) {
// if we come from a gap, we just skip this point
i += ps;
continue;
}
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
// we might be able to interpolate a point below,
// this can give us a better y
if (withlines && j > 0 && otherpoints[j - otherps] != null)
bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx);
newpoints[l + accumulateOffset] += bottom;
i += ps;
}
fromgap = false;
if (l != newpoints.length && withbottom)
newpoints[l + 2] += bottom;
}
// maintain the line steps invariant
if (withsteps && l != newpoints.length && l > 0
&& newpoints[l] != null
&& newpoints[l] != newpoints[l - ps]
&& newpoints[l + 1] != newpoints[l - ps + 1]) {
for (m = 0; m < ps; ++m)
newpoints[l + ps + m] = newpoints[l + m];
newpoints[l + 1] = newpoints[l - ps + 1];
}
}
datapoints.points = newpoints;
}
plot.hooks.processDatapoints.push(stackData);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'stack',
version: '1.2'
});
})(jQuery);

View File

@ -0,0 +1,36 @@
/* Flot plugin for stacking data sets rather than overlyaing them.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
The plugin assumes the data is sorted on x (or y if stacking horizontally).
For line charts, it is assumed that if a line has an undefined gap (from a
null point), then the line above it should have the same gap - insert zeros
instead of "null" if you want another behaviour. This also holds for the start
and end of the chart. Note that stacking a mix of positive and negative values
in most instances doesn't make sense (so it looks weird).
Two or more series are stacked when their "stack" attribute is set to the same
key (which can be any number or string or just "true"). To specify the default
stack, you can set the stack option like this:
series: {
stack: null/false, true, or a key (number/string)
}
You can also specify it for a single series, like this:
$.plot( $("#placeholder"), [{
data: [ ... ],
stack: true
}])
The stacking order is determined by the order of the data series in the array
(later series end up on top of the previous).
Internally, the plugin modifies the datapoints in each series, adding an
offset to the y value. For line series, extra data points are inserted through
interpolation. If there's a second y value, it's also adjusted (e.g for bar
charts or filled areas).
*/(function(e){function n(e){function t(e,t){var n=null;for(var r=0;r<t.length;++r){if(e==t[r])break;t[r].stack==e.stack&&(n=t[r])}return n}function n(e,n,r){if(n.stack==null||n.stack===!1)return;var i=t(n,e.getData());if(!i)return;var s=r.pointsize,o=r.points,u=i.datapoints.pointsize,a=i.datapoints.points,f=[],l,c,h,p,d,v,m=n.lines.show,g=n.bars.horizontal,y=s>2&&(g?r.format[2].x:r.format[2].y),b=m&&n.lines.steps,w=!0,E=g?1:0,S=g?0:1,x=0,T=0,N,C;for(;;){if(x>=o.length)break;N=f.length;if(o[x]==null){for(C=0;C<s;++C)f.push(o[x+C]);x+=s}else if(T>=a.length){if(!m)for(C=0;C<s;++C)f.push(o[x+C]);x+=s}else if(a[T]==null){for(C=0;C<s;++C)f.push(null);w=!0;T+=u}else{l=o[x+E];c=o[x+S];p=a[T+E];d=a[T+S];v=0;if(l==p){for(C=0;C<s;++C)f.push(o[x+C]);f[N+S]+=d;v=d;x+=s;T+=u}else if(l>p){if(m&&x>0&&o[x-s]!=null){h=c+(o[x-s+S]-c)*(p-l)/(o[x-s+E]-l);f.push(p);f.push(h+d);for(C=2;C<s;++C)f.push(o[x+C]);v=d}T+=u}else{if(w&&m){x+=s;continue}for(C=0;C<s;++C)f.push(o[x+C]);m&&T>0&&a[T-u]!=null&&(v=d+(a[T-u+S]-d)*(l-p)/(a[T-u+E]-p));f[N+S]+=v;x+=s}w=!1;N!=f.length&&y&&(f[N+2]+=v)}if(b&&N!=f.length&&N>0&&f[N]!=null&&f[N]!=f[N-s]&&f[N+1]!=f[N-s+1]){for(C=0;C<s;++C)f[N+s+C]=f[N+C];f[N+1]=f[N-s+1]}}r.points=f}e.hooks.processDatapoints.push(n)}var t={series:{stack:null}};e.plot.plugins.push({init:n,options:t,name:"stack",version:"1.2"})})(jQuery);

View File

@ -7,18 +7,20 @@ jQuery(document).ready(function($) {
}).appendTo("body").fadeIn(200);
}
var previousPoint = null;
var prev_data_index = null;
var prev_series_index = null;
jQuery(".chart-placeholder").bind( "plothover", function (event, pos, item) {
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
if ( prev_data_index != item.dataIndex || prev_series_index != item.seriesIndex ) {
prev_data_index = item.dataIndex;
prev_series_index = item.seriesIndex;
jQuery( ".chart-tooltip" ).remove();
if ( item.series.points.show || item.series.enable_tooltip ) {
var y = item.datapoint[1];
var y = item.series.data[item.dataIndex][1];
tooltip_content = '';
@ -28,14 +30,27 @@ jQuery(document).ready(function($) {
if ( item.series.prepend_tooltip )
tooltip_content = tooltip_content + item.series.prepend_tooltip;
showTooltip( item.pageX, item.pageY, tooltip_content + y );
tooltip_content = tooltip_content + y;
if ( item.series.append_tooltip )
tooltip_content = tooltip_content + item.series.append_tooltip;
if ( item.series.pie.show ) {
showTooltip( pos.pageX, pos.pageY, tooltip_content );
} else {
showTooltip( item.pageX, item.pageY, tooltip_content );
}
}
}
}
else {
jQuery(".chart-tooltip").remove();
previousPoint = null;
prev_data_index = null;
}
});

View File

@ -1 +1 @@
jQuery(document).ready(function(e){function t(e,t,n){jQuery('<div class="chart-tooltip">'+n+"</div>").css({top:t-16,left:e+20}).appendTo("body").fadeIn(200)}var n=null;jQuery(".chart-placeholder").bind("plothover",function(e,r,i){if(i){if(n!=i.dataIndex){n=i.dataIndex;jQuery(".chart-tooltip").remove();if(i.series.points.show||i.series.enable_tooltip){var s=i.datapoint[1];tooltip_content="";i.series.prepend_label&&(tooltip_content=tooltip_content+i.series.label+": ");i.series.prepend_tooltip&&(tooltip_content+=i.series.prepend_tooltip);t(i.pageX,i.pageY,tooltip_content+s)}}}else{jQuery(".chart-tooltip").remove();n=null}});e(".wc_sparkline.bars").each(function(){var t=e(this).data("sparkline"),n={grid:{show:!1}},r=[{data:t,color:e(this).data("color"),bars:{fillColor:e(this).data("color"),fill:!0,show:!0,lineWidth:1,barWidth:e(this).data("barwidth"),align:"center"},shadowSize:0}],i=e.plot(e(this),r,n)});e(".wc_sparkline.lines").each(function(){var t=e(this).data("sparkline"),n={grid:{show:!1}},r=[{data:t,color:e(this).data("color"),lines:{fill:!1,show:!0,lineWidth:1,align:"center"},shadowSize:0}],i=e.plot(e(this),r,n)});var r=jQuery(".range_datepicker").datepicker({defaultDate:"",dateFormat:"yy-mm-dd",numberOfMonths:1,maxDate:"+0D",showButtonPanel:!0,showOn:"focus",buttonImageOnly:!0,onSelect:function(e){var t=jQuery(this).is(".from")?"minDate":"maxDate",n=jQuery(this).data("datepicker"),i=jQuery.datepicker.parseDate(n.settings.dateFormat||jQuery.datepicker._defaults.dateFormat,e,n.settings);r.not(this).datepicker("option",t,i)}})});
jQuery(document).ready(function(e){function t(e,t,n){jQuery('<div class="chart-tooltip">'+n+"</div>").css({top:t-16,left:e+20}).appendTo("body").fadeIn(200)}var n=null,r=null;jQuery(".chart-placeholder").bind("plothover",function(e,i,s){if(s){if(n!=s.dataIndex||r!=s.seriesIndex){n=s.dataIndex;r=s.seriesIndex;jQuery(".chart-tooltip").remove();if(s.series.points.show||s.series.enable_tooltip){var o=s.series.data[s.dataIndex][1];tooltip_content="";s.series.prepend_label&&(tooltip_content=tooltip_content+s.series.label+": ");s.series.prepend_tooltip&&(tooltip_content+=s.series.prepend_tooltip);tooltip_content+=o;s.series.append_tooltip&&(tooltip_content+=s.series.append_tooltip);s.series.pie.show?t(i.pageX,i.pageY,tooltip_content):t(s.pageX,s.pageY,tooltip_content)}}}else{jQuery(".chart-tooltip").remove();n=null}});e(".wc_sparkline.bars").each(function(){var t=e(this).data("sparkline"),n={grid:{show:!1}},r=[{data:t,color:e(this).data("color"),bars:{fillColor:e(this).data("color"),fill:!0,show:!0,lineWidth:1,barWidth:e(this).data("barwidth"),align:"center"},shadowSize:0}],i=e.plot(e(this),r,n)});e(".wc_sparkline.lines").each(function(){var t=e(this).data("sparkline"),n={grid:{show:!1}},r=[{data:t,color:e(this).data("color"),lines:{fill:!1,show:!0,lineWidth:1,align:"center"},shadowSize:0}],i=e.plot(e(this),r,n)});var i=jQuery(".range_datepicker").datepicker({defaultDate:"",dateFormat:"yy-mm-dd",numberOfMonths:1,maxDate:"+0D",showButtonPanel:!0,showOn:"focus",buttonImageOnly:!0,onSelect:function(e){var t=jQuery(this).is(".from")?"minDate":"maxDate",n=jQuery(this).data("datepicker"),r=jQuery.datepicker.parseDate(n.settings.dateFormat||jQuery.datepicker._defaults.dateFormat,e,n.settings);i.not(this).datepicker("option",t,r)}})});