PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 8.8.7
WooCommerce v8.8.7
10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / reports / class-wc-report-sales-by-product.php
woocommerce / includes / admin / reports Last commit date
class-wc-admin-report.php 3 years ago class-wc-report-coupon-usage.php 5 years ago class-wc-report-customer-list.php 5 years ago class-wc-report-customers.php 5 years ago class-wc-report-downloads.php 5 years ago class-wc-report-low-in-stock.php 5 years ago class-wc-report-most-stocked.php 5 years ago class-wc-report-out-of-stock.php 5 years ago class-wc-report-sales-by-category.php 2 years ago class-wc-report-sales-by-date.php 3 years ago class-wc-report-sales-by-product.php 5 years ago class-wc-report-stock.php 5 years ago class-wc-report-taxes-by-code.php 3 years ago class-wc-report-taxes-by-date.php 4 years ago
class-wc-report-sales-by-product.php
633 lines
1 <?php
2 /**
3 * Sales By Product Reporting
4 *
5 * @package WooCommerce\Admin\Reporting
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 /**
13 * WC_Report_Sales_By_Product
14 *
15 * @package WooCommerce\Admin\Reports
16 * @version 2.1.0
17 */
18 class WC_Report_Sales_By_Product extends WC_Admin_Report {
19
20 /**
21 * Chart colors.
22 *
23 * @var array
24 */
25 public $chart_colours = array();
26
27 /**
28 * Product ids.
29 *
30 * @var array
31 */
32 public $product_ids = array();
33
34 /**
35 * Product ids with titles.
36 *
37 * @var array
38 */
39 public $product_ids_titles = array();
40
41 /**
42 * Constructor.
43 */
44 public function __construct() {
45 // @codingStandardsIgnoreStart
46 if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) ) {
47 $this->product_ids = array_filter( array_map( 'absint', $_GET['product_ids'] ) );
48 } elseif ( isset( $_GET['product_ids'] ) ) {
49 $this->product_ids = array_filter( array( absint( $_GET['product_ids'] ) ) );
50 }
51 // @codingStandardsIgnoreEnd
52 }
53
54 /**
55 * Get the legend for the main chart sidebar.
56 *
57 * @return array
58 */
59 public function get_chart_legend() {
60
61 if ( empty( $this->product_ids ) ) {
62 return array();
63 }
64
65 $legend = array();
66
67 $total_sales = $this->get_order_report_data(
68 array(
69 'data' => array(
70 '_line_total' => array(
71 'type' => 'order_item_meta',
72 'order_item_type' => 'line_item',
73 'function' => 'SUM',
74 'name' => 'order_item_amount',
75 ),
76 ),
77 'where_meta' => array(
78 'relation' => 'OR',
79 array(
80 'type' => 'order_item_meta',
81 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
82 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
83 'operator' => 'IN',
84 ),
85 ),
86 'query_type' => 'get_var',
87 'filter_range' => true,
88 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
89 )
90 );
91
92 $total_items = absint(
93 $this->get_order_report_data(
94 array(
95 'data' => array(
96 '_qty' => array(
97 'type' => 'order_item_meta',
98 'order_item_type' => 'line_item',
99 'function' => 'SUM',
100 'name' => 'order_item_count',
101 ),
102 ),
103 'where_meta' => array(
104 'relation' => 'OR',
105 array(
106 'type' => 'order_item_meta',
107 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
108 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
109 'operator' => 'IN',
110 ),
111 ),
112 'query_type' => 'get_var',
113 'filter_range' => true,
114 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
115 )
116 )
117 );
118
119 $legend[] = array(
120 /* translators: %s: total items sold */
121 'title' => sprintf( __( '%s sales for the selected items', 'woocommerce' ), '<strong>' . wc_price( $total_sales ) . '</strong>' ),
122 'color' => $this->chart_colours['sales_amount'],
123 'highlight_series' => 1,
124 );
125
126 $legend[] = array(
127 /* translators: %s: total items purchased */
128 'title' => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . ( $total_items ) . '</strong>' ),
129 'color' => $this->chart_colours['item_count'],
130 'highlight_series' => 0,
131 );
132
133 return $legend;
134 }
135
136 /**
137 * Output the report.
138 */
139 public function output_report() {
140
141 $ranges = array(
142 'year' => __( 'Year', 'woocommerce' ),
143 'last_month' => __( 'Last month', 'woocommerce' ),
144 'month' => __( 'This month', 'woocommerce' ),
145 '7day' => __( 'Last 7 days', 'woocommerce' ),
146 );
147
148 $this->chart_colours = array(
149 'sales_amount' => '#3498db',
150 'item_count' => '#d4d9dc',
151 );
152
153 $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
154
155 if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
156 $current_range = '7day';
157 }
158
159 $this->check_current_range_nonce( $current_range );
160 $this->calculate_current_range( $current_range );
161
162 include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
163 }
164
165 /**
166 * Get chart widgets.
167 *
168 * @return array
169 */
170 public function get_chart_widgets() {
171
172 $widgets = array();
173
174 if ( ! empty( $this->product_ids ) ) {
175 $widgets[] = array(
176 'title' => __( 'Showing reports for:', 'woocommerce' ),
177 'callback' => array( $this, 'current_filters' ),
178 );
179 }
180
181 $widgets[] = array(
182 'title' => '',
183 'callback' => array( $this, 'products_widget' ),
184 );
185
186 return $widgets;
187 }
188
189 /**
190 * Output current filters.
191 */
192 public function current_filters() {
193
194 $this->product_ids_titles = array();
195
196 foreach ( $this->product_ids as $product_id ) {
197
198 $product = wc_get_product( $product_id );
199
200 if ( $product ) {
201 $this->product_ids_titles[] = $product->get_formatted_name();
202 } else {
203 $this->product_ids_titles[] = '#' . $product_id;
204 }
205 }
206
207 echo '<p><strong>' . wp_kses_post( implode( ', ', $this->product_ids_titles ) ) . '</strong></p>';
208 echo '<p><a class="button" href="' . esc_url( remove_query_arg( 'product_ids' ) ) . '">' . esc_html__( 'Reset', 'woocommerce' ) . '</a></p>';
209 }
210
211 /**
212 * Output products widget.
213 */
214 public function products_widget() {
215 ?>
216 <h4 class="section_title"><span><?php esc_html_e( 'Product search', 'woocommerce' ); ?></span></h4>
217 <div class="section">
218 <form method="GET">
219 <div>
220 <?php // @codingStandardsIgnoreStart ?>
221 <select class="wc-product-search" style="width:203px;" multiple="multiple" id="product_ids" name="product_ids[]" data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'woocommerce' ); ?>" data-action="woocommerce_json_search_products_and_variations"></select>
222 <button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
223 <input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( $_GET['range'] ) : ''; ?>" />
224 <input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( $_GET['start_date'] ) : ''; ?>" />
225 <input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( $_GET['end_date'] ) : ''; ?>" />
226 <input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( $_GET['page'] ) : ''; ?>" />
227 <input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( $_GET['tab'] ) : ''; ?>" />
228 <input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( $_GET['report'] ) : ''; ?>" />
229 <?php wp_nonce_field( 'custom_range', 'wc_reports_nonce', false ); ?>
230 <?php // @codingStandardsIgnoreEnd ?>
231 </div>
232 </form>
233 </div>
234 <h4 class="section_title"><span><?php esc_html_e( 'Top sellers', 'woocommerce' ); ?></span></h4>
235 <div class="section">
236 <table cellspacing="0">
237 <?php
238 $top_sellers = $this->get_order_report_data(
239 array(
240 'data' => array(
241 '_product_id' => array(
242 'type' => 'order_item_meta',
243 'order_item_type' => 'line_item',
244 'function' => '',
245 'name' => 'product_id',
246 ),
247 '_qty' => array(
248 'type' => 'order_item_meta',
249 'order_item_type' => 'line_item',
250 'function' => 'SUM',
251 'name' => 'order_item_qty',
252 ),
253 ),
254 'order_by' => 'order_item_qty DESC',
255 'group_by' => 'product_id',
256 'limit' => 12,
257 'query_type' => 'get_results',
258 'filter_range' => true,
259 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
260 )
261 );
262
263 if ( $top_sellers ) {
264 // @codingStandardsIgnoreStart
265 foreach ( $top_sellers as $product ) {
266 echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
267 <td class="count">' . esc_html( $product->order_item_qty ) . '</td>
268 <td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
269 <td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
270 </tr>';
271 }
272 // @codingStandardsIgnoreEnd
273 } else {
274 echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
275 }
276 ?>
277 </table>
278 </div>
279 <h4 class="section_title"><span><?php esc_html_e( 'Top freebies', 'woocommerce' ); ?></span></h4>
280 <div class="section">
281 <table cellspacing="0">
282 <?php
283 $top_freebies = $this->get_order_report_data(
284 array(
285 'data' => array(
286 '_product_id' => array(
287 'type' => 'order_item_meta',
288 'order_item_type' => 'line_item',
289 'function' => '',
290 'name' => 'product_id',
291 ),
292 '_qty' => array(
293 'type' => 'order_item_meta',
294 'order_item_type' => 'line_item',
295 'function' => 'SUM',
296 'name' => 'order_item_qty',
297 ),
298 ),
299 'where_meta' => array(
300 array(
301 'type' => 'order_item_meta',
302 'meta_key' => '_line_subtotal', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
303 'meta_value' => '0', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
304 'operator' => '=',
305 ),
306 ),
307 'order_by' => 'order_item_qty DESC',
308 'group_by' => 'product_id',
309 'limit' => 12,
310 'query_type' => 'get_results',
311 'filter_range' => true,
312 )
313 );
314
315 if ( $top_freebies ) {
316 // @codingStandardsIgnoreStart
317 foreach ( $top_freebies as $product ) {
318 echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
319 <td class="count">' . esc_html( $product->order_item_qty ) . '</td>
320 <td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
321 <td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
322 </tr>';
323 }
324 // @codingStandardsIgnoreEnd
325 } else {
326 echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
327 }
328 ?>
329 </table>
330 </div>
331 <h4 class="section_title"><span><?php esc_html_e( 'Top earners', 'woocommerce' ); ?></span></h4>
332 <div class="section">
333 <table cellspacing="0">
334 <?php
335 $top_earners = $this->get_order_report_data(
336 array(
337 'data' => array(
338 '_product_id' => array(
339 'type' => 'order_item_meta',
340 'order_item_type' => 'line_item',
341 'function' => '',
342 'name' => 'product_id',
343 ),
344 '_line_total' => array(
345 'type' => 'order_item_meta',
346 'order_item_type' => 'line_item',
347 'function' => 'SUM',
348 'name' => 'order_item_total',
349 ),
350 ),
351 'order_by' => 'order_item_total DESC',
352 'group_by' => 'product_id',
353 'limit' => 12,
354 'query_type' => 'get_results',
355 'filter_range' => true,
356 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
357 )
358 );
359
360 if ( $top_earners ) {
361 // @codingStandardsIgnoreStart
362 foreach ( $top_earners as $product ) {
363 echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
364 <td class="count">' . wc_price( $product->order_item_total ) . '</td>
365 <td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
366 <td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'sales' ) . '</td>
367 </tr>';
368 }
369 // @codingStandardsIgnoreEnd
370 } else {
371 echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
372 }
373 ?>
374 </table>
375 </div>
376 <script type="text/javascript">
377 jQuery( '.section_title' ).on( 'click', function() {
378 var next_section = jQuery( this ).next( '.section' );
379
380 if ( jQuery( next_section ).is( ':visible' ) ) {
381 return false;
382 }
383
384 jQuery( '.section:visible' ).slideUp();
385 jQuery( '.section_title' ).removeClass( 'open' );
386 jQuery( this ).addClass( 'open' ).next( '.section' ).slideDown();
387
388 return false;
389 } );
390 jQuery( '.section' ).slideUp( 100, function() {
391 <?php if ( empty( $this->product_ids ) ) : ?>
392 jQuery( '.section_title:eq(1)' ).trigger( 'click' );
393 <?php endif; ?>
394 } );
395 </script>
396 <?php
397 }
398
399 /**
400 * Output an export link.
401 */
402 public function get_export_button() {
403
404 $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
405 ?>
406 <a
407 href="#"
408 download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_html( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
409 class="export_csv"
410 data-export="chart"
411 data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
412 data-groupby="<?php echo $this->chart_groupby; ?>"<?php // @codingStandardsIgnoreLine ?>
413 >
414 <?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
415 </a>
416 <?php
417 }
418
419 /**
420 * Get the main chart.
421 */
422 public function get_main_chart() {
423 global $wp_locale;
424
425 if ( empty( $this->product_ids ) ) {
426 ?>
427 <div class="chart-container">
428 <p class="chart-prompt"><?php esc_html_e( 'Choose a product to view stats', 'woocommerce' ); ?></p>
429 </div>
430 <?php
431 } else {
432 // Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
433 $order_item_counts = $this->get_order_report_data(
434 array(
435 'data' => array(
436 '_qty' => array(
437 'type' => 'order_item_meta',
438 'order_item_type' => 'line_item',
439 'function' => 'SUM',
440 'name' => 'order_item_count',
441 ),
442 'post_date' => array(
443 'type' => 'post_data',
444 'function' => '',
445 'name' => 'post_date',
446 ),
447 '_product_id' => array(
448 'type' => 'order_item_meta',
449 'order_item_type' => 'line_item',
450 'function' => '',
451 'name' => 'product_id',
452 ),
453 ),
454 'where_meta' => array(
455 'relation' => 'OR',
456 array(
457 'type' => 'order_item_meta',
458 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
459 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
460 'operator' => 'IN',
461 ),
462 ),
463 'group_by' => 'product_id,' . $this->group_by_query,
464 'order_by' => 'post_date ASC',
465 'query_type' => 'get_results',
466 'filter_range' => true,
467 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
468 )
469 );
470
471 $order_item_amounts = $this->get_order_report_data(
472 array(
473 'data' => array(
474 '_line_total' => array(
475 'type' => 'order_item_meta',
476 'order_item_type' => 'line_item',
477 'function' => 'SUM',
478 'name' => 'order_item_amount',
479 ),
480 'post_date' => array(
481 'type' => 'post_data',
482 'function' => '',
483 'name' => 'post_date',
484 ),
485 '_product_id' => array(
486 'type' => 'order_item_meta',
487 'order_item_type' => 'line_item',
488 'function' => '',
489 'name' => 'product_id',
490 ),
491 ),
492 'where_meta' => array(
493 'relation' => 'OR',
494 array(
495 'type' => 'order_item_meta',
496 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
497 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
498 'operator' => 'IN',
499 ),
500 ),
501 'group_by' => 'product_id, ' . $this->group_by_query,
502 'order_by' => 'post_date ASC',
503 'query_type' => 'get_results',
504 'filter_range' => true,
505 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
506 )
507 );
508
509 // Prepare data for report.
510 $order_item_counts = $this->prepare_chart_data( $order_item_counts, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
511 $order_item_amounts = $this->prepare_chart_data( $order_item_amounts, 'post_date', 'order_item_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
512
513 // Encode in json format.
514 $chart_data = wp_json_encode(
515 array(
516 'order_item_counts' => array_values( $order_item_counts ),
517 'order_item_amounts' => array_values( $order_item_amounts ),
518 )
519 );
520 ?>
521 <div class="chart-container">
522 <div class="chart-placeholder main"></div>
523 </div>
524 <?php // @codingStandardsIgnoreStart ?>
525 <script type="text/javascript">
526 var main_chart;
527
528 jQuery(function(){
529 var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
530
531 var drawGraph = function( highlight ) {
532
533 var series = [
534 {
535 label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ) ?>",
536 data: order_data.order_item_counts,
537 color: '<?php echo $this->chart_colours['item_count']; ?>',
538 bars: { fillColor: '<?php echo $this->chart_colours['item_count']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
539 shadowSize: 0,
540 hoverable: false
541 },
542 {
543 label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>",
544 data: order_data.order_item_amounts,
545 yaxis: 2,
546 color: '<?php echo $this->chart_colours['sales_amount']; ?>',
547 points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
548 lines: { show: true, lineWidth: 4, fill: false },
549 shadowSize: 0,
550 <?php echo $this->get_currency_tooltip(); ?>
551 }
552 ];
553
554 if ( highlight !== 'undefined' && series[ highlight ] ) {
555 highlight_series = series[ highlight ];
556
557 highlight_series.color = '#9c5d90';
558
559 if ( highlight_series.bars )
560 highlight_series.bars.fillColor = '#9c5d90';
561
562 if ( highlight_series.lines ) {
563 highlight_series.lines.lineWidth = 5;
564 }
565 }
566
567 main_chart = jQuery.plot(
568 jQuery('.chart-placeholder.main'),
569 series,
570 {
571 legend: {
572 show: false
573 },
574 grid: {
575 color: '#aaa',
576 borderColor: 'transparent',
577 borderWidth: 0,
578 hoverable: true
579 },
580 xaxes: [ {
581 color: '#aaa',
582 position: "bottom",
583 tickColor: 'transparent',
584 mode: "time",
585 timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
586 monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
587 tickLength: 1,
588 minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
589 font: {
590 color: "#aaa"
591 }
592 } ],
593 yaxes: [
594 {
595 min: 0,
596 minTickSize: 1,
597 tickDecimals: 0,
598 color: '#ecf0f1',
599 font: { color: "#aaa" }
600 },
601 {
602 position: "right",
603 min: 0,
604 tickDecimals: 2,
605 alignTicksWithAxis: 1,
606 color: 'transparent',
607 font: { color: "#aaa" }
608 }
609 ],
610 }
611 );
612
613 jQuery('.chart-placeholder').trigger( 'resize' );
614 }
615
616 drawGraph();
617
618 jQuery('.highlight_series').on( 'mouseenter',
619 function() {
620 drawGraph( jQuery(this).data('series') );
621 } ).on( 'mouseleave',
622 function() {
623 drawGraph();
624 }
625 );
626 });
627 </script>
628 <?php
629 // @codingStandardsIgnoreEnd
630 }
631 }
632 }
633