Merge branch 'Charts/Reports' Closes #3281
This commit is contained in:
commit
f295a5d568
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
/**
|
||||
* Admin Reports
|
||||
*
|
||||
* Functions used for displaying sales and customer reports in admin.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce/Admin/Reports
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
||||
|
||||
/**
|
||||
* WC_Admin_Reports Class
|
||||
*/
|
||||
class WC_Admin_Reports {
|
||||
|
||||
private $start_date;
|
||||
private $end_date;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'admin_menu', array( $this, 'add_menu_item' ), 20 );
|
||||
add_filter( 'woocommerce_screen_ids', array( $this, 'add_screen_id' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'scripts_and_styles' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add menu item
|
||||
*/
|
||||
public function add_menu_item() {
|
||||
add_submenu_page( 'woocommerce', __( 'Reports', 'woocommerce' ), __( 'Reports', 'woocommerce' ) , 'view_woocommerce_reports', 'wc_reports', array( $this, 'admin_page' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add screen ID
|
||||
* @param array $ids
|
||||
*/
|
||||
public function add_screen_id( $ids ) {
|
||||
$wc_screen_id = strtolower( __( 'WooCommerce', 'woocommerce' ) );
|
||||
$ids[] = $wc_screen_id . '_page_wc_reports';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Script and styles
|
||||
*/
|
||||
public function scripts_and_styles() {
|
||||
$screen = get_current_screen();
|
||||
$wc_screen_id = strtolower( __( 'WooCommerce', 'woocommerce' ) );
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
|
||||
if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_wc_reports' ) ) ) ) {
|
||||
wp_enqueue_script( 'wc-reports', WC()->plugin_url() . '/assets/js/admin/reports' . $suffix . '.js', array( 'jquery', 'jquery-ui-datepicker' ), '1.0' );
|
||||
wp_enqueue_script( 'flot', WC()->plugin_url() . '/assets/js/admin/jquery.flot' . $suffix . '.js', array( 'jquery' ), '1.0' );
|
||||
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' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the definitions for the reports to show in admin.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_reports() {
|
||||
$reports = array(
|
||||
'orders' => array(
|
||||
'title' => __( 'Orders', 'woocommerce' ),
|
||||
'reports' => array(
|
||||
"sales_by_date" => array(
|
||||
'title' => __( 'Sales by date', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"sales_by_product" => array(
|
||||
'title' => __( 'Sales by product', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"sales_by_category" => array(
|
||||
'title' => __( 'Sales by category', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"coupon_usage" => array(
|
||||
'title' => __( 'Coupons by date', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
)
|
||||
)
|
||||
),
|
||||
'customers' => array(
|
||||
'title' => __( 'Customers', 'woocommerce' ),
|
||||
'reports' => array(
|
||||
"customers" => array(
|
||||
'title' => __( 'Customers vs. Guests', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"customer_list" => array(
|
||||
'title' => __( 'Customer List', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
)
|
||||
),
|
||||
'stock' => array(
|
||||
'title' => __( 'Stock', 'woocommerce' ),
|
||||
'reports' => array(
|
||||
"low_in_stock" => array(
|
||||
'title' => __( 'Low in stock', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"out_of_stock" => array(
|
||||
'title' => __( 'Out of stock', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"most_stocked" => array(
|
||||
'title' => __( 'Most Stocked', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if ( get_option( 'woocommerce_calc_taxes' ) == 'yes' ) {
|
||||
$reports['taxes'] = array(
|
||||
'title' => __( 'Taxes', 'woocommerce' ),
|
||||
'reports' => array(
|
||||
"taxes_by_code" => array(
|
||||
'title' => __( 'Taxes by code', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
"taxes_by_date" => array(
|
||||
'title' => __( 'Taxes by date', 'woocommerce' ),
|
||||
'description' => '',
|
||||
'hide_title' => true,
|
||||
'callback' => array( $this, 'get_report' )
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$reports = apply_filters( 'woocommerce_admin_reports', $reports );
|
||||
|
||||
// Backwards compat
|
||||
$reports = apply_filters( 'woocommerce_reports_charts', $reports );
|
||||
|
||||
foreach ( $reports as $key => $report_group ) {
|
||||
if ( isset( $reports[ $key ]['charts'] ) )
|
||||
$reports[ $key ]['reports'] = $reports[ $key ]['charts'];
|
||||
|
||||
foreach ( $reports[ $key ]['reports'] as $report_key => $report ) {
|
||||
if ( isset( $reports[ $key ]['reports'][ $report_key ]['function'] ) )
|
||||
$reports[ $key ]['reports'][ $report_key ]['callback'] = $reports[ $key ]['reports'][ $report_key ]['function'];
|
||||
}
|
||||
}
|
||||
|
||||
return $reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles output of the reports page in admin.
|
||||
*/
|
||||
public function admin_page() {
|
||||
$reports = $this->get_reports();
|
||||
$first_tab = array_keys( $reports );
|
||||
$current_tab = ! empty( $_GET['tab'] ) ? sanitize_title( urldecode( $_GET['tab'] ) ) : $first_tab[0];
|
||||
$current_report = isset( $_GET['report'] ) ? sanitize_title( urldecode( $_GET['report'] ) ) : current( array_keys( $reports[ $current_tab ]['reports'] ) );
|
||||
|
||||
include_once( 'reports/class-wc-admin-report.php' );
|
||||
include_once( 'views/html-admin-page-reports.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a report from our reports subfolder
|
||||
*/
|
||||
public function get_report( $name ) {
|
||||
$name = sanitize_title( str_replace( '_', '-', $name ) );
|
||||
$class = 'WC_Report_' . str_replace( '-', '_', $name );
|
||||
|
||||
include_once( 'reports/class-wc-report-' . $name . '.php' );
|
||||
|
||||
if ( ! class_exists( $class ) )
|
||||
return;
|
||||
|
||||
$report = new $class();
|
||||
$report->output_report();
|
||||
}
|
||||
}
|
||||
|
||||
new WC_Admin_Reports();
|
|
@ -947,6 +947,8 @@ function woocommerce_process_shop_order_meta( $post_id, $post ) {
|
|||
}
|
||||
|
||||
delete_transient( 'woocommerce_processing_order_count' );
|
||||
|
||||
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_wc_report_%') OR `option_name` LIKE ('_transient_timeout_wc_report_%')" );
|
||||
}
|
||||
|
||||
add_action( 'woocommerce_process_shop_order_meta', 'woocommerce_process_shop_order_meta', 10, 2 );
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
<?php
|
||||
/**
|
||||
* Admin Report
|
||||
*
|
||||
* Extended by reports to show charts and stats in admin.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce/Admin/Reports
|
||||
* @version 2.1.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
||||
|
||||
/**
|
||||
* WC_Admin_Report Class
|
||||
*/
|
||||
class WC_Admin_Report {
|
||||
|
||||
public $start_date;
|
||||
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 array of results
|
||||
*/
|
||||
public function get_order_report_data( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'data' => array(),
|
||||
'where' => array(),
|
||||
'where_meta' => array(),
|
||||
'query_type' => 'get_row',
|
||||
'group_by' => '',
|
||||
'order_by' => '',
|
||||
'limit' => '',
|
||||
'filter_range' => false,
|
||||
'nocache' => false,
|
||||
'debug' => false
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
extract( $args );
|
||||
|
||||
if ( empty( $data ) )
|
||||
return false;
|
||||
|
||||
$select = array();
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
$distinct = '';
|
||||
|
||||
if ( isset( $value['distinct'] ) )
|
||||
$distinct = 'DISTINCT';
|
||||
|
||||
if ( $value['type'] == 'meta' )
|
||||
$get_key = "meta_{$key}.meta_value";
|
||||
elseif( $value['type'] == 'post_data' )
|
||||
$get_key = "posts.{$key}";
|
||||
elseif( $value['type'] == 'order_item_meta' )
|
||||
$get_key = "order_item_meta_{$key}.meta_value";
|
||||
elseif( $value['type'] == 'order_item' )
|
||||
$get_key = "order_items.{$key}";
|
||||
|
||||
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();
|
||||
$joins['rel'] = "LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID";
|
||||
$joins['tax'] = "LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )";
|
||||
$joins['term'] = "LEFT JOIN {$wpdb->terms} AS term USING( term_id )";
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( $value['type'] == 'meta' ) {
|
||||
|
||||
$joins["meta_{$key}"] = "LEFT JOIN {$wpdb->postmeta} AS meta_{$key} ON posts.ID = meta_{$key}.post_id";
|
||||
|
||||
} elseif ( $value['type'] == 'order_item_meta' ) {
|
||||
|
||||
$joins["order_items"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id";
|
||||
$joins["order_item_meta_{$key}"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON order_items.order_item_id = order_item_meta_{$key}.order_item_id";
|
||||
|
||||
} elseif ( $value['type'] == 'order_item' ) {
|
||||
|
||||
$joins["order_items"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $where_meta ) ) {
|
||||
foreach ( $where_meta as $value ) {
|
||||
if ( ! is_array( $value ) )
|
||||
continue;
|
||||
|
||||
if ( isset( $value['type'] ) && $value['type'] == 'order_item_meta' ) {
|
||||
|
||||
$joins["order_items"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id";
|
||||
$joins["order_item_meta_{$value['meta_key']}"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$value['meta_key']} ON order_items.order_item_id = order_item_meta_{$value['meta_key']}.order_item_id";
|
||||
|
||||
} else {
|
||||
// If we have a where clause for meta, join the postmeta table
|
||||
$joins["meta_{$value['meta_key']}"] = "LEFT JOIN {$wpdb->postmeta} AS meta_{$value['meta_key']} ON posts.ID = meta_{$value['meta_key']}.post_id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query['join'] = implode( ' ', $joins );
|
||||
|
||||
$query['where'] = "
|
||||
WHERE posts.post_type = 'shop_order'
|
||||
AND posts.post_status = 'publish'
|
||||
AND tax.taxonomy = 'shop_order_status'
|
||||
AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
|
||||
";
|
||||
|
||||
if ( $filter_range ) {
|
||||
$query['where'] .= "
|
||||
AND post_date > '" . date('Y-m-d', $this->start_date ) . "'
|
||||
AND post_date < '" . date('Y-m-d', strtotime( '+1 DAY', $this->end_date ) ) . "'
|
||||
";
|
||||
}
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( $value['type'] == 'meta' ) {
|
||||
|
||||
$query['where'] .= " AND meta_{$key}.meta_key = '{$key}'";
|
||||
|
||||
} elseif ( $value['type'] == 'order_item_meta' ) {
|
||||
|
||||
$query['where'] .= " AND order_items.order_item_type = '{$value['order_item_type']}'";
|
||||
$query['where'] .= " AND order_item_meta_{$key}.meta_key = '{$key}'";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if ( strtolower( $value['operator'] ) == 'in' ) {
|
||||
if ( is_array( $value['meta_value'] ) )
|
||||
$value['meta_value'] = implode( "','", $value['meta_value'] );
|
||||
if ( ! empty( $value['meta_value'] ) )
|
||||
$where_value = "IN ('{$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'] ) && $value['type'] == 'order_item_meta' ) {
|
||||
$query['where'] .= " ( order_item_meta_{$value['meta_key']}.meta_key = '{$value['meta_key']}'";
|
||||
$query['where'] .= " AND order_item_meta_{$value['meta_key']}.meta_value {$where_value} )";
|
||||
} else {
|
||||
$query['where'] .= " ( meta_{$value['meta_key']}.meta_key = '{$value['meta_key']}'";
|
||||
$query['where'] .= " AND meta_{$value['meta_key']}.meta_value {$where_value} )";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query['where'] .= ")";
|
||||
}
|
||||
|
||||
if ( ! empty( $where ) ) {
|
||||
foreach ( $where as $value ) {
|
||||
if ( strtolower( $value['operator'] ) == 'in' ) {
|
||||
if ( is_array( $value['value'] ) )
|
||||
$value['value'] = implode( "','", $value['value'] );
|
||||
if ( ! empty( $value['value'] ) )
|
||||
$where_value = "IN ('{$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 = implode( ' ', $query );
|
||||
$query_hash = md5( $query_type . $query );
|
||||
|
||||
if ( $debug )
|
||||
var_dump( $query );
|
||||
|
||||
if ( $debug || $nocache || ( false === ( $result = get_transient( 'wc_report_' . $query_hash ) ) ) ) {
|
||||
$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
|
||||
|
||||
if ( $filter_range ) {
|
||||
if ( date('Y-m-d', strtotime( $this->end_date ) ) == date('Y-m-d', current_time( 'timestamp' ) ) ) {
|
||||
$expiration = 60 * 60 * 1; // 1 hour
|
||||
} else {
|
||||
$expiration = 60 * 60 * 24; // 24 hour
|
||||
}
|
||||
} else {
|
||||
$expiration = 60 * 60 * 24; // 24 hour
|
||||
}
|
||||
|
||||
set_transient( 'wc_report_' . $query_hash, $result, $expiration );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string
|
||||
*/
|
||||
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 first in this range
|
||||
for ( $i = 0; $i <= $interval; $i ++ ) {
|
||||
switch ( $group_by ) {
|
||||
case 'day' :
|
||||
$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $start_date ) ) ) * 1000;
|
||||
break;
|
||||
case 'month' :
|
||||
$time = strtotime( date( 'Ym', strtotime( "+{$i} MONTH", $start_date ) ) . '01' ) * 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! isset( $prepared_data[ $time ] ) )
|
||||
$prepared_data[ $time ] = array( esc_js( $time ), 0 );
|
||||
}
|
||||
|
||||
foreach ( $data as $d ) {
|
||||
switch ( $group_by ) {
|
||||
case 'day' :
|
||||
$time = strtotime( date( 'Ymd', strtotime( $d->$date_key ) ) ) * 1000;
|
||||
break;
|
||||
case 'month' :
|
||||
$time = strtotime( date( 'Ym', strtotime( $d->$date_key ) ) . '01' ) * 1000;
|
||||
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
|
||||
* @param int $days
|
||||
*/
|
||||
public function sales_sparkline( $id, $days, $type ) {
|
||||
$meta_key = $type == 'sales' ? '_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' => 'order_item_value'
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date'
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'post_date',
|
||||
'value' => date( 'Y-m-d', strtotime( 'midnight -7 days', current_time( 'timestamp' ) ) ),
|
||||
'operator' => '>'
|
||||
),
|
||||
array(
|
||||
'key' => 'order_item_meta__product_id.meta_value',
|
||||
'value' => $id,
|
||||
'operator' => '='
|
||||
)
|
||||
),
|
||||
'group_by' => 'YEAR(post_date), MONTH(post_date), DAY(post_date)',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => false
|
||||
) );
|
||||
|
||||
$total = 0;
|
||||
foreach ( $data as $d )
|
||||
$total += $d->order_item_value;
|
||||
|
||||
if ( $type == 'sales' ) {
|
||||
$tooltip = sprintf( __( 'Sold %s worth in the last %d days', 'woocommerce' ), strip_tags( woocommerce_price( $total ) ), $days );
|
||||
} else {
|
||||
$tooltip = sprintf( _n( 'Sold 1 time in the last %d days', 'Sold %d times in the last %d days', $total, 'woocommerce' ), $total, $days );
|
||||
}
|
||||
|
||||
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'order_item_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
|
||||
|
||||
return '<span class="wc_sparkline ' . ( $type == 'sales' ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . $tooltip . '" data-barwidth="' . 60*60*16*1000 . '" data-sparkline="' . esc_attr( json_encode( $sparkline_data ) ) . '"></span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
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 description]
|
||||
* @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() {}
|
||||
}
|
|
@ -0,0 +1,558 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Coupon_Usage class
|
||||
*/
|
||||
class WC_Report_Coupon_Usage extends WC_Admin_Report {
|
||||
|
||||
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', $_GET['coupon_codes'] ) );
|
||||
elseif ( isset( $_GET['coupon_codes'] ) )
|
||||
$this->coupon_codes = array_filter( array( sanitize_text_field( $_GET['coupon_codes'] ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
$legend = array();
|
||||
|
||||
$total_discount = $this->get_order_report_data( array(
|
||||
'data' => array(
|
||||
'discount_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'SUM',
|
||||
'name' => 'discount_amount'
|
||||
)
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
$total_coupons = absint( $this->get_order_report_data( array(
|
||||
'data' => array(
|
||||
'order_item_name' => array(
|
||||
'type' => 'order_item',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'order_coupon_count'
|
||||
)
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) ) );
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s discounts in total', 'woocommerce' ), '<strong>' . woocommerce_price( $total_discount ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['discount_amount'],
|
||||
'highlight_series' => 1
|
||||
);
|
||||
|
||||
$legend[] = array(
|
||||
'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() {
|
||||
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(
|
||||
'discount_amount' => '#3498db',
|
||||
'coupon_count' => '#d4d9dc',
|
||||
);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* [get_chart_widgets description]
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
$widgets = array();
|
||||
|
||||
|
||||
$widgets[] = array(
|
||||
'title' => '',
|
||||
'callback' => array( $this, 'coupons_widget' )
|
||||
);
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Product selection
|
||||
* @return void
|
||||
*/
|
||||
public function coupons_widget() {
|
||||
?>
|
||||
<h4 class="section_title"><span><?php _e( 'Filter by coupon', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<form method="GET">
|
||||
<div>
|
||||
<select id="coupon_codes" name="coupon_codes" class="chosen_select" data-placeholder="<?php _e( 'Choose coupons…', 'woocommerce' ); ?>" style="width:100%;">
|
||||
<?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 ( $used_coupons ) {
|
||||
echo '<option value="">' . __( 'All coupons', 'woocommerce' ) . '</option>';
|
||||
foreach ( $used_coupons as $coupon ) {
|
||||
echo '<option value="' . $coupon . '" ' . selected( in_array( $coupon, $this->coupon_codes ), true, false ) . '>' . $coupon . '</option>';
|
||||
}
|
||||
} else
|
||||
echo '<option value="">' . __( 'No used coupons found', 'woocommerce' ) . '</option>';
|
||||
?>
|
||||
</select>
|
||||
<input type="submit" class="submit button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
|
||||
<input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" />
|
||||
<input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" />
|
||||
<input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" />
|
||||
<input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" />
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function(){
|
||||
jQuery("select.chosen_select").chosen();
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php _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 ( $most_popular ) {
|
||||
foreach ( $most_popular as $coupon ) {
|
||||
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
|
||||
<td class="count" width="1%">' . $coupon->coupon_count . '</td>
|
||||
<td class="name"><a href="' . add_query_arg( 'coupon_codes', $coupon->coupon_code ) . '">' . $coupon->coupon_code . '</a></td>
|
||||
</tr>';
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="2">' . __( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php _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 ( $most_discount ) {
|
||||
foreach ( $most_discount as $coupon ) {
|
||||
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
|
||||
<td class="count" width="1%">' . woocommerce_price( $coupon->discount_amount ) . '</td>
|
||||
<td class="name"><a href="' . add_query_arg( 'coupon_codes', $coupon->coupon_code ) . '">' . $coupon->coupon_code . '</a></td>
|
||||
</tr>';
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . __( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery('.section_title').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)').click();
|
||||
<?php else : ?>
|
||||
jQuery('.section_title:eq(0)').click();
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
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 = $this->get_order_report_data( 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(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
$order_discount_amounts = $this->get_order_report_data( array(
|
||||
'data' => array(
|
||||
'discount_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'SUM',
|
||||
'name' => 'discount_amount'
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date'
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'order_item',
|
||||
'key' => 'order_item_name',
|
||||
'value' => $this->coupon_codes,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'group_by' => $this->group_by_query . ', order_item_name',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
// 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 = 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 = jQuery.parseJSON( '<?php echo $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 $this->chart_colours['coupon_count' ]; ?>',
|
||||
bars: { fillColor: '<?php echo $this->chart_colours['coupon_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( __( 'Discount amount', 'woocommerce' ) ) ?>",
|
||||
data: order_data.order_discount_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo $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,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
|
||||
}
|
||||
];
|
||||
|
||||
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 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; ?>"],
|
||||
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').resize();
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').hover(
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
},
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -6,16 +6,9 @@ if ( ! class_exists( 'WP_List_Table' ) )
|
|||
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
||||
|
||||
/**
|
||||
* Admin customers table
|
||||
*
|
||||
* Lists customers.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce/Admin/Users
|
||||
* @version 2.0.1
|
||||
* WC_Report_Customer_List class
|
||||
*/
|
||||
class WC_Admin_Customers extends WP_List_Table {
|
||||
class WC_Report_Customer_List extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* __construct function.
|
||||
|
@ -32,6 +25,36 @@ class WC_Admin_Customers extends WP_List_Table {
|
|||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text
|
||||
*/
|
||||
public function no_items() {
|
||||
_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' ) ) {
|
||||
$linked = woocommerce_update_new_customer_past_orders( absint( $_GET['link_orders'] ) );
|
||||
|
||||
echo '<div class="updated"><p>' . sprintf( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) . '</p></div>';
|
||||
}
|
||||
|
||||
echo '<form method="post" id="woocommerce_customers">';
|
||||
|
||||
$this->search_box( __( 'Search customers', 'woocommerce' ), 'customer_search' );
|
||||
$this->display();
|
||||
|
||||
echo '</form>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* column_default function.
|
||||
*
|
||||
|
@ -66,18 +89,13 @@ class WC_Admin_Customers extends WP_List_Table {
|
|||
|
||||
$value .= $country;
|
||||
|
||||
return $value;
|
||||
if ( $value )
|
||||
return $value;
|
||||
else
|
||||
return '-';
|
||||
break;
|
||||
case 'email' :
|
||||
return '<a href="mailto:' . $user->user_email . '">' . $user->user_email . '</a>';
|
||||
case 'paying' :
|
||||
$paying_customer = get_user_meta( $user->ID, 'paying_customer', true );
|
||||
|
||||
if ( $paying_customer )
|
||||
return '<img src="' . $woocommerce->plugin_url() . '/assets/images/success@2x.png" alt="yes" width="16px" />';
|
||||
else
|
||||
return ' - ';
|
||||
break;
|
||||
case 'spent' :
|
||||
if ( ! $spent = get_user_meta( $user->ID, '_money_spent', true ) ) {
|
||||
|
||||
|
@ -148,7 +166,7 @@ class WC_Admin_Customers extends WP_List_Table {
|
|||
$order = new WC_Order( $order_ids[0] );
|
||||
|
||||
echo '<a href="' . admin_url( 'post.php?post=' . $order->id . '&action=edit' ) . '">' . $order->get_order_number() . '</a> – ' . date_i18n( get_option( 'date_format', strtotime( $order->order_date ) ) );
|
||||
}
|
||||
} else echo '-';
|
||||
|
||||
break;
|
||||
case 'user_actions' :
|
||||
|
@ -218,11 +236,10 @@ class WC_Admin_Customers extends WP_List_Table {
|
|||
$columns = array(
|
||||
'customer_name' => __( 'Name (Last, First)', 'woocommerce' ),
|
||||
'username' => __( 'Username', 'woocommerce' ),
|
||||
'email' => __( 'Email address', 'woocommerce' ),
|
||||
'email' => __( 'Email', 'woocommerce' ),
|
||||
'location' => __( 'Location', 'woocommerce' ),
|
||||
'paying' => __( 'Paying customer?', 'woocommerce' ),
|
||||
'orders' => __( 'Complete orders', 'woocommerce' ),
|
||||
'spent' => __( 'Money spent', 'woocommerce' ),
|
||||
'orders' => __( 'Orders', 'woocommerce' ),
|
||||
'spent' => __( 'Spent', 'woocommerce' ),
|
||||
'last_order' => __( 'Last order', 'woocommerce' ),
|
||||
'user_actions' => __( 'Actions', 'woocommerce' )
|
||||
);
|
||||
|
@ -272,8 +289,22 @@ class WC_Admin_Customers extends WP_List_Table {
|
|||
/**
|
||||
* 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( array(
|
||||
'role' => 'customer',
|
||||
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ),
|
||||
'number' => $per_page,
|
||||
'offset' => ( $current_page - 1 ) * $per_page
|
||||
) );
|
|
@ -0,0 +1,445 @@
|
|||
<?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'],
|
||||
'highlight_series' => 2
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
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() . '/admin/views/html-report-by-date.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?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">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var chart_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
|
||||
|
||||
var drawGraph = function( highlight ) {
|
||||
var series = [
|
||||
{
|
||||
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
|
||||
},
|
||||
];
|
||||
|
||||
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 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').resize();
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').hover(
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
},
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) )
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) )
|
||||
require_once( 'class-wc-report-stock.php' );
|
||||
|
||||
/**
|
||||
* WC_Report_Low_In_Stock class
|
||||
*
|
||||
* @extends WC_Report_Stock
|
||||
*/
|
||||
class WC_Report_Low_In_Stock extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* No items found text
|
||||
*/
|
||||
public function no_items() {
|
||||
_e( 'No low in stock products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
// Get products using a query - this is too advanced for get_posts :(
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
|
||||
$nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = "FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ('product', 'product_variation')
|
||||
AND posts.post_status = 'publish'
|
||||
AND (
|
||||
postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != ''
|
||||
)
|
||||
AND (
|
||||
( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' )
|
||||
)
|
||||
";
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT posts.ID as id, posts.post_parent as parent {$query_from} GROUP BY posts.ID ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) );
|
||||
$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) )
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) )
|
||||
require_once( 'class-wc-report-stock.php' );
|
||||
|
||||
/**
|
||||
* WC_Report_Most_Stocked class
|
||||
*
|
||||
* @extends WC_Report_Stock
|
||||
*/
|
||||
class WC_Report_Most_Stocked extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
// Get products using a query - this is too advanced for get_posts :(
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = "FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ('product', 'product_variation')
|
||||
AND posts.post_status = 'publish'
|
||||
AND (
|
||||
postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) > '{$stock}' AND postmeta.meta_value != ''
|
||||
)
|
||||
AND (
|
||||
( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' )
|
||||
)
|
||||
";
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT posts.ID as id, posts.post_parent as parent {$query_from} GROUP BY posts.ID ORDER BY CAST(postmeta.meta_value AS SIGNED) DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) );
|
||||
$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) )
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
if ( ! class_exists( 'WC_Report_Stock' ) )
|
||||
require_once( 'class-wc-report-stock.php' );
|
||||
|
||||
/**
|
||||
* WC_Report_Out_Of_Stock class
|
||||
*
|
||||
* @extends WC_Report_Stock
|
||||
*/
|
||||
class WC_Report_Out_Of_Stock extends WC_Report_Stock {
|
||||
|
||||
/**
|
||||
* No items found text
|
||||
*/
|
||||
public function no_items() {
|
||||
_e( 'No out of stock products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Products matching stock criteria
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function get_items( $current_page, $per_page ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->max_items = 0;
|
||||
$this->items = array();
|
||||
|
||||
// Get products using a query - this is too advanced for get_posts :(
|
||||
$stock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
|
||||
|
||||
$query_from = "FROM {$wpdb->posts} as posts
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
|
||||
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id
|
||||
WHERE 1=1
|
||||
AND posts.post_type IN ('product', 'product_variation')
|
||||
AND posts.post_status = 'publish'
|
||||
AND (
|
||||
postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND postmeta.meta_value != ''
|
||||
)
|
||||
AND (
|
||||
( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' )
|
||||
)
|
||||
";
|
||||
|
||||
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT posts.ID as id, posts.post_parent as parent {$query_from} GROUP BY posts.ID ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) );
|
||||
$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,435 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Sales_By_Category class
|
||||
*/
|
||||
class WC_Report_Sales_By_Category extends WC_Admin_Report {
|
||||
|
||||
public $show_categories = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( isset( $_GET['show_categories'] ) && is_array( $_GET['show_categories'] ) )
|
||||
$this->show_categories = array_map( 'absint', $_GET['show_categories'] );
|
||||
elseif ( isset( $_GET['show_categories'] ) )
|
||||
$this->show_categories = array( absint( $_GET['show_categories'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
if ( ! $this->show_categories )
|
||||
return array();
|
||||
|
||||
$legend = array();
|
||||
$index = 0;
|
||||
|
||||
foreach( $this->show_categories as $category ) {
|
||||
$category = get_term( $category, 'product_cat' );
|
||||
$term_ids = get_term_children( $category->term_id, 'product_cat' );
|
||||
$term_ids[] = $category->term_id;
|
||||
$total = 0;
|
||||
$product_ids = array_unique( get_objects_in_term( $term_ids, 'product_cat' ) );
|
||||
|
||||
foreach ( $product_ids as $id ) {
|
||||
if ( isset( $this->item_sales[ $id ] ) ) {
|
||||
$total += $this->item_sales[ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
//if ( ! $total )
|
||||
// continue;
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s sales in %s', 'woocommerce' ), '<strong>' . woocommerce_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() {
|
||||
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( '#3498db', '#34495e', '#1abc9c', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c', '#2980b9', '#8e44ad', '#2c3e50', '#16a085', '#27ae60', '#f39c12', '#d35400', '#c0392b' );
|
||||
|
||||
$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 ++;
|
||||
}
|
||||
|
||||
// 1 months max for day view
|
||||
if ( $interval > 1 )
|
||||
$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;
|
||||
}
|
||||
|
||||
// Get item sales data
|
||||
if ( $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' => '',
|
||||
'name' => 'order_item_amount'
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date'
|
||||
),
|
||||
),
|
||||
'group_by' => 'ID, product_id',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
$this->item_sales = array();
|
||||
$this->item_sales_and_times = array();
|
||||
|
||||
if ( $order_items ) {
|
||||
foreach ( $order_items as $order_item ) {
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day' :
|
||||
$time = strtotime( date( 'Ymd', strtotime( $order_item->post_date ) ) ) * 1000;
|
||||
break;
|
||||
case 'month' :
|
||||
$time = strtotime( date( '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() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* [get_chart_widgets description]
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_widgets() {
|
||||
return array(
|
||||
array(
|
||||
'title' => __( 'Categories', 'woocommerce' ),
|
||||
'callback' => array( $this, 'category_widget' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Category selection
|
||||
* @return void
|
||||
*/
|
||||
public function category_widget() {
|
||||
$categories = get_terms( 'product_cat', array( 'orderby' => 'name' ) );
|
||||
?>
|
||||
<form method="GET">
|
||||
<div>
|
||||
<select multiple="multiple" class="chosen_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-product-cat-dropdown-walker.php' );
|
||||
|
||||
echo woocommerce_walk_category_dropdown_tree( $categories, 0, $r );
|
||||
?>
|
||||
</select>
|
||||
<a href="#" class="select_none"><?php _e( 'None', 'woocommerce' ); ?></a>
|
||||
<a href="#" class="select_all"><?php _e( 'All', 'woocommerce' ); ?></a>
|
||||
<input type="submit" class="submit button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
|
||||
<input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" />
|
||||
<input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" />
|
||||
<input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" />
|
||||
<input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" />
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function(){
|
||||
jQuery("select.chosen_select").chosen();
|
||||
|
||||
// Select all/none
|
||||
jQuery('.select_all').live('click', function() {
|
||||
jQuery(this).closest( 'div' ).find( 'select option' ).attr( "selected", "selected" );
|
||||
jQuery(this).closest( 'div' ).find('select').trigger( 'liszt:updated' );
|
||||
return false;
|
||||
});
|
||||
|
||||
jQuery('.select_none').live('click', function() {
|
||||
jQuery(this).closest( 'div' ).find( 'select option' ).removeAttr( "selected" );
|
||||
jQuery(this).closest( 'div' ).find('select').trigger( 'liszt:updated' );
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
if ( ! $this->show_categories ) {
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<p class="chart-prompt"><?php _e( '← Choose a category to view stats', 'woocommerce' ); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
$include_categories = array();
|
||||
$chart_data = array();
|
||||
$chart_ticks = array();
|
||||
$index = 0;
|
||||
|
||||
foreach ( $this->show_categories as $category ) {
|
||||
$category = get_term( $category, 'product_cat' );
|
||||
$term_ids = get_term_children( $category->term_id, 'product_cat' );
|
||||
$term_ids[] = $category->term_id;
|
||||
$product_ids = array_unique( get_objects_in_term( $term_ids, 'product_cat' ) );
|
||||
$category_total = 0;
|
||||
$category_chart_data = array();
|
||||
|
||||
for ( $i = 0; $i <= $this->chart_interval; $i ++ ) {
|
||||
$interval_total = 0;
|
||||
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day' :
|
||||
$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $this->start_date ) ) ) * 1000;
|
||||
break;
|
||||
case 'month' :
|
||||
$time = strtotime( date( '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_total += $this->item_sales_and_times[ $time ][ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
$category_chart_data[] = array( $time, $interval_total );
|
||||
}
|
||||
|
||||
//if ( ! $category_total )
|
||||
// continue;
|
||||
|
||||
$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>
|
||||
<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;
|
||||
echo '{
|
||||
label: "' . esc_js( $data['category'] ) . '",
|
||||
data: jQuery.parseJSON( "' . json_encode( $series ) . '" ),
|
||||
color: "' . $color . '",
|
||||
bars: { fillColor: "' . $color . '", fill: true, show: true, lineWidth: 1, align: "center", barWidth: ' . $width * 0.75 . ', stack: false },
|
||||
prepend_tooltip: "' . get_woocommerce_currency_symbol() . '",
|
||||
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 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,
|
||||
tickDecimals: 2,
|
||||
color: 'transparent',
|
||||
font: { color: "#aaa" }
|
||||
}
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
jQuery('.chart-placeholder').resize();
|
||||
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').hover(
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
},
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,483 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Sales_By_Date class
|
||||
*/
|
||||
class WC_Report_Sales_By_Date extends WC_Admin_Report {
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
$legend = array();
|
||||
|
||||
$order_totals = $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'
|
||||
),
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders'
|
||||
)
|
||||
),
|
||||
'filter_range' => true
|
||||
) );
|
||||
$total_sales = $order_totals->total_sales;
|
||||
$total_shipping = $order_totals->total_shipping;
|
||||
$total_orders = absint( $order_totals->total_orders );
|
||||
$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_qty'
|
||||
)
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) ) );
|
||||
// Get discount amounts in range
|
||||
$total_coupons = $this->get_order_report_data( array(
|
||||
'data' => array(
|
||||
'discount_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'coupon',
|
||||
'function' => 'SUM',
|
||||
'name' => 'discount_amount'
|
||||
)
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'coupon',
|
||||
'operator' => '='
|
||||
)
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
$this->average_sales = $total_sales / ( $this->chart_interval + 1 );
|
||||
|
||||
switch ( $this->chart_groupby ) {
|
||||
case 'day' :
|
||||
$average_sales_title = sprintf( __( '%s average daily sales', 'woocommerce' ), '<strong>' . woocommerce_price( $this->average_sales ) . '</strong>' );
|
||||
break;
|
||||
case 'month' :
|
||||
$average_sales_title = sprintf( __( '%s average monthly sales', 'woocommerce' ), '<strong>' . woocommerce_price( $this->average_sales ) . '</strong>' );
|
||||
break;
|
||||
}
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s sales in this period', 'woocommerce' ), '<strong>' . woocommerce_price( $total_sales ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['sales_amount'],
|
||||
'highlight_series' => 5
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => $average_sales_title,
|
||||
'color' => $this->chart_colours['average'],
|
||||
'highlight_series' => 2
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s orders placed', 'woocommerce' ), '<strong>' . $total_orders . '</strong>' ),
|
||||
'color' => $this->chart_colours['order_count'],
|
||||
'highlight_series' => 1
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s items purchased', 'woocommerce' ), '<strong>' . $total_items . '</strong>' ),
|
||||
'color' => $this->chart_colours['item_count'],
|
||||
'highlight_series' => 0
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s charged for shipping', 'woocommerce' ), '<strong>' . woocommerce_price( $total_shipping ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['shipping_amount'],
|
||||
'highlight_series' => 4
|
||||
);
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s worth of coupons used', 'woocommerce' ), '<strong>' . woocommerce_price( $total_coupons ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['coupon_amount'],
|
||||
'highlight_series' => 3
|
||||
);
|
||||
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
'sales_amount' => '#3498db',
|
||||
'average' => '#75b9e7',
|
||||
'order_count' => '#b8c0c5',
|
||||
'item_count' => '#d4d9dc',
|
||||
'coupon_amount' => '#e67e22',
|
||||
'shipping_amount' => '#1abc9c'
|
||||
);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
|
||||
data-exclude_series="2"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
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
|
||||
$orders = $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'
|
||||
),
|
||||
'ID' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
'distinct' => true,
|
||||
),
|
||||
'post_date' => array(
|
||||
'type' => 'post_data',
|
||||
'function' => '',
|
||||
'name' => 'post_date'
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
// Order items
|
||||
$order_items = $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_item_type',
|
||||
'value' => 'line_item',
|
||||
'operator' => '='
|
||||
)
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
// Get discount amounts in range
|
||||
$coupons = $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_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
|
||||
) );
|
||||
|
||||
// Prepare data for report
|
||||
$order_counts = $this->prepare_chart_data( $orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$order_item_counts = $this->prepare_chart_data( $order_items, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$order_amounts = $this->prepare_chart_data( $orders, 'post_date', 'total_sales', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$coupon_amounts = $this->prepare_chart_data( $coupons, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
$shipping_amounts = $this->prepare_chart_data( $orders, 'post_date', 'total_shipping', $this->chart_interval, $this->start_date, $this->chart_groupby );
|
||||
|
||||
// Encode in json format
|
||||
$chart_data = json_encode( array(
|
||||
'order_counts' => array_values( $order_counts ),
|
||||
'order_item_counts' => array_values( $order_item_counts ),
|
||||
'order_amounts' => array_values( $order_amounts ),
|
||||
'coupon_amounts' => array_values( $coupon_amounts ),
|
||||
'shipping_amounts' => array_values( $shipping_amounts )
|
||||
) );
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<div class="chart-placeholder main"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var order_data = jQuery.parseJSON( '<?php echo $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( __( 'Number of orders', 'woocommerce' ) ) ?>",
|
||||
data: order_data.order_counts,
|
||||
color: '<?php echo $this->chart_colours['order_count']; ?>',
|
||||
bars: { fillColor: '<?php echo $this->chart_colours['order_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( __( 'Average sales amount', 'woocommerce' ) ) ?>",
|
||||
data: [ [ <?php echo min( array_keys( $order_amounts ) ); ?>, <?php echo $this->average_sales; ?> ], [ <?php echo max( array_keys( $order_amounts ) ); ?>, <?php echo $this->average_sales; ?> ] ],
|
||||
yaxis: 2,
|
||||
color: '<?php echo $this->chart_colours['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 $this->chart_colours['coupon_amount']; ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 4, fill: false },
|
||||
shadowSize: 0,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Shipping amount', 'woocommerce' ) ) ?>",
|
||||
data: order_data.shipping_amounts,
|
||||
yaxis: 2,
|
||||
color: '<?php echo $this->chart_colours['shipping_amount']; ?>',
|
||||
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
|
||||
lines: { show: true, lineWidth: 4, fill: false },
|
||||
shadowSize: 0,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
|
||||
},
|
||||
{
|
||||
label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>",
|
||||
data: order_data.order_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,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
|
||||
}
|
||||
];
|
||||
|
||||
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 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; ?>"],
|
||||
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').resize();
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').hover(
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
},
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,614 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Sales_By_Product class
|
||||
*/
|
||||
class WC_Report_Sales_By_Product extends WC_Admin_Report {
|
||||
|
||||
public $product_ids = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) )
|
||||
$this->product_ids = array_map( 'absint', $_GET['product_ids'] );
|
||||
elseif ( isset( $_GET['product_ids'] ) )
|
||||
$this->product_ids = array( absint( $_GET['product_ids'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the legend for the main chart sidebar
|
||||
* @return array
|
||||
*/
|
||||
public function get_chart_legend() {
|
||||
if ( ! $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'
|
||||
),
|
||||
'_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' => '_product_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
),
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => '_variation_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) );
|
||||
$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'
|
||||
),
|
||||
'_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' => '_product_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
),
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => '_variation_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'group_by' => 'product_id',
|
||||
'query_type' => 'get_var',
|
||||
'filter_range' => true
|
||||
) ) );
|
||||
|
||||
$legend[] = array(
|
||||
'title' => sprintf( __( '%s sales for the selected items', 'woocommerce' ), '<strong>' . woocommerce_price( $total_sales ) . '</strong>' ),
|
||||
'color' => $this->chart_colours['sales_amount'],
|
||||
'highlight_series' => 1
|
||||
);
|
||||
$legend[] = array(
|
||||
'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() {
|
||||
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(
|
||||
'sales_amount' => '#3498db',
|
||||
'item_count' => '#d4d9dc',
|
||||
);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* [get_chart_widgets description]
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show current filters
|
||||
* @return void
|
||||
*/
|
||||
public function current_filters() {
|
||||
$this->product_ids_titles = array();
|
||||
|
||||
foreach ( $this->product_ids as $product_id ) {
|
||||
$product = get_product( $product_id );
|
||||
$this->product_ids_titles[] = $product->get_formatted_name();
|
||||
}
|
||||
|
||||
echo '<p>' . ' <strong>' . implode( ', ', $this->product_ids_titles ) . '</strong></p>';
|
||||
echo '<p><a class="button" href="' . remove_query_arg( 'product_ids' ) . '">' . __( 'Reset', 'woocommerce' ) . '</a></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Product selection
|
||||
* @return void
|
||||
*/
|
||||
public function products_widget() {
|
||||
?>
|
||||
<h4 class="section_title"><span><?php _e( 'Product Search', 'woocommerce' ); ?></span></h4>
|
||||
<div class="section">
|
||||
<form method="GET">
|
||||
<div>
|
||||
<select id="product_ids" name="product_ids[]" class="ajax_chosen_select_products" multiple="multiple" data-placeholder="<?php _e( 'Search for a product…', 'woocommerce' ); ?>" style="width:203px;"></select>
|
||||
<input type="submit" class="submit button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
|
||||
<input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" />
|
||||
<input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" />
|
||||
<input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" />
|
||||
<input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" />
|
||||
<input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" />
|
||||
<input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" />
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function(){
|
||||
// Ajax Chosen Product Selectors
|
||||
jQuery("select.ajax_chosen_select_products").ajaxChosen({
|
||||
method: 'GET',
|
||||
url: '<?php echo admin_url('admin-ajax.php'); ?>',
|
||||
dataType: 'json',
|
||||
afterTypeDelay: 100,
|
||||
data: {
|
||||
action: 'woocommerce_json_search_products_and_variations',
|
||||
security: '<?php echo wp_create_nonce("search-products"); ?>'
|
||||
}
|
||||
}, function (data) {
|
||||
var terms = {};
|
||||
|
||||
jQuery.each(data, function (i, val) {
|
||||
terms[i] = val;
|
||||
});
|
||||
return terms;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php _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
|
||||
) );
|
||||
|
||||
if ( $top_sellers ) {
|
||||
foreach ( $top_sellers as $product ) {
|
||||
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
|
||||
<td class="count">' . $product->order_item_qty . '</td>
|
||||
<td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
|
||||
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 14, 'count' ) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . __( 'No products found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<h4 class="section_title"><span><?php _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
|
||||
) );
|
||||
|
||||
if ( $top_earners ) {
|
||||
foreach ( $top_earners as $product ) {
|
||||
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
|
||||
<td class="count">' . woocommerce_price( $product->order_item_total ) . '</td>
|
||||
<td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
|
||||
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 14, 'sales' ) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="3">' . __( 'No products found in range', 'woocommerce' ) . '</td></tr>';
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery('.section_title').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)').click();
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
|
||||
class="export_csv"
|
||||
data-export="chart"
|
||||
data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
|
||||
data-groupby="<?php echo $this->chart_groupby; ?>"
|
||||
>
|
||||
<?php _e( 'Export CSV', 'woocommerce' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wp_locale;
|
||||
|
||||
if ( ! $this->product_ids ) {
|
||||
?>
|
||||
<div class="chart-container">
|
||||
<p class="chart-prompt"><?php _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' => '_product_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
),
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => '_variation_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'group_by' => 'product_id,' . $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
$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' => '_product_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
),
|
||||
array(
|
||||
'type' => 'order_item_meta',
|
||||
'meta_key' => '_variation_id',
|
||||
'meta_value' => $this->product_ids,
|
||||
'operator' => 'IN'
|
||||
)
|
||||
),
|
||||
'group_by' => 'product_id,' . $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
|
||||
// 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 = 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>
|
||||
<script type="text/javascript">
|
||||
var main_chart;
|
||||
|
||||
jQuery(function(){
|
||||
var order_data = jQuery.parseJSON( '<?php echo $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,
|
||||
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
|
||||
}
|
||||
];
|
||||
|
||||
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 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; ?>"],
|
||||
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').resize();
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
|
||||
jQuery('.highlight_series').hover(
|
||||
function() {
|
||||
drawGraph( jQuery(this).data('series') );
|
||||
},
|
||||
function() {
|
||||
drawGraph();
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<?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 class
|
||||
*/
|
||||
class WC_Report_Stock extends WP_List_Table {
|
||||
|
||||
/**
|
||||
* __construct function.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(){
|
||||
parent::__construct( array(
|
||||
'singular' => __( 'Stock', 'woocommerce' ),
|
||||
'plural' => __( 'Stock', 'woocommerce' ),
|
||||
'ajax' => false
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* No items found text
|
||||
*/
|
||||
public function no_items() {
|
||||
_e( 'No products found.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't need this
|
||||
*/
|
||||
public function display_tablenav( $position ) {
|
||||
if ( $position != 'top' )
|
||||
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>';
|
||||
}
|
||||
|
||||
/**
|
||||
* column_default function.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $item
|
||||
* @param mixed $column_name
|
||||
*/
|
||||
function column_default( $item, $column_name ) {
|
||||
global $woocommerce, $wpdb, $product;
|
||||
|
||||
if ( ! $product || $product->id !== $item->id )
|
||||
$product = get_product( $item->id );
|
||||
|
||||
switch( $column_name ) {
|
||||
case 'product' :
|
||||
if ( $sku = $product->get_sku() )
|
||||
echo $sku . ' - ';
|
||||
|
||||
echo $product->get_title();
|
||||
|
||||
// Get variation data
|
||||
if ( $product->is_type( 'variation' ) ) {
|
||||
$list_attributes = array();
|
||||
$attributes = $product->get_variation_attributes();
|
||||
|
||||
foreach ( $attributes as $name => $attribute ) {
|
||||
$list_attributes[] = $woocommerce->get_helper( 'attribute' )->attribute_label( str_replace( 'attribute_', '', $name ) ) . ': <strong>' . $attribute . '</strong>';
|
||||
}
|
||||
|
||||
echo '<div class="description">' . implode( ', ', $list_attributes ) . '</div>';
|
||||
}
|
||||
break;
|
||||
case 'parent' :
|
||||
if ( $item->parent )
|
||||
echo get_the_title( $item->parent );
|
||||
else
|
||||
echo '-';
|
||||
break;
|
||||
case 'stock_status' :
|
||||
if ( $product->is_in_stock() ) {
|
||||
echo '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>';
|
||||
} else {
|
||||
echo '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>';
|
||||
}
|
||||
break;
|
||||
case 'stock_level' :
|
||||
echo $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 ) {
|
||||
$image = ( isset( $action['image_url'] ) ) ? $action['image_url'] : $woocommerce->plugin_url() . '/assets/images/icons/' . $action['action'] . '.png';
|
||||
printf( '<a class="button tips" href="%s" data-tip="%s"><img src="%s" alt="%s" width="14" /></a>', esc_url( $action['url'] ), esc_attr( $action['name'] ), esc_attr( $image ), esc_attr( $action['name'] ) );
|
||||
}
|
||||
?>
|
||||
</p><?php
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_columns function.
|
||||
*
|
||||
* @access 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_items function.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
|
||||
$current_page = absint( $this->get_pagenum() );
|
||||
$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 )
|
||||
) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Taxes_By_Code class
|
||||
*/
|
||||
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() {
|
||||
$legend = array();
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : 'last_month';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $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() {
|
||||
global $woocommerce, $wpdb, $wp_locale;
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last Month', 'woocommerce' ),
|
||||
'month' => __( 'This Month', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : 'last_month';
|
||||
|
||||
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;
|
||||
default :
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
$hide_sidebar = true;
|
||||
|
||||
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wpdb;
|
||||
|
||||
$tax_rows = $this->get_order_report_data( array(
|
||||
'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' => 'SUM',
|
||||
'name' => 'tax_amount'
|
||||
),
|
||||
'shipping_tax_amount' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'tax',
|
||||
'function' => 'SUM',
|
||||
'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' => 'COUNT',
|
||||
'name' => 'total_orders',
|
||||
'distinct' => true,
|
||||
),
|
||||
),
|
||||
'where' => array(
|
||||
array(
|
||||
'key' => 'order_item_type',
|
||||
'value' => 'tax',
|
||||
'operator' => '='
|
||||
),
|
||||
array(
|
||||
'key' => 'order_item_name',
|
||||
'value' => '',
|
||||
'operator' => '!='
|
||||
)
|
||||
),
|
||||
'group_by' => 'tax_rate',
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
?>
|
||||
<table class="widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e( 'Tax', 'woocommerce' ); ?></th>
|
||||
<th><?php _e( 'Rate', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Number of orders', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php _e( 'Tax Amount', 'woocommerce' ); ?> <a class="tips" data-tip="<?php esc_attr_e( 'This is the sum of the "Tax Rows" tax amount within your orders.', 'woocommerce' ); ?>" href="#">[?]</a></th>
|
||||
<th class="total_row"><?php _e( 'Shipping Tax Amount', 'woocommerce' ); ?> <a class="tips" data-tip="<?php esc_attr_e( 'This is the sum of the "Tax Rows" shipping tax amount within your orders.', 'woocommerce' ); ?>" href="#">[?]</a></th>
|
||||
<th class="total_row"><?php _e( 'Total Tax', 'woocommerce' ); ?> <a class="tips" data-tip="<?php esc_attr_e( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ); ?>" href="#">[?]</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<?php if ( $tax_rows ) : ?>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row" colspan="3"><?php _e( 'Total', 'woocommerce' ); ?></th>
|
||||
<th class="total_row"><?php echo woocommerce_price( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) ); ?></th>
|
||||
<th class="total_row"><?php echo woocommerce_price( array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ); ?></th>
|
||||
<th class="total_row"><strong><?php echo woocommerce_price( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) + array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ); ?></strong></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $tax_rows as $tax_row ) {
|
||||
$rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_row->rate_id ) );
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row"><?php echo $tax_row->tax_rate; ?></th>
|
||||
<td><?php echo $rate; ?>%</td>
|
||||
<td class="total_row"><?php echo $tax_row->total_orders; ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $tax_row->tax_amount ); ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $tax_row->shipping_tax_amount ); ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $tax_row->tax_amount + $tax_row->shipping_tax_amount ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<?php else : ?>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?php _e( 'No taxes found in this period', 'woocommerce' ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
<?php
|
||||
/**
|
||||
* WC_Report_Taxes_By_Date class
|
||||
*/
|
||||
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() {
|
||||
$legend = array();
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an export link
|
||||
*/
|
||||
public function get_export_button() {
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : 'last_month';
|
||||
?>
|
||||
<a
|
||||
href="#"
|
||||
download="report-<?php echo $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() {
|
||||
global $woocommerce, $wpdb, $wp_locale;
|
||||
|
||||
$ranges = array(
|
||||
'year' => __( 'Year', 'woocommerce' ),
|
||||
'last_month' => __( 'Last Month', 'woocommerce' ),
|
||||
'month' => __( 'This Month', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : 'last_month';
|
||||
|
||||
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;
|
||||
default :
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
$hide_sidebar = true;
|
||||
|
||||
include( WC()->plugin_path() . '/admin/views/html-report-by-date.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main chart
|
||||
* @return string
|
||||
*/
|
||||
public function get_main_chart() {
|
||||
global $wpdb;
|
||||
|
||||
$tax_rows = $this->get_order_report_data( array(
|
||||
'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'
|
||||
),
|
||||
),
|
||||
'group_by' => $this->group_by_query,
|
||||
'order_by' => 'post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true
|
||||
) );
|
||||
?>
|
||||
<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' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Order Total' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
|
||||
<th class="total_row"><?php _e( 'Total Shipping', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Shipping Total' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
|
||||
<th class="total_row"><?php _e( 'Total Tax', 'woocommerce' ); ?> <a class="tips" data-tip="<?php esc_attr_e( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ); ?>" href="#">[?]</a></th>
|
||||
<th class="total_row"><?php _e( 'Net profit', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("Total sales minus shipping and tax.", 'woocommerce'); ?>" href="#">[?]</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<?php if ( $tax_rows ) :
|
||||
$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' ) );
|
||||
?>
|
||||
<tfoot>
|
||||
<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 woocommerce_price( $gross ); ?></th>
|
||||
<th class="total_row"><?php echo woocommerce_price( array_sum( wp_list_pluck( (array) $tax_rows, 'total_shipping' ) ) ); ?></th>
|
||||
<th class="total_row"><?php echo woocommerce_price( $total_tax ); ?></th>
|
||||
<th class="total_row"><?php echo woocommerce_price( $gross - $total_tax ); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $tax_rows as $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
|
||||
if ( $this->chart_groupby == 'month' )
|
||||
echo date_i18n( 'F', strtotime( $tax_row->post_date ) );
|
||||
else
|
||||
echo date_i18n( get_option( 'date_format' ), strtotime( $tax_row->post_date ) );
|
||||
?></th>
|
||||
<td class="total_row"><?php echo $tax_row->total_orders; ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $gross ); ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $tax_row->total_shipping ); ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $total_tax ); ?></td>
|
||||
<td class="total_row"><?php echo woocommerce_price( $gross - $total_tax ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<?php else : ?>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?php _e( 'No taxes found in this period', 'woocommerce' ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<div class="wrap woocommerce">
|
||||
<div class="icon32 icon32-woocommerce-reports" id="icon-woocommerce"><br /></div><h2 class="nav-tab-wrapper woo-nav-tab-wrapper">
|
||||
<?php
|
||||
foreach ( $reports as $key => $report_group ) {
|
||||
echo '<a href="'.admin_url( 'admin.php?page=wc_reports&tab=' . urlencode( $key ) ).'" class="nav-tab ';
|
||||
if ( $current_tab == $key ) echo 'nav-tab-active';
|
||||
echo '">' . esc_html( $report_group[ 'title' ] ) . '</a>';
|
||||
}
|
||||
?>
|
||||
<?php do_action('wc_reports_tabs'); ?>
|
||||
</h2>
|
||||
|
||||
<?php if ( sizeof( $reports[ $current_tab ]['reports'] ) > 1 ) {
|
||||
?>
|
||||
<ul class="subsubsub">
|
||||
<li><?php
|
||||
|
||||
$links = array();
|
||||
|
||||
foreach ( $reports[ $current_tab ]['reports'] as $key => $report ) {
|
||||
|
||||
$link = '<a href="admin.php?page=wc_reports&tab=' . urlencode( $current_tab ) . '&report=' . urlencode( $key ) . '" class="';
|
||||
|
||||
if ( $key == $current_report ) $link .= 'current';
|
||||
|
||||
$link .= '">' . $report['title'] . '</a>';
|
||||
|
||||
$links[] = $link;
|
||||
|
||||
}
|
||||
|
||||
echo implode(' | </li><li>', $links);
|
||||
|
||||
?></li>
|
||||
</ul>
|
||||
<br class="clear" />
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( isset( $reports[ $current_tab ][ 'reports' ][ $current_report ] ) ) {
|
||||
|
||||
$report = $reports[ $current_tab ][ 'reports' ][ $current_report ];
|
||||
|
||||
if ( ! isset( $report['hide_title'] ) || $report['hide_title'] != true )
|
||||
echo '<h3>' . $report['title'] . '</h3>';
|
||||
|
||||
if ( $report['description'] )
|
||||
echo '<p>' . $report['description'] . '</p>';
|
||||
|
||||
if ( $report['callback'] && ( is_callable( $report['callback'] ) ) )
|
||||
call_user_func( $report['callback'], $current_report );
|
||||
}
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* HTML for a report with date filters in admin.
|
||||
*/
|
||||
?>
|
||||
<div id="poststuff" class="woocommerce-reports-wide">
|
||||
<div class="postbox">
|
||||
<h3 class="stats_range">
|
||||
<?php $this->get_export_button(); ?>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ( $ranges as $range => $name )
|
||||
echo '<li class="' . ( $current_range == $range ? 'active' : '' ) . '"><a href="' . remove_query_arg( array( 'start_date', 'end_date' ), add_query_arg( 'range', $range ) ) . '">' . $name . '</a></li>';
|
||||
?>
|
||||
<li class="custom <?php echo $current_range == 'custom' ? 'active' : ''; ?>">
|
||||
<?php _e( 'Custom:', 'woocommerce' ); ?>
|
||||
<form method="GET">
|
||||
<div>
|
||||
<?php
|
||||
// Maintain query string
|
||||
foreach ( $_GET as $key => $value )
|
||||
if ( is_array( $value ) )
|
||||
foreach ( $value as $v )
|
||||
echo '<input type="hidden" name="' . esc_attr( sanitize_text_field( $key ) ) . '[]" value="' . esc_attr( sanitize_text_field( $v ) ) . '" />';
|
||||
else
|
||||
echo '<input type="hidden" name="' . esc_attr( sanitize_text_field( $key ) ) . '" value="' . esc_attr( sanitize_text_field( $value ) ) . '" />';
|
||||
?>
|
||||
<input type="hidden" name="range" value="custom" />
|
||||
<input type="text" size="9" placeholder="yyyy-mm-dd" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ); ?>" name="start_date" class="range_datepicker from" />
|
||||
<input type="text" size="9" placeholder="yyyy-mm-dd" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ); ?>" name="end_date" class="range_datepicker to" />
|
||||
<input type="submit" class="button" value="<?php _e( 'Go', 'woocommerce' ); ?>" />
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</h3>
|
||||
<?php if ( empty( $hide_sidebar ) ) : ?>
|
||||
<div class="inside chart-with-sidebar">
|
||||
<div class="chart-sidebar">
|
||||
<?php if ( $legends = $this->get_chart_legend() ) : ?>
|
||||
<ul class="chart-legend">
|
||||
<?php foreach ( $legends as $legend ) : ?>
|
||||
<li style="border-color: <?php echo $legend['color']; ?>" <?php if ( isset( $legend['highlight_series'] ) ) echo 'class="highlight_series" data-series="' . $legend['highlight_series'] . '"'; ?>>
|
||||
<?php echo $legend['title']; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<ul class="chart-widgets">
|
||||
<?php foreach ( $this->get_chart_widgets() as $widget ) : ?>
|
||||
<li class="chart-widget">
|
||||
<?php if ( $widget['title'] ) : ?><h4><?php echo $widget['title']; ?></h4><?php endif; ?>
|
||||
<?php call_user_func( $widget['callback'] ); ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="main">
|
||||
<?php $this->get_main_chart(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="inside">
|
||||
<?php $this->get_main_chart(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
|
@ -46,7 +46,7 @@ function woocommerce_init_dashboard_widgets() {
|
|||
}
|
||||
|
||||
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'publish_shop_orders' ) ) {
|
||||
wp_add_dashboard_widget( 'woocommerce_dashboard_sales', $sales_heading, 'woocommerce_dashboard_sales' );
|
||||
//wp_add_dashboard_widget( 'woocommerce_dashboard_sales', $sales_heading, 'woocommerce_dashboard_sales' );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -286,176 +286,4 @@ function woocommerce_dashboard_recent_reviews() {
|
|||
} else {
|
||||
echo '<p>' . __( 'There are no product reviews yet.', 'woocommerce' ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Orders this month filter function - filters orders for the month
|
||||
*
|
||||
* @access public
|
||||
* @param string $where (default: '')
|
||||
* @return void
|
||||
*/
|
||||
function orders_this_month( $where = '' ) {
|
||||
global $the_month_num, $the_year;
|
||||
|
||||
$month = $the_month_num;
|
||||
$year = (int) $the_year;
|
||||
|
||||
$first_day = strtotime("{$year}-{$month}-01");
|
||||
//$last_day = strtotime('-1 second', strtotime('+1 month', $first_day));
|
||||
$last_day = strtotime('+1 month', $first_day);
|
||||
|
||||
$after = date('Y-m-d', $first_day);
|
||||
$before = date('Y-m-d', $last_day);
|
||||
|
||||
$where .= " AND post_date > '$after'";
|
||||
$where .= " AND post_date < '$before'";
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sales widget
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function woocommerce_dashboard_sales() {
|
||||
|
||||
add_action( 'admin_footer', 'woocommerce_dashboard_sales_js' );
|
||||
|
||||
?><div id="placeholder" style="width:100%; height:300px; position:relative;"></div><?php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sales widget javascript
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function woocommerce_dashboard_sales_js() {
|
||||
|
||||
global $woocommerce, $wp_locale;
|
||||
|
||||
$screen = get_current_screen();
|
||||
|
||||
if (!$screen || $screen->id!=='dashboard') return;
|
||||
|
||||
global $current_month_offset, $the_month_num, $the_year;
|
||||
|
||||
// Get orders to display in widget
|
||||
add_filter( 'posts_where', 'orders_this_month' );
|
||||
|
||||
$args = array(
|
||||
'numberposts' => -1,
|
||||
'orderby' => 'post_date',
|
||||
'order' => 'DESC',
|
||||
'post_type' => 'shop_order',
|
||||
'post_status' => 'publish' ,
|
||||
'suppress_filters' => false,
|
||||
'tax_query' => array(
|
||||
array(
|
||||
'taxonomy' => 'shop_order_status',
|
||||
'terms' => apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ),
|
||||
'field' => 'slug',
|
||||
'operator' => 'IN'
|
||||
)
|
||||
)
|
||||
);
|
||||
$orders = get_posts( $args );
|
||||
|
||||
$order_counts = array();
|
||||
$order_amounts = array();
|
||||
|
||||
// Blank date ranges to begin
|
||||
$month = $the_month_num;
|
||||
$year = (int) $the_year;
|
||||
|
||||
$first_day = strtotime("{$year}-{$month}-01");
|
||||
$last_day = strtotime('-1 second', strtotime('+1 month', $first_day));
|
||||
|
||||
if ((date('m') - $the_month_num)==0) :
|
||||
$up_to = date('d', strtotime('NOW'));
|
||||
else :
|
||||
$up_to = date('d', $last_day);
|
||||
endif;
|
||||
$count = 0;
|
||||
|
||||
while ($count < $up_to) :
|
||||
|
||||
$time = strtotime(date('Ymd', strtotime('+ '.$count.' DAY', $first_day))).'000';
|
||||
|
||||
$order_counts[$time] = 0;
|
||||
$order_amounts[$time] = 0;
|
||||
|
||||
$count++;
|
||||
endwhile;
|
||||
|
||||
if ($orders) :
|
||||
foreach ($orders as $order) :
|
||||
|
||||
$order_data = new WC_Order($order->ID);
|
||||
|
||||
if ($order_data->status=='cancelled' || $order_data->status=='refunded') continue;
|
||||
|
||||
$time = strtotime(date('Ymd', strtotime($order->post_date))).'000';
|
||||
|
||||
if (isset($order_counts[$time])) :
|
||||
$order_counts[$time]++;
|
||||
else :
|
||||
$order_counts[$time] = 1;
|
||||
endif;
|
||||
|
||||
if (isset($order_amounts[$time])) :
|
||||
$order_amounts[$time] = $order_amounts[$time] + $order_data->order_total;
|
||||
else :
|
||||
$order_amounts[$time] = (float) $order_data->order_total;
|
||||
endif;
|
||||
|
||||
endforeach;
|
||||
endif;
|
||||
|
||||
remove_filter( 'posts_where', 'orders_this_month' );
|
||||
|
||||
/* Script variables */
|
||||
$params = array(
|
||||
'currency_format_num_decimals' => absint( get_option( 'woocommerce_price_num_decimals' ) ),
|
||||
'currency_format_symbol' => get_woocommerce_currency_symbol(),
|
||||
'currency_format_decimal_sep' => esc_attr( stripslashes( get_option( 'woocommerce_price_decimal_sep' ) ) ),
|
||||
'currency_format_thousand_sep' => esc_attr( stripslashes( get_option( 'woocommerce_price_thousand_sep' ) ) ),
|
||||
'currency_format' => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ),
|
||||
'number_of_sales' => absint( array_sum( $order_counts ) ),
|
||||
'sales_amount' => woocommerce_price( array_sum( $order_amounts ) ),
|
||||
'i18n_sold' => __( 'Sold', 'woocommerce' ),
|
||||
'i18n_earned' => __( 'Earned', 'woocommerce' ),
|
||||
'i18n_month_names' => array_values( $wp_locale->month_abbrev ),
|
||||
);
|
||||
|
||||
$order_counts_array = array();
|
||||
foreach ($order_counts as $key => $count) :
|
||||
$order_counts_array[] = array($key, $count);
|
||||
endforeach;
|
||||
|
||||
$order_amounts_array = array();
|
||||
foreach ($order_amounts as $key => $amount) :
|
||||
$order_amounts_array[] = array($key, $amount);
|
||||
endforeach;
|
||||
|
||||
$order_data = array( 'order_counts' => $order_counts_array, 'order_amounts' => $order_amounts_array );
|
||||
|
||||
$params['order_data'] = json_encode($order_data);
|
||||
|
||||
// Queue scripts
|
||||
$suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
|
||||
|
||||
wp_register_script( 'woocommerce_dashboard_sales', $woocommerce->plugin_url() . '/assets/js/admin/dashboard_sales' . $suffix . '.js', array( 'jquery', 'flot', 'flot-resize', 'accounting' ), $woocommerce->version );
|
||||
wp_register_script( 'flot', $woocommerce->plugin_url() . '/assets/js/admin/jquery.flot'.$suffix.'.js', 'jquery', '1.0' );
|
||||
wp_register_script( 'flot-resize', $woocommerce->plugin_url() . '/assets/js/admin/jquery.flot.resize'.$suffix.'.js', 'jquery', '1.0' );
|
||||
|
||||
wp_localize_script( 'woocommerce_dashboard_sales', 'params', $params );
|
||||
|
||||
wp_print_scripts( 'woocommerce_dashboard_sales' );
|
||||
}
|
|
@ -12,6 +12,12 @@
|
|||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
||||
|
||||
|
||||
include_once( 'class-wc-admin-reports.php' );
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Functions for the product post type
|
||||
*/
|
||||
|
@ -57,26 +63,23 @@ function woocommerce_admin_menu() {
|
|||
global $menu, $woocommerce;
|
||||
|
||||
if ( current_user_can( 'manage_woocommerce' ) )
|
||||
$menu[] = array( '', 'read', 'separator-woocommerce', '', 'wp-menu-separator woocommerce' );
|
||||
$menu[] = array( '', 'read', 'separator-woocommerce', '', 'wp-menu-separator woocommerce' );
|
||||
|
||||
$main_page = add_menu_page( __( 'WooCommerce', 'woocommerce' ), __( 'WooCommerce', 'woocommerce' ), 'manage_woocommerce', 'woocommerce' , 'woocommerce_settings_page', null, '55.5' );
|
||||
|
||||
$reports_page = add_submenu_page( 'woocommerce', __( 'Reports', 'woocommerce' ), __( 'Reports', 'woocommerce' ) , 'view_woocommerce_reports', 'woocommerce_reports', 'woocommerce_reports_page' );
|
||||
|
||||
add_submenu_page( 'edit.php?post_type=product', __( 'Attributes', 'woocommerce' ), __( 'Attributes', 'woocommerce' ), 'manage_product_terms', 'woocommerce_attributes', 'woocommerce_attributes_page');
|
||||
|
||||
add_action( 'load-' . $main_page, 'woocommerce_admin_help_tab' );
|
||||
add_action( 'load-' . $reports_page, 'woocommerce_admin_help_tab' );
|
||||
|
||||
$wc_screen_id = strtolower( __( 'WooCommerce', 'woocommerce' ) );
|
||||
|
||||
$print_css_on = apply_filters( 'woocommerce_screen_ids', array( 'toplevel_page_' . $wc_screen_id, $wc_screen_id . '_page_woocommerce_settings', $wc_screen_id . '_page_woocommerce_reports', $wc_screen_id . '_page_woocommerce_status', $wc_screen_id . '_page_woocommerce_customers', 'toplevel_page_woocommerce', 'woocommerce_page_woocommerce_settings', 'woocommerce_page_woocommerce_reports', 'woocommerce_page_woocommerce_customers', 'woocommerce_page_woocommerce_status', 'product_page_woocommerce_attributes', 'edit-tags.php', 'edit.php', 'index.php', 'post-new.php', 'post.php' ) );
|
||||
$print_css_on = apply_filters( 'woocommerce_screen_ids', array( 'toplevel_page_' . $wc_screen_id, $wc_screen_id . '_page_woocommerce_settings', $wc_screen_id . '_page_woocommerce_status', $wc_screen_id . '_page_woocommerce_customers', 'toplevel_page_woocommerce', 'woocommerce_page_woocommerce_settings', 'woocommerce_page_woocommerce_customers', 'woocommerce_page_woocommerce_status', 'product_page_woocommerce_attributes', 'edit-tags.php', 'edit.php', 'index.php', 'post-new.php', 'post.php' ) );
|
||||
|
||||
foreach ( $print_css_on as $page )
|
||||
add_action( 'admin_print_styles-'. $page, 'woocommerce_admin_css' );
|
||||
}
|
||||
|
||||
add_action('admin_menu', 'woocommerce_admin_menu', 9);
|
||||
add_action( 'admin_menu', 'woocommerce_admin_menu', 9 );
|
||||
|
||||
/**
|
||||
* Setup the Admin menu in WordPress - later priority so they appear last
|
||||
|
@ -85,7 +88,6 @@ add_action('admin_menu', 'woocommerce_admin_menu', 9);
|
|||
* @return void
|
||||
*/
|
||||
function woocommerce_admin_menu_after() {
|
||||
$customers_page = add_submenu_page( 'woocommerce', __( 'Customers', 'woocommerce' ), __( 'Customers', 'woocommerce' ) , 'manage_woocommerce', 'woocommerce_customers', 'woocommerce_customers_page' );
|
||||
$settings_page = add_submenu_page( 'woocommerce', __( 'WooCommerce Settings', 'woocommerce' ), __( 'Settings', 'woocommerce' ) , 'manage_woocommerce', 'woocommerce_settings', 'woocommerce_settings_page');
|
||||
$status_page = add_submenu_page( 'woocommerce', __( 'WooCommerce Status', 'woocommerce' ), __( 'System Status', 'woocommerce' ) , 'manage_woocommerce', 'woocommerce_status', 'woocommerce_status_page');
|
||||
|
||||
|
@ -132,9 +134,9 @@ function woocommerce_admin_menu_highlight() {
|
|||
}
|
||||
}
|
||||
|
||||
if ( isset( $submenu['woocommerce'] ) && isset( $submenu['woocommerce'][2] ) ) {
|
||||
$submenu['woocommerce'][0] = $submenu['woocommerce'][2];
|
||||
unset( $submenu['woocommerce'][2] );
|
||||
if ( isset( $submenu['woocommerce'] ) && isset( $submenu['woocommerce'][1] ) ) {
|
||||
$submenu['woocommerce'][0] = $submenu['woocommerce'][1];
|
||||
unset( $submenu['woocommerce'][1] );
|
||||
}
|
||||
|
||||
// Sort out Orders menu when on the top level
|
||||
|
@ -328,49 +330,6 @@ function woocommerce_settings_page() {
|
|||
woocommerce_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Include and display the customers page.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function woocommerce_customers_page() {
|
||||
include_once( 'woocommerce-admin-customers.php' );
|
||||
|
||||
$WC_Admin_Customers = new WC_Admin_Customers();
|
||||
$WC_Admin_Customers->prepare_items();
|
||||
?>
|
||||
<div class="wrap">
|
||||
<div id="icon-woocommerce" class="icon32 icon32-woocommerce-users"><br/></div>
|
||||
<h2><?php _e( 'Customers', 'wc_software' ); ?></h2>
|
||||
|
||||
<?php
|
||||
if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) {
|
||||
$linked = woocommerce_update_new_customer_past_orders( absint( $_GET['link_orders'] ) );
|
||||
|
||||
echo '<div class="updated"><p>' . sprintf( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) . '</p></div>';
|
||||
}
|
||||
?>
|
||||
|
||||
<form method="post" id="woocommerce_customers">
|
||||
<?php $WC_Admin_Customers->search_box( __( 'Search customers', 'woocommerce' ), 'customer_search' ); ?>
|
||||
<?php $WC_Admin_Customers->display() ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Include and display the reports page.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function woocommerce_reports_page() {
|
||||
include_once( 'woocommerce-admin-reports.php' );
|
||||
woocommerce_reports();
|
||||
}
|
||||
|
||||
/**
|
||||
* Include and display the attibutes page.
|
||||
*
|
||||
|
@ -439,7 +398,7 @@ function woocommerce_admin_scripts() {
|
|||
$wc_screen_id = strtolower( __( 'WooCommerce', 'woocommerce' ) );
|
||||
|
||||
// WooCommerce admin pages
|
||||
if ( in_array( $screen->id, apply_filters( 'woocommerce_screen_ids', array( 'toplevel_page_' . $wc_screen_id, $wc_screen_id . '_page_woocommerce_settings', $wc_screen_id . '_page_woocommerce_reports', $wc_screen_id . '_page_woocommerce_status', $wc_screen_id . '_page_woocommerce_customers', 'toplevel_page_woocommerce', 'woocommerce_page_woocommerce_settings', 'woocommerce_page_woocommerce_reports', 'woocommerce_page_woocommerce_status', 'woocommerce_page_woocommerce_customers', 'edit-shop_order', 'edit-shop_coupon', 'shop_coupon', 'shop_order', 'edit-product', 'product' ) ) ) ) {
|
||||
if ( in_array( $screen->id, apply_filters( 'woocommerce_screen_ids', array( 'toplevel_page_' . $wc_screen_id, $wc_screen_id . '_page_woocommerce_settings', $wc_screen_id . '_page_woocommerce_status', $wc_screen_id . '_page_woocommerce_customers', 'toplevel_page_woocommerce', 'woocommerce_page_woocommerce_settings', 'woocommerce_page_woocommerce_status', 'woocommerce_page_woocommerce_customers', 'edit-shop_order', 'edit-shop_coupon', 'shop_coupon', 'shop_order', 'edit-product', 'product' ) ) ) ) {
|
||||
|
||||
wp_enqueue_script( 'woocommerce_admin' );
|
||||
wp_enqueue_script( 'farbtastic' );
|
||||
|
@ -531,15 +490,6 @@ function woocommerce_admin_scripts() {
|
|||
wp_enqueue_script( 'woocommerce_product_ordering', $woocommerce->plugin_url() . '/assets/js/admin/product-ordering.js', array('jquery-ui-sortable'), '1.0', true );
|
||||
|
||||
}
|
||||
|
||||
// Reports pages
|
||||
if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_woocommerce_reports', apply_filters( 'woocommerce_reports_screen_id', 'woocommerce_page_woocommerce_reports' ) ) ) ) ) {
|
||||
|
||||
wp_enqueue_script( 'jquery-ui-datepicker' );
|
||||
wp_enqueue_script( 'flot', $woocommerce->plugin_url() . '/assets/js/admin/jquery.flot' . $suffix . '.js', 'jquery', '1.0' );
|
||||
wp_enqueue_script( 'flot-resize', $woocommerce->plugin_url() . '/assets/js/admin/jquery.flot.resize' . $suffix . '.js', array('jquery', 'flot'), '1.0' );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', 'woocommerce_admin_scripts' );
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1078,7 +1078,7 @@ ul.wc_coupon_list_block {
|
|||
.column-last_order {
|
||||
width: 11%;
|
||||
}
|
||||
.column-order_actions, .column-user_actions {
|
||||
.column-order_actions, .column-user_actions, .column-wc_actions {
|
||||
width:110px;
|
||||
a.button {
|
||||
float:left;
|
||||
|
@ -1462,13 +1462,6 @@ ul.woocommerce_stats {
|
|||
top: -5px;
|
||||
}
|
||||
|
||||
#woocommerce_dashboard_sales {
|
||||
h3 a {
|
||||
float: right;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
ul.recent-orders, ul.stock_list {
|
||||
li {
|
||||
overflow: hidden;
|
||||
|
@ -2596,11 +2589,11 @@ img.tips {
|
|||
#tiptip_holder.tip_left {
|
||||
padding-right: 5px;
|
||||
}
|
||||
#tiptip_content {
|
||||
#tiptip_content, .chart-tooltip {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
padding: 4px 8px;
|
||||
background:#a2678c;
|
||||
padding: .75em 1em;
|
||||
background:#464646;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
|
@ -2609,7 +2602,7 @@ img.tips {
|
|||
-moz-box-shadow: 1px 1px 3px rgba(0,0,0,0.10);
|
||||
text-align: center;
|
||||
code {
|
||||
background: #855c76;
|
||||
background: #888;
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
|
@ -2624,25 +2617,25 @@ img.tips {
|
|||
#tiptip_holder.tip_top #tiptip_arrow_inner {
|
||||
margin-top: -7px;
|
||||
margin-left: -6px;
|
||||
border-top-color: #a2678c;
|
||||
border-top-color: #464646;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_bottom #tiptip_arrow_inner {
|
||||
margin-top: -5px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #a2678c;
|
||||
border-bottom-color: #464646;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_right #tiptip_arrow_inner {
|
||||
margin-top: -6px;
|
||||
margin-left: -5px;
|
||||
border-right-color: #a2678c;
|
||||
border-right-color: #464646;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_left #tiptip_arrow_inner {
|
||||
margin-top: -6px;
|
||||
margin-left: -7px;
|
||||
border-left-color: #a2678c;
|
||||
border-left-color: #464646;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2652,25 +2645,295 @@ img.ui-datepicker-trigger { vertical-align: middle; margin-top: -1px; cursor: po
|
|||
#ui-datepicker-div { display: none }
|
||||
|
||||
/* Reports */
|
||||
.woocommerce-reports-wrap {
|
||||
margin-left: 300px;
|
||||
padding-top: 18px;
|
||||
.woocommerce-reports-wrap, .woocommerce-reports-wide {
|
||||
&.woocommerce-reports-wrap {
|
||||
margin-left: 300px;
|
||||
padding-top: 18px;
|
||||
}
|
||||
&.halved {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
zoom: 1;
|
||||
}
|
||||
.widefat {
|
||||
td {
|
||||
vertical-align: top;
|
||||
padding: 7px 7px;
|
||||
.description {
|
||||
margin: 4px 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.postbox {
|
||||
&:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
h3 {
|
||||
cursor: default !important;
|
||||
}
|
||||
.inside {
|
||||
padding: 10px;
|
||||
margin: 0 !important;
|
||||
}
|
||||
h3.stats_range {
|
||||
padding: 0 !important;
|
||||
.export_csv {
|
||||
float: right;
|
||||
line-height: 26px;
|
||||
border-left: 1px solid #dfdfdf;
|
||||
padding: 9px 10px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
ul {
|
||||
list-style: none outside;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
zoom:1;
|
||||
&:before, &:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
&:after {
|
||||
clear:both;
|
||||
}
|
||||
li {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 26px;
|
||||
a {
|
||||
border-right: 1px solid #dfdfdf;
|
||||
padding: 9px 10px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
&.active {
|
||||
background: #f9f9f9;
|
||||
-webkit-box-shadow:0 4px 0 0 #f9f9f9;
|
||||
box-shadow:0 4px 0 0 #f9f9f9;
|
||||
a {
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
&.custom {
|
||||
padding: 9px 10px;
|
||||
vertical-align: middle;
|
||||
form, div {
|
||||
display: inline;
|
||||
margin:0;
|
||||
input.range_datepicker {
|
||||
padding: 0;
|
||||
margin:0 10px 0 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
-webkit-box-shadow:none;
|
||||
box-shadow:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-with-sidebar {
|
||||
padding: 12px 12px 12px 249px;
|
||||
margin: 0 !important;
|
||||
.chart-sidebar {
|
||||
width: 225px;
|
||||
margin-left: -237px;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.chart-widgets {
|
||||
margin:0;
|
||||
padding: 0;
|
||||
li.chart-widget {
|
||||
margin: 0 0 1em;
|
||||
background: #fafafa;
|
||||
border:1px solid #dfdfdf;
|
||||
|
||||
&:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
h4 {
|
||||
background: #fff;
|
||||
border:1px solid #dfdfdf;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
padding: 10px;
|
||||
margin:0;
|
||||
color: #21759b;
|
||||
border-top-width:0;
|
||||
background-image: -webkit-gradient(linear,left bottom,left top,from(#ececec),to(#f9f9f9));
|
||||
background-image: -webkit-linear-gradient(bottom,#ececec,#f9f9f9);
|
||||
background-image: -moz-linear-gradient(bottom,#ececec,#f9f9f9);
|
||||
background-image: -o-linear-gradient(bottom,#ececec,#f9f9f9);
|
||||
background-image: linear-gradient(to top,#ececec,#f9f9f9);
|
||||
&.section_title:hover {
|
||||
color:#d54e21;
|
||||
}
|
||||
}
|
||||
.section_title {
|
||||
cursor: pointer;
|
||||
span {
|
||||
display: block;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAFCAYAAACXU8ZrAAAAMElEQVQIW2OcOXPmfwYgSE9PZwTRyAAmB5bAphBZDK4bWRBdE4oVMEl067G6A919AJAYHtDVRR5EAAAAAElFTkSuQmCC) no-repeat right;
|
||||
}
|
||||
&.open {
|
||||
color: #333;
|
||||
span {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.section {
|
||||
border-bottom:1px solid #dfdfdf;
|
||||
&:last-of-type {
|
||||
border-radius:0 0 3px 3px;
|
||||
}
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
td {
|
||||
padding: 7px 10px;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
tr:first-child td {
|
||||
border-top:0;
|
||||
}
|
||||
td.count {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
td.name {
|
||||
padding-right: 0;
|
||||
}
|
||||
td.sparkline {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.wc_sparkline {
|
||||
width: 32px;
|
||||
height: 1em;
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
tr.active td {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
form, p {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
.submit {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
#product_ids {
|
||||
width: 100%;
|
||||
}
|
||||
.select_all, .select_none {
|
||||
float: right;
|
||||
color: #999;
|
||||
margin-left: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-legend {
|
||||
list-style: none outside;
|
||||
margin: 0 0 1em;
|
||||
padding: 0;
|
||||
border:1px solid #dfdfdf;
|
||||
border-right-width:0;
|
||||
border-bottom-width:0;
|
||||
background: #fff;
|
||||
li {
|
||||
border-right: 5px solid #aaa;
|
||||
color: #aaa;
|
||||
padding: 1em 1.5em;
|
||||
display: block;
|
||||
margin: 0;
|
||||
-webkit-transition:all ease .5s;
|
||||
box-shadow:
|
||||
inset 0 -1px 0 0 #dfdfdf;
|
||||
strong {
|
||||
font-size: 2.618em;
|
||||
line-height: 1.2em;
|
||||
color: #464646;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
font-family: "HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",sans-serif;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow:
|
||||
inset 0 -1px 0 0 #dfdfdf,
|
||||
inset 300px 0 0 fade(#9c5d90,10%);
|
||||
border-right: 5px solid #9c5d90 !important;
|
||||
padding-left:2em;
|
||||
color: #9c5d90;
|
||||
}
|
||||
}
|
||||
}
|
||||
.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;
|
||||
text-align: center;
|
||||
}
|
||||
.chart {
|
||||
padding: 16px 16px 0;
|
||||
.chart-placeholder {
|
||||
width:100%;
|
||||
height: 650px;
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
}
|
||||
.chart-prompt {
|
||||
line-height: 650px;
|
||||
margin: 0;
|
||||
color: #999;
|
||||
font-size: 1.2em;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
.chart-container {
|
||||
background: #fff;
|
||||
padding: 12px;
|
||||
position: relative;
|
||||
border:1px solid #dfdfdf;
|
||||
border-radius:3px;
|
||||
}
|
||||
.main .chart-legend {
|
||||
margin-top: 12px;
|
||||
li {
|
||||
border-right: 0;
|
||||
margin: 0 8px 0 0;
|
||||
float: left;
|
||||
border-top: 4px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
.woocommerce-reports-main {
|
||||
|
@ -2722,15 +2985,10 @@ form.report_filters {
|
|||
}
|
||||
|
||||
/* Chart tooltips */
|
||||
#tooltip {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
-moz-border-radius:4px;
|
||||
-webkit-border-radius:4px;
|
||||
-o-border-radius:4px;
|
||||
-khtml-border-radius:4px;
|
||||
border-radius:4px;
|
||||
opacity: 0.80;
|
||||
.chart-tooltip {
|
||||
position: absolute;
|
||||
display: none;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Custom charts */
|
||||
|
|
|
@ -1 +1,155 @@
|
|||
.clear{clear:both}.nobr{white-space:nowrap}.woocommerce .col2-set .col-1,.woocommerce-page .col2-set .col-1,.woocommerce .col2-set .col-2,.woocommerce-page .col2-set .col-2{float:none;width:100%}.woocommerce ul.products li.product,.woocommerce-page ul.products li.product{width:48%;float:left;clear:both;margin:0 0 2.992em}.woocommerce ul.products li.product:nth-child(2n),.woocommerce-page ul.products li.product:nth-child(2n){float:right;clear:none!important}.woocommerce div.product div.images,.woocommerce-page div.product div.images,.woocommerce #content div.product div.images,.woocommerce-page #content div.product div.images,.woocommerce div.product div.summary,.woocommerce-page div.product div.summary,.woocommerce #content div.product div.summary,.woocommerce-page #content div.product div.summary{float:none;width:100%}.woocommerce table.cart .product-thumbnail,.woocommerce-page table.cart .product-thumbnail,.woocommerce #content table.cart .product-thumbnail,.woocommerce-page #content table.cart .product-thumbnail{display:none}.woocommerce table.cart td.actions,.woocommerce-page table.cart td.actions,.woocommerce #content table.cart td.actions,.woocommerce-page #content table.cart td.actions{text-align:left}.woocommerce table.cart td.actions .coupon,.woocommerce-page table.cart td.actions .coupon,.woocommerce #content table.cart td.actions .coupon,.woocommerce-page #content table.cart td.actions .coupon{float:none;padding-bottom:.5em}.woocommerce table.cart td.actions .coupon:after,.woocommerce-page table.cart td.actions .coupon:after,.woocommerce #content table.cart td.actions .coupon:after,.woocommerce-page #content table.cart td.actions .coupon:after{content:"";display:block;clear:both}.woocommerce table.cart td.actions input,.woocommerce-page table.cart td.actions input,.woocommerce #content table.cart td.actions input,.woocommerce-page #content table.cart td.actions input,.woocommerce table.cart td.actions .button,.woocommerce-page table.cart td.actions .button,.woocommerce #content table.cart td.actions .button,.woocommerce-page #content table.cart td.actions .button,.woocommerce table.cart td.actions .input-text,.woocommerce-page table.cart td.actions .input-text,.woocommerce #content table.cart td.actions .input-text,.woocommerce-page #content table.cart td.actions .input-text{width:48%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.woocommerce table.cart td.actions .input-text+.button,.woocommerce-page table.cart td.actions .input-text+.button,.woocommerce #content table.cart td.actions .input-text+.button,.woocommerce-page #content table.cart td.actions .input-text+.button,.woocommerce table.cart td.actions .button.alt,.woocommerce-page table.cart td.actions .button.alt,.woocommerce #content table.cart td.actions .button.alt,.woocommerce-page #content table.cart td.actions .button.alt{float:right}.woocommerce .cart-collaterals .cart_totals,.woocommerce-page .cart-collaterals .cart_totals,.woocommerce .cart-collaterals .shipping_calculator,.woocommerce-page .cart-collaterals .shipping_calculator,.woocommerce .cart-collaterals .cross-sells,.woocommerce-page .cart-collaterals .cross-sells{width:100%;float:none;text-align:left}.woocommerce #payment .terms,.woocommerce-page #payment .terms{text-align:left;padding:0}.woocommerce #payment #place_order,.woocommerce-page #payment #place_order{float:none;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:1em}
|
||||
/**
|
||||
* This stylesheet optimises the default WooCommerce layout when viewed on smaller screens.
|
||||
*/
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
.nobr {
|
||||
white-space: nowrap;
|
||||
}
|
||||
/**
|
||||
* Objects
|
||||
*/
|
||||
.woocommerce,
|
||||
.woocommerce-page {
|
||||
/**
|
||||
* General layout
|
||||
*/
|
||||
|
||||
/**
|
||||
* Products
|
||||
*/
|
||||
|
||||
/**
|
||||
* Product Details
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cart
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checkout
|
||||
*/
|
||||
|
||||
}
|
||||
.woocommerce .col2-set .col-1,
|
||||
.woocommerce-page .col2-set .col-1,
|
||||
.woocommerce .col2-set .col-2,
|
||||
.woocommerce-page .col2-set .col-2 {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
.woocommerce ul.products li.product,
|
||||
.woocommerce-page ul.products li.product {
|
||||
width: 48%;
|
||||
float: left;
|
||||
clear: both;
|
||||
margin: 0 0 2.992em;
|
||||
}
|
||||
.woocommerce ul.products li.product:nth-child(2n),
|
||||
.woocommerce-page ul.products li.product:nth-child(2n) {
|
||||
float: right;
|
||||
clear: none !important;
|
||||
}
|
||||
.woocommerce div.product div.images,
|
||||
.woocommerce-page div.product div.images,
|
||||
.woocommerce #content div.product div.images,
|
||||
.woocommerce-page #content div.product div.images,
|
||||
.woocommerce div.product div.summary,
|
||||
.woocommerce-page div.product div.summary,
|
||||
.woocommerce #content div.product div.summary,
|
||||
.woocommerce-page #content div.product div.summary {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
.woocommerce table.cart .product-thumbnail,
|
||||
.woocommerce-page table.cart .product-thumbnail,
|
||||
.woocommerce #content table.cart .product-thumbnail,
|
||||
.woocommerce-page #content table.cart .product-thumbnail {
|
||||
display: none;
|
||||
}
|
||||
.woocommerce table.cart td.actions,
|
||||
.woocommerce-page table.cart td.actions,
|
||||
.woocommerce #content table.cart td.actions,
|
||||
.woocommerce-page #content table.cart td.actions {
|
||||
text-align: left;
|
||||
}
|
||||
.woocommerce table.cart td.actions .coupon,
|
||||
.woocommerce-page table.cart td.actions .coupon,
|
||||
.woocommerce #content table.cart td.actions .coupon,
|
||||
.woocommerce-page #content table.cart td.actions .coupon {
|
||||
float: none;
|
||||
padding-bottom: .5em;
|
||||
}
|
||||
.woocommerce table.cart td.actions .coupon:after,
|
||||
.woocommerce-page table.cart td.actions .coupon:after,
|
||||
.woocommerce #content table.cart td.actions .coupon:after,
|
||||
.woocommerce-page #content table.cart td.actions .coupon:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
.woocommerce table.cart td.actions input,
|
||||
.woocommerce-page table.cart td.actions input,
|
||||
.woocommerce #content table.cart td.actions input,
|
||||
.woocommerce-page #content table.cart td.actions input,
|
||||
.woocommerce table.cart td.actions .button,
|
||||
.woocommerce-page table.cart td.actions .button,
|
||||
.woocommerce #content table.cart td.actions .button,
|
||||
.woocommerce-page #content table.cart td.actions .button,
|
||||
.woocommerce table.cart td.actions .input-text,
|
||||
.woocommerce-page table.cart td.actions .input-text,
|
||||
.woocommerce #content table.cart td.actions .input-text,
|
||||
.woocommerce-page #content table.cart td.actions .input-text {
|
||||
width: 48%;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* Safari/Chrome, other WebKit */
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
/* Firefox, other Gecko */
|
||||
|
||||
box-sizing: border-box;
|
||||
/* Opera/IE 8+ */
|
||||
|
||||
}
|
||||
.woocommerce table.cart td.actions .input-text + .button,
|
||||
.woocommerce-page table.cart td.actions .input-text + .button,
|
||||
.woocommerce #content table.cart td.actions .input-text + .button,
|
||||
.woocommerce-page #content table.cart td.actions .input-text + .button,
|
||||
.woocommerce table.cart td.actions .button.alt,
|
||||
.woocommerce-page table.cart td.actions .button.alt,
|
||||
.woocommerce #content table.cart td.actions .button.alt,
|
||||
.woocommerce-page #content table.cart td.actions .button.alt {
|
||||
float: right;
|
||||
}
|
||||
.woocommerce .cart-collaterals .cart_totals,
|
||||
.woocommerce-page .cart-collaterals .cart_totals,
|
||||
.woocommerce .cart-collaterals .shipping_calculator,
|
||||
.woocommerce-page .cart-collaterals .shipping_calculator,
|
||||
.woocommerce .cart-collaterals .cross-sells,
|
||||
.woocommerce-page .cart-collaterals .cross-sells {
|
||||
width: 100%;
|
||||
float: none;
|
||||
text-align: left;
|
||||
}
|
||||
.woocommerce #payment .terms,
|
||||
.woocommerce-page #payment .terms {
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
}
|
||||
.woocommerce #payment #place_order,
|
||||
.woocommerce-page #payment #place_order {
|
||||
float: none;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* Safari/Chrome, other WebKit */
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
/* Firefox, other Gecko */
|
||||
|
||||
box-sizing: border-box;
|
||||
/* Opera/IE 8+ */
|
||||
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
jQuery(function(){
|
||||
|
||||
function weekendAreas(axes) {
|
||||
var markings = [];
|
||||
var d = new Date(axes.xaxis.min);
|
||||
// go to the first Saturday
|
||||
d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
|
||||
d.setUTCSeconds(0);
|
||||
d.setUTCMinutes(0);
|
||||
d.setUTCHours(0);
|
||||
var i = d.getTime();
|
||||
do {
|
||||
// when we don't set yaxis, the rectangle automatically
|
||||
// extends to infinity upwards and downwards
|
||||
markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000 } });
|
||||
i += 7 * 24 * 60 * 60 * 1000;
|
||||
} while (i < axes.xaxis.max);
|
||||
|
||||
return markings;
|
||||
}
|
||||
|
||||
var order_data = jQuery.parseJSON( params.order_data.replace(/"/g, '"') );
|
||||
|
||||
var d = order_data.order_counts;
|
||||
var d2 = order_data.order_amounts;
|
||||
|
||||
for (var i = 0; i < d.length; ++i) d[i][0] += 60 * 60 * 1000;
|
||||
for (var i = 0; i < d2.length; ++i) d2[i][0] += 60 * 60 * 1000;
|
||||
|
||||
var placeholder = jQuery("#placeholder");
|
||||
|
||||
var plot = jQuery.plot(placeholder, [ { label: params.number_of_sales, data: d }, { label: params.sales_amount, data: d2, yaxis: 2 } ], {
|
||||
series: {
|
||||
lines: { show: true, fill: true },
|
||||
points: { show: true }
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
aboveData: false,
|
||||
color: '#ccc',
|
||||
backgroundColor: '#fff',
|
||||
borderWidth: 2,
|
||||
borderColor: '#ccc',
|
||||
clickable: false,
|
||||
hoverable: true,
|
||||
markings: weekendAreas
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
timeformat: "%d %b",
|
||||
monthNames: params.i18n_month_names,
|
||||
tickLength: 1,
|
||||
minTickSize: [1, "day"]
|
||||
},
|
||||
yaxes: [ { min: 0, tickSize: 10, tickDecimals: 0 }, { position: "right", min: 0, tickDecimals: 2 } ],
|
||||
colors: ["#8a4b75", "#47a03e"],
|
||||
legend: {
|
||||
show: true,
|
||||
position: "nw"
|
||||
}
|
||||
});
|
||||
|
||||
placeholder.resize();
|
||||
|
||||
function showTooltip(x, y, contents) {
|
||||
jQuery('<div id="tooltip">' + contents + '</div>').css( {
|
||||
position: 'absolute',
|
||||
display: 'none',
|
||||
top: y + 5,
|
||||
left: x + 5,
|
||||
padding: '5px 10px',
|
||||
border: '3px solid #3da5d5',
|
||||
background: '#288ab7'
|
||||
}).appendTo("body").fadeIn(200);
|
||||
}
|
||||
|
||||
var previousPoint = null;
|
||||
jQuery("#placeholder").bind("plothover", function (event, pos, item) {
|
||||
if (item) {
|
||||
if (previousPoint != item.dataIndex) {
|
||||
previousPoint = item.dataIndex;
|
||||
|
||||
jQuery("#tooltip").remove();
|
||||
|
||||
if (item.series.label==params.number_of_sales) {
|
||||
|
||||
var y = item.datapoint[1];
|
||||
showTooltip(item.pageX, item.pageY, params.i18n_sold + ": " + y);
|
||||
|
||||
} else {
|
||||
|
||||
var y = item.datapoint[1].toFixed(2);
|
||||
|
||||
var formatted_total = accounting.formatMoney( y, {
|
||||
symbol : params.currency_format_symbol,
|
||||
decimal : params.currency_format_decimal_sep,
|
||||
thousand : params.currency_format_thousand_sep,
|
||||
precision : params.currency_format_num_decimals,
|
||||
format : params.currency_format
|
||||
} );
|
||||
|
||||
showTooltip( item.pageX, item.pageY, params.i18n_earned + ": " + formatted_total );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
jQuery("#tooltip").remove();
|
||||
previousPoint = null;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
jQuery(function(){function e(e){var t=[],n=new Date(e.xaxis.min);n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+1)%7);n.setUTCSeconds(0);n.setUTCMinutes(0);n.setUTCHours(0);var r=n.getTime();do{t.push({xaxis:{from:r,to:r+1728e5}});r+=6048e5}while(r<e.xaxis.max);return t}function u(e,t,n){jQuery('<div id="tooltip">'+n+"</div>").css({position:"absolute",display:"none",top:t+5,left:e+5,padding:"5px 10px",border:"3px solid #3da5d5",background:"#288ab7"}).appendTo("body").fadeIn(200)}var t=jQuery.parseJSON(params.order_data.replace(/"/g,'"')),n=t.order_counts,r=t.order_amounts;for(var i=0;i<n.length;++i)n[i][0]+=36e5;for(var i=0;i<r.length;++i)r[i][0]+=36e5;var s=jQuery("#placeholder"),o=jQuery.plot(s,[{label:params.number_of_sales,data:n},{label:params.sales_amount,data:r,yaxis:2}],{series:{lines:{show:!0,fill:!0},points:{show:!0}},grid:{show:!0,aboveData:!1,color:"#ccc",backgroundColor:"#fff",borderWidth:2,borderColor:"#ccc",clickable:!1,hoverable:!0,markings:e},xaxis:{mode:"time",timeformat:"%d %b",monthNames:params.i18n_month_names,tickLength:1,minTickSize:[1,"day"]},yaxes:[{min:0,tickSize:10,tickDecimals:0},{position:"right",min:0,tickDecimals:2}],colors:["#8a4b75","#47a03e"],legend:{show:!0,position:"nw"}});s.resize();var a=null;jQuery("#placeholder").bind("plothover",function(e,t,n){if(n){if(a!=n.dataIndex){a=n.dataIndex;jQuery("#tooltip").remove();if(n.series.label==params.number_of_sales){var r=n.datapoint[1];u(n.pageX,n.pageY,params.i18n_sold+": "+r)}else{var r=n.datapoint[1].toFixed(2),i=accounting.formatMoney(r,{symbol:params.currency_format_symbol,decimal:params.currency_format_decimal_sep,thousand:params.currency_format_thousand_sep,precision:params.currency_format_num_decimals,format:params.currency_format});u(n.pageX,n.pageY,params.i18n_earned+": "+i)}}}else{jQuery("#tooltip").remove();a=null}})});
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,817 @@
|
|||
/* Flot plugin for rendering pie charts.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
The plugin assumes that each series has a single data value, and that each
|
||||
value is a positive integer or zero. Negative numbers don't make sense for a
|
||||
pie chart, and have unpredictable results. The values do NOT need to be
|
||||
passed in as percentages; the plugin will calculate the total and per-slice
|
||||
percentages internally.
|
||||
|
||||
* Created by Brian Medendorp
|
||||
|
||||
* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars
|
||||
|
||||
The plugin supports these options:
|
||||
|
||||
series: {
|
||||
pie: {
|
||||
show: true/false
|
||||
radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
|
||||
innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
|
||||
startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
|
||||
tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
|
||||
offset: {
|
||||
top: integer value to move the pie up or down
|
||||
left: integer value to move the pie left or right, or 'auto'
|
||||
},
|
||||
stroke: {
|
||||
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
|
||||
width: integer pixel width of the stroke
|
||||
},
|
||||
label: {
|
||||
show: true/false, or 'auto'
|
||||
formatter: a user-defined function that modifies the text/style of the label text
|
||||
radius: 0-1 for percentage of fullsize, or a specified pixel length
|
||||
background: {
|
||||
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
|
||||
opacity: 0-1
|
||||
},
|
||||
threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
|
||||
},
|
||||
combine: {
|
||||
threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
|
||||
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
|
||||
label: any text value of what the combined slice should be labeled
|
||||
}
|
||||
highlight: {
|
||||
opacity: 0-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
More detail and specific examples can be found in the included HTML file.
|
||||
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
// Maximum redraw attempts when fitting labels within the plot
|
||||
|
||||
var REDRAW_ATTEMPTS = 10;
|
||||
|
||||
// Factor by which to shrink the pie when fitting labels within the plot
|
||||
|
||||
var REDRAW_SHRINK = 0.95;
|
||||
|
||||
function init(plot) {
|
||||
|
||||
var canvas = null,
|
||||
target = null,
|
||||
maxRadius = null,
|
||||
centerLeft = null,
|
||||
centerTop = null,
|
||||
processed = false,
|
||||
ctx = null;
|
||||
|
||||
// interactive variables
|
||||
|
||||
var highlights = [];
|
||||
|
||||
// add hook to determine if pie plugin in enabled, and then perform necessary operations
|
||||
|
||||
plot.hooks.processOptions.push(function(plot, options) {
|
||||
if (options.series.pie.show) {
|
||||
|
||||
options.grid.show = false;
|
||||
|
||||
// set labels.show
|
||||
|
||||
if (options.series.pie.label.show == "auto") {
|
||||
if (options.legend.show) {
|
||||
options.series.pie.label.show = false;
|
||||
} else {
|
||||
options.series.pie.label.show = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set radius
|
||||
|
||||
if (options.series.pie.radius == "auto") {
|
||||
if (options.series.pie.label.show) {
|
||||
options.series.pie.radius = 3/4;
|
||||
} else {
|
||||
options.series.pie.radius = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure sane tilt
|
||||
|
||||
if (options.series.pie.tilt > 1) {
|
||||
options.series.pie.tilt = 1;
|
||||
} else if (options.series.pie.tilt < 0) {
|
||||
options.series.pie.tilt = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
plot.hooks.bindEvents.push(function(plot, eventHolder) {
|
||||
var options = plot.getOptions();
|
||||
if (options.series.pie.show) {
|
||||
if (options.grid.hoverable) {
|
||||
eventHolder.unbind("mousemove").mousemove(onMouseMove);
|
||||
}
|
||||
if (options.grid.clickable) {
|
||||
eventHolder.unbind("click").click(onClick);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) {
|
||||
var options = plot.getOptions();
|
||||
if (options.series.pie.show) {
|
||||
processDatapoints(plot, series, data, datapoints);
|
||||
}
|
||||
});
|
||||
|
||||
plot.hooks.drawOverlay.push(function(plot, octx) {
|
||||
var options = plot.getOptions();
|
||||
if (options.series.pie.show) {
|
||||
drawOverlay(plot, octx);
|
||||
}
|
||||
});
|
||||
|
||||
plot.hooks.draw.push(function(plot, newCtx) {
|
||||
var options = plot.getOptions();
|
||||
if (options.series.pie.show) {
|
||||
draw(plot, newCtx);
|
||||
}
|
||||
});
|
||||
|
||||
function processDatapoints(plot, series, datapoints) {
|
||||
if (!processed) {
|
||||
processed = true;
|
||||
canvas = plot.getCanvas();
|
||||
target = $(canvas).parent();
|
||||
options = plot.getOptions();
|
||||
plot.setData(combine(plot.getData()));
|
||||
}
|
||||
}
|
||||
|
||||
function combine(data) {
|
||||
|
||||
var total = 0,
|
||||
combined = 0,
|
||||
numCombined = 0,
|
||||
color = options.series.pie.combine.color,
|
||||
newdata = [];
|
||||
|
||||
// Fix up the raw data from Flot, ensuring the data is numeric
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
|
||||
var value = data[i].data;
|
||||
|
||||
// If the data is an array, we'll assume that it's a standard
|
||||
// Flot x-y pair, and are concerned only with the second value.
|
||||
|
||||
// Note how we use the original array, rather than creating a
|
||||
// new one; this is more efficient and preserves any extra data
|
||||
// that the user may have stored in higher indexes.
|
||||
|
||||
if ($.isArray(value) && value.length == 1) {
|
||||
value = value[0];
|
||||
}
|
||||
|
||||
if ($.isArray(value)) {
|
||||
// Equivalent to $.isNumeric() but compatible with jQuery < 1.7
|
||||
if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) {
|
||||
value[1] = +value[1];
|
||||
} else {
|
||||
value[1] = 0;
|
||||
}
|
||||
} else if (!isNaN(parseFloat(value)) && isFinite(value)) {
|
||||
value = [1, +value];
|
||||
} else {
|
||||
value = [1, 0];
|
||||
}
|
||||
|
||||
data[i].data = [value];
|
||||
}
|
||||
|
||||
// Sum up all the slices, so we can calculate percentages for each
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
total += data[i].data[0][1];
|
||||
}
|
||||
|
||||
// Count the number of slices with percentages below the combine
|
||||
// threshold; if it turns out to be just one, we won't combine.
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var value = data[i].data[0][1];
|
||||
if (value / total <= options.series.pie.combine.threshold) {
|
||||
combined += value;
|
||||
numCombined++;
|
||||
if (!color) {
|
||||
color = data[i].color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var value = data[i].data[0][1];
|
||||
if (numCombined < 2 || value / total > options.series.pie.combine.threshold) {
|
||||
newdata.push({
|
||||
data: [[1, value]],
|
||||
color: data[i].color,
|
||||
label: data[i].label,
|
||||
angle: value * Math.PI * 2 / total,
|
||||
percent: value / (total / 100)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (numCombined > 1) {
|
||||
newdata.push({
|
||||
data: [[1, combined]],
|
||||
color: color,
|
||||
label: options.series.pie.combine.label,
|
||||
angle: combined * Math.PI * 2 / total,
|
||||
percent: combined / (total / 100)
|
||||
});
|
||||
}
|
||||
|
||||
return newdata;
|
||||
}
|
||||
|
||||
function draw(plot, newCtx) {
|
||||
|
||||
if (!target) {
|
||||
return; // if no series were passed
|
||||
}
|
||||
|
||||
var canvasWidth = plot.getPlaceholder().width(),
|
||||
canvasHeight = plot.getPlaceholder().height(),
|
||||
legendWidth = target.children().filter(".legend").children().width() || 0;
|
||||
|
||||
ctx = newCtx;
|
||||
|
||||
// WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!
|
||||
|
||||
// When combining smaller slices into an 'other' slice, we need to
|
||||
// add a new series. Since Flot gives plugins no way to modify the
|
||||
// list of series, the pie plugin uses a hack where the first call
|
||||
// to processDatapoints results in a call to setData with the new
|
||||
// list of series, then subsequent processDatapoints do nothing.
|
||||
|
||||
// The plugin-global 'processed' flag is used to control this hack;
|
||||
// it starts out false, and is set to true after the first call to
|
||||
// processDatapoints.
|
||||
|
||||
// Unfortunately this turns future setData calls into no-ops; they
|
||||
// call processDatapoints, the flag is true, and nothing happens.
|
||||
|
||||
// To fix this we'll set the flag back to false here in draw, when
|
||||
// all series have been processed, so the next sequence of calls to
|
||||
// processDatapoints once again starts out with a slice-combine.
|
||||
// This is really a hack; in 0.9 we need to give plugins a proper
|
||||
// way to modify series before any processing begins.
|
||||
|
||||
processed = false;
|
||||
|
||||
// calculate maximum radius and center point
|
||||
|
||||
maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
|
||||
centerTop = canvasHeight / 2 + options.series.pie.offset.top;
|
||||
centerLeft = canvasWidth / 2;
|
||||
|
||||
if (options.series.pie.offset.left == "auto") {
|
||||
if (options.legend.position.match("w")) {
|
||||
centerLeft += legendWidth / 2;
|
||||
} else {
|
||||
centerLeft -= legendWidth / 2;
|
||||
}
|
||||
} else {
|
||||
centerLeft += options.series.pie.offset.left;
|
||||
}
|
||||
|
||||
if (centerLeft < maxRadius) {
|
||||
centerLeft = maxRadius;
|
||||
} else if (centerLeft > canvasWidth - maxRadius) {
|
||||
centerLeft = canvasWidth - maxRadius;
|
||||
}
|
||||
|
||||
var slices = plot.getData(),
|
||||
attempts = 0;
|
||||
|
||||
// Keep shrinking the pie's radius until drawPie returns true,
|
||||
// indicating that all the labels fit, or we try too many times.
|
||||
|
||||
do {
|
||||
if (attempts > 0) {
|
||||
maxRadius *= REDRAW_SHRINK;
|
||||
}
|
||||
attempts += 1;
|
||||
clear();
|
||||
if (options.series.pie.tilt <= 0.8) {
|
||||
drawShadow();
|
||||
}
|
||||
} while (!drawPie() && attempts < REDRAW_ATTEMPTS)
|
||||
|
||||
if (attempts >= REDRAW_ATTEMPTS) {
|
||||
clear();
|
||||
target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
|
||||
}
|
||||
|
||||
if (plot.setSeries && plot.insertLegend) {
|
||||
plot.setSeries(slices);
|
||||
plot.insertLegend();
|
||||
}
|
||||
|
||||
// we're actually done at this point, just defining internal functions at this point
|
||||
|
||||
function clear() {
|
||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
target.children().filter(".pieLabel, .pieLabelBackground").remove();
|
||||
}
|
||||
|
||||
function drawShadow() {
|
||||
|
||||
var shadowLeft = options.series.pie.shadow.left;
|
||||
var shadowTop = options.series.pie.shadow.top;
|
||||
var edge = 10;
|
||||
var alpha = options.series.pie.shadow.alpha;
|
||||
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
|
||||
|
||||
if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
|
||||
return; // shadow would be outside canvas, so don't draw it
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(shadowLeft,shadowTop);
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fillStyle = "#000";
|
||||
|
||||
// center and rotate to starting position
|
||||
|
||||
ctx.translate(centerLeft,centerTop);
|
||||
ctx.scale(1, options.series.pie.tilt);
|
||||
|
||||
//radius -= edge;
|
||||
|
||||
for (var i = 1; i <= edge; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
radius -= i;
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawPie() {
|
||||
|
||||
var startAngle = Math.PI * options.series.pie.startAngle;
|
||||
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
|
||||
|
||||
// center and rotate to starting position
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(centerLeft,centerTop);
|
||||
ctx.scale(1, options.series.pie.tilt);
|
||||
//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
|
||||
|
||||
// draw slices
|
||||
|
||||
ctx.save();
|
||||
var currentAngle = startAngle;
|
||||
for (var i = 0; i < slices.length; ++i) {
|
||||
slices[i].startAngle = currentAngle;
|
||||
drawSlice(slices[i].angle, slices[i].color, true);
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
// draw slice outlines
|
||||
|
||||
if (options.series.pie.stroke.width > 0) {
|
||||
ctx.save();
|
||||
ctx.lineWidth = options.series.pie.stroke.width;
|
||||
currentAngle = startAngle;
|
||||
for (var i = 0; i < slices.length; ++i) {
|
||||
drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// draw donut hole
|
||||
|
||||
drawDonutHole(ctx);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
// Draw the labels, returning true if they fit within the plot
|
||||
|
||||
if (options.series.pie.label.show) {
|
||||
return drawLabels();
|
||||
} else return true;
|
||||
|
||||
function drawSlice(angle, color, fill) {
|
||||
|
||||
if (angle <= 0 || isNaN(angle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fill) {
|
||||
ctx.fillStyle = color;
|
||||
} else {
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineJoin = "round";
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
|
||||
ctx.moveTo(0, 0); // Center of the pie
|
||||
}
|
||||
|
||||
//ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
|
||||
ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
|
||||
ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
|
||||
ctx.closePath();
|
||||
//ctx.rotate(angle); // This doesn't work properly in Opera
|
||||
currentAngle += angle;
|
||||
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function drawLabels() {
|
||||
|
||||
var currentAngle = startAngle;
|
||||
var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
|
||||
|
||||
for (var i = 0; i < slices.length; ++i) {
|
||||
if (slices[i].percent >= options.series.pie.label.threshold * 100) {
|
||||
if (!drawLabel(slices[i], currentAngle, i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
currentAngle += slices[i].angle;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
function drawLabel(slice, startAngle, index) {
|
||||
|
||||
if (slice.data[0][1] == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// format label text
|
||||
|
||||
var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
|
||||
|
||||
if (lf) {
|
||||
text = lf(slice.label, slice);
|
||||
} else {
|
||||
text = slice.label;
|
||||
}
|
||||
|
||||
if (plf) {
|
||||
text = plf(text, slice);
|
||||
}
|
||||
|
||||
var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
|
||||
var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
|
||||
var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
|
||||
|
||||
var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
|
||||
target.append(html);
|
||||
|
||||
var label = target.children("#pieLabel" + index);
|
||||
var labelTop = (y - label.height() / 2);
|
||||
var labelLeft = (x - label.width() / 2);
|
||||
|
||||
label.css("top", labelTop);
|
||||
label.css("left", labelLeft);
|
||||
|
||||
// check to make sure that the label is not outside the canvas
|
||||
|
||||
if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.series.pie.label.background.opacity != 0) {
|
||||
|
||||
// put in the transparent background separately to avoid blended labels and label boxes
|
||||
|
||||
var c = options.series.pie.label.background.color;
|
||||
|
||||
if (c == null) {
|
||||
c = slice.color;
|
||||
}
|
||||
|
||||
var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
|
||||
$("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
|
||||
.css("opacity", options.series.pie.label.background.opacity)
|
||||
.insertBefore(label);
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end individual label function
|
||||
} // end drawLabels function
|
||||
} // end drawPie function
|
||||
} // end draw function
|
||||
|
||||
// Placed here because it needs to be accessed from multiple locations
|
||||
|
||||
function drawDonutHole(layer) {
|
||||
if (options.series.pie.innerRadius > 0) {
|
||||
|
||||
// subtract the center
|
||||
|
||||
layer.save();
|
||||
var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
|
||||
layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
|
||||
layer.beginPath();
|
||||
layer.fillStyle = options.series.pie.stroke.color;
|
||||
layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
|
||||
layer.fill();
|
||||
layer.closePath();
|
||||
layer.restore();
|
||||
|
||||
// add inner stroke
|
||||
|
||||
layer.save();
|
||||
layer.beginPath();
|
||||
layer.strokeStyle = options.series.pie.stroke.color;
|
||||
layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
|
||||
layer.stroke();
|
||||
layer.closePath();
|
||||
layer.restore();
|
||||
|
||||
// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
|
||||
}
|
||||
}
|
||||
|
||||
//-- Additional Interactive related functions --
|
||||
|
||||
function isPointInPoly(poly, pt) {
|
||||
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
|
||||
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
|
||||
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
|
||||
&& (c = !c);
|
||||
return c;
|
||||
}
|
||||
|
||||
function findNearbySlice(mouseX, mouseY) {
|
||||
|
||||
var slices = plot.getData(),
|
||||
options = plot.getOptions(),
|
||||
radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius,
|
||||
x, y;
|
||||
|
||||
for (var i = 0; i < slices.length; ++i) {
|
||||
|
||||
var s = slices[i];
|
||||
|
||||
if (s.pie.show) {
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0); // Center of the pie
|
||||
//ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
|
||||
ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
|
||||
ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
|
||||
ctx.closePath();
|
||||
x = mouseX - centerLeft;
|
||||
y = mouseY - centerTop;
|
||||
|
||||
if (ctx.isPointInPath) {
|
||||
if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
|
||||
ctx.restore();
|
||||
return {
|
||||
datapoint: [s.percent, s.data],
|
||||
dataIndex: 0,
|
||||
series: s,
|
||||
seriesIndex: i
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
||||
// excanvas for IE doesn;t support isPointInPath, this is a workaround.
|
||||
|
||||
var p1X = radius * Math.cos(s.startAngle),
|
||||
p1Y = radius * Math.sin(s.startAngle),
|
||||
p2X = radius * Math.cos(s.startAngle + s.angle / 4),
|
||||
p2Y = radius * Math.sin(s.startAngle + s.angle / 4),
|
||||
p3X = radius * Math.cos(s.startAngle + s.angle / 2),
|
||||
p3Y = radius * Math.sin(s.startAngle + s.angle / 2),
|
||||
p4X = radius * Math.cos(s.startAngle + s.angle / 1.5),
|
||||
p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5),
|
||||
p5X = radius * Math.cos(s.startAngle + s.angle),
|
||||
p5Y = radius * Math.sin(s.startAngle + s.angle),
|
||||
arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]],
|
||||
arrPoint = [x, y];
|
||||
|
||||
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
|
||||
|
||||
if (isPointInPoly(arrPoly, arrPoint)) {
|
||||
ctx.restore();
|
||||
return {
|
||||
datapoint: [s.percent, s.data],
|
||||
dataIndex: 0,
|
||||
series: s,
|
||||
seriesIndex: i
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function onMouseMove(e) {
|
||||
triggerClickHoverEvent("plothover", e);
|
||||
}
|
||||
|
||||
function onClick(e) {
|
||||
triggerClickHoverEvent("plotclick", e);
|
||||
}
|
||||
|
||||
// trigger click or hover event (they send the same parameters so we share their code)
|
||||
|
||||
function triggerClickHoverEvent(eventname, e) {
|
||||
|
||||
var offset = plot.offset();
|
||||
var canvasX = parseInt(e.pageX - offset.left);
|
||||
var canvasY = parseInt(e.pageY - offset.top);
|
||||
var item = findNearbySlice(canvasX, canvasY);
|
||||
|
||||
if (options.grid.autoHighlight) {
|
||||
|
||||
// clear auto-highlights
|
||||
|
||||
for (var i = 0; i < highlights.length; ++i) {
|
||||
var h = highlights[i];
|
||||
if (h.auto == eventname && !(item && h.series == item.series)) {
|
||||
unhighlight(h.series);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// highlight the slice
|
||||
|
||||
if (item) {
|
||||
highlight(item.series, eventname);
|
||||
}
|
||||
|
||||
// trigger any hover bind events
|
||||
|
||||
var pos = { pageX: e.pageX, pageY: e.pageY };
|
||||
target.trigger(eventname, [pos, item]);
|
||||
}
|
||||
|
||||
function highlight(s, auto) {
|
||||
//if (typeof s == "number") {
|
||||
// s = series[s];
|
||||
//}
|
||||
|
||||
var i = indexOfHighlight(s);
|
||||
|
||||
if (i == -1) {
|
||||
highlights.push({ series: s, auto: auto });
|
||||
plot.triggerRedrawOverlay();
|
||||
} else if (!auto) {
|
||||
highlights[i].auto = false;
|
||||
}
|
||||
}
|
||||
|
||||
function unhighlight(s) {
|
||||
if (s == null) {
|
||||
highlights = [];
|
||||
plot.triggerRedrawOverlay();
|
||||
}
|
||||
|
||||
//if (typeof s == "number") {
|
||||
// s = series[s];
|
||||
//}
|
||||
|
||||
var i = indexOfHighlight(s);
|
||||
|
||||
if (i != -1) {
|
||||
highlights.splice(i, 1);
|
||||
plot.triggerRedrawOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
function indexOfHighlight(s) {
|
||||
for (var i = 0; i < highlights.length; ++i) {
|
||||
var h = highlights[i];
|
||||
if (h.series == s)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function drawOverlay(plot, octx) {
|
||||
|
||||
var options = plot.getOptions();
|
||||
|
||||
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
|
||||
|
||||
octx.save();
|
||||
octx.translate(centerLeft, centerTop);
|
||||
octx.scale(1, options.series.pie.tilt);
|
||||
|
||||
for (var i = 0; i < highlights.length; ++i) {
|
||||
drawHighlight(highlights[i].series);
|
||||
}
|
||||
|
||||
drawDonutHole(octx);
|
||||
|
||||
octx.restore();
|
||||
|
||||
function drawHighlight(series) {
|
||||
|
||||
if (series.angle <= 0 || isNaN(series.angle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
|
||||
octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
|
||||
octx.beginPath();
|
||||
if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
|
||||
octx.moveTo(0, 0); // Center of the pie
|
||||
}
|
||||
octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
|
||||
octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
|
||||
octx.closePath();
|
||||
octx.fill();
|
||||
}
|
||||
}
|
||||
} // end init (plugin body)
|
||||
|
||||
// define pie specific options and their default values
|
||||
|
||||
var options = {
|
||||
series: {
|
||||
pie: {
|
||||
show: false,
|
||||
radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
|
||||
innerRadius: 0, /* for donut */
|
||||
startAngle: 3/2,
|
||||
tilt: 1,
|
||||
shadow: {
|
||||
left: 5, // shadow left offset
|
||||
top: 15, // shadow top offset
|
||||
alpha: 0.02 // shadow alpha
|
||||
},
|
||||
offset: {
|
||||
top: 0,
|
||||
left: "auto"
|
||||
},
|
||||
stroke: {
|
||||
color: "#fff",
|
||||
width: 1
|
||||
},
|
||||
label: {
|
||||
show: "auto",
|
||||
formatter: function(label, slice) {
|
||||
return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
|
||||
}, // formatter function
|
||||
radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
|
||||
background: {
|
||||
color: null,
|
||||
opacity: 0
|
||||
},
|
||||
threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
|
||||
},
|
||||
combine: {
|
||||
threshold: -1, // percentage at which to combine little slices into one larger slice
|
||||
color: null, // color to give the new slice (auto-generated if null)
|
||||
label: "Other" // label to give the new slice
|
||||
},
|
||||
highlight: {
|
||||
//color: "#fff", // will add this functionality once parseColor is available
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: "pie",
|
||||
version: "1.1"
|
||||
});
|
||||
|
||||
})(jQuery);
|
File diff suppressed because one or more lines are too long
|
@ -1,26 +1,26 @@
|
|||
/*
|
||||
Flot plugin for automatically redrawing plots when the placeholder
|
||||
size changes, e.g. on window resizes.
|
||||
/* Flot plugin for automatically redrawing plots as the placeholder resizes.
|
||||
|
||||
It works by listening for changes on the placeholder div (through the
|
||||
jQuery resize event plugin) - if the size changes, it will redraw the
|
||||
plot.
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
It works by listening for changes on the placeholder div (through the jQuery
|
||||
resize event plugin) - if the size changes, it will redraw the plot.
|
||||
|
||||
There are no options. If you need to disable the plugin for some plots, you
|
||||
can just fix the size of their placeholders.
|
||||
|
||||
There are no options. If you need to disable the plugin for some
|
||||
plots, you can just fix the size of their placeholders.
|
||||
*/
|
||||
|
||||
|
||||
/* Inline dependency:
|
||||
/* Inline dependency:
|
||||
* jQuery resize event - v1.1 - 3/14/2010
|
||||
* http://benalman.com/projects/jquery-resize-plugin/
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
|
||||
|
||||
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
|
||||
|
||||
(function ($) {
|
||||
var options = { }; // no options
|
||||
|
|
|
@ -1 +1,19 @@
|
|||
(function(n,p,u){var w=n([]),s=n.resize=n.extend(n.resize,{}),o,l="setTimeout",m="resize",t=m+"-special-event",v="delay",r="throttleWindow";s[v]=250;s[r]=true;n.event.special[m]={setup:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.add(a);n.data(this,t,{w:a.width(),h:a.height()});if(w.length===1){q()}},teardown:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.not(a);a.removeData(t);if(!w.length){clearTimeout(o)}},add:function(b){if(!s[r]&&this[l]){return false}var c;function a(d,h,g){var f=n(this),e=n.data(this,t);e.w=h!==u?h:f.width();e.h=g!==u?g:f.height();c.apply(this,arguments)}if(n.isFunction(b)){c=b;return a}else{c=b.handler;b.handler=a}}};function q(){o=p[l](function(){w.each(function(){var d=n(this),a=d.width(),b=d.height(),c=n.data(this,t);if(a!==c.w||b!==c.h){d.trigger(m,[c.w=a,c.h=b])}});q()},s[v])}})(jQuery,this);(function(b){var a={};function c(f){function e(){var h=f.getPlaceholder();if(h.width()==0||h.height()==0){return}f.resize();f.setupGrid();f.draw()}function g(i,h){i.getPlaceholder().resize(e)}function d(i,h){i.getPlaceholder().unbind("resize",e)}f.hooks.bindEvents.push(g);f.hooks.shutdown.push(d)}b.plot.plugins.push({init:c,options:a,name:"resize",version:"1.0"})})(jQuery);
|
||||
/* Flot plugin for automatically redrawing plots as the placeholder resizes.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
It works by listening for changes on the placeholder div (through the jQuery
|
||||
resize event plugin) - if the size changes, it will redraw the plot.
|
||||
|
||||
There are no options. If you need to disable the plugin for some plots, you
|
||||
can just fix the size of their placeholders.
|
||||
|
||||
*//* Inline dependency:
|
||||
* jQuery resize event - v1.1 - 3/14/2010
|
||||
* http://benalman.com/projects/jquery-resize-plugin/
|
||||
*
|
||||
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/(function(e,t,n){function c(){s=t[o](function(){r.each(function(){var t=e(this),n=t.width(),r=t.height(),i=e.data(this,a);(n!==i.w||r!==i.h)&&t.trigger(u,[i.w=n,i.h=r])});c()},i[f])}var r=e([]),i=e.resize=e.extend(e.resize,{}),s,o="setTimeout",u="resize",a=u+"-special-event",f="delay",l="throttleWindow";i[f]=250;i[l]=!0;e.event.special[u]={setup:function(){if(!i[l]&&this[o])return!1;var t=e(this);r=r.add(t);e.data(this,a,{w:t.width(),h:t.height()});r.length===1&&c()},teardown:function(){if(!i[l]&&this[o])return!1;var t=e(this);r=r.not(t);t.removeData(a);r.length||clearTimeout(s)},add:function(t){function s(t,i,s){var o=e(this),u=e.data(this,a);u.w=i!==n?i:o.width();u.h=s!==n?s:o.height();r.apply(this,arguments)}if(!i[l]&&this[o])return!1;var r;if(e.isFunction(t)){r=t;return s}r=t.handler;t.handler=s}}})(jQuery,this);(function(e){function n(e){function t(){var t=e.getPlaceholder();if(t.width()==0||t.height()==0)return;e.resize();e.setupGrid();e.draw()}function n(e,n){e.getPlaceholder().resize(t)}function r(e,n){e.getPlaceholder().unbind("resize",t)}e.hooks.bindEvents.push(n);e.hooks.shutdown.push(r)}var t={};e.plot.plugins.push({init:n,options:t,name:"resize",version:"1.0"})})(jQuery);
|
|
@ -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);
|
|
@ -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);
|
|
@ -0,0 +1,431 @@
|
|||
/* Pretty handling of time axes.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||
API.txt for details.
|
||||
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var options = {
|
||||
xaxis: {
|
||||
timezone: null, // "browser" for local to the client or timezone for timezone-js
|
||||
timeformat: null, // format string to use
|
||||
twelveHourClock: false, // 12 or 24 time in time mode
|
||||
monthNames: null // list of names of months
|
||||
}
|
||||
};
|
||||
|
||||
// round to nearby lower multiple of base
|
||||
|
||||
function floorInBase(n, base) {
|
||||
return base * Math.floor(n / base);
|
||||
}
|
||||
|
||||
// Returns a string with the date d formatted according to fmt.
|
||||
// A subset of the Open Group's strftime format is supported.
|
||||
|
||||
function formatDate(d, fmt, monthNames, dayNames) {
|
||||
|
||||
if (typeof d.strftime == "function") {
|
||||
return d.strftime(fmt);
|
||||
}
|
||||
|
||||
var leftPad = function(n, pad) {
|
||||
n = "" + n;
|
||||
pad = "" + (pad == null ? "0" : pad);
|
||||
return n.length == 1 ? pad + n : n;
|
||||
};
|
||||
|
||||
var r = [];
|
||||
var escape = false;
|
||||
var hours = d.getHours();
|
||||
var isAM = hours < 12;
|
||||
|
||||
if (monthNames == null) {
|
||||
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
}
|
||||
|
||||
if (dayNames == null) {
|
||||
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
var hours12;
|
||||
|
||||
if (hours > 12) {
|
||||
hours12 = hours - 12;
|
||||
} else if (hours == 0) {
|
||||
hours12 = 12;
|
||||
} else {
|
||||
hours12 = hours;
|
||||
}
|
||||
|
||||
for (var i = 0; i < fmt.length; ++i) {
|
||||
|
||||
var c = fmt.charAt(i);
|
||||
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
case 'a': c = "" + dayNames[d.getDay()]; break;
|
||||
case 'b': c = "" + monthNames[d.getMonth()]; break;
|
||||
case 'd': c = leftPad(d.getDate()); break;
|
||||
case 'e': c = leftPad(d.getDate(), " "); break;
|
||||
case 'h': // For back-compat with 0.7; remove in 1.0
|
||||
case 'H': c = leftPad(hours); break;
|
||||
case 'I': c = leftPad(hours12); break;
|
||||
case 'l': c = leftPad(hours12, " "); break;
|
||||
case 'm': c = leftPad(d.getMonth() + 1); break;
|
||||
case 'M': c = leftPad(d.getMinutes()); break;
|
||||
// quarters not in Open Group's strftime specification
|
||||
case 'q':
|
||||
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
|
||||
case 'S': c = leftPad(d.getSeconds()); break;
|
||||
case 'y': c = leftPad(d.getFullYear() % 100); break;
|
||||
case 'Y': c = "" + d.getFullYear(); break;
|
||||
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
|
||||
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
|
||||
case 'w': c = "" + d.getDay(); break;
|
||||
}
|
||||
r.push(c);
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == "%") {
|
||||
escape = true;
|
||||
} else {
|
||||
r.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.join("");
|
||||
}
|
||||
|
||||
// To have a consistent view of time-based data independent of which time
|
||||
// zone the client happens to be in we need a date-like object independent
|
||||
// of time zones. This is done through a wrapper that only calls the UTC
|
||||
// versions of the accessor methods.
|
||||
|
||||
function makeUtcWrapper(d) {
|
||||
|
||||
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
|
||||
sourceObj[sourceMethod] = function() {
|
||||
return targetObj[targetMethod].apply(targetObj, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var utc = {
|
||||
date: d
|
||||
};
|
||||
|
||||
// support strftime, if found
|
||||
|
||||
if (d.strftime != undefined) {
|
||||
addProxyMethod(utc, "strftime", d, "strftime");
|
||||
}
|
||||
|
||||
addProxyMethod(utc, "getTime", d, "getTime");
|
||||
addProxyMethod(utc, "setTime", d, "setTime");
|
||||
|
||||
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
|
||||
|
||||
for (var p = 0; p < props.length; p++) {
|
||||
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
|
||||
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
|
||||
}
|
||||
|
||||
return utc;
|
||||
};
|
||||
|
||||
// select time zone strategy. This returns a date-like object tied to the
|
||||
// desired timezone
|
||||
|
||||
function dateGenerator(ts, opts) {
|
||||
if (opts.timezone == "browser") {
|
||||
return new Date(ts);
|
||||
} else if (!opts.timezone || opts.timezone == "utc") {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
|
||||
var d = new timezoneJS.Date();
|
||||
// timezone-js is fickle, so be sure to set the time zone before
|
||||
// setting the time.
|
||||
d.setTimezone(opts.timezone);
|
||||
d.setTime(ts);
|
||||
return d;
|
||||
} else {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
}
|
||||
}
|
||||
|
||||
// map of app. size of time units in milliseconds
|
||||
|
||||
var timeUnitSize = {
|
||||
"second": 1000,
|
||||
"minute": 60 * 1000,
|
||||
"hour": 60 * 60 * 1000,
|
||||
"day": 24 * 60 * 60 * 1000,
|
||||
"month": 30 * 24 * 60 * 60 * 1000,
|
||||
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
|
||||
"year": 365.2425 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
|
||||
// the allowed tick sizes, after 1 year we use
|
||||
// an integer algorithm
|
||||
|
||||
var baseSpec = [
|
||||
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
|
||||
[30, "second"],
|
||||
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
|
||||
[30, "minute"],
|
||||
[1, "hour"], [2, "hour"], [4, "hour"],
|
||||
[8, "hour"], [12, "hour"],
|
||||
[1, "day"], [2, "day"], [3, "day"],
|
||||
[0.25, "month"], [0.5, "month"], [1, "month"],
|
||||
[2, "month"]
|
||||
];
|
||||
|
||||
// we don't know which variant(s) we'll need yet, but generating both is
|
||||
// cheap
|
||||
|
||||
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
|
||||
[1, "year"]]);
|
||||
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
|
||||
[1, "year"]]);
|
||||
|
||||
function init(plot) {
|
||||
plot.hooks.processOptions.push(function (plot, options) {
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
|
||||
var opts = axis.options;
|
||||
|
||||
if (opts.mode == "time") {
|
||||
axis.tickGenerator = function(axis) {
|
||||
|
||||
var ticks = [];
|
||||
var d = dateGenerator(axis.min, opts);
|
||||
var minSize = 0;
|
||||
|
||||
// make quarter use a possibility if quarters are
|
||||
// mentioned in either of these options
|
||||
|
||||
var spec = (opts.tickSize && opts.tickSize[1] ===
|
||||
"quarter") ||
|
||||
(opts.minTickSize && opts.minTickSize[1] ===
|
||||
"quarter") ? specQuarters : specMonths;
|
||||
|
||||
if (opts.minTickSize != null) {
|
||||
if (typeof opts.tickSize == "number") {
|
||||
minSize = opts.tickSize;
|
||||
} else {
|
||||
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < spec.length - 1; ++i) {
|
||||
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
|
||||
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
|
||||
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var size = spec[i][0];
|
||||
var unit = spec[i][1];
|
||||
|
||||
// special-case the possibility of several years
|
||||
|
||||
if (unit == "year") {
|
||||
|
||||
// if given a minTickSize in years, just use it,
|
||||
// ensuring that it's an integer
|
||||
|
||||
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
|
||||
size = Math.floor(opts.minTickSize[0]);
|
||||
} else {
|
||||
|
||||
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
|
||||
var norm = (axis.delta / timeUnitSize.year) / magn;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
}
|
||||
|
||||
// minimum size for years is 1
|
||||
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
axis.tickSize = opts.tickSize || [size, unit];
|
||||
var tickSize = axis.tickSize[0];
|
||||
unit = axis.tickSize[1];
|
||||
|
||||
var step = tickSize * timeUnitSize[unit];
|
||||
|
||||
if (unit == "second") {
|
||||
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
|
||||
} else if (unit == "minute") {
|
||||
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
|
||||
} else if (unit == "hour") {
|
||||
d.setHours(floorInBase(d.getHours(), tickSize));
|
||||
} else if (unit == "month") {
|
||||
d.setMonth(floorInBase(d.getMonth(), tickSize));
|
||||
} else if (unit == "quarter") {
|
||||
d.setMonth(3 * floorInBase(d.getMonth() / 3,
|
||||
tickSize));
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
|
||||
}
|
||||
|
||||
// reset smaller components
|
||||
|
||||
d.setMilliseconds(0);
|
||||
|
||||
if (step >= timeUnitSize.minute) {
|
||||
d.setSeconds(0);
|
||||
}
|
||||
if (step >= timeUnitSize.hour) {
|
||||
d.setMinutes(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day) {
|
||||
d.setHours(0);
|
||||
}
|
||||
if (step >= timeUnitSize.day * 4) {
|
||||
d.setDate(1);
|
||||
}
|
||||
if (step >= timeUnitSize.month * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 3));
|
||||
}
|
||||
if (step >= timeUnitSize.quarter * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 6));
|
||||
}
|
||||
if (step >= timeUnitSize.year) {
|
||||
d.setMonth(0);
|
||||
}
|
||||
|
||||
var carry = 0;
|
||||
var v = Number.NaN;
|
||||
var prev;
|
||||
|
||||
do {
|
||||
|
||||
prev = v;
|
||||
v = d.getTime();
|
||||
ticks.push(v);
|
||||
|
||||
if (unit == "month" || unit == "quarter") {
|
||||
if (tickSize < 1) {
|
||||
|
||||
// a bit complicated - we'll divide the
|
||||
// month/quarter up but we need to take
|
||||
// care of fractions so we don't end up in
|
||||
// the middle of a day
|
||||
|
||||
d.setDate(1);
|
||||
var start = d.getTime();
|
||||
d.setMonth(d.getMonth() +
|
||||
(unit == "quarter" ? 3 : 1));
|
||||
var end = d.getTime();
|
||||
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
|
||||
carry = d.getHours();
|
||||
d.setHours(0);
|
||||
} else {
|
||||
d.setMonth(d.getMonth() +
|
||||
tickSize * (unit == "quarter" ? 3 : 1));
|
||||
}
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(d.getFullYear() + tickSize);
|
||||
} else {
|
||||
d.setTime(v + step);
|
||||
}
|
||||
} while (v < axis.max && v != prev);
|
||||
|
||||
return ticks;
|
||||
};
|
||||
|
||||
axis.tickFormatter = function (v, axis) {
|
||||
|
||||
var d = dateGenerator(v, axis.options);
|
||||
|
||||
// first check global format
|
||||
|
||||
if (opts.timeformat != null) {
|
||||
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
|
||||
}
|
||||
|
||||
// possibly use quarters if quarters are mentioned in
|
||||
// any of these places
|
||||
|
||||
var useQuarters = (axis.options.tickSize &&
|
||||
axis.options.tickSize[1] == "quarter") ||
|
||||
(axis.options.minTickSize &&
|
||||
axis.options.minTickSize[1] == "quarter");
|
||||
|
||||
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
|
||||
var span = axis.max - axis.min;
|
||||
var suffix = (opts.twelveHourClock) ? " %p" : "";
|
||||
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
|
||||
var fmt;
|
||||
|
||||
if (t < timeUnitSize.minute) {
|
||||
fmt = hourCode + ":%M:%S" + suffix;
|
||||
} else if (t < timeUnitSize.day) {
|
||||
if (span < 2 * timeUnitSize.day) {
|
||||
fmt = hourCode + ":%M" + suffix;
|
||||
} else {
|
||||
fmt = "%b %d " + hourCode + ":%M" + suffix;
|
||||
}
|
||||
} else if (t < timeUnitSize.month) {
|
||||
fmt = "%b %d";
|
||||
} else if ((useQuarters && t < timeUnitSize.quarter) ||
|
||||
(!useQuarters && t < timeUnitSize.year)) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "%b";
|
||||
} else {
|
||||
fmt = "%b %Y";
|
||||
}
|
||||
} else if (useQuarters && t < timeUnitSize.year) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "Q%q";
|
||||
} else {
|
||||
fmt = "Q%q %Y";
|
||||
}
|
||||
} else {
|
||||
fmt = "%Y";
|
||||
}
|
||||
|
||||
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
|
||||
|
||||
return rt;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'time',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
// Time-axis support used to be in Flot core, which exposed the
|
||||
// formatDate function on the plot object. Various plugins depend
|
||||
// on the function, so we need to re-expose it here.
|
||||
|
||||
$.plot.formatDate = formatDate;
|
||||
|
||||
})(jQuery);
|
|
@ -0,0 +1,9 @@
|
|||
/* Pretty handling of time axes.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||
API.txt for details.
|
||||
|
||||
*/(function(e){function n(e,t){return t*Math.floor(e/t)}function r(e,t,n,r){if(typeof e.strftime=="function")return e.strftime(t);var i=function(e,t){e=""+e;t=""+(t==null?"0":t);return e.length==1?t+e:e},s=[],o=!1,u=e.getHours(),a=u<12;n==null&&(n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]);r==null&&(r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);var f;u>12?f=u-12:u==0?f=12:f=u;for(var l=0;l<t.length;++l){var c=t.charAt(l);if(o){switch(c){case"a":c=""+r[e.getDay()];break;case"b":c=""+n[e.getMonth()];break;case"d":c=i(e.getDate());break;case"e":c=i(e.getDate()," ");break;case"h":case"H":c=i(u);break;case"I":c=i(f);break;case"l":c=i(f," ");break;case"m":c=i(e.getMonth()+1);break;case"M":c=i(e.getMinutes());break;case"q":c=""+(Math.floor(e.getMonth()/3)+1);break;case"S":c=i(e.getSeconds());break;case"y":c=i(e.getFullYear()%100);break;case"Y":c=""+e.getFullYear();break;case"p":c=a?"am":"pm";break;case"P":c=a?"AM":"PM";break;case"w":c=""+e.getDay()}s.push(c);o=!1}else c=="%"?o=!0:s.push(c)}return s.join("")}function i(e){function t(e,t,n,r){e[t]=function(){return n[r].apply(n,arguments)}}var n={date:e};e.strftime!=undefined&&t(n,"strftime",e,"strftime");t(n,"getTime",e,"getTime");t(n,"setTime",e,"setTime");var r=["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds"];for(var i=0;i<r.length;i++){t(n,"get"+r[i],e,"getUTC"+r[i]);t(n,"set"+r[i],e,"setUTC"+r[i])}return n}function s(e,t){if(t.timezone=="browser")return new Date(e);if(!t.timezone||t.timezone=="utc")return i(new Date(e));if(typeof timezoneJS!="undefined"&&typeof timezoneJS.Date!="undefined"){var n=new timezoneJS.Date;n.setTimezone(t.timezone);n.setTime(e);return n}return i(new Date(e))}function l(t){t.hooks.processOptions.push(function(t,i){e.each(t.getAxes(),function(e,t){var i=t.options;if(i.mode=="time"){t.tickGenerator=function(e){var t=[],r=s(e.min,i),u=0,l=i.tickSize&&i.tickSize[1]==="quarter"||i.minTickSize&&i.minTickSize[1]==="quarter"?f:a;i.minTickSize!=null&&(typeof i.tickSize=="number"?u=i.tickSize:u=i.minTickSize[0]*o[i.minTickSize[1]]);for(var c=0;c<l.length-1;++c)if(e.delta<(l[c][0]*o[l[c][1]]+l[c+1][0]*o[l[c+1][1]])/2&&l[c][0]*o[l[c][1]]>=u)break;var h=l[c][0],p=l[c][1];if(p=="year"){if(i.minTickSize!=null&&i.minTickSize[1]=="year")h=Math.floor(i.minTickSize[0]);else{var d=Math.pow(10,Math.floor(Math.log(e.delta/o.year)/Math.LN10)),v=e.delta/o.year/d;v<1.5?h=1:v<3?h=2:v<7.5?h=5:h=10;h*=d}h<1&&(h=1)}e.tickSize=i.tickSize||[h,p];var m=e.tickSize[0];p=e.tickSize[1];var g=m*o[p];p=="second"?r.setSeconds(n(r.getSeconds(),m)):p=="minute"?r.setMinutes(n(r.getMinutes(),m)):p=="hour"?r.setHours(n(r.getHours(),m)):p=="month"?r.setMonth(n(r.getMonth(),m)):p=="quarter"?r.setMonth(3*n(r.getMonth()/3,m)):p=="year"&&r.setFullYear(n(r.getFullYear(),m));r.setMilliseconds(0);g>=o.minute&&r.setSeconds(0);g>=o.hour&&r.setMinutes(0);g>=o.day&&r.setHours(0);g>=o.day*4&&r.setDate(1);g>=o.month*2&&r.setMonth(n(r.getMonth(),3));g>=o.quarter*2&&r.setMonth(n(r.getMonth(),6));g>=o.year&&r.setMonth(0);var y=0,b=Number.NaN,w;do{w=b;b=r.getTime();t.push(b);if(p=="month"||p=="quarter")if(m<1){r.setDate(1);var E=r.getTime();r.setMonth(r.getMonth()+(p=="quarter"?3:1));var S=r.getTime();r.setTime(b+y*o.hour+(S-E)*m);y=r.getHours();r.setHours(0)}else r.setMonth(r.getMonth()+m*(p=="quarter"?3:1));else p=="year"?r.setFullYear(r.getFullYear()+m):r.setTime(b+g)}while(b<e.max&&b!=w);return t};t.tickFormatter=function(e,t){var n=s(e,t.options);if(i.timeformat!=null)return r(n,i.timeformat,i.monthNames,i.dayNames);var u=t.options.tickSize&&t.options.tickSize[1]=="quarter"||t.options.minTickSize&&t.options.minTickSize[1]=="quarter",a=t.tickSize[0]*o[t.tickSize[1]],f=t.max-t.min,l=i.twelveHourClock?" %p":"",c=i.twelveHourClock?"%I":"%H",h;a<o.minute?h=c+":%M:%S"+l:a<o.day?f<2*o.day?h=c+":%M"+l:h="%b %d "+c+":%M"+l:a<o.month?h="%b %d":u&&a<o.quarter||!u&&a<o.year?f<o.year?h="%b":h="%b %Y":u&&a<o.year?f<o.year?h="Q%q":h="Q%q %Y":h="%Y";var p=r(n,h,i.monthNames,i.dayNames);return p}}})})}var t={xaxis:{timezone:null,timeformat:null,twelveHourClock:!1,monthNames:null}},o={second:1e3,minute:6e4,hour:36e5,day:864e5,month:2592e6,quarter:7776e6,year:525949.2*60*1e3},u=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[.25,"month"],[.5,"month"],[1,"month"],[2,"month"]],a=u.concat([[3,"month"],[6,"month"],[1,"year"]]),f=u.concat([[1,"quarter"],[2,"quarter"],[1,"year"]]);e.plot.plugins.push({init:l,options:t,name:"time",version:"1.0"});e.plot.formatDate=r})(jQuery);
|
|
@ -0,0 +1,224 @@
|
|||
jQuery(document).ready(function($) {
|
||||
|
||||
function showTooltip(x, y, contents) {
|
||||
jQuery('<div class="chart-tooltip">' + contents + '</div>').css( {
|
||||
top: y - 16,
|
||||
left: x + 20
|
||||
}).appendTo("body").fadeIn(200);
|
||||
}
|
||||
|
||||
var prev_data_index = null;
|
||||
var prev_series_index = null;
|
||||
|
||||
jQuery(".chart-placeholder").bind( "plothover", function (event, pos, item) {
|
||||
if (item) {
|
||||
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.series.data[item.dataIndex][1];
|
||||
|
||||
tooltip_content = '';
|
||||
|
||||
if ( item.series.prepend_label )
|
||||
tooltip_content = tooltip_content + item.series.label + ": ";
|
||||
|
||||
if ( item.series.prepend_tooltip )
|
||||
tooltip_content = tooltip_content + item.series.prepend_tooltip;
|
||||
|
||||
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();
|
||||
prev_data_index = null;
|
||||
}
|
||||
});
|
||||
|
||||
$('.wc_sparkline.bars').each(function() {
|
||||
var chart_data = $(this).data('sparkline');
|
||||
|
||||
var options = {
|
||||
grid: {
|
||||
show: false
|
||||
}
|
||||
};
|
||||
|
||||
// main series
|
||||
var series = [{
|
||||
data: chart_data,
|
||||
color: $(this).data('color'),
|
||||
bars: { fillColor: $(this).data('color'), fill: true, show: true, lineWidth: 1, barWidth: $(this).data('barwidth'), align: 'center' },
|
||||
shadowSize: 0
|
||||
}];
|
||||
|
||||
// draw the sparkline
|
||||
var plot = $.plot( $(this), series, options );
|
||||
});
|
||||
|
||||
$('.wc_sparkline.lines').each(function() {
|
||||
var chart_data = $(this).data('sparkline');
|
||||
|
||||
var options = {
|
||||
grid: {
|
||||
show: false
|
||||
}
|
||||
};
|
||||
|
||||
// main series
|
||||
var series = [{
|
||||
data: chart_data,
|
||||
color: $(this).data('color'),
|
||||
lines: { fill: false, show: true, lineWidth: 1, align: 'center' },
|
||||
shadowSize: 0
|
||||
}];
|
||||
|
||||
// draw the sparkline
|
||||
var plot = $.plot( $(this), series, options );
|
||||
});
|
||||
|
||||
var dates = jQuery( ".range_datepicker" ).datepicker({
|
||||
defaultDate: "",
|
||||
dateFormat: "yy-mm-dd",
|
||||
numberOfMonths: 1,
|
||||
maxDate: "+0D",
|
||||
showButtonPanel: true,
|
||||
showOn: "focus",
|
||||
buttonImageOnly: true,
|
||||
onSelect: function( selectedDate ) {
|
||||
var option = jQuery(this).is('.from') ? "minDate" : "maxDate",
|
||||
instance = jQuery( this ).data( "datepicker" ),
|
||||
date = jQuery.datepicker.parseDate(
|
||||
instance.settings.dateFormat ||
|
||||
jQuery.datepicker._defaults.dateFormat,
|
||||
selectedDate, instance.settings );
|
||||
dates.not( this ).datepicker( "option", option, date );
|
||||
}
|
||||
});
|
||||
|
||||
// Export
|
||||
$('.export_csv').click(function(){
|
||||
var exclude_series = $(this).data( 'exclude_series' ) || '';
|
||||
exclude_series = exclude_series.toString();
|
||||
exclude_series = exclude_series.split(',');
|
||||
var xaxes_label = $(this).data('xaxes');
|
||||
var groupby = $(this).data('groupby');
|
||||
var export = $(this).data('export');
|
||||
var csv_data = "data:application/csv;charset=utf-8,"
|
||||
|
||||
if ( export == 'table' ) {
|
||||
|
||||
$(this).closest('div').find('thead tr,tbody tr').each(function() {
|
||||
$(this).find('th,td').each(function() {
|
||||
value = $(this).text();
|
||||
value = value.replace( '[?]', '' );
|
||||
csv_data += '"' + value + '"' + ",";
|
||||
});
|
||||
csv_data = csv_data.substring( 0, csv_data.length - 1 );
|
||||
csv_data += "\n";
|
||||
});
|
||||
|
||||
$(this).closest('div').find('tfoot tr').each(function() {
|
||||
$(this).find('th,td').each(function() {
|
||||
value = $(this).text();
|
||||
value = value.replace( '[?]', '' );
|
||||
csv_data += '"' + value + '"' + ",";
|
||||
if ( $(this).attr('colspan') > 0 )
|
||||
for ( i = 1; i < $(this).attr('colspan'); i++ )
|
||||
csv_data += '"",';
|
||||
});
|
||||
csv_data = csv_data.substring( 0, csv_data.length - 1 );
|
||||
csv_data += "\n";
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
if ( ! window.main_chart )
|
||||
return false;
|
||||
|
||||
var the_series = window.main_chart.getData();
|
||||
var series = [];
|
||||
csv_data += xaxes_label + ",";
|
||||
|
||||
$.each(the_series, function( index, value ) {
|
||||
if ( ! exclude_series || $.inArray( index.toString(), exclude_series ) == -1 )
|
||||
series.push( value );
|
||||
});
|
||||
|
||||
// CSV Headers
|
||||
for ( var s = 0; s < series.length; ++s ) {
|
||||
csv_data += series[s].label + ',';
|
||||
}
|
||||
|
||||
csv_data = csv_data.substring( 0, csv_data.length - 1 );
|
||||
csv_data += "\n";
|
||||
|
||||
// Get x axis values
|
||||
var xaxis = {}
|
||||
|
||||
for ( var s = 0; s < series.length; ++s ) {
|
||||
var series_data = series[s].data;
|
||||
for ( var d = 0; d < series_data.length; ++d ) {
|
||||
xaxis[series_data[d][0]] = new Array();
|
||||
// Zero values to start
|
||||
for ( var i = 0; i < series.length; ++i ) {
|
||||
xaxis[series_data[d][0]].push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add chart data
|
||||
for ( var s = 0; s < series.length; ++s ) {
|
||||
var series_data = series[s].data;
|
||||
for ( var d = 0; d < series_data.length; ++d ) {
|
||||
xaxis[series_data[d][0]][s] = series_data[d][1];
|
||||
}
|
||||
}
|
||||
|
||||
// Loop data and output to csv string
|
||||
$.each( xaxis, function( index, value ) {
|
||||
var date = new Date( parseInt( index ) );
|
||||
|
||||
if ( groupby == 'day' )
|
||||
csv_data += date.getFullYear() + "-" + parseInt( date.getMonth() + 1 ) + "-" + date.getDate() + ',';
|
||||
else
|
||||
csv_data += date.getFullYear() + "-" + parseInt( date.getMonth() + 1 ) + ',';
|
||||
|
||||
for ( var d = 0; d < value.length; ++d ) {
|
||||
val = value[d];
|
||||
|
||||
if( Math.round( val ) != val )
|
||||
val = val.toFixed(2);
|
||||
|
||||
csv_data += val + ',';
|
||||
}
|
||||
csv_data = csv_data.substring( 0, csv_data.length - 1 );
|
||||
csv_data += "\n";
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
// Set data as href and return
|
||||
$(this).attr( 'href', encodeURI( csv_data ) );
|
||||
return true;
|
||||
});
|
||||
});
|
|
@ -0,0 +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,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)}});e(".export_csv").click(function(){var t=e(this).data("exclude_series")||"";t=t.toString();t=t.split(",");var n=e(this).data("xaxes"),r=e(this).data("groupby"),i=e(this).data("export"),s="data:application/csv;charset=utf-8,";if(i=="table"){e(this).closest("div").find("thead tr,tbody tr").each(function(){e(this).find("th,td").each(function(){value=e(this).text();value=value.replace("[?]","");s+='"'+value+'"'+","});s=s.substring(0,s.length-1);s+="\n"});e(this).closest("div").find("tfoot tr").each(function(){e(this).find("th,td").each(function(){value=e(this).text();value=value.replace("[?]","");s+='"'+value+'"'+",";if(e(this).attr("colspan")>0)for(h=1;h<e(this).attr("colspan");h++)s+='"",'});s=s.substring(0,s.length-1);s+="\n"})}else{if(!window.main_chart)return!1;var o=window.main_chart.getData(),u=[];s+=n+",";e.each(o,function(n,r){(!t||e.inArray(n.toString(),t)==-1)&&u.push(r)});for(var a=0;a<u.length;++a)s+=u[a].label+",";s=s.substring(0,s.length-1);s+="\n";var f={};for(var a=0;a<u.length;++a){var l=u[a].data;for(var c=0;c<l.length;++c){f[l[c][0]]=new Array;for(var h=0;h<u.length;++h)f[l[c][0]].push(0)}}for(var a=0;a<u.length;++a){var l=u[a].data;for(var c=0;c<l.length;++c)f[l[c][0]][a]=l[c][1]}e.each(f,function(e,t){var n=new Date(parseInt(e));r=="day"?s+=n.getFullYear()+"-"+parseInt(n.getMonth()+1)+"-"+n.getDate()+",":s+=n.getFullYear()+"-"+parseInt(n.getMonth()+1)+",";for(var i=0;i<t.length;++i){val=t[i];Math.round(val)!=val&&(val=val.toFixed(2));s+=val+","}s=s.substring(0,s.length-1);s+="\n"})}e(this).attr("href",encodeURI(s));return!0})});
|
|
@ -1,17 +1,16 @@
|
|||
// Chosen, a Select Box Enhancer for jQuery and Prototype
|
||||
// by Patrick Filler for Harvest, http://getharvest.com
|
||||
//
|
||||
// Version 0.9.14
|
||||
// Version 0.10.0
|
||||
// Full source at https://github.com/harvesthq/chosen
|
||||
// Copyright (c) 2011 Harvest http://getharvest.com
|
||||
|
||||
// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
|
||||
// This file is generated by `cake build`, do not edit it by hand.
|
||||
// This file is generated by `grunt build`, do not edit it by hand.
|
||||
(function() {
|
||||
var SelectParser;
|
||||
|
||||
SelectParser = (function() {
|
||||
|
||||
function SelectParser() {
|
||||
this.options_index = 0;
|
||||
this.parsed = [];
|
||||
|
@ -92,19 +91,12 @@
|
|||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
Chosen source: generate output using 'cake build'
|
||||
Copyright (c) 2011 by Harvest
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var AbstractChosen, root;
|
||||
|
||||
root = this;
|
||||
|
||||
AbstractChosen = (function() {
|
||||
|
||||
function AbstractChosen(form_field, options) {
|
||||
this.form_field = form_field;
|
||||
this.options = options != null ? options : {};
|
||||
|
@ -189,23 +181,25 @@ Copyright (c) 2011 by Harvest
|
|||
|
||||
AbstractChosen.prototype.result_add_option = function(option) {
|
||||
var classes, style;
|
||||
if (!option.disabled) {
|
||||
option.dom_id = this.container_id + "_o_" + option.array_index;
|
||||
classes = option.selected && this.is_multiple ? [] : ["active-result"];
|
||||
if (option.selected) {
|
||||
classes.push("result-selected");
|
||||
}
|
||||
if (option.group_array_index != null) {
|
||||
classes.push("group-option");
|
||||
}
|
||||
if (option.classes !== "") {
|
||||
classes.push(option.classes);
|
||||
}
|
||||
style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
|
||||
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
|
||||
} else {
|
||||
return "";
|
||||
option.dom_id = this.container_id + "_o_" + option.array_index;
|
||||
classes = [];
|
||||
if (!option.disabled && !(option.selected && this.is_multiple)) {
|
||||
classes.push("active-result");
|
||||
}
|
||||
if (option.disabled && !(option.selected && this.is_multiple)) {
|
||||
classes.push("disabled-result");
|
||||
}
|
||||
if (option.selected) {
|
||||
classes.push("result-selected");
|
||||
}
|
||||
if (option.group_array_index != null) {
|
||||
classes.push("group-option");
|
||||
}
|
||||
if (option.classes !== "") {
|
||||
classes.push(option.classes);
|
||||
}
|
||||
style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
|
||||
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
|
||||
};
|
||||
|
||||
AbstractChosen.prototype.results_update_field = function() {
|
||||
|
@ -252,7 +246,7 @@ Copyright (c) 2011 by Harvest
|
|||
|
||||
AbstractChosen.prototype.choices_click = function(evt) {
|
||||
evt.preventDefault();
|
||||
if (!this.results_showing) {
|
||||
if (!(this.results_showing || this.is_disabled)) {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
|
@ -337,14 +331,8 @@ Copyright (c) 2011 by Harvest
|
|||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
Chosen source: generate output using 'cake build'
|
||||
Copyright (c) 2011 by Harvest
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var $, Chosen, root,
|
||||
var $, Chosen, root, _ref,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
|
@ -368,11 +356,11 @@ Copyright (c) 2011 by Harvest
|
|||
});
|
||||
|
||||
Chosen = (function(_super) {
|
||||
|
||||
__extends(Chosen, _super);
|
||||
|
||||
function Chosen() {
|
||||
return Chosen.__super__.constructor.apply(this, arguments);
|
||||
_ref = Chosen.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
}
|
||||
|
||||
Chosen.prototype.setup = function() {
|
||||
|
@ -534,8 +522,8 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.search_results_mousewheel = function(evt) {
|
||||
var delta, _ref, _ref1;
|
||||
delta = -((_ref = evt.originalEvent) != null ? _ref.wheelDelta : void 0) || ((_ref1 = evt.originialEvent) != null ? _ref1.detail : void 0);
|
||||
var delta, _ref1, _ref2;
|
||||
delta = -((_ref1 = evt.originalEvent) != null ? _ref1.wheelDelta : void 0) || ((_ref2 = evt.originialEvent) != null ? _ref2.detail : void 0);
|
||||
if (delta != null) {
|
||||
evt.preventDefault();
|
||||
if (evt.type === 'DOMMouseScroll') {
|
||||
|
@ -556,7 +544,6 @@ Copyright (c) 2011 by Harvest
|
|||
this.active_field = false;
|
||||
this.results_hide();
|
||||
this.container.removeClass("chzn-container-active");
|
||||
this.winnow_results_clear();
|
||||
this.clear_backstroke();
|
||||
this.show_search_field_default();
|
||||
return this.search_field_scale();
|
||||
|
@ -578,24 +565,26 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.results_build = function() {
|
||||
var content, data, _i, _len, _ref;
|
||||
var content, data, _i, _len, _ref1;
|
||||
this.parsing = true;
|
||||
this.selected_option_count = null;
|
||||
this.results_data = root.SelectParser.select_to_array(this.form_field);
|
||||
if (this.is_multiple && this.choices_count() > 0) {
|
||||
if (this.is_multiple) {
|
||||
this.search_choices.find("li.search-choice").remove();
|
||||
} else if (!this.is_multiple) {
|
||||
this.selected_item.addClass("chzn-default").find("span").text(this.default_text);
|
||||
if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
|
||||
this.search_field.prop('readonly', true);
|
||||
this.container.addClass("chzn-container-single-nosearch");
|
||||
} else {
|
||||
this.search_field.prop('readonly', false);
|
||||
this.container.removeClass("chzn-container-single-nosearch");
|
||||
}
|
||||
}
|
||||
content = '';
|
||||
_ref = this.results_data;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
data = _ref[_i];
|
||||
_ref1 = this.results_data;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
data = _ref1[_i];
|
||||
if (data.group) {
|
||||
content += this.result_add_group(data);
|
||||
} else if (!data.empty) {
|
||||
|
@ -618,12 +607,8 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.result_add_group = function(group) {
|
||||
if (!group.disabled) {
|
||||
group.dom_id = this.container_id + "_g_" + group.array_index;
|
||||
return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
group.dom_id = this.container_id + "_g_" + group.array_index;
|
||||
return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
|
||||
};
|
||||
|
||||
Chosen.prototype.result_do_highlight = function(el) {
|
||||
|
@ -653,9 +638,7 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.results_show = function() {
|
||||
if (this.result_single_selected != null) {
|
||||
this.result_do_highlight(this.result_single_selected);
|
||||
} else if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
|
||||
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
|
||||
this.form_field_jq.trigger("liszt:maxselected", {
|
||||
chosen: this
|
||||
});
|
||||
|
@ -672,11 +655,13 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.results_hide = function() {
|
||||
this.result_clear_highlight();
|
||||
this.container.removeClass("chzn-with-drop");
|
||||
this.form_field_jq.trigger("liszt:hiding_dropdown", {
|
||||
chosen: this
|
||||
});
|
||||
if (this.results_showing) {
|
||||
this.result_clear_highlight();
|
||||
this.container.removeClass("chzn-with-drop");
|
||||
this.form_field_jq.trigger("liszt:hiding_dropdown", {
|
||||
chosen: this
|
||||
});
|
||||
}
|
||||
return this.results_showing = false;
|
||||
};
|
||||
|
||||
|
@ -693,7 +678,7 @@ Copyright (c) 2011 by Harvest
|
|||
var _this = this;
|
||||
this.form_field_label = this.form_field_jq.parents("label");
|
||||
if (!this.form_field_label.length && this.form_field.id.length) {
|
||||
this.form_field_label = $("label[for=" + this.form_field.id + "]");
|
||||
this.form_field_label = $("label[for='" + this.form_field.id + "']");
|
||||
}
|
||||
if (this.form_field_label.length > 0) {
|
||||
return this.form_field_label.click(function(evt) {
|
||||
|
@ -814,7 +799,7 @@ Copyright (c) 2011 by Harvest
|
|||
return false;
|
||||
}
|
||||
if (this.is_multiple) {
|
||||
this.result_deactivate(high);
|
||||
high.removeClass("active-result");
|
||||
} else {
|
||||
this.search_results.find(".result-selected").removeClass("result-selected");
|
||||
this.result_single_selected = high;
|
||||
|
@ -848,12 +833,18 @@ Copyright (c) 2011 by Harvest
|
|||
}
|
||||
};
|
||||
|
||||
Chosen.prototype.result_activate = function(el) {
|
||||
return el.addClass("active-result");
|
||||
Chosen.prototype.result_activate = function(el, option) {
|
||||
if (option.disabled) {
|
||||
return el.addClass("disabled-result");
|
||||
} else if (this.is_multiple && option.selected) {
|
||||
return el.addClass("result-selected");
|
||||
} else {
|
||||
return el.addClass("active-result");
|
||||
}
|
||||
};
|
||||
|
||||
Chosen.prototype.result_deactivate = function(el) {
|
||||
return el.removeClass("active-result");
|
||||
return el.removeClass("active-result result-selected disabled-result");
|
||||
};
|
||||
|
||||
Chosen.prototype.result_deselect = function(pos) {
|
||||
|
@ -878,26 +869,30 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.single_deselect_control_build = function() {
|
||||
if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) {
|
||||
return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
|
||||
if (!this.allow_single_deselect) {
|
||||
return;
|
||||
}
|
||||
if (!this.selected_item.find("abbr").length) {
|
||||
this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
|
||||
}
|
||||
return this.selected_item.addClass("chzn-single-with-deselect");
|
||||
};
|
||||
|
||||
Chosen.prototype.winnow_results = function() {
|
||||
var found, option, part, parts, regex, regexAnchor, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len1, _ref;
|
||||
var found, option, part, parts, regex, regexAnchor, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len1, _ref1;
|
||||
this.no_results_clear();
|
||||
results = 0;
|
||||
searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html();
|
||||
regexAnchor = this.search_contains ? "" : "^";
|
||||
regex = new RegExp(regexAnchor + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
|
||||
zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
|
||||
_ref = this.results_data;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
option = _ref[_i];
|
||||
if (!option.disabled && !option.empty) {
|
||||
_ref1 = this.results_data;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
option = _ref1[_i];
|
||||
if (!option.empty) {
|
||||
if (option.group) {
|
||||
$('#' + option.dom_id).css('display', 'none');
|
||||
} else if (!(this.is_multiple && option.selected)) {
|
||||
} else {
|
||||
found = false;
|
||||
result_id = option.dom_id;
|
||||
result = $("#" + result_id);
|
||||
|
@ -925,7 +920,7 @@ Copyright (c) 2011 by Harvest
|
|||
text = option.html;
|
||||
}
|
||||
result.html(text);
|
||||
this.result_activate(result);
|
||||
this.result_activate(result, option);
|
||||
if (option.group_array_index != null) {
|
||||
$("#" + this.results_data[option.group_array_index].dom_id).css('display', 'list-item');
|
||||
}
|
||||
|
@ -945,25 +940,6 @@ Copyright (c) 2011 by Harvest
|
|||
}
|
||||
};
|
||||
|
||||
Chosen.prototype.winnow_results_clear = function() {
|
||||
var li, lis, _i, _len, _results;
|
||||
this.search_field.val("");
|
||||
lis = this.search_results.find("li");
|
||||
_results = [];
|
||||
for (_i = 0, _len = lis.length; _i < _len; _i++) {
|
||||
li = lis[_i];
|
||||
li = $(li);
|
||||
if (li.hasClass("group-result")) {
|
||||
_results.push(li.css('display', 'auto'));
|
||||
} else if (!this.is_multiple || !li.hasClass("result-selected")) {
|
||||
_results.push(this.result_activate(li));
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Chosen.prototype.winnow_results_set_highlight = function() {
|
||||
var do_high, selected_results;
|
||||
if (!this.result_highlight) {
|
||||
|
@ -987,19 +963,13 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.keydown_arrow = function() {
|
||||
var first_active, next_sib;
|
||||
if (!this.result_highlight) {
|
||||
first_active = this.search_results.find("li.active-result").first();
|
||||
if (first_active) {
|
||||
this.result_do_highlight($(first_active));
|
||||
}
|
||||
} else if (this.results_showing) {
|
||||
var next_sib;
|
||||
if (this.results_showing && this.result_highlight) {
|
||||
next_sib = this.result_highlight.nextAll("li.active-result").first();
|
||||
if (next_sib) {
|
||||
this.result_do_highlight(next_sib);
|
||||
return this.result_do_highlight(next_sib);
|
||||
}
|
||||
}
|
||||
if (!this.results_showing) {
|
||||
} else {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
|
@ -1047,8 +1017,8 @@ Copyright (c) 2011 by Harvest
|
|||
};
|
||||
|
||||
Chosen.prototype.keydown_checker = function(evt) {
|
||||
var stroke, _ref;
|
||||
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
|
||||
var stroke, _ref1;
|
||||
stroke = (_ref1 = evt.which) != null ? _ref1 : evt.keyCode;
|
||||
this.search_field_scale();
|
||||
if (stroke !== 8 && this.pending_backstroke) {
|
||||
this.clear_backstroke();
|
||||
|
@ -1071,6 +1041,7 @@ Copyright (c) 2011 by Harvest
|
|||
this.keyup_arrow();
|
||||
break;
|
||||
case 40:
|
||||
evt.preventDefault();
|
||||
this.keydown_arrow();
|
||||
break;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,6 +17,16 @@ function woocommerce_show_messages() {
|
|||
wc_show_messages();
|
||||
}
|
||||
|
||||
function woocommerce_weekend_area_js() {
|
||||
_deprecated_function( 'woocommerce_weekend_area_js', '2.1', '' );
|
||||
}
|
||||
function woocommerce_tooltip_js() {
|
||||
_deprecated_function( 'woocommerce_tooltip_js', '2.1', '' );
|
||||
}
|
||||
function woocommerce_datepicker_js() {
|
||||
_deprecated_function( 'woocommerce_datepicker_js', '2.1', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle renamed filters
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Reporting Functions
|
||||
*
|
||||
* Functions for getting sales reports.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Core
|
||||
* @package WooCommerce/Functions
|
||||
* @version 2.1.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
||||
|
Loading…
Reference in New Issue