WC_CLI_Report::sales()   F
last analyzed

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 123
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
dl 0
loc 123
rs 2
c 0
b 0
f 0
eloc 73
nc 26250
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Show Reports.
5
 *
6
 * @since    2.5.0
7
 * @package  WooCommerce/CLI
8
 * @category CLI
9
 * @author   WooThemes
10
 */
11
class WC_CLI_Report extends WC_CLI_Command {
12
13
	/**
14
	 * List reports.
15
	 *
16
	 * ## OPTIONS
17
	 *
18
	 * [--format=<format>]
19
	 * : Acceptec values: table, csv, json, count, ids. Default: table.
20
	 *
21
	 * ## EXAMPLES
22
	 *
23
	 *     wp wc report list
24
	 *
25
	 * @subcommand list
26
	 * @since      2.5.0
27
	 */
28
	public function list_( $__, $assoc_args ) {
29
		$reports   = array( 'sales', 'sales/top_sellers' );
30
		$formatter = $this->get_formatter(
31
			array_merge(
32
				array( 'fields' => array_keys( $reports ) ),
33
				$assoc_args
34
			)
35
		);
36
37
		if ( 'ids' === $formatter->format ) {
38
			echo implode( ' ', $reports );
39
		} else {
40
			$formatter->display_item( $reports );
41
		}
42
	}
43
44
	/**
45
	 * View sales report.
46
	 *
47
	 * ## OPTIONS
48
	 *
49
	 * [--field=<field>]
50
	 * : Instead of returning the whole report fields, returns the value of a single fields.
51
	 *
52
	 * [--fields=<fields>]
53
	 * : Get a specific subset of the report's fields.
54
	 *
55
	 * [--format=<format>]
56
	 * : Accepted values: table, json, csv. Default: table.
57
	 *
58
	 * [--period=<period>]
59
	 * : The supported periods are: week, month, last_month, and year. If invalid
60
	 * period is supplied, week is used. If period is not specified, the current
61
	 * day is used.
62
	 *
63
	 * [--date_min]
64
	 * : Return sales for a specific start date. The date need to be in the YYYY-MM-AA format.
65
	 *
66
	 * [--date_max]
67
	 * : Return sales for a specific end date. The dates need to be in the YYYY-MM-AA format.
68
	 *
69
	 * [--limit]
70
	 * : Limit report result. Default: 12.
71
	 *
72
	 * ## AVAILABLE FIELDS
73
	 *
74
	 * These fields are available for get command:
75
	 *
76
	 * * total_sales
77
	 * * average_sales
78
	 * * total_orders
79
	 * * total_items
80
	 * * total_tax
81
	 * * total_shipping
82
	 * * total_discount
83
	 * * totals_grouped_by
84
	 * * totals
85
	 * * total_customers
86
	 *
87
	 * ## EXAMPLES
88
	 *
89
	 *     wp wc report sales
90
	 *
91
	 *     wp wc report sales --period=last_month
92
	 *
93
	 * @since 2.5.0
94
	 */
95
	public function sales( $__, $assoc_args ) {
96
		$reporter = $this->get_reporter( $assoc_args );
97
98
		// new customers
99
		$users_query = new WP_User_Query(
100
			array(
101
				'fields' => array( 'user_registered' ),
102
				'role'   => 'customer',
103
			)
104
		);
105
106
		$customers = $users_query->get_results();
107
108
		foreach ( $customers as $key => $customer ) {
109
			if ( strtotime( $customer->user_registered ) < $reporter->start_date || strtotime( $customer->user_registered ) > $reporter->end_date ) {
110
				unset( $customers[ $key ] );
111
			}
112
		}
113
114
		$total_customers = count( $customers );
115
		$report_data     = $reporter->get_report_data();
116
		$period_totals   = array();
117
118
		// setup period totals by ensuring each period in the interval has data
119
		for ( $i = 0; $i <= $reporter->chart_interval; $i ++ ) {
120
121
			switch ( $reporter->chart_groupby ) {
122
				case 'day' :
123
					$time = date( 'Y-m-d', strtotime( "+{$i} DAY", $reporter->start_date ) );
124
					break;
125
				default :
126
					$time = date( 'Y-m', strtotime( "+{$i} MONTH", $reporter->start_date ) );
127
					break;
128
			}
129
130
			// set the customer signups for each period
131
			$customer_count = 0;
132
			foreach ( $customers as $customer ) {
133
				if ( date( ( 'day' == $reporter->chart_groupby ) ? 'Y-m-d' : 'Y-m', strtotime( $customer->user_registered ) ) == $time ) {
134
					$customer_count++;
135
				}
136
 			}
137
138
			$period_totals[ $time ] = array(
139
				'sales'     => wc_format_decimal( 0.00, 2 ),
140
				'orders'    => 0,
141
				'items'     => 0,
142
				'tax'       => wc_format_decimal( 0.00, 2 ),
143
				'shipping'  => wc_format_decimal( 0.00, 2 ),
144
				'discount'  => wc_format_decimal( 0.00, 2 ),
145
				'customers' => $customer_count,
146
			);
147
		}
148
149
		// add total sales, total order count, total tax and total shipping for each period
150
		foreach ( $report_data->orders as $order ) {
151
			$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
152
153
			if ( ! isset( $period_totals[ $time ] ) ) {
154
				continue;
155
			}
156
157
			$period_totals[ $time ]['sales']    = wc_format_decimal( $order->total_sales, 2 );
158
			$period_totals[ $time ]['tax']      = wc_format_decimal( $order->total_tax + $order->total_shipping_tax, 2 );
159
			$period_totals[ $time ]['shipping'] = wc_format_decimal( $order->total_shipping, 2 );
160
		}
161
162
		foreach ( $report_data->order_counts as $order ) {
163
			$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
164
165
			if ( ! isset( $period_totals[ $time ] ) ) {
166
				continue;
167
			}
168
169
			$period_totals[ $time ]['orders']   = (int) $order->count;
170
		}
171
172
		// add total order items for each period
173
		foreach ( $report_data->order_items as $order_item ) {
174
			$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order_item->post_date ) ) : date( 'Y-m', strtotime( $order_item->post_date ) );
175
176
			if ( ! isset( $period_totals[ $time ] ) ) {
177
				continue;
178
			}
179
180
			$period_totals[ $time ]['items'] = (int) $order_item->order_item_count;
181
		}
182
183
		// add total discount for each period
184
		foreach ( $report_data->coupons as $discount ) {
185
			$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $discount->post_date ) ) : date( 'Y-m', strtotime( $discount->post_date ) );
186
187
			if ( ! isset( $period_totals[ $time ] ) ) {
188
				continue;
189
			}
190
191
			$period_totals[ $time ]['discount'] = wc_format_decimal( $discount->discount_amount, 2 );
192
		}
193
194
		$sales_data  = array(
195
			'total_sales'       => $report_data->total_sales,
196
			'net_sales'         => $report_data->net_sales,
197
			'average_sales'     => $report_data->average_sales,
198
			'total_orders'      => $report_data->total_orders,
199
			'total_items'       => $report_data->total_items,
200
			'total_tax'         => wc_format_decimal( $report_data->total_tax + $report_data->total_shipping_tax, 2 ),
201
			'total_shipping'    => $report_data->total_shipping,
202
			'total_refunds'     => $report_data->total_refunds,
203
			'total_discount'    => $report_data->total_coupons,
204
			'totals_grouped_by' => $reporter->chart_groupby,
205
			'totals'            => $period_totals,
206
			'total_customers'   => $total_customers,
207
		);
208
209
		$sales_data = apply_filters( 'woocommerce_cli_sales_report', $sales_data );
210
211
		if ( empty( $assoc_args['fields'] ) ) {
212
			$assoc_args['fields'] = array_keys( $sales_data );
213
		}
214
215
		$formatter = $this->get_formatter( $assoc_args );
216
		$formatter->display_item( $sales_data );
217
	}
218
219
	/**
220
	 * View report of top sellers.
221
	 *
222
	 * ## OPTIONS
223
	 *
224
	 * [--<field>=<value>]
225
	 * : Filter report based on report property.
226
	 *
227
	 * [--field=<field>]
228
	 * : Prints the value of a single field for each seller.
229
	 *
230
	 * [--fields=<fields>]
231
	 * : Limit the output to specific report fields.
232
	 *
233
	 * [--format=<format>]
234
	 * : Acceptec values: table, csv, json, count, ids. Default: table.
235
	 *
236
	 * [--period=<period>]
237
	 * : The supported periods are: week, month, last_month, and year. If invalid
238
	 * period is supplied, week is used. If period is not specified, the current
239
	 * day is used.
240
	 *
241
	 * [--date_min]
242
	 * : Return sales for a specific start date. The date need to be in the YYYY-MM-AA format.
243
	 *
244
	 * [--date_max]
245
	 * : Return sales for a specific end date. The dates need to be in the YYYY-MM-AA format.
246
	 *
247
	 * [--limit]
248
	 * : Limit report result. Default: 12.
249
	 *
250
	 * ## AVAILABLE FIELDS
251
	 *
252
	 * These fields will be displayed by default for each row:
253
	 *
254
	 * * title
255
	 * * product_id
256
	 * * quantity
257
	 *
258
	 * ## EXAMPLES
259
	 *
260
	 *     wp wc report top_sellers
261
	 *
262
	 *     wp wc report top_sellers --period=last_month
263
	 *
264
	 * @since 2.5.0
265
	 */
266
	public function top_sellers( $__, $assoc_args ) {
267
		$reporter    = $this->get_reporter( $assoc_args );
268
		$top_sellers = $reporter->get_order_report_data( array(
269
			'data' => array(
270
				'_product_id' => array(
271
					'type'            => 'order_item_meta',
272
					'order_item_type' => 'line_item',
273
					'function'        => '',
274
					'name'            => 'product_id'
275
				),
276
				'_qty' => array(
277
					'type'            => 'order_item_meta',
278
					'order_item_type' => 'line_item',
279
					'function'        => 'SUM',
280
					'name'            => 'order_item_qty'
281
				)
282
			),
283
			'order_by'     => 'order_item_qty DESC',
284
			'group_by'     => 'product_id',
285
			'limit'        => isset( $assoc_args['limit'] ) ? absint( $assoc_args['limit'] ) : 12,
286
			'query_type'   => 'get_results',
287
			'filter_range' => true,
288
		) );
289
290
		$top_sellers_data = array();
291 View Code Duplication
		foreach ( $top_sellers as $top_seller ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
			$product = wc_get_product( $top_seller->product_id );
293
294
			if ( $product ) {
295
				$top_sellers_data[] = array(
296
					'title'      => $product->get_title(),
297
					'product_id' => $top_seller->product_id,
298
					'quantity'   => $top_seller->order_item_qty,
299
				);
300
			}
301
		}
302
		$top_sellers_data = apply_filters( 'woocommerce_cli_top_sellers_report', $top_sellers_data );
303
304
		$formatter = $this->get_formatter( $assoc_args );
305
		if ( 'ids' === $formatter->format ) {
306
			$query_args['fields'] = 'ids';
307
			echo implode( ' ', wp_list_pluck( $top_sellers_data, 'product_id' ) );
308
		} else {
309
			$formatter->display_items( $top_sellers_data );
310
		}
311
	}
312
313
	/**
314
	 * Setup the report object and parse any date filtering
315
	 *
316
	 * @since  2.5.0
317
	 * @param  array $assoc_args Arguments provided in when invoking the command
318
	 * @return WC_Report_Sales_By_Date
319
	 */
320
	private function get_reporter( $assoc_args ) {
321
322
		include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php' );
323
		include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php' );
324
325
		$report = new WC_Report_Sales_By_Date();
326
327
		if ( empty( $assoc_args['period'] ) ) {
328
329
			// custom date range
330
			$assoc_args['period'] = 'custom';
331
332 View Code Duplication
			if ( ! empty( $assoc_args['date_min'] ) || ! empty( $assoc_args['date_max'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
334
				// overwrite _GET to make use of WC_Admin_Report::calculate_current_range() for custom date ranges
335
				$_GET['start_date'] = $this->parse_datetime( $assoc_args['date_min'] );
336
				$_GET['end_date'] = isset( $assoc_args['date_max'] ) ? $this->parse_datetime( $assoc_args['date_max'] ) : null;
337
338
			} else {
339
340
				// default custom range to today
341
				$_GET['start_date'] = $_GET['end_date'] = date( 'Y-m-d', current_time( 'timestamp' ) );
342
			}
343
344
		} else {
345
346
			// ensure period is valid
347
			if ( ! in_array( $assoc_args['period'], array( 'week', 'month', 'last_month', 'year' ) ) ) {
348
				$assoc_args['period'] = 'week';
349
			}
350
351
			// TODO: change WC_Admin_Report class to use "week" instead, as it's more consistent with other periods
352
			// allow "week" for period instead of "7day"
353
			if ( 'week' === $assoc_args['period'] ) {
354
				$assoc_args['period'] = '7day';
355
			}
356
		}
357
358
		$report->calculate_current_range( $assoc_args['period'] );
359
360
		return $report;
361
	}
362
363
	/**
364
	 * Get default format fields that will be used in `list` and `get` subcommands.
365
	 *
366
	 * @since  2.5.0
367
	 * @return string
368
	 */
369
	protected function get_default_format_fields() {
370
		return 'title,product_id,quantity';
371
	}
372
}
373