1 <?php
  2 /**
  3  * WC_Report_Sales_By_Product
  4  *
  5  * @author      WooThemes
  6  * @category    Admin
  7  * @package     WooCommerce/Admin/Reports
  8  * @version     2.1.0
  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      * Constructor
 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      * Get the legend for the main chart sidebar
 27      * @return array
 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      * Output the report
 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      * [get_chart_widgets description]
122      * @return array
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      * Show current filters
145      * @return void
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      * Product selection
165      * @return void
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&hellip;', '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                         // Ajax Chosen Product Selectors
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      * Output an export link
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      * Get the main chart
329      * @return string
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( '&larr; Choose a product to view stats', 'woocommerce' ); ?></p>
338             </div>
339             <?php
340         } else {
341             // Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date
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             // Prepare data for report
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             // Encode in json format
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 }
WooCommerce API documentation generated by ApiGen 2.8.0