1 <?php
2 3 4 5 6 7 8 9
10 class WC_Report_Sales_By_Product extends WC_Admin_Report {
11
12 public $chart_colours = array();
13 public $product_ids = array();
14
15 16 17
18 public function __construct() {
19 if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) )
20 $this->product_ids = array_map( 'absint', $_GET['product_ids'] );
21 elseif ( isset( $_GET['product_ids'] ) )
22 $this->product_ids = array( absint( $_GET['product_ids'] ) );
23 }
24
25 26 27 28
29 public function get_chart_legend() {
30 if ( ! $this->product_ids )
31 return array();
32
33 $legend = array();
34
35 $total_sales = $this->get_order_report_data( array(
36 'data' => array(
37 '_line_total' => array(
38 'type' => 'order_item_meta',
39 'order_item_type' => 'line_item',
40 'function' => 'SUM',
41 'name' => 'order_item_amount'
42 )
43 ),
44 'where_meta' => array(
45 'relation' => 'OR',
46 array(
47 'type' => 'order_item_meta',
48 'meta_key' => array( '_product_id', '_variation_id' ),
49 'meta_value' => $this->product_ids,
50 'operator' => 'IN'
51 )
52 ),
53 'query_type' => 'get_var',
54 'filter_range' => true
55 ) );
56 $total_items = absint( $this->get_order_report_data( array(
57 'data' => array(
58 '_qty' => array(
59 'type' => 'order_item_meta',
60 'order_item_type' => 'line_item',
61 'function' => 'SUM',
62 'name' => 'order_item_count'
63 )
64 ),
65 'where_meta' => array(
66 'relation' => 'OR',
67 array(
68 'type' => 'order_item_meta',
69 'meta_key' => array( '_product_id', '_variation_id' ),
70 'meta_value' => $this->product_ids,
71 'operator' => 'IN'
72 )
73 ),
74 'query_type' => 'get_var',
75 'filter_range' => true
76 ) ) );
77
78 $legend[] = array(
79 'title' => sprintf( __( '%s sales for the selected items', 'woocommerce' ), '<strong>' . wc_price( $total_sales ) . '</strong>' ),
80 'color' => $this->chart_colours['sales_amount'],
81 'highlight_series' => 1
82 );
83 $legend[] = array(
84 'title' => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . $total_items . '</strong>' ),
85 'color' => $this->chart_colours['item_count'],
86 'highlight_series' => 0
87 );
88
89 return $legend;
90 }
91
92 93 94
95 public function output_report() {
96 global $woocommerce, $wpdb, $wp_locale;
97
98 $ranges = array(
99 'year' => __( 'Year', 'woocommerce' ),
100 'last_month' => __( 'Last Month', 'woocommerce' ),
101 'month' => __( 'This Month', 'woocommerce' ),
102 '7day' => __( 'Last 7 Days', 'woocommerce' )
103 );
104
105 $this->chart_colours = array(
106 'sales_amount' => '#3498db',
107 'item_count' => '#d4d9dc',
108 );
109
110 $current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
111
112 if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) )
113 $current_range = '7day';
114
115 $this->calculate_current_range( $current_range );
116
117 include( WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php');
118 }
119
120 121 122 123
124 public function get_chart_widgets() {
125
126 $widgets = array();
127
128 if ( ! empty( $this->product_ids ) ) {
129 $widgets[] = array(
130 'title' => __( 'Showing reports for:', 'woocommerce' ),
131 'callback' => array( $this, 'current_filters' )
132 );
133 }
134
135 $widgets[] = array(
136 'title' => '',
137 'callback' => array( $this, 'products_widget' )
138 );
139
140 return $widgets;
141 }
142
143 144 145 146
147 public function current_filters() {
148 $this->product_ids_titles = array();
149
150 foreach ( $this->product_ids as $product_id ) {
151 $product = get_product( $product_id );
152 if ( $product ) {
153 $this->product_ids_titles[] = $product->get_formatted_name();
154 } else {
155 $this->product_ids_titles[] = '#' . $product_id;
156 }
157 }
158
159 echo '<p>' . ' <strong>' . implode( ', ', $this->product_ids_titles ) . '</strong></p>';
160 echo '<p><a class="button" href="' . remove_query_arg( 'product_ids' ) . '">' . __( 'Reset', 'woocommerce' ) . '</a></p>';
161 }
162
163 164 165 166
167 public function products_widget() {
168 ?>
169 <h4 class="section_title"><span><?php _e( 'Product Search', 'woocommerce' ); ?></span></h4>
170 <div class="section">
171 <form method="GET">
172 <div>
173 <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>
174 <input type="submit" class="submit button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
175 <input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" />
176 <input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" />
177 <input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" />
178 <input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" />
179 <input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" />
180 <input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" />
181 </div>
182 <script type="text/javascript">
183 jQuery(function(){
184
185 jQuery("select.ajax_chosen_select_products").ajaxChosen({
186 method: 'GET',
187 url: '<?php echo admin_url('admin-ajax.php'); ?>',
188 dataType: 'json',
189 afterTypeDelay: 100,
190 data: {
191 action: 'woocommerce_json_search_products_and_variations',
192 security: '<?php echo wp_create_nonce("search-products"); ?>'
193 }
194 }, function (data) {
195 var terms = {};
196
197 jQuery.each(data, function (i, val) {
198 terms[i] = val;
199 });
200 return terms;
201 });
202 });
203 </script>
204 </form>
205 </div>
206 <h4 class="section_title"><span><?php _e( 'Top Sellers', 'woocommerce' ); ?></span></h4>
207 <div class="section">
208 <table cellspacing="0">
209 <?php
210 $top_sellers = $this->get_order_report_data( array(
211 'data' => array(
212 '_product_id' => array(
213 'type' => 'order_item_meta',
214 'order_item_type' => 'line_item',
215 'function' => '',
216 'name' => 'product_id'
217 ),
218 '_qty' => array(
219 'type' => 'order_item_meta',
220 'order_item_type' => 'line_item',
221 'function' => 'SUM',
222 'name' => 'order_item_qty'
223 )
224 ),
225 'order_by' => 'order_item_qty DESC',
226 'group_by' => 'product_id',
227 'limit' => 12,
228 'query_type' => 'get_results',
229 'filter_range' => true
230 ) );
231
232 if ( $top_sellers ) {
233 foreach ( $top_sellers as $product ) {
234 echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
235 <td class="count">' . $product->order_item_qty . '</td>
236 <td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
237 <td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
238 </tr>';
239 }
240 } else {
241 echo '<tr><td colspan="3">' . __( 'No products found in range', 'woocommerce' ) . '</td></tr>';
242 }
243 ?>
244 </table>
245 </div>
246 <h4 class="section_title"><span><?php _e( 'Top Earners', 'woocommerce' ); ?></span></h4>
247 <div class="section">
248 <table cellspacing="0">
249 <?php
250 $top_earners = $this->get_order_report_data( array(
251 'data' => array(
252 '_product_id' => array(
253 'type' => 'order_item_meta',
254 'order_item_type' => 'line_item',
255 'function' => '',
256 'name' => 'product_id'
257 ),
258 '_line_total' => array(
259 'type' => 'order_item_meta',
260 'order_item_type' => 'line_item',
261 'function' => 'SUM',
262 'name' => 'order_item_total'
263 )
264 ),
265 'order_by' => 'order_item_total DESC',
266 'group_by' => 'product_id',
267 'limit' => 12,
268 'query_type' => 'get_results',
269 'filter_range' => true
270 ) );
271
272 if ( $top_earners ) {
273 foreach ( $top_earners as $product ) {
274 echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
275 <td class="count">' . wc_price( $product->order_item_total ) . '</td>
276 <td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
277 <td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'sales' ) . '</td>
278 </tr>';
279 }
280 } else {
281 echo '<tr><td colspan="3">' . __( 'No products found in range', 'woocommerce' ) . '</td></tr>';
282 }
283 ?>
284 </table>
285 </div>
286 <script type="text/javascript">
287 jQuery('.section_title').click(function(){
288 var next_section = jQuery(this).next('.section');
289
290 if ( jQuery(next_section).is(':visible') )
291 return false;
292
293 jQuery('.section:visible').slideUp();
294 jQuery('.section_title').removeClass('open');
295 jQuery(this).addClass('open').next('.section').slideDown();
296
297 return false;
298 });
299 jQuery('.section').slideUp( 100, function() {
300 <?php if ( empty( $this->product_ids ) ) : ?>
301 jQuery('.section_title:eq(1)').click();
302 <?php endif; ?>
303 });
304 </script>
305 <?php
306 }
307
308 309 310
311 public function get_export_button() {
312 $current_range = ! empty( $_GET['range'] ) ? $_GET['range'] : '7day';
313 ?>
314 <a
315 href="#"
316 download="report-<?php echo $current_range; ?>-<?php echo date_i18n( 'Y-m-d', current_time('timestamp') ); ?>.csv"
317 class="export_csv"
318 data-export="chart"
319 data-xaxes="<?php _e( 'Date', 'woocommerce' ); ?>"
320 data-groupby="<?php echo $this->chart_groupby; ?>"
321 >
322 <?php _e( 'Export CSV', 'woocommerce' ); ?>
323 </a>
324 <?php
325 }
326
327 328 329 330
331 public function get_main_chart() {
332 global $wp_locale;
333
334 if ( ! $this->product_ids ) {
335 ?>
336 <div class="chart-container">
337 <p class="chart-prompt"><?php _e( '← Choose a product to view stats', 'woocommerce' ); ?></p>
338 </div>
339 <?php
340 } else {
341
342 $order_item_counts = $this->get_order_report_data( array(
343 'data' => array(
344 '_qty' => array(
345 'type' => 'order_item_meta',
346 'order_item_type' => 'line_item',
347 'function' => 'SUM',
348 'name' => 'order_item_count'
349 ),
350 'post_date' => array(
351 'type' => 'post_data',
352 'function' => '',
353 'name' => 'post_date'
354 ),
355 '_product_id' => array(
356 'type' => 'order_item_meta',
357 'order_item_type' => 'line_item',
358 'function' => '',
359 'name' => 'product_id'
360 )
361 ),
362 'where_meta' => array(
363 'relation' => 'OR',
364 array(
365 'type' => 'order_item_meta',
366 'meta_key' => array( '_product_id', '_variation_id' ),
367 'meta_value' => $this->product_ids,
368 'operator' => 'IN'
369 ),
370 ),
371 'group_by' => 'product_id,' . $this->group_by_query,
372 'order_by' => 'post_date ASC',
373 'query_type' => 'get_results',
374 'filter_range' => true
375 ) );
376
377 $order_item_amounts = $this->get_order_report_data( array(
378 'data' => array(
379 '_line_total' => array(
380 'type' => 'order_item_meta',
381 'order_item_type' => 'line_item',
382 'function' => 'SUM',
383 'name' => 'order_item_amount'
384 ),
385 'post_date' => array(
386 'type' => 'post_data',
387 'function' => '',
388 'name' => 'post_date'
389 ),
390 '_product_id' => array(
391 'type' => 'order_item_meta',
392 'order_item_type' => 'line_item',
393 'function' => '',
394 'name' => 'product_id'
395 ),
396 ),
397 'where_meta' => array(
398 'relation' => 'OR',
399 array(
400 'type' => 'order_item_meta',
401 'meta_key' => array( '_product_id', '_variation_id' ),
402 'meta_value' => $this->product_ids,
403 'operator' => 'IN'
404 ),
405 ),
406 'group_by' => 'product_id, ' . $this->group_by_query,
407 'order_by' => 'post_date ASC',
408 'query_type' => 'get_results',
409 'filter_range' => true
410 ) );
411
412
413 $order_item_counts = $this->prepare_chart_data( $order_item_counts, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
414 $order_item_amounts = $this->prepare_chart_data( $order_item_amounts, 'post_date', 'order_item_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
415
416
417 $chart_data = json_encode( array(
418 'order_item_counts' => array_values( $order_item_counts ),
419 'order_item_amounts' => array_values( $order_item_amounts )
420 ) );
421 ?>
422 <div class="chart-container">
423 <div class="chart-placeholder main"></div>
424 </div>
425 <script type="text/javascript">
426 var main_chart;
427
428 jQuery(function(){
429 var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
430
431 var drawGraph = function( highlight ) {
432
433 var series = [
434 {
435 label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ) ?>",
436 data: order_data.order_item_counts,
437 color: '<?php echo $this->chart_colours['item_count']; ?>',
438 bars: { fillColor: '<?php echo $this->chart_colours['item_count']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
439 shadowSize: 0,
440 hoverable: false
441 },
442 {
443 label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>",
444 data: order_data.order_item_amounts,
445 yaxis: 2,
446 color: '<?php echo $this->chart_colours['sales_amount']; ?>',
447 points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
448 lines: { show: true, lineWidth: 4, fill: false },
449 shadowSize: 0,
450 prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); ?>"
451 }
452 ];
453
454 if ( highlight !== 'undefined' && series[ highlight ] ) {
455 highlight_series = series[ highlight ];
456
457 highlight_series.color = '#9c5d90';
458
459 if ( highlight_series.bars )
460 highlight_series.bars.fillColor = '#9c5d90';
461
462 if ( highlight_series.lines ) {
463 highlight_series.lines.lineWidth = 5;
464 }
465 }
466
467 main_chart = jQuery.plot(
468 jQuery('.chart-placeholder.main'),
469 series,
470 {
471 legend: {
472 show: false
473 },
474 grid: {
475 color: '#aaa',
476 borderColor: 'transparent',
477 borderWidth: 0,
478 hoverable: true
479 },
480 xaxes: [ {
481 color: '#aaa',
482 position: "bottom",
483 tickColor: 'transparent',
484 mode: "time",
485 timeformat: "<?php if ( $this->chart_groupby == 'day' ) echo '%d %b'; else echo '%b'; ?>",
486 monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
487 tickLength: 1,
488 minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
489 font: {
490 color: "#aaa"
491 }
492 } ],
493 yaxes: [
494 {
495 min: 0,
496 minTickSize: 1,
497 tickDecimals: 0,
498 color: '#ecf0f1',
499 font: { color: "#aaa" }
500 },
501 {
502 position: "right",
503 min: 0,
504 tickDecimals: 2,
505 alignTicksWithAxis: 1,
506 color: 'transparent',
507 font: { color: "#aaa" }
508 }
509 ],
510 }
511 );
512
513 jQuery('.chart-placeholder').resize();
514 }
515
516 drawGraph();
517
518 jQuery('.highlight_series').hover(
519 function() {
520 drawGraph( jQuery(this).data('series') );
521 },
522 function() {
523 drawGraph();
524 }
525 );
526 });
527 </script>
528 <?php
529 }
530 }
531 }