WC_CLI_Order::get()   A
last analyzed

Complexity

Conditions 3
Paths 6

Size

Total Lines 14
Code Lines 10

Duplication

Lines 14
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
dl 14
loc 14
rs 9.4285
c 0
b 0
f 0
eloc 10
nc 6
nop 2
1
<?php
2
3
/**
4
 * Manage Orders.
5
 *
6
 * @since    2.5.0
7
 * @package  WooCommerce/CLI
8
 * @category CLI
9
 * @author   WooThemes
10
 */
11
class WC_CLI_Order extends WC_CLI_Command {
12
13
	/**
14
	 * Create an order.
15
	 *
16
	 * ## OPTIONS
17
	 *
18
	 * [--<field>=<value>]
19
	 * : Associative args for the new order.
20
	 *
21
	 * [--porcelain]
22
	 * : Outputs just the new order id.
23
	 *
24
	 * ## AVAILABLE FIELDS
25
	 *
26
	 * Required fields:
27
	 *
28
	 * * customer_id
29
	 *
30
	 * Optional fields:
31
	 *
32
	 * * status
33
	 * * note
34
	 * * currency
35
	 * * order_meta
36
	 *
37
	 * Payment detail fields:
38
	 *
39
	 * * payment_details.method_id
40
	 * * payment_details.method_title
41
	 * * payment_details.paid
42
	 *
43
	 * Billing address fields:
44
	 *
45
	 * * billing_address.first_name
46
	 * * billing_address.last_name
47
	 * * billing_address.company
48
	 * * billing_address.address_1
49
	 * * billing_address.address_2
50
	 * * billing_address.city
51
	 * * billing_address.state
52
	 * * billing_address.postcode
53
	 * * billing_address.country
54
	 * * billing_address.email
55
	 * * billing_address.phone
56
	 *
57
	 * Shipping address fields:
58
	 *
59
	 * * shipping_address.first_name
60
	 * * shipping_address.last_name
61
	 * * shipping_address.company
62
	 * * shipping_address.address_1
63
	 * * shipping_address.address_2
64
	 * * shipping_address.city
65
	 * * shipping_address.state
66
	 * * shipping_address.postcode
67
	 * * shipping_address.country
68
	 *
69
	 * Line item fields (numeric array, started with index zero):
70
	 *
71
	 * * line_items.0.product_id
72
	 * * line_items.0.quantity
73
	 * * line_items.0.variations.pa_color
74
	 *
75
	 * For second line item: line_items.1.product_id and so on.
76
	 *
77
	 * Shipping line fields (numeric array, started with index zero):
78
	 *
79
	 * * shipping_lines.0.method_id
80
	 * * shipping_lines.0.method_title
81
	 * * shipping_lines.0.total
82
	 *
83
	 * For second shipping item: shipping_lines.1.method_id and so on.
84
	 *
85
	 * ## EXAMPLES
86
	 *
87
	 *     wp wc order create --customer_id=1 --status=pending ...
88
	 *
89
	 * @since  2.5.0
90
	 */
91
	public function create( $__, $assoc_args ) {
92
		global $wpdb;
93
94
		wc_transaction_query( 'start' );
95
96
		try {
97
			$porcelain = isset( $assoc_args['porcelain'] );
98
			unset( $assoc_args['porcelain'] );
99
100
			$data = apply_filters( 'woocommerce_cli_create_order_data', $this->unflatten_array( $assoc_args ) );
101
102
			// default order args, note that status is checked for validity in wc_create_order()
103
			$default_order_args = array(
104
				'status'        => isset( $data['status'] ) ? $data['status'] : '',
105
				'customer_note' => isset( $data['note'] ) ? $data['note'] : null,
106
			);
107
108
			if ( empty( $data['customer_id'] ) ) {
109
				throw new WC_CLI_Exception( 'woocommerce_cli_missing_customer_id', __( 'Missing customer_id field', 'woocommerce' ) );
110
			}
111
112
			// make sure customer exists
113
			if ( false === get_user_by( 'id', $data['customer_id'] ) ) {
114
				throw new WC_CLI_Exception( 'woocommerce_cli_invalid_customer_id', __( 'Customer ID is invalid', 'woocommerce' ) );
115
			}
116
			$default_order_args['customer_id'] = $data['customer_id'];
117
118
			// create the pending order
119
			$order = $this->create_base_order( $default_order_args, $data );
120
121 View Code Duplication
			if ( is_wp_error( $order ) ) {
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...
122
				throw new WC_CLI_Exception( 'woocommerce_cli_cannot_create_order', sprintf( __( 'Cannot create order: %s', 'woocommerce' ), implode( ', ', $order->get_error_messages() ) ) );
123
			}
124
125
			// billing/shipping addresses
126
			$this->set_order_addresses( $order, $data );
127
128
			$lines = array(
129
				'line_item' => 'line_items',
130
				'shipping'  => 'shipping_lines',
131
				'fee'       => 'fee_lines',
132
				'coupon'    => 'coupon_lines',
133
			);
134
135 View Code Duplication
			foreach ( $lines as $line_type => $line ) {
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...
136
				if ( isset( $data[ $line ] ) && is_array( $data[ $line ] ) ) {
137
					$set_item = "set_{$line_type}";
138
					foreach ( $data[ $line ] as $item ) {
139
						$this->$set_item( $order, $item, 'create' );
140
					}
141
				}
142
			}
143
144
			// calculate totals and set them
145
			$order->calculate_totals();
146
147
			// payment method (and payment_complete() if `paid` == true)
148
			if ( isset( $data['payment_details'] ) && is_array( $data['payment_details'] ) ) {
149
				// method ID & title are required
150
				if ( empty( $data['payment_details']['method_id'] ) || empty( $data['payment_details']['method_title'] ) ) {
151
					throw new WC_CLI_Exception( 'woocommerce_invalid_payment_details', __( 'Payment method ID and title are required', 'woocommerce' ) );
152
				}
153
154
				update_post_meta( $order->id, '_payment_method', $data['payment_details']['method_id'] );
155
				update_post_meta( $order->id, '_payment_method_title', $data['payment_details']['method_title'] );
156
157
				// Mark as paid if set.
158 View Code Duplication
				if ( isset( $data['payment_details']['paid'] ) && $this->is_true( $data['payment_details']['paid'] ) ) {
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...
159
					$order->payment_complete( isset( $data['payment_details']['transaction_id'] ) ? $data['payment_details']['transaction_id'] : '' );
160
				}
161
			}
162
163
			// Set order currency.
164
			if ( isset( $data['currency'] ) ) {
165
				if ( ! array_key_exists( $data['currency'], get_woocommerce_currencies() ) ) {
166
					throw new WC_CLI_Exception( 'woocommerce_invalid_order_currency', __( 'Provided order currency is invalid', 'woocommerce') );
167
				}
168
169
				update_post_meta( $order->id, '_order_currency', $data['currency'] );
170
			}
171
172
			// Set order meta.
173 View Code Duplication
			if ( isset( $data['order_meta'] ) && is_array( $data['order_meta'] ) ) {
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...
174
				$this->set_order_meta( $order->id, $data['order_meta'] );
175
			}
176
177
			wc_delete_shop_order_transients( $order->id );
178
179
			do_action( 'woocommerce_cli_create_order', $order->id, $data );
180
181
			wc_transaction_query( 'commit' );
182
183
			if ( $porcelain ) {
184
				WP_CLI::line( $order->id );
185
			} else {
186
				WP_CLI::success( "Created order {$order->id}." );
187
			}
188
		} catch ( WC_CLI_Exception $e ) {
189
			wc_transaction_query( 'rollback' );
190
191
			WP_CLI::error( $e->getMessage() );
192
		}
193
	}
194
195
	/**
196
	 * Delete one or more orders.
197
	 *
198
	 * ## OPTIONS
199
	 *
200
	 * <id>...
201
	 * : The order ID to delete.
202
	 *
203
	 * ## EXAMPLES
204
	 *
205
	 *     wp wc order delete 123
206
	 *
207
	 * @since 2.5.0
208
	 */
209
	public function delete( $args, $assoc_args ) {
210
		$exit_code = 0;
211
		foreach ( $args as $id ) {
212
			$order = wc_get_order( $id );
213
			if ( ! $order ) {
214
				WP_CLI::warning( "Invalid order ID $id" );
215
				continue;
216
			}
217
218
			wc_delete_shop_order_transients( $id );
219
			do_action( 'woocommerce_cli_delete_order', $id );
220
			$r = wp_delete_post( $id, true );
221
222
			if ( $r ) {
223
				WP_CLI::success( "Deleted order $id." );
224
			} else {
225
				$exit_code += 1;
226
				WP_CLI::warning( "Failed deleting order $id." );
227
			}
228
		}
229
		exit( $exit_code ? 1 : 0 );
230
	}
231
232
	/**
233
	 * Get an order.
234
	 *
235
	 * ## OPTIONS
236
	 *
237
	 * <id>
238
	 * : Order ID.
239
	 *
240
	 * [--field=<field>]
241
	 * : Instead of returning the whole order fields, returns the value of a single fields.
242
	 *
243
	 * [--fields=<fields>]
244
	 * : Get a specific subset of the order's fields.
245
	 *
246
	 * [--format=<format>]
247
	 * : Accepted values: table, json, csv. Default: table.
248
	 *
249
	 * ## AVAILABLE FIELDS
250
	 *
251
	 * These fields will be displayed by default:
252
	 *
253
	 * * id
254
	 * * order_number
255
	 * * customer_id
256
	 * * total
257
	 * * status
258
	 * * created_at
259
	 *
260
	 * For more fields, see: wp wc order list --help
261
	 *
262
	 * ## EXAMPLES
263
	 *
264
	 *     wp wc order get 123 --fields=id,title,sku
265
	 *
266
	 * @since 2.5.0
267
	 */
268 View Code Duplication
	public function get( $args, $assoc_args ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
269
		try {
270
			$order = wc_get_order( $args[0] );
271
			if ( ! $order ) {
272
				throw new WC_CLI_Exception( 'woocommerce_cli_invalid_order', sprintf( __( 'Invalid order "%s"', 'woocommerce' ), $args[0] ) );
273
			}
274
			$order_data = $this->get_order_data( $order );
275
276
			$formatter = $this->get_formatter( $assoc_args );
277
			$formatter->display_item( $order_data );
278
		} catch ( WC_CLI_Exception $e ) {
279
			WP_CLI::error( $e->getMessage() );
280
		}
281
	}
282
283
	/**
284
	 * List orders.
285
	 *
286
	 * ## OPTIONS
287
	 *
288
	 * [--<field>=<value>]
289
	 * : Filter orders based on order property.
290
	 *
291
	 * [--field=<field>]
292
	 * : Prints the value of a single field for each order.
293
	 *
294
	 * [--fields=<fields>]
295
	 * : Limit the output to specific order fields.
296
	 *
297
	 * [--format=<format>]
298
	 * : Acceptec values: table, csv, json, count, ids. Default: table.
299
	 *
300
	 * ## AVAILABLE FIELDS
301
	 *
302
	 * These fields will be displayed by default for each order:
303
	 *
304
	 * * id
305
	 * * order_number
306
	 * * customer_id
307
	 * * total
308
	 * * status
309
	 * * created_at
310
	 *
311
	 * These fields are optionally available:
312
	 *
313
	 * * updated_at
314
	 * * completed_at
315
	 * * currency
316
	 * * subtotal
317
	 * * total_line_items_quantity
318
	 * * total_tax
319
	 * * total_shipping
320
	 * * cart_tax
321
	 * * shipping_tax
322
	 * * total_discount
323
	 * * shipping_methods
324
	 * * note
325
	 * * customer_ip
326
	 * * customer_user_agent
327
	 * * view_order_url
328
	 *
329
	 * Payment detail fields:
330
	 *
331
	 * * payment_details.method_id
332
	 * * payment_details.method_title
333
	 * * payment_details.paid
334
	 *
335
	 * Billing address fields:
336
	 *
337
	 * * billing_address.first_name
338
	 * * billing_address.last_name
339
	 * * billing_address.company
340
	 * * billing_address.address_1
341
	 * * billing_address.address_2
342
	 * * billing_address.city
343
	 * * billing_address.state
344
	 * * billing_address.postcode
345
	 * * billing_address.country
346
	 * * billing_address.email
347
	 * * billing_address.phone
348
	 *
349
	 * Shipping address fields:
350
	 *
351
	 * * shipping_address.first_name
352
	 * * shipping_address.last_name
353
	 * * shipping_address.company
354
	 * * shipping_address.address_1
355
	 * * shipping_address.address_2
356
	 * * shipping_address.city
357
	 * * shipping_address.state
358
	 * * shipping_address.postcode
359
	 * * shipping_address.country
360
	 *
361
	 * Line item fields (numeric array, started with index zero):
362
	 *
363
	 * * line_items.0.product_id
364
	 * * line_items.0.quantity
365
	 * * line_items.0.variations.pa_color
366
	 *
367
	 * For second line item: line_items.1.product_id and so on.
368
	 *
369
	 * Shipping line fields (numeric array, started with index zero):
370
	 *
371
	 * * shipping_lines.0.method_id
372
	 * * shipping_lines.0.method_title
373
	 * * shipping_lines.0.total
374
	 *
375
	 * For second shipping item: shipping_lines.1.method_id and so on.
376
	 *
377
	 * ## EXAMPLES
378
	 *
379
	 *     wp wc order list
380
	 *
381
	 * @subcommand list
382
	 * @since      2.5.0
383
	 */
384 View Code Duplication
	public function list_( $args, $assoc_args ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
385
		$query_args = $this->merge_wp_query_args( $this->get_list_query_args( $assoc_args ), $assoc_args );
386
		$formatter  = $this->get_formatter( $assoc_args );
387
388
		if ( 'ids' === $formatter->format ) {
389
			$query_args['fields'] = 'ids';
390
			$query = new WP_Query( $query_args );
391
			echo implode( ' ', $query->posts );
392
		} else {
393
			$query = new WP_Query( $query_args );
394
			$items = $this->format_posts_to_items( $query->posts );
395
			$formatter->display_items( $items );
396
		}
397
	}
398
399
	/**
400
	 * Update an order.
401
	 *
402
	 * ## OPTIONS
403
	 *
404
	 * <id>
405
	 * : Product ID
406
	 *
407
	 * [--<field>=<value>]
408
	 * : One or more fields to update.
409
	 *
410
	 * ## AVAILABLE FIELDS
411
	 *
412
	 * For available fields, see: wp wc order create --help
413
	 *
414
	 * ## EXAMPLES
415
	 *
416
	 *    wp wc order update 123 --status=completed
417
	 *
418
	 * @todo  gedex
419
	 * @since 2.5.0
420
	 */
421
	public function update( $args, $assoc_args ) {
422
		try {
423
			$id            = $args[0];
424
			$data          = apply_filters( 'woocommerce_cli_update_order_data', $this->unflatten_array( $assoc_args ) );
425
			$update_totals = false;
426
			$order         = wc_get_order( $id );
427
428
			if ( empty( $order ) ) {
429
				throw new WC_CLI_Exception( 'woocommerce_cli_invalid_order_id', __( 'Order ID is invalid', 'woocommerce' ) );
430
			}
431
432
			$order_args = array( 'order_id' => $order->id );
433
434
			// customer note
435
			if ( isset( $data['note'] ) ) {
436
				$order_args['customer_note'] = $data['note'];
437
			}
438
439
			// order status
440 View Code Duplication
			if ( ! empty( $data['status'] ) ) {
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...
441
442
				$order->update_status( $data['status'], isset( $data['status_note'] ) ? $data['status_note'] : '', true );
443
			}
444
445
			// customer ID
446 View Code Duplication
			if ( isset( $data['customer_id'] ) && $data['customer_id'] != $order->get_user_id() ) {
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...
447
448
				// make sure customer exists
449
				if ( false === get_user_by( 'id', $data['customer_id'] ) ) {
450
					throw new WC_CLI_Exception( 'woocommerce_cli_invalid_customer_id', __( 'Customer ID is invalid', 'woocommerce' ) );
451
				}
452
453
				update_post_meta( $order->id, '_customer_user', $data['customer_id'] );
454
			}
455
456
			// billing/shipping address
457
			$this->set_order_addresses( $order, $data );
458
459
			$lines = array(
460
				'line_item' => 'line_items',
461
				'shipping'  => 'shipping_lines',
462
				'fee'       => 'fee_lines',
463
				'coupon'    => 'coupon_lines',
464
			);
465
466 View Code Duplication
			foreach ( $lines as $line_type => $line ) {
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...
467
468
				if ( isset( $data[ $line ] ) && is_array( $data[ $line ] ) ) {
469
470
					$update_totals = true;
471
472
					foreach ( $data[ $line ] as $item ) {
473
474
						// item ID is always required
475
						if ( ! array_key_exists( 'id', $item ) ) {
476
							throw new WC_CLI_Exception( 'woocommerce_invalid_item_id', __( 'Order item ID is required', 'woocommerce' ) );
477
						}
478
479
						// create item
480
						if ( is_null( $item['id'] ) ) {
481
482
							$this->set_item( $order, $line_type, $item, 'create' );
483
484
						} elseif ( $this->item_is_null( $item ) ) {
485
486
							// delete item
487
							wc_delete_order_item( $item['id'] );
488
489
						} else {
490
491
							// update item
492
							$this->set_item( $order, $line_type, $item, 'update' );
493
						}
494
					}
495
				}
496
			}
497
498
			// payment method (and payment_complete() if `paid` == true and order needs payment)
499
			if ( isset( $data['payment_details'] ) && is_array( $data['payment_details'] ) ) {
500
501
				// method ID
502
				if ( isset( $data['payment_details']['method_id'] ) ) {
503
					update_post_meta( $order->id, '_payment_method', $data['payment_details']['method_id'] );
504
				}
505
506
				// method title
507
				if ( isset( $data['payment_details']['method_title'] ) ) {
508
					update_post_meta( $order->id, '_payment_method_title', $data['payment_details']['method_title'] );
509
				}
510
511
				// mark as paid if set
512 View Code Duplication
				if ( $order->needs_payment() && isset( $data['payment_details']['paid'] ) && $this->is_true( $data['payment_details']['paid'] ) ) {
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...
513
					$order->payment_complete( isset( $data['payment_details']['transaction_id'] ) ? $data['payment_details']['transaction_id'] : '' );
514
				}
515
			}
516
517
			// set order currency
518
			if ( isset( $data['currency'] ) ) {
519
520
				if ( ! array_key_exists( $data['currency'], get_woocommerce_currencies() ) ) {
521
					throw new WC_CLI_Exception( 'woocommerce_invalid_order_currency', __( 'Provided order currency is invalid', 'woocommerce' ) );
522
				}
523
524
				update_post_meta( $order->id, '_order_currency', $data['currency'] );
525
			}
526
527
			// set order number
528
			if ( isset( $data['order_number'] ) ) {
529
530
				update_post_meta( $order->id, '_order_number', $data['order_number'] );
531
			}
532
533
			// if items have changed, recalculate order totals
534
			if ( $update_totals ) {
535
				$order->calculate_totals();
536
			}
537
538
			// update order meta
539 View Code Duplication
			if ( isset( $data['order_meta'] ) && is_array( $data['order_meta'] ) ) {
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...
540
				$this->set_order_meta( $order->id, $data['order_meta'] );
541
			}
542
543
			// update the order post to set customer note/modified date
544
			wc_update_order( $order_args );
545
546
			wc_delete_shop_order_transients( $order->id );
547
548
			do_action( 'woocommerce_cli_update_order', $order->id, $data );
549
550
			WP_CLI::success( "Updated order {$order->id}." );
551
552
		} catch ( WC_CLI_Exception $e ) {
553
			WP_CLI::error( $e->getMessage() );
554
		}
555
	}
556
557
	/**
558
	 * Get query args for list subcommand.
559
	 *
560
	 * @since  2.5.0
561
	 * @param  array $args Args from command line
562
	 * @return array
563
	 */
564
	protected function get_list_query_args( $args ) {
565
		$query_args = array(
566
			'post_type'      => 'shop_order',
567
			'post_status'    => array_keys( wc_get_order_statuses() ),
568
			'posts_per_page' => -1,
569
		);
570
571
		if ( ! empty( $args['status'] ) ) {
572
			$statuses                  = 'wc-' . str_replace( ',', ',wc-', $args['status'] );
573
			$statuses                  = explode( ',', $statuses );
574
			$query_args['post_status'] = $statuses;
575
		}
576
577 View Code Duplication
		if ( ! empty( $args['customer_id'] ) ) {
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...
578
			$query_args['meta_query'] = array(
579
				array(
580
					'key'   => '_customer_user',
581
					'value' => (int) $args['customer_id'],
582
					'compare' => '='
583
				)
584
			);
585
		}
586
587
		return $query_args;
588
	}
589
590
	/**
591
	 * Get default format fields that will be used in `list` and `get` subcommands.
592
	 *
593
	 * @since  2.5.0
594
	 * @return string
595
	 */
596
	protected function get_default_format_fields() {
597
		return 'id,order_number,customer_id,total,status,created_at';
598
	}
599
600
	/**
601
	 * Format posts from WP_Query result to items in which each item contain
602
	 * common properties of item.
603
	 *
604
	 * @since  2.5.0
605
	 * @param  array $posts Array of post
606
	 * @return array Items
607
	 */
608
	protected function format_posts_to_items( $posts ) {
609
		$items = array();
610
		foreach ( $posts as $post ) {
611
			$order = wc_get_order( $post->ID );
612
			if ( ! $order ) {
613
				continue;
614
			}
615
			$items[] = $this->get_order_data( $order );
616
		}
617
618
		return $items;
619
	}
620
621
	/**
622
	 * Get the order data for the given ID.
623
	 *
624
	 * @since  2.5.0
625
	 * @param  WC_Order $order The order instance
626
	 * @return array
627
	 */
628
	protected function get_order_data( $order ) {
629
		$order_post = get_post( $order->id );
630
		$dp         = wc_get_price_decimals();
631
		$order_data = array(
632
			'id'                        => $order->id,
633
			'order_number'              => $order->get_order_number(),
634
			'created_at'                => $this->format_datetime( $order_post->post_date_gmt ),
635
			'updated_at'                => $this->format_datetime( $order_post->post_modified_gmt ),
636
			'completed_at'              => $this->format_datetime( $order->completed_date, true ),
637
			'status'                    => $order->get_status(),
638
			'currency'                  => $order->get_order_currency(),
639
			'total'                     => wc_format_decimal( $order->get_total(), $dp ),
640
			'subtotal'                  => wc_format_decimal( $order->get_subtotal(), $dp ),
641
			'total_line_items_quantity' => $order->get_item_count(),
642
			'total_tax'                 => wc_format_decimal( $order->get_total_tax(), $dp ),
643
			'total_shipping'            => wc_format_decimal( $order->get_total_shipping(), $dp ),
644
			'cart_tax'                  => wc_format_decimal( $order->get_cart_tax(), $dp ),
645
			'shipping_tax'              => wc_format_decimal( $order->get_shipping_tax(), $dp ),
646
			'total_discount'            => wc_format_decimal( $order->get_total_discount(), $dp ),
647
			'shipping_methods'          => $order->get_shipping_method(),
648
			'payment_details' => array(
649
				'method_id'    => $order->payment_method,
650
				'method_title' => $order->payment_method_title,
651
				'paid'         => isset( $order->paid_date ),
652
			),
653
			'billing_address' => array(
654
				'first_name' => $order->billing_first_name,
655
				'last_name'  => $order->billing_last_name,
656
				'company'    => $order->billing_company,
657
				'address_1'  => $order->billing_address_1,
658
				'address_2'  => $order->billing_address_2,
659
				'city'       => $order->billing_city,
660
				'state'      => $order->billing_state,
661
				'postcode'   => $order->billing_postcode,
662
				'country'    => $order->billing_country,
663
				'email'      => $order->billing_email,
664
				'phone'      => $order->billing_phone,
665
			),
666
			'shipping_address' => array(
667
				'first_name' => $order->shipping_first_name,
668
				'last_name'  => $order->shipping_last_name,
669
				'company'    => $order->shipping_company,
670
				'address_1'  => $order->shipping_address_1,
671
				'address_2'  => $order->shipping_address_2,
672
				'city'       => $order->shipping_city,
673
				'state'      => $order->shipping_state,
674
				'postcode'   => $order->shipping_postcode,
675
				'country'    => $order->shipping_country,
676
			),
677
			'note'                      => $order->customer_note,
678
			'customer_ip'               => $order->customer_ip_address,
679
			'customer_user_agent'       => $order->customer_user_agent,
680
			'customer_id'               => $order->get_user_id(),
681
			'view_order_url'            => $order->get_view_order_url(),
682
			'line_items'                => array(),
683
			'shipping_lines'            => array(),
684
			'tax_lines'                 => array(),
685
			'fee_lines'                 => array(),
686
			'coupon_lines'              => array(),
687
		);
688
689
		// add line items
690
		foreach ( $order->get_items() as $item_id => $item ) {
691
692
			$product     = $order->get_product_from_item( $item );
693
			$product_id  = null;
694
			$product_sku = null;
695
696
			// Check if the product exists.
697
			if ( is_object( $product ) ) {
698
				$product_id  = ( isset( $product->variation_id ) ) ? $product->variation_id : $product->id;
699
				$product_sku = $product->get_sku();
700
			}
701
702
			$meta = new WC_Order_Item_Meta( $item, $product );
703
			$item_meta = array();
704
			foreach ( $meta->get_formatted( null ) as $meta_key => $formatted_meta ) {
705
				$item_meta[] = array(
706
					'key' => $meta_key,
707
					'label' => $formatted_meta['label'],
708
					'value' => $formatted_meta['value'],
709
				);
710
			}
711
712
			$order_data['line_items'][] = array(
713
				'id'           => $item_id,
714
				'subtotal'     => wc_format_decimal( $order->get_line_subtotal( $item, false, false ), $dp ),
715
				'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),
716
				'total'        => wc_format_decimal( $order->get_line_total( $item, false, false ), $dp ),
717
				'total_tax'    => wc_format_decimal( $item['line_tax'], $dp ),
718
				'price'        => wc_format_decimal( $order->get_item_total( $item, false, false ), $dp ),
719
				'quantity'     => wc_stock_amount( $item['qty'] ),
720
				'tax_class'    => ( ! empty( $item['tax_class'] ) ) ? $item['tax_class'] : null,
721
				'name'         => $item['name'],
722
				'product_id'   => $product_id,
723
				'sku'          => $product_sku,
724
				'meta'         => $item_meta,
725
			);
726
		}
727
728
		// Add shipping.
729
		foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) {
730
			$order_data['shipping_lines'][] = array(
731
				'id'           => $shipping_item_id,
732
				'method_id'    => $shipping_item['method_id'],
733
				'method_title' => $shipping_item['name'],
734
				'total'        => wc_format_decimal( $shipping_item['cost'], $dp ),
735
			);
736
		}
737
738
		// Add taxes.
739
		foreach ( $order->get_tax_totals() as $tax_code => $tax ) {
740
			$order_data['tax_lines'][] = array(
741
				'id'       => $tax->id,
742
				'rate_id'  => $tax->rate_id,
743
				'code'     => $tax_code,
744
				'title'    => $tax->label,
745
				'total'    => wc_format_decimal( $tax->amount, $dp ),
746
				'compound' => (bool) $tax->is_compound,
747
			);
748
		}
749
750
		// Add fees.
751
		foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
752
			$order_data['fee_lines'][] = array(
753
				'id'        => $fee_item_id,
754
				'title'     => $fee_item['name'],
755
				'tax_class' => ( ! empty( $fee_item['tax_class'] ) ) ? $fee_item['tax_class'] : null,
756
				'total'     => wc_format_decimal( $order->get_line_total( $fee_item ), $dp ),
757
				'total_tax' => wc_format_decimal( $order->get_line_tax( $fee_item ), $dp ),
758
			);
759
		}
760
761
		// Add coupons.
762
		foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
763
			$order_data['coupon_lines'][] = array(
764
				'id'     => $coupon_item_id,
765
				'code'   => $coupon_item['name'],
766
				'amount' => wc_format_decimal( $coupon_item['discount_amount'], $dp ),
767
			);
768
		}
769
770
		$order_data = apply_filters( 'woocommerce_cli_order_data', $order_data );
771
772
		return $this->flatten_array( $order_data );
773
	}
774
775
	/**
776
	 * Creates new WC_Order.
777
	 *
778
	 * @since  2.5.0
779
	 * @param  $args array
780
	 * @return WC_Order
781
	 */
782
	protected function create_base_order( $args ) {
783
		return wc_create_order( $args );
784
	}
785
786
	/**
787
	 * Helper method to set/update the billing & shipping addresses for an order.
788
	 *
789
	 * @since 2.5.0
790
	 * @param WC_Order $order
791
	 * @param array $data
792
	 */
793
	protected function set_order_addresses( $order, $data ) {
794
		$address_fields = array(
795
			'first_name',
796
			'last_name',
797
			'company',
798
			'email',
799
			'phone',
800
			'address_1',
801
			'address_2',
802
			'city',
803
			'state',
804
			'postcode',
805
			'country',
806
		);
807
		$billing_address = $shipping_address = array();
808
809
		// billing address.
810
		if ( isset( $data['billing_address'] ) && is_array( $data['billing_address'] ) ) {
811
			foreach ( $address_fields as $field ) {
812
				if ( isset( $data['billing_address'][ $field ] ) ) {
813
					$billing_address[ $field ] = wc_clean( $data['billing_address'][ $field ] );
814
				}
815
			}
816
817
			unset( $address_fields['email'] );
818
			unset( $address_fields['phone'] );
819
		}
820
821
		// shipping address.
822
		if ( isset( $data['shipping_address'] ) && is_array( $data['shipping_address'] ) ) {
823
			foreach ( $address_fields as $field ) {
824
				if ( isset( $data['shipping_address'][ $field ] ) ) {
825
					$shipping_address[ $field ] = wc_clean( $data['shipping_address'][ $field ] );
826
				}
827
			}
828
		}
829
830
		$order->set_address( $billing_address, 'billing' );
831
		$order->set_address( $shipping_address, 'shipping' );
832
833
		// update user meta
834
		if ( $order->get_user_id() ) {
835
			foreach ( $billing_address as $key => $value ) {
836
				update_user_meta( $order->get_user_id(), 'billing_' . $key, $value );
837
			}
838
			foreach ( $shipping_address as $key => $value ) {
839
				update_user_meta( $order->get_user_id(), 'shipping_' . $key, $value );
840
			}
841
		}
842
	}
843
844
	/**
845
	 * Helper method to add/update order meta, with two restrictions:
846
	 *
847
	 * 1) Only non-protected meta (no leading underscore) can be set
848
	 * 2) Meta values must be scalar (int, string, bool)
849
	 *
850
	 * @since 2.5.0
851
	 * @param int $order_id valid order ID
852
	 * @param array $order_meta order meta in array( 'meta_key' => 'meta_value' ) format
853
	 */
854 View Code Duplication
	protected function set_order_meta( $order_id, $order_meta ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
855
856
		foreach ( $order_meta as $meta_key => $meta_value ) {
857
858
			if ( is_string( $meta_key) && ! is_protected_meta( $meta_key ) && is_scalar( $meta_value ) ) {
859
				update_post_meta( $order_id, $meta_key, $meta_value );
860
			}
861
		}
862
	}
863
864
	/**
865
	 * Wrapper method to create/update order items
866
	 *
867
	 * When updating, the item ID provided is checked to ensure it is associated
868
	 * with the order.
869
	 *
870
	 * @since  2.5.0
871
	 * @param  WC_Order $order order
872
	 * @param  string $item_type
873
	 * @param  array $item item provided in the request body
874
	 * @param  string $action either 'create' or 'update'
875
	 * @throws WC_CLI_Exception if item ID is not associated with order
876
	 */
877 View Code Duplication
	protected function set_item( $order, $item_type, $item, $action ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
878
		global $wpdb;
879
880
		$set_method = "set_{$item_type}";
881
882
		// verify provided line item ID is associated with order
883
		if ( 'update' === $action ) {
884
			$result = $wpdb->get_row(
885
				$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d",
886
				absint( $item['id'] ),
887
				absint( $order->id )
888
			) );
889
890
			if ( is_null( $result ) ) {
891
				throw new WC_CLI_Exception( 'woocommerce_invalid_item_id', __( 'Order item ID provided is not associated with order', 'woocommerce' ) );
892
			}
893
		}
894
895
		$this->$set_method( $order, $item, $action );
896
	}
897
898
	/**
899
	 * Create or update a line item
900
	 *
901
	 * @since  2.5.0
902
	 * @param  WC_Order $order
903
	 * @param  array $item line item data
904
	 * @param  string $action 'create' to add line item or 'update' to update it
905
	 * @throws WC_CLI_Exception invalid data, server error
906
	 */
907
	protected function set_line_item( $order, $item, $action ) {
908
909
		$creating = ( 'create' === $action );
910
911
		// product is always required
912
		if ( ! isset( $item['product_id'] ) && ! isset( $item['sku'] ) ) {
913
			throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_id', __( 'Product ID or SKU is required', 'woocommerce' ) );
914
		}
915
916
		// when updating, ensure product ID provided matches
917
		if ( 'update' === $action ) {
918
919
			$item_product_id   = wc_get_order_item_meta( $item['id'], '_product_id' );
920
			$item_variation_id = wc_get_order_item_meta( $item['id'], '_variation_id' );
921
922
			if ( $item['product_id'] != $item_product_id && $item['product_id'] != $item_variation_id ) {
923
				throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_id', __( 'Product ID provided does not match this line item', 'woocommerce' ) );
924
			}
925
		}
926
927
		if ( isset( $item['product_id'] ) ) {
928
			$product_id = $item['product_id'];
929
		} elseif ( isset( $item['sku'] ) ) {
930
			$product_id = wc_get_product_id_by_sku( $item['sku'] );
931
		}
932
933
		// variations must each have a key & value
934
		$variation_id = 0;
935
		if ( isset( $item['variations'] ) && is_array( $item['variations'] ) ) {
936
			foreach ( $item['variations'] as $key => $value ) {
937
				if ( ! $key || ! $value ) {
938
					throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_variation', __( 'The product variation is invalid', 'woocommerce' ) );
939
				}
940
			}
941
			$item_args['variation'] = $item['variations'];
942
			$variation_id = $this->get_variation_id( wc_get_product( $product_id ), $item_args['variation'] );
943
		}
944
945
		$product = wc_get_product( $variation_id ? $variation_id : $product_id );
946
947
		// must be a valid WC_Product
948
		if ( ! is_object( $product ) ) {
949
			throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product', __( 'Product is invalid', 'woocommerce' ) );
950
		}
951
952
		// quantity must be positive float
953 View Code Duplication
		if ( isset( $item['quantity'] ) && floatval( $item['quantity'] ) <= 0 ) {
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...
954
			throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_quantity', __( 'Product quantity must be a positive float', 'woocommerce' ) );
955
		}
956
957
		// quantity is required when creating
958
		if ( $creating && ! isset( $item['quantity'] ) ) {
959
			throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_quantity', __( 'Product quantity is required', 'woocommerce' ) );
960
		}
961
962
		$item_args = array();
963
964
		// quantity
965
		if ( isset( $item['quantity'] ) ) {
966
			$item_args['qty'] = $item['quantity'];
967
		}
968
969
		// total
970 View Code Duplication
		if ( isset( $item['total'] ) ) {
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...
971
			$item_args['totals']['total'] = floatval( $item['total'] );
972
		}
973
974
		// total tax
975 View Code Duplication
		if ( isset( $item['total_tax'] ) ) {
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...
976
			$item_args['totals']['tax'] = floatval( $item['total_tax'] );
977
		}
978
979
		// subtotal
980 View Code Duplication
		if ( isset( $item['subtotal'] ) ) {
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...
981
			$item_args['totals']['subtotal'] = floatval( $item['subtotal'] );
982
		}
983
984
		// subtotal tax
985 View Code Duplication
		if ( isset( $item['subtotal_tax'] ) ) {
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...
986
			$item_args['totals']['subtotal_tax'] = floatval( $item['subtotal_tax'] );
987
		}
988
989
		$item_args = apply_filters( 'woocommerce_cli_order_line_item_args', $item_args, $item, $order, $action );
990
991 View Code Duplication
		if ( $creating ) {
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...
992
993
			$item_id = $order->add_product( $product, $item_args['qty'], $item_args );
994
995
			if ( ! $item_id ) {
996
				throw new WC_CLI_Exception( 'woocommerce_cannot_create_line_item', __( 'Cannot create line item, try again', 'woocommerce' ) );
997
			}
998
999
		} else {
1000
1001
			$item_id = $order->update_product( $item['id'], $product, $item_args );
1002
1003
			if ( ! $item_id ) {
1004
				throw new WC_CLI_Exception( 'woocommerce_cannot_update_line_item', __( 'Cannot update line item, try again', 'woocommerce' ) );
1005
			}
1006
		}
1007
	}
1008
1009
	/**
1010
	 * Given a product ID & variations, find the correct variation ID to use for
1011
	 * calculation. We can't just trust input from the CLI to pass a variation_id
1012
	 * manually, otherwise you could pass the cheapest variation ID but provide
1013
	 * other information so we have to look up the variation ID.
1014
	 *
1015
	 * @since  2.5.0
1016
	 * @param  WC_Product $product Product instance
1017
	 * @return int                 Returns an ID if a valid variation was found for this product
1018
	 */
1019
	protected function get_variation_id( $product, $variations = array() ) {
1020
		$variation_id = null;
1021
		$variations_normalized = array();
1022
1023
		if ( $product->is_type( 'variable' ) && $product->has_child() ) {
1024
			if ( isset( $variations ) && is_array( $variations ) ) {
1025
				// start by normalizing the passed variations
1026
				foreach ( $variations as $key => $value ) {
1027
					$key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) ); // from get_attributes in class-wc-api-products.php
1028
					$variations_normalized[ $key ] = strtolower( $value );
1029
				}
1030
				// now search through each product child and see if our passed variations match anything
1031
				foreach ( $product->get_children() as $variation ) {
1032
					$meta = array();
1033
					foreach ( get_post_meta( $variation ) as $key => $value ) {
1034
						$value = $value[0];
1035
						$key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) );
1036
						$meta[ $key ] = strtolower( $value );
1037
					}
1038
					// if the variation array is a part of the $meta array, we found our match
1039
					if ( $this->array_contains( $variations_normalized, $meta ) ) {
1040
						$variation_id = $variation;
1041
						break;
1042
					}
1043
				}
1044
			}
1045
		}
1046
1047
		return $variation_id;
1048
	}
1049
1050
	/**
1051
	 * Utility function to see if the meta array contains data from variations.
1052
	 *
1053
	 * @since  2.5.0
1054
	 * @return bool  Returns true if meta array contains data from variations
1055
	 */
1056
	protected function array_contains( $needles, $haystack ) {
1057
		foreach ( $needles as $key => $value ) {
1058
			if ( $haystack[ $key ] !== $value ) {
1059
				return false;
1060
			}
1061
		}
1062
		return true;
1063
	}
1064
1065
	/**
1066
	 * Create or update an order shipping method
1067
	 *
1068
	 * @since  2.5.0
1069
	 * @param  \WC_Order $order
1070
	 * @param  array $shipping item data
1071
	 * @param  string $action 'create' to add shipping or 'update' to update it
1072
	 * @throws WC_CLI_Exception invalid data, server error
1073
	 */
1074
	protected function set_shipping( $order, $shipping, $action ) {
1075
1076
		// total must be a positive float
1077
		if ( isset( $shipping['total'] ) && floatval( $shipping['total'] ) < 0 ) {
1078
			throw new WC_CLI_Exception( 'woocommerce_invalid_shipping_total', __( 'Shipping total must be a positive amount', 'woocommerce' ) );
1079
		}
1080
1081
		if ( 'create' === $action ) {
1082
1083
			// method ID is required
1084
			if ( ! isset( $shipping['method_id'] ) ) {
1085
				throw new WC_CLI_Exception( 'woocommerce_invalid_shipping_item', __( 'Shipping method ID is required', 'woocommerce' ) );
1086
			}
1087
1088
			$rate = new WC_Shipping_Rate( $shipping['method_id'], isset( $shipping['method_title'] ) ? $shipping['method_title'] : '', isset( $shipping['total'] ) ? floatval( $shipping['total'] ) : 0, array(), $shipping['method_id'] );
1089
1090
			$shipping_id = $order->add_shipping( $rate );
1091
1092
			if ( ! $shipping_id ) {
1093
				throw new WC_CLI_Exception( 'woocommerce_cannot_create_shipping', __( 'Cannot create shipping method, try again', 'woocommerce' ) );
1094
			}
1095
1096 View Code Duplication
		} else {
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...
1097
1098
			$shipping_args = array();
1099
1100
			if ( isset( $shipping['method_id'] ) ) {
1101
				$shipping_args['method_id'] = $shipping['method_id'];
1102
			}
1103
1104
			if ( isset( $shipping['method_title'] ) ) {
1105
				$shipping_args['method_title'] = $shipping['method_title'];
1106
			}
1107
1108
			if ( isset( $shipping['total'] ) ) {
1109
				$shipping_args['cost'] = floatval( $shipping['total'] );
1110
			}
1111
1112
			$shipping_id = $order->update_shipping( $shipping['id'], $shipping_args );
1113
1114
			if ( ! $shipping_id ) {
1115
				throw new WC_CLI_Exception( 'woocommerce_cannot_update_shipping', __( 'Cannot update shipping method, try again', 'woocommerce' ) );
1116
			}
1117
		}
1118
	}
1119
1120
	/**
1121
	 * Create or update an order fee.
1122
	 *
1123
	 * @since  2.5.0
1124
	 * @param  \WC_Order $order
1125
	 * @param  array $fee item data
1126
	 * @param  string $action 'create' to add fee or 'update' to update it
1127
	 * @throws WC_CLI_Exception invalid data, server error
1128
	 */
1129
	protected function set_fee( $order, $fee, $action ) {
1130
1131
		if ( 'create' === $action ) {
1132
1133
			// fee title is required
1134
			if ( ! isset( $fee['title'] ) ) {
1135
				throw new WC_CLI_Exception( 'woocommerce_invalid_fee_item', __( 'Fee title is required', 'woocommerce' ) );
1136
			}
1137
1138
			$order_fee            = new stdClass();
1139
			$order_fee->id        = sanitize_title( $fee['title'] );
1140
			$order_fee->name      = $fee['title'];
1141
			$order_fee->amount    = isset( $fee['total'] ) ? floatval( $fee['total'] ) : 0;
1142
			$order_fee->taxable   = false;
1143
			$order_fee->tax       = 0;
1144
			$order_fee->tax_data  = array();
1145
			$order_fee->tax_class = '';
1146
1147
			// if taxable, tax class and total are required
1148
			if ( isset( $fee['taxable'] ) && $fee['taxable'] ) {
1149
1150
				if ( ! isset( $fee['tax_class'] ) ) {
1151
					throw new WC_CLI_Exception( 'woocommerce_invalid_fee_item', __( 'Fee tax class is required when fee is taxable', 'woocommerce' ) );
1152
				}
1153
1154
				$order_fee->taxable   = true;
1155
				$order_fee->tax_class = $fee['tax_class'];
1156
1157
				if ( isset( $fee['total_tax'] ) ) {
1158
					$order_fee->tax = isset( $fee['total_tax'] ) ? wc_format_refund_total( $fee['total_tax'] ) : 0;
1159
				}
1160
1161
				if ( isset( $fee['tax_data'] ) ) {
1162
					$order_fee->tax      = wc_format_refund_total( array_sum( $fee['tax_data'] ) );
1163
					$order_fee->tax_data = array_map( 'wc_format_refund_total', $fee['tax_data'] );
1164
				}
1165
			}
1166
1167
			$fee_id = $order->add_fee( $order_fee );
1168
1169
			if ( ! $fee_id ) {
1170
				throw new WC_CLI_Exception( 'woocommerce_cannot_create_fee', __( 'Cannot create fee, try again', 'woocommerce' ) );
1171
			}
1172
1173
		} else {
1174
1175
			$fee_args = array();
1176
1177
			if ( isset( $fee['title'] ) ) {
1178
				$fee_args['name'] = $fee['title'];
1179
			}
1180
1181
			if ( isset( $fee['tax_class'] ) ) {
1182
				$fee_args['tax_class'] = $fee['tax_class'];
1183
			}
1184
1185
			if ( isset( $fee['total'] ) ) {
1186
				$fee_args['line_total'] = floatval( $fee['total'] );
1187
			}
1188
1189
			if ( isset( $fee['total_tax'] ) ) {
1190
				$fee_args['line_tax'] = floatval( $fee['total_tax'] );
1191
			}
1192
1193
			$fee_id = $order->update_fee( $fee['id'], $fee_args );
1194
1195
			if ( ! $fee_id ) {
1196
				throw new WC_CLI_Exception( 'woocommerce_cannot_update_fee', __( 'Cannot update fee, try again', 'woocommerce' ) );
1197
			}
1198
		}
1199
	}
1200
1201
	/**
1202
	 * Create or update an order coupon
1203
	 *
1204
	 * @since  2.5.0
1205
	 * @param  \WC_Order $order
1206
	 * @param  array $coupon item data
1207
	 * @param  string $action 'create' to add coupon or 'update' to update it
1208
	 * @throws WC_CLI_Exception invalid data, server error
1209
	 */
1210
	protected function set_coupon( $order, $coupon, $action ) {
1211
1212
		// coupon amount must be positive float.
1213
		if ( isset( $coupon['amount'] ) && floatval( $coupon['amount'] ) < 0 ) {
1214
			throw new WC_CLI_Exception( 'woocommerce_invalid_coupon_total', __( 'Coupon discount total must be a positive amount', 'woocommerce' ) );
1215
		}
1216
1217 View Code Duplication
		if ( 'create' === $action ) {
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...
1218
1219
			// coupon code is required
1220
			if ( empty( $coupon['code'] ) ) {
1221
				throw new WC_CLI_Exception( 'woocommerce_invalid_coupon_coupon', __( 'Coupon code is required', 'woocommerce' ) );
1222
			}
1223
1224
			$coupon_id = $order->add_coupon( $coupon['code'], isset( $coupon['amount'] ) ? floatval( $coupon['amount'] ) : 0 );
1225
1226
			if ( ! $coupon_id ) {
1227
				throw new WC_CLI_Exception( 'woocommerce_cannot_create_order_coupon', __( 'Cannot create coupon, try again', 'woocommerce' ) );
1228
			}
1229
1230
		} else {
1231
1232
			$coupon_args = array();
1233
1234
			if ( isset( $coupon['code'] ) ) {
1235
				$coupon_args['code'] = $coupon['code'];
1236
			}
1237
1238
			if ( isset( $coupon['amount'] ) ) {
1239
				$coupon_args['discount_amount'] = floatval( $coupon['amount'] );
1240
			}
1241
1242
			$coupon_id = $order->update_coupon( $coupon['id'], $coupon_args );
1243
1244
			if ( ! $coupon_id ) {
1245
				throw new WC_CLI_Exception( 'woocommerce_cannot_update_order_coupon', __( 'Cannot update coupon, try again', 'woocommerce' ) );
1246
			}
1247
		}
1248
	}
1249
}
1250