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