/includes/admin/reports/class-wc-report-sales-by-category.php

  1. <?php 
  2.  
  3. if ( ! defined( 'ABSPATH' ) ) { 
  4. exit; // Exit if accessed directly 
  5.  
  6. /** 
  7. * WC_Report_Sales_By_Category 
  8. * 
  9. * @author WooThemes 
  10. * @category Admin 
  11. * @package WooCommerce/Admin/Reports 
  12. * @version 2.1.0 
  13. */ 
  14. class WC_Report_Sales_By_Category extends WC_Admin_Report { 
  15.  
  16. /** 
  17. * Chart colors. 
  18. * 
  19. * @var array 
  20. */ 
  21. public $chart_colours = array(); 
  22.  
  23. /** 
  24. * Categories ids. 
  25. * 
  26. * @var array 
  27. */ 
  28. public $show_categories = array(); 
  29.  
  30. /** 
  31. * Item sales. 
  32. * 
  33. * @var array 
  34. */ 
  35. private $item_sales = array(); 
  36.  
  37. /** 
  38. * Item sales and times. 
  39. * 
  40. * @var array 
  41. */ 
  42. private $item_sales_and_times = array(); 
  43.  
  44. /** 
  45. * Constructor. 
  46. */ 
  47. public function __construct() { 
  48. if ( isset( $_GET['show_categories'] ) ) { 
  49. $this->show_categories = is_array( $_GET['show_categories'] ) ? array_map( 'absint', $_GET['show_categories'] ) : array( absint( $_GET['show_categories'] ) ); 
  50.  
  51. /** 
  52. * Get all product ids in a category (and its children). 
  53. * 
  54. * @param int $category_id 
  55. * @return array 
  56. */ 
  57. public function get_products_in_category( $category_id ) { 
  58. $term_ids = get_term_children( $category_id, 'product_cat' ); 
  59. $term_ids[] = $category_id; 
  60. $product_ids = get_objects_in_term( $term_ids, 'product_cat' ); 
  61.  
  62. return array_unique( apply_filters( 'woocommerce_report_sales_by_category_get_products_in_category', $product_ids, $category_id ) ); 
  63.  
  64. /** 
  65. * Get the legend for the main chart sidebar. 
  66. * 
  67. * @return array 
  68. */ 
  69. public function get_chart_legend() { 
  70.  
  71. if ( empty( $this->show_categories ) ) { 
  72. return array(); 
  73.  
  74. $legend = array(); 
  75. $index = 0; 
  76.  
  77. foreach ( $this->show_categories as $category ) { 
  78.  
  79. $category = get_term( $category, 'product_cat' ); 
  80. $total = 0; 
  81. $product_ids = $this->get_products_in_category( $category->term_id ); 
  82.  
  83. foreach ( $product_ids as $id ) { 
  84.  
  85. if ( isset( $this->item_sales[ $id ] ) ) { 
  86. $total += $this->item_sales[ $id ]; 
  87.  
  88. $legend[] = array( 
  89. /** translators: 1: total items sold 2: category name */ 
  90. 'title' => sprintf( __( '%1$s sales in %2$s', 'woocommerce' ), '<strong>' . wc_price( $total ) . '</strong>', $category->name ),  
  91. 'color' => isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0],  
  92. 'highlight_series' => $index,  
  93. ); 
  94.  
  95. $index++; 
  96.  
  97. return $legend; 
  98.  
  99. /** 
  100. * Output the report. 
  101. */ 
  102. public function output_report() { 
  103.  
  104. $ranges = array( 
  105. 'year' => __( 'Year', 'woocommerce' ),  
  106. 'last_month' => __( 'Last month', 'woocommerce' ),  
  107. 'month' => __( 'This month', 'woocommerce' ),  
  108. '7day' => __( 'Last 7 days', 'woocommerce' ),  
  109. ); 
  110.  
  111. $this->chart_colours = array( '#3498db', '#34495e', '#1abc9c', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c', '#2980b9', '#8e44ad', '#2c3e50', '#16a085', '#27ae60', '#f39c12', '#d35400', '#c0392b' ); 
  112.  
  113. $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : '7day'; 
  114.  
  115. if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) { 
  116. $current_range = '7day'; 
  117.  
  118. $this->calculate_current_range( $current_range ); 
  119.  
  120. // Get item sales data 
  121. if ( ! empty( $this->show_categories ) ) { 
  122. $order_items = $this->get_order_report_data( array( 
  123. 'data' => array( 
  124. '_product_id' => array( 
  125. 'type' => 'order_item_meta',  
  126. 'order_item_type' => 'line_item',  
  127. 'function' => '',  
  128. 'name' => 'product_id',  
  129. ),  
  130. '_line_total' => array( 
  131. 'type' => 'order_item_meta',  
  132. 'order_item_type' => 'line_item',  
  133. 'function' => 'SUM',  
  134. 'name' => 'order_item_amount',  
  135. ),  
  136. 'post_date' => array( 
  137. 'type' => 'post_data',  
  138. 'function' => '',  
  139. 'name' => 'post_date',  
  140. ),  
  141. ),  
  142. 'group_by' => 'ID, product_id, post_date',  
  143. 'query_type' => 'get_results',  
  144. 'filter_range' => true,  
  145. ) ); 
  146.  
  147. $this->item_sales = array(); 
  148. $this->item_sales_and_times = array(); 
  149.  
  150. if ( is_array( $order_items ) ) { 
  151.  
  152. foreach ( $order_items as $order_item ) { 
  153.  
  154. switch ( $this->chart_groupby ) { 
  155. case 'day' : 
  156. $time = strtotime( date( 'Ymd', strtotime( $order_item->post_date ) ) ) * 1000; 
  157. break; 
  158. case 'month' : 
  159. default : 
  160. $time = strtotime( date( 'Ym', strtotime( $order_item->post_date ) ) . '01' ) * 1000; 
  161. break; 
  162.  
  163. $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; 
  164.  
  165. $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; 
  166.  
  167. include( WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php' ); 
  168.  
  169. /** 
  170. * Get chart widgets. 
  171. * 
  172. * @return array 
  173. */ 
  174. public function get_chart_widgets() { 
  175.  
  176. return array( 
  177. array( 
  178. 'title' => __( 'Categories', 'woocommerce' ),  
  179. 'callback' => array( $this, 'category_widget' ),  
  180. ),  
  181. ); 
  182.  
  183. /** 
  184. * Output category widget. 
  185. */ 
  186. public function category_widget() { 
  187.  
  188. $categories = get_terms( 'product_cat', array( 'orderby' => 'name' ) ); 
  189. ?> 
  190. <form method="GET"> 
  191. <div> 
  192. <select multiple="multiple" data-placeholder="<?php esc_attr_e( 'Select categories…', 'woocommerce' ); ?>" class="wc-enhanced-select" id="show_categories" name="show_categories[]" style="width: 205px;"> 
  193. <?php 
  194. $r = array(); 
  195. $r['pad_counts'] = 1; 
  196. $r['hierarchical'] = 1; 
  197. $r['hide_empty'] = 1; 
  198. $r['value'] = 'id'; 
  199. $r['selected'] = $this->show_categories; 
  200.  
  201. include_once( WC()->plugin_path() . '/includes/walkers/class-product-cat-dropdown-walker.php' ); 
  202.  
  203. echo wc_walk_category_dropdown_tree( $categories, 0, $r ); 
  204. ?> 
  205. </select> 
  206. <a href="#" class="select_none"><?php _e( 'None', 'woocommerce' ); ?></a> 
  207. <a href="#" class="select_all"><?php _e( 'All', 'woocommerce' ); ?></a> 
  208. <input type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>" /> 
  209. <input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" /> 
  210. <input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" /> 
  211. <input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" /> 
  212. <input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" /> 
  213. <input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" /> 
  214. <input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" /> 
  215. </div> 
  216. <script type="text/javascript"> 
  217. jQuery(function() { 
  218. // Select all/None 
  219. jQuery( '.chart-widget' ).on( 'click', '.select_all', function() { 
  220. jQuery(this).closest( 'div' ).find( 'select option' ).attr( 'selected', 'selected' ); 
  221. jQuery(this).closest( 'div' ).find('select').change(); 
  222. return false; 
  223. }); 
  224.  
  225. jQuery( '.chart-widget').on( 'click', '.select_none', function() { 
  226. jQuery(this).closest( 'div' ).find( 'select option' ).removeAttr( 'selected' ); 
  227. jQuery(this).closest( 'div' ).find('select').change(); 
  228. return false; 
  229. }); 
  230. }); 
  231. </script> 
  232. </form> 
  233. <?php 
  234.  
  235. /** 
  236. * Output an export link. 
  237. */ 
  238. public function get_export_button() { 
  239.  
  240. $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : '7day'; 
  241. ?> 
  242. <a 
  243. href="#" 
  244. download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo date_i18n( 'Y-m-d', current_time( 'timestamp' ) ); ?>.csv" 
  245. class="export_csv" 
  246. data-export="chart" 
  247. data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>" 
  248. data-groupby="<?php echo $this->chart_groupby; ?>" 
  249. <?php _e( 'Export CSV', 'woocommerce' ); ?> 
  250. </a> 
  251. <?php 
  252.  
  253. /** 
  254. * Get the main chart. 
  255. * 
  256. * @return string 
  257. */ 
  258. public function get_main_chart() { 
  259. global $wp_locale; 
  260.  
  261. if ( empty( $this->show_categories ) ) { 
  262. ?> 
  263. <div class="chart-container"> 
  264. <p class="chart-prompt"><?php _e( 'Choose a category to view stats', 'woocommerce' ); ?></p> 
  265. </div> 
  266. <?php 
  267. } else { 
  268. $chart_data = array(); 
  269. $index = 0; 
  270.  
  271. foreach ( $this->show_categories as $category ) { 
  272.  
  273. $category = get_term( $category, 'product_cat' ); 
  274. $product_ids = $this->get_products_in_category( $category->term_id ); 
  275. $category_chart_data = array(); 
  276.  
  277. for ( $i = 0; $i <= $this->chart_interval; $i ++ ) { 
  278.  
  279. $interval_total = 0; 
  280.  
  281. switch ( $this->chart_groupby ) { 
  282. case 'day' : 
  283. $time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $this->start_date ) ) ) * 1000; 
  284. break; 
  285. case 'month' : 
  286. default : 
  287. $time = strtotime( date( 'Ym', strtotime( "+{$i} MONTH", $this->start_date ) ) . '01' ) * 1000; 
  288. break; 
  289.  
  290. foreach ( $product_ids as $id ) { 
  291.  
  292. if ( isset( $this->item_sales_and_times[ $time ][ $id ] ) ) { 
  293. $interval_total += $this->item_sales_and_times[ $time ][ $id ]; 
  294.  
  295. $category_chart_data[] = array( $time, (float) wc_format_decimal( $interval_total, wc_get_price_decimals() ) ); 
  296.  
  297. $chart_data[ $category->term_id ]['category'] = $category->name; 
  298. $chart_data[ $category->term_id ]['data'] = $category_chart_data; 
  299.  
  300. $index++; 
  301. ?> 
  302. <div class="chart-container"> 
  303. <div class="chart-placeholder main"></div> 
  304. </div> 
  305. <script type="text/javascript"> 
  306. var main_chart; 
  307.  
  308. jQuery(function() { 
  309. var drawGraph = function( highlight ) { 
  310. var series = [ 
  311. <?php 
  312. $index = 0; 
  313. foreach ( $chart_data as $data ) { 
  314. $color = isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0]; 
  315. $width = $this->barwidth / sizeof( $chart_data ); 
  316. $offset = ( $width * $index ); 
  317. $series = $data['data']; 
  318. foreach ( $series as $key => $series_data ) { 
  319. $series[ $key ][0] = $series_data[0] + $offset; 
  320. echo '{ 
  321. label: "' . esc_js( $data['category'] ) . '",  
  322. data: jQuery.parseJSON( "' . json_encode( $series ) . '" ),  
  323. color: "' . $color . '",  
  324. bars: { 
  325. fillColor: "' . $color . '",  
  326. fill: true,  
  327. show: true,  
  328. lineWidth: 1,  
  329. align: "center",  
  330. barWidth: ' . $width * 0.75 . ',  
  331. stack: false 
  332. },  
  333. ' . $this->get_currency_tooltip() . ',  
  334. enable_tooltip: true,  
  335. prepend_label: true 
  336. }, '; 
  337. $index++; 
  338. ?> 
  339. ]; 
  340.  
  341. if ( highlight !== 'undefined' && series[ highlight ] ) { 
  342. highlight_series = series[ highlight ]; 
  343.  
  344. highlight_series.color = '#9c5d90'; 
  345.  
  346. if ( highlight_series.bars ) { 
  347. highlight_series.bars.fillColor = '#9c5d90'; 
  348.  
  349. if ( highlight_series.lines ) { 
  350. highlight_series.lines.lineWidth = 5; 
  351.  
  352. main_chart = jQuery.plot( 
  353. jQuery('.chart-placeholder.main'),  
  354. series,  
  355. legend: { 
  356. show: false 
  357. },  
  358. grid: { 
  359. color: '#aaa',  
  360. borderColor: 'transparent',  
  361. borderWidth: 0,  
  362. hoverable: true 
  363. },  
  364. xaxes: [ { 
  365. color: '#aaa',  
  366. reserveSpace: true,  
  367. position: "bottom",  
  368. tickColor: 'transparent',  
  369. mode: "time",  
  370. timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",  
  371. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,  
  372. tickLength: 1,  
  373. minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],  
  374. tickSize: [1, "<?php echo $this->chart_groupby; ?>"],  
  375. font: { 
  376. color: "#aaa" 
  377. } ],  
  378. yaxes: [ 
  379. min: 0,  
  380. tickDecimals: 2,  
  381. color: 'transparent',  
  382. font: { color: "#aaa" } 
  383. ],  
  384. ); 
  385.  
  386. jQuery('.chart-placeholder').resize(); 
  387.  
  388.  
  389. drawGraph(); 
  390.  
  391. jQuery('.highlight_series').hover( 
  392. function() { 
  393. drawGraph( jQuery(this).data('series') ); 
  394. },  
  395. function() { 
  396. drawGraph(); 
  397. ); 
  398. }); 
  399. </script> 
  400. <?php 
.