Completed
Push — master ( 34f2bd...fdc177 )
by Claudio
08:28
created

WC_REST_Orders_Controller::create_order()   F

Complexity

Conditions 16
Paths 1054

Size

Total Lines 78
Code Lines 41

Duplication

Lines 6
Ratio 7.69 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 41
c 1
b 0
f 0
nc 1054
nop 1
dl 6
loc 78
rs 2.1772

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
 * REST API Orders controller
4
 *
5
 * Handles requests to the /orders endpoint.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.6.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Orders controller class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WC_REST_Posts_Controller
22
 */
23
class WC_REST_Orders_Controller extends WC_REST_Posts_Controller {
24
25
	/**
26
	 * Endpoint namespace.
27
	 *
28
	 * @var string
29
	 */
30
	protected $namespace = 'wc/v1';
31
32
	/**
33
	 * Route base.
34
	 *
35
	 * @var string
36
	 */
37
	protected $rest_base = 'orders';
38
39
	/**
40
	 * Post type.
41
	 *
42
	 * @var string
43
	 */
44
	protected $post_type = 'shop_order';
45
46
	/**
47
	 * Initialize orders actions.
48
	 */
49
	public function __construct() {
50
		add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
51
	}
52
53
	/**
54
	 * Register the routes for orders.
55
	 */
56
	public function register_routes() {
57
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
58
			array(
59
				'methods'             => WP_REST_Server::READABLE,
60
				'callback'            => array( $this, 'get_items' ),
61
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
62
				'args'                => $this->get_collection_params(),
63
			),
64
			array(
65
				'methods'             => WP_REST_Server::CREATABLE,
66
				'callback'            => array( $this, 'create_item' ),
67
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
68
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
69
			),
70
			'schema' => array( $this, 'get_public_item_schema' ),
71
		) );
72
73
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
74
			array(
75
				'methods'             => WP_REST_Server::READABLE,
76
				'callback'            => array( $this, 'get_item' ),
77
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
78
				'args'                => array(
79
					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
80
				),
81
			),
82
			array(
83
				'methods'             => WP_REST_Server::EDITABLE,
84
				'callback'            => array( $this, 'update_item' ),
85
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
86
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
87
			),
88
			array(
89
				'methods'             => WP_REST_Server::DELETABLE,
90
				'callback'            => array( $this, 'delete_item' ),
91
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
92
				'args'                => array(
93
					'force' => array(
94
						'default'     => false,
95
						'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
96
					),
97
					'reassign' => array(),
98
				),
99
			),
100
			'schema' => array( $this, 'get_public_item_schema' ),
101
		) );
102
103
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
104
			array(
105
				'methods'             => WP_REST_Server::EDITABLE,
106
				'callback'            => array( $this, 'batch_items' ),
107
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
108
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
109
			),
110
			'schema' => array( $this, 'get_public_batch_schema' ),
111
		) );
112
	}
113
114
	/**
115
	 * Prepare a single order output for response.
116
	 *
117
	 * @param WP_Post $post Post object.
118
	 * @param WP_REST_Request $request Request object.
119
	 * @return WP_REST_Response $data
120
	 */
121
	public function prepare_item_for_response( $post, $request ) {
122
		global $wpdb;
123
124
		$order = wc_get_order( $post );
125
		$dp    = $request['dp'];
126
127
		$data = array(
128
			'id'                   => $order->id,
129
			'parent_id'            => $post->post_parent,
130
			'status'               => $order->get_status(),
131
			'order_key'            => $order->order_key,
132
			'currency'             => $order->get_order_currency(),
133
			'version'              => $order->order_version,
134
			'prices_include_tax'   => $order->prices_include_tax,
135
			'date_created'         => wc_rest_prepare_date_response( $post->post_date_gmt ),
136
			'date_modified'        => wc_rest_prepare_date_response( $post->post_modified_gmt ),
137
			'customer_id'          => $order->get_user_id(),
138
			'discount_total'       => wc_format_decimal( $order->get_total_discount(), $dp ),
139
			'discount_tax'         => wc_format_decimal( $order->cart_discount_tax, $dp ),
140
			'shipping_total'       => wc_format_decimal( $order->get_total_shipping(), $dp ),
141
			'shipping_tax'         => wc_format_decimal( $order->get_shipping_tax(), $dp ),
142
			'cart_tax'             => wc_format_decimal( $order->get_cart_tax(), $dp ),
143
			'total'                => wc_format_decimal( $order->get_total(), $dp ),
144
			'total_tax'            => wc_format_decimal( $order->get_total_tax(), $dp ),
145
			'billing'              => array(),
146
			'shipping'             => array(),
147
			'payment_method'       => $order->payment_method,
148
			'payment_method_title' => $order->payment_method_title,
149
			'transaction_id'       => $order->get_transaction_id(),
150
			'customer_ip_address'  => $order->customer_ip_address,
151
			'customer_user_agent'  => $order->customer_user_agent,
152
			'created_via'          => $order->created_via,
153
			'customer_note'        => $order->customer_note,
154
			'date_completed'       => wc_rest_prepare_date_response( $order->completed_date ),
155
			'date_paid'            => $order->paid_date,
156
			'cart_hash'            => $order->cart_hash,
157
			'line_items'           => array(),
158
			'tax_lines'            => array(),
159
			'shipping_lines'       => array(),
160
			'fee_lines'            => array(),
161
			'coupon_lines'         => array(),
162
			'refunds'              => array(),
163
		);
164
165
		// Add addresses.
166
		$data['billing']  = $order->get_address( 'billing' );
167
		$data['shipping'] = $order->get_address( 'shipping' );
168
169
		// Add line items.
170 View Code Duplication
		foreach ( $order->get_items() as $item_id => $item ) {
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...
171
			$product      = $order->get_product_from_item( $item );
172
			$product_id   = 0;
173
			$variation_id = 0;
174
			$product_sku  = null;
175
176
			// Check if the product exists.
177
			if ( is_object( $product ) ) {
178
				$product_id   = $product->id;
179
				$variation_id = $product->variation_id;
180
				$product_sku  = $product->get_sku();
181
			}
182
183
			$meta = new WC_Order_Item_Meta( $item, $product );
184
185
			$item_meta = array();
186
187
			$hideprefix = 'true' === $request['all_item_meta'] ? null : '_';
188
189
			foreach ( $meta->get_formatted( $hideprefix ) as $meta_key => $formatted_meta ) {
190
				$item_meta[] = array(
191
					'key'   => $formatted_meta['key'],
192
					'label' => $formatted_meta['label'],
193
					'value' => $formatted_meta['value'],
194
				);
195
			}
196
197
			$line_item = array(
198
				'id'           => $item_id,
199
				'name'         => $item['name'],
200
				'sku'          => $product_sku,
201
				'product_id'   => (int) $product_id,
202
				'variation_id' => (int) $variation_id,
203
				'quantity'     => wc_stock_amount( $item['qty'] ),
204
				'tax_class'    => ! empty( $item['tax_class'] ) ? $item['tax_class'] : '',
205
				'price'        => wc_format_decimal( $order->get_item_total( $item, false, false ), $dp ),
206
				'subtotal'     => wc_format_decimal( $order->get_line_subtotal( $item, false, false ), $dp ),
207
				'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),
208
				'total'        => wc_format_decimal( $order->get_line_total( $item, false, false ), $dp ),
209
				'total_tax'    => wc_format_decimal( $item['line_tax'], $dp ),
210
				'taxes'        => array(),
211
				'meta'         => $item_meta,
212
			);
213
214
			$item_line_taxes = maybe_unserialize( $item['line_tax_data'] );
215
			if ( isset( $item_line_taxes['total'] ) ) {
216
				$line_tax = array();
217
218
				foreach ( $item_line_taxes['total'] as $tax_rate_id => $tax ) {
219
					$line_tax[ $tax_rate_id ] = array(
220
						'id'       => $tax_rate_id,
221
						'total'    => $tax,
222
						'subtotal' => '',
223
					);
224
				}
225
226
				foreach ( $item_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
227
					$line_tax[ $tax_rate_id ]['subtotal'] = $tax;
228
				}
229
230
				$line_item['taxes'] = array_values( $line_tax );
231
			}
232
233
			$data['line_items'][] = $line_item;
234
		}
235
236
		// Add taxes.
237
		foreach ( $order->get_items( 'tax' ) as $key => $tax ) {
238
			$tax_line = array(
239
				'id'                 => $key,
240
				'rate_code'          => $tax['name'],
241
				'rate_id'            => $tax['rate_id'],
242
				'label'              => isset( $tax['label'] ) ? $tax['label'] : $tax['name'],
243
				'compound'           => (bool) $tax['compound'],
244
				'tax_total'          => wc_format_decimal( $tax['tax_amount'], $dp ),
245
				'shipping_tax_total' => wc_format_decimal( $tax['shipping_tax_amount'], $dp ),
246
			);
247
248
			$data['tax_lines'][] = $tax_line;
249
		}
250
251
		// Add shipping.
252
		foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) {
253
			$shipping_line = array(
254
				'id'           => $shipping_item_id,
255
				'method_title' => $shipping_item['name'],
256
				'method_id'    => $shipping_item['method_id'],
257
				'total'        => wc_format_decimal( $shipping_item['cost'], $dp ),
258
				'total_tax'    => wc_format_decimal( '', $dp ),
259
				'taxes'        => array(),
260
			);
261
262
			$shipping_taxes = maybe_unserialize( $shipping_item['taxes'] );
263
264
			if ( ! empty( $shipping_taxes ) ) {
265
				$shipping_line['total_tax'] = wc_format_decimal( array_sum( $shipping_taxes ), $dp );
266
267
				foreach ( $shipping_taxes as $tax_rate_id => $tax ) {
268
					$shipping_line['taxes'][] = array(
269
						'id'       => $tax_rate_id,
270
						'total'    => $tax,
271
					);
272
				}
273
			}
274
275
			$data['shipping_lines'][] = $shipping_line;
276
		}
277
278
		// Add fees.
279
		foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
280
			$fee_line = array(
281
				'id'         => $fee_item_id,
282
				'name'       => $fee_item['name'],
283
				'tax_class'  => ! empty( $fee_item['tax_class'] ) ? $fee_item['tax_class'] : '',
284
				'tax_status' => 'taxable',
285
				'total'      => wc_format_decimal( $order->get_line_total( $fee_item ), $dp ),
286
				'total_tax'  => wc_format_decimal( $order->get_line_tax( $fee_item ), $dp ),
287
				'taxes'      => array(),
288
			);
289
290
			$fee_line_taxes = maybe_unserialize( $fee_item['line_tax_data'] );
291
			if ( isset( $fee_line_taxes['total'] ) ) {
292
				$fee_tax = array();
293
294
				foreach ( $fee_line_taxes['total'] as $tax_rate_id => $tax ) {
295
					$fee_tax[ $tax_rate_id ] = array(
296
						'id'       => $tax_rate_id,
297
						'total'    => $tax,
298
						'subtotal' => '',
299
					);
300
				}
301
302
				foreach ( $fee_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
303
					$fee_tax[ $tax_rate_id ]['subtotal'] = $tax;
304
				}
305
306
				$fee_line['taxes'] = array_values( $fee_tax );
307
			}
308
309
			$data['fee_lines'][] = $fee_line;
310
		}
311
312
		// Add coupons.
313
		foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
314
			$coupon_line = array(
315
				'id'           => $coupon_item_id,
316
				'code'         => $coupon_item['name'],
317
				'discount'     => wc_format_decimal( $coupon_item['discount_amount'], $dp ),
318
				'discount_tax' => wc_format_decimal( $coupon_item['discount_amount_tax'], $dp ),
319
			);
320
321
			$data['coupon_lines'][] = $coupon_line;
322
		}
323
324
		// Add refunds.
325
		foreach ( $order->get_refunds() as $refund ) {
326
			$data['refunds'][] = array(
327
				'id'     => $refund->id,
328
				'refund' => $refund->get_refund_reason() ? $refund->get_refund_reason() : '',
329
				'total'  => '-' . wc_format_decimal( $refund->get_refund_amount(), $dp ),
330
			);
331
		}
332
333
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
334
		$data    = $this->add_additional_fields_to_object( $data, $request );
335
		$data    = $this->filter_response_by_context( $data, $context );
336
337
		// Wrap the data in a response object.
338
		$response = rest_ensure_response( $data );
339
340
		$response->add_links( $this->prepare_links( $order ) );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
341
342
		/**
343
		 * Filter the data for a response.
344
		 *
345
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
346
		 * prepared for the response.
347
		 *
348
		 * @param WP_REST_Response   $response   The response object.
349
		 * @param WP_Post            $post       Post object.
350
		 * @param WP_REST_Request    $request    Request object.
351
		 */
352
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
353
	}
354
355
	/**
356
	 * Prepare links for the request.
357
	 *
358
	 * @param WC_Order $order Order object.
359
	 * @return array Links for the given order.
360
	 */
361
	protected function prepare_links( $order ) {
362
		$links = array(
363
			'self' => array(
364
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $order->id ) ),
365
			),
366
			'collection' => array(
367
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
368
			),
369
		);
370
371
		if ( 0 !== (int) $order->get_user_id() ) {
372
			$links['customer'] = array(
373
				'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $order->get_user_id() ) ),
374
			);
375
		}
376
377 View Code Duplication
		if ( 0 !== (int) $order->post->post_parent ) {
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...
378
			$links['up'] = array(
379
				'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order->post->post_parent ) ),
380
			);
381
		}
382
383
		return $links;
384
	}
385
386
	/**
387
	 * Query args.
388
	 *
389
	 * @param array $args
390
	 * @param WP_REST_Request $request
391
	 * @return array
392
	 */
393
	public function query_args( $args, $request ) {
394
		global $wpdb;
395
396
		// Set post_status.
397
		if ( 'any' !== $request['status'] ) {
398
			$args['post_status'] = 'wc-' . $request['status'];
399
		} else {
400
			$args['post_status'] = 'any';
401
		}
402
403 View Code Duplication
		if ( ! empty( $request['customer'] ) ) {
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...
404
			if ( ! empty( $args['meta_query'] ) ) {
405
				$args['meta_query'] = array();
406
			}
407
408
			$args['meta_query'][] = array(
409
				'key'   => '_customer_user',
410
				'value' => $request['customer'],
411
				'type'  => 'NUMERIC',
412
			);
413
		}
414
415
		// Search by product.
416
		if ( ! empty( $request['product'] ) ) {
417
			$order_ids = $wpdb->get_col( $wpdb->prepare( "
418
				SELECT order_id
419
				FROM {$wpdb->prefix}woocommerce_order_items
420
				WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d )
421
				AND order_item_type = 'line_item'
422
			 ", $request['product'] ) );
423
424
			// Force WP_Query return empty if don't found any order.
425
			$order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 );
426
427
			$args['post__in'] = $order_ids;
428
		}
429
430
		// Search.
431
		if ( ! empty( $args['s'] ) ) {
432
			$order_ids = wc_order_search( $args['s'] );
0 ignored issues
show
Bug introduced by
It seems like $args['s'] can also be of type array; however, wc_order_search() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
433
434
			if ( is_array( $order_ids ) ) {
435
				unset( $args['s'] );
436
				$args['post__in'] =  array_merge( $order_ids, array( 0 ) );
437
			}
438
		}
439
440
		return $args;
441
	}
442
443
	/**
444
	 * Prepare a single order for create.
445
	 *
446
	 * @param WP_REST_Request $request Request object.
447
	 * @return WP_Error|stdClass $data Object.
448
	 */
449
	protected function prepare_item_for_database( $request ) {
450
		$data = new stdClass;
451
452
		// Set default order args.
453
		$data->status        = $request['status'];
454
		$data->customer_id   = $request['customer_id'];
455
		$data->customer_note = $request['customer_note'];
456
457
		/**
458
		 * Filter the query_vars used in `get_items` for the constructed query.
459
		 *
460
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
461
		 * prepared for insertion.
462
		 *
463
		 * @param stdClass        $data    An object representing a single item prepared
464
		 *                                 for inserting the database.
465
		 * @param WP_REST_Request $request Request object.
466
		 */
467
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $data, $request );
468
	}
469
470
	/**
471
	 * Create base WC Order object.
472
	 *
473
	 * @param array $data
474
	 * @return WC_Order
475
	 */
476
	protected function create_base_order( $data ) {
477
		return wc_create_order( $data );
478
	}
479
480
	/**
481
	 * Create order.
482
	 *
483
	 * @param WP_REST_Request $request Full details about the request.
484
	 * @return int|WP_Error
485
	 */
486
	protected function create_order( $request ) {
487
		wc_transaction_query( 'start' );
488
489
		try {
490
			// Make sure customer exists.
491
			if ( 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) {
492
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce' ), 400 );
493
			}
494
495
			$data = $this->prepare_item_for_database( $request );
496
			if ( is_wp_error( $data ) ) {
497
				return $data;
498
			}
499
500
			$data->created_via = 'rest-api';
501
502
			$order = $this->create_base_order( (array) $data );
503
504 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...
505
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_order', sprintf( __( 'Cannot create order: %s.', 'woocommerce' ), implode( ', ', $order->get_error_messages() ) ), 400 );
506
			}
507
508
			// Set addresses.
509
			if ( is_array( $request['billing'] ) ) {
510
				$this->update_address( $order, $request['billing'], 'billing' );
511
			}
512
			if ( is_array( $request['shipping'] ) ) {
513
				$this->update_address( $order, $request['shipping'], 'shipping' );
514
			}
515
516
			// Set currency.
517
			update_post_meta( $order->id, '_order_currency', $request['currency'] );
518
519
			// Set lines.
520
			$lines = array(
521
				'line_item' => 'line_items',
522
				'shipping'  => 'shipping_lines',
523
				'fee'       => 'fee_lines',
524
				'coupon'    => 'coupon_lines',
525
			);
526
527
			foreach ( $lines as $line_type => $line ) {
528
				if ( is_array( $request[ $line ] ) ) {
529
					foreach ( $request[ $line ] as $item ) {
530
						$set_item = 'set_' . $line_type;
531
						$this->$set_item( $order, $item, 'create' );
532
					}
533
				}
534
			}
535
536
			// Calculate totals and set them.
537
			$order->calculate_totals();
538
539
			// Set payment method.
540
			if ( ! empty( $request['payment_method'] ) ) {
541
				update_post_meta( $order->id, '_payment_method', $request['payment_method'] );
542
			}
543
			if ( ! empty( $request['payment_method_title'] ) ) {
544
				update_post_meta( $order->id, '_payment_method_title', $request['payment_method'] );
545
			}
546
			if ( true === $request['set_paid'] ) {
547
				$order->payment_complete( $request['transaction_id'] );
548
			}
549
550
			// Set meta data.
551 View Code Duplication
			if ( ! empty( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) {
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...
552
				$this->update_meta_data( $order->id, $request['meta_data'] );
553
			}
554
555
			wc_transaction_query( 'commit' );
556
557
			return $order->id;
558
		} catch ( WC_REST_Exception $e ) {
559
			wc_transaction_query( 'rollback' );
560
561
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
562
		}
563
	}
564
565
	/**
566
	 * Update address.
567
	 *
568
	 * @param WC_Order $order
569
	 * @param array $posted
570
	 * @param string $type
571
	 */
572
	protected function update_address( $order, $posted, $type = 'billing' ) {
573
		$fields = $order->get_address( $type );
574
575
		foreach ( array_keys( $fields ) as $field ) {
576
			if ( isset( $posted[ $field ] ) ) {
577
				$fields[ $field ] = $posted[ $field ];
578
			}
579
		}
580
581
		// Set address.
582
		$order->set_address( $fields, $type );
583
584
		// Update user meta.
585
		if ( $order->get_user_id() ) {
586
			foreach ( $fields as $key => $value ) {
587
				update_user_meta( $order->get_user_id(), $type . '_' . $key, $value );
588
			}
589
		}
590
	}
591
592
	/**
593
	 * Create or update a line item.
594
	 *
595
	 * @param WC_Order $order Order data.
596
	 * @param array $item Line item data.
597
	 * @param string $action 'create' to add line item or 'update' to update it.
598
	 * @throws WC_REST_Exception Invalid data, server error.
599
	 */
600
	protected function set_line_item( $order, $item, $action = 'create' ) {
601
		$creating  = 'create' === $action;
602
		$item_args = array();
603
604
		// Product is always required.
605
		if ( empty( $item['product_id'] ) && empty( $item['sku'] ) && empty( $item['variation_id'] ) ) {
606
			throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 );
607
		}
608
609
		if ( ! empty( $item['sku'] ) ) {
610
			$product_id = (int) wc_get_product_id_by_sku( $item['sku'] );
611
		} elseif ( ! empty( $item['product_id'] ) && empty( $item['variation_id'] ) ) {
612
			$product_id = (int) $item['product_id'];
613
		} elseif ( ! empty( $item['variation_id'] ) ) {
614
			$product_id = (int) $item['variation_id'];
615
		}
616
617
		// When updating, ensure product ID provided matches.
618
		if ( 'update' === $action && ! empty( $item['id'] ) ) {
619
			$item_product_id   = (int) wc_get_order_item_meta( $item['id'], '_product_id' );
620
			$item_variation_id = (int) wc_get_order_item_meta( $item['id'], '_variation_id' );
621
622
			if ( $product_id !== $item_product_id && $product_id !== $item_variation_id ) {
623
				throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or variation ID provided does not match this line item.', 'woocommerce' ), 400 );
624
			}
625
		}
626
627
		$product = wc_get_product( $product_id );
628
629
		// Must be a valid WC_Product.
630
		if ( ! is_object( $product ) ) {
631
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product', __( 'Product is invalid.', 'woocommerce' ), 400 );
632
		}
633
634
		// Quantity must be positive float.
635
		if ( isset( $item['quantity'] ) && 0 >= floatval( $item['quantity'] ) ) {
636
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product_quantity', __( 'Product quantity must be a positive float.', 'woocommerce' ), 400 );
637
		}
638
639
		// Quantity is required when creating.
640
		if ( $creating && ! isset( $item['quantity'] ) ) {
641
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product_quantity', __( 'Product quantity is required.', 'woocommerce' ), 400 );
642
		}
643
644
		// Get variation attributes.
645
		if ( method_exists( $product, 'get_variation_attributes' ) ) {
646
			$item_args['variation'] = $product->get_variation_attributes();
647
		}
648
649
		// Quantity.
650
		if ( isset( $item['quantity'] ) ) {
651
			$item_args['qty'] = $item['quantity'];
652
		}
653
654
		// Total.
655
		if ( isset( $item['total'] ) ) {
656
			$item_args['totals']['total'] = floatval( $item['total'] );
657
		}
658
659
		// Total tax.
660
		if ( isset( $item['total_tax'] ) ) {
661
			$item_args['totals']['tax'] = floatval( $item['total_tax'] );
662
		}
663
664
		// Subtotal.
665
		if ( isset( $item['subtotal'] ) ) {
666
			$item_args['totals']['subtotal'] = floatval( $item['subtotal'] );
667
		}
668
669
		// Subtotal tax.
670
		if ( isset( $item['subtotal_tax'] ) ) {
671
			$item_args['totals']['subtotal_tax'] = floatval( $item['subtotal_tax'] );
672
		}
673
674 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...
675
			$item_id = $order->add_product( $product, $item_args['qty'], $item_args );
676
			if ( ! $item_id ) {
677
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_line_item', __( 'Cannot create line item, try again.', 'woocommerce' ), 500 );
678
			}
679
		} else {
680
			$item_id = $order->update_product( $item['id'], $product, $item_args );
681
			if ( ! $item_id ) {
682
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_line_item', __( 'Cannot update line item, try again.', 'woocommerce' ), 500 );
683
			}
684
		}
685
	}
686
687
	/**
688
	 * Create or update an order shipping method.
689
	 *
690
	 * @param WC_Order $order Order data.
691
	 * @param array $shipping Item data.
692
	 * @param string $action 'create' to add shipping or 'update' to update it.
693
	 * @throws WC_REST_Exception Invalid data, server error.
694
	 */
695
	protected function set_shipping( $order, $shipping, $action ) {
696
		// Total must be a positive float.
697
		if ( ! empty( $shipping['total'] ) && 0 > floatval( $shipping['total'] ) ) {
698
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_total', __( 'Shipping total must be a positive amount.', 'woocommerce' ), 400 );
699
		}
700
701
		if ( 'create' === $action ) {
702
			// Method ID is required.
703
			if ( empty( $shipping['method_id'] ) ) {
704
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 );
705
			}
706
707
			$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'] );
708
709
			$shipping_id = $order->add_shipping( $rate );
710
711
			if ( ! $shipping_id ) {
712
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_shipping', __( 'Cannot create shipping method, try again.', 'woocommerce' ), 500 );
713
			}
714
715
		} else {
716
			$shipping_args = array();
717
718
			if ( isset( $shipping['method_id'] ) ) {
719
				$shipping_args['method_id'] = $shipping['method_id'];
720
			}
721
722
			if ( isset( $shipping['method_title'] ) ) {
723
				$shipping_args['method_title'] = $shipping['method_title'];
724
			}
725
726
			if ( isset( $shipping['total'] ) ) {
727
				$shipping_args['cost'] = floatval( $shipping['total'] );
728
			}
729
730
			$shipping_id = $order->update_shipping( $shipping['id'], $shipping_args );
731
732
			if ( ! $shipping_id ) {
733
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_shipping', __( 'Cannot update shipping method, try again.', 'woocommerce' ), 500 );
734
			}
735
		}
736
	}
737
738
	/**
739
	 * Create or update an order fee.
740
	 *
741
	 * @param WC_Order $order Order data.
742
	 * @param array $fee Item data.
743
	 * @param string $action 'create' to add fee or 'update' to update it.
744
	 * @throws WC_REST_Exception Invalid data, server error.
745
	 */
746
	protected function set_fee( $order, $fee, $action ) {
747
		if ( 'create' === $action ) {
748
749
			// Fee name is required.
750
			if ( empty( $fee['name'] ) ) {
751
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 );
752
			}
753
754
			$fee_data            = new stdClass();
755
			$fee_data->id        = sanitize_title( $fee['name'] );
756
			$fee_data->name      = $fee['name'];
757
			$fee_data->amount    = isset( $fee['total'] ) ? floatval( $fee['total'] ) : 0;
758
			$fee_data->taxable   = false;
759
			$fee_data->tax       = 0;
760
			$fee_data->tax_data  = array();
761
			$fee_data->tax_class = '';
762
763
			// If taxable, tax class and total are required.
764
			if ( isset( $fee['tax_status'] ) && 'taxable' === $fee['tax_status'] ) {
765
766
				if ( ! isset( $fee['tax_class'] ) ) {
767
					throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee tax class is required when fee is taxable.', 'woocommerce' ), 400 );
768
				}
769
770
				$fee_data->taxable   = true;
771
				$fee_data->tax_class = $fee['tax_class'];
772
773
				if ( isset( $fee['total_tax'] ) ) {
774
					$fee_data->tax = isset( $fee['total_tax'] ) ? wc_format_refund_total( $fee['total_tax'] ) : 0;
775
				}
776
			}
777
778
			$fee_id = $order->add_fee( $fee_data );
779
780
			if ( ! $fee_id ) {
781
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_fee', __( 'Cannot create fee, try again.', 'woocommerce' ), 500 );
782
			}
783
784
		} else {
785
			$fee_args = array();
786
787
			if ( isset( $fee['name'] ) ) {
788
				$fee_args['name'] = $fee['name'];
789
			}
790
791
			if ( isset( $fee['tax_class'] ) ) {
792
				$fee_args['tax_class'] = $fee['tax_class'];
793
			}
794
795
			if ( isset( $fee['total'] ) ) {
796
				$fee_args['line_total'] = floatval( $fee['total'] );
797
			}
798
799
			if ( isset( $fee['total_tax'] ) ) {
800
				$fee_args['line_tax'] = floatval( $fee['total_tax'] );
801
			}
802
803
			$fee_id = $order->update_fee( $fee['id'], $fee_args );
804
805
			if ( ! $fee_id ) {
806
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_fee', __( 'Cannot update fee, try again.', 'woocommerce' ), 500 );
807
			}
808
		}
809
	}
810
811
	/**
812
	 * Create or update an order coupon.
813
	 *
814
	 * @param WC_Order $order Order data.
815
	 * @param array $coupon Item data.
816
	 * @param string $action 'create' to add coupon or 'update' to update it.
817
	 * @throws WC_REST_Exception Invalid data, server error.
818
	 */
819
	protected function set_coupon( $order, $coupon, $action ) {
820
		// Coupon discount must be positive float.
821
		if ( isset( $coupon['discount'] ) && 0 > floatval( $coupon['discount'] ) ) {
822
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_total', __( 'Coupon discount must be a positive amount.', 'woocommerce' ), 400 );
823
		}
824
825
		if ( 'create' === $action ) {
826
			// Coupon code is required.
827
			if ( empty( $coupon['code'] ) ) {
828
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
829
			}
830
831
			$coupon_id = $order->add_coupon( $coupon['code'], floatval( $coupon['discount'] ) );
832
833
			if ( ! $coupon_id ) {
834
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_order_coupon', __( 'Cannot create coupon, try again.', 'woocommerce' ), 500 );
835
			}
836
837
		} else {
838
			$coupon_args = array();
839
840
			if ( isset( $coupon['code'] ) ) {
841
				$coupon_args['code'] = $coupon['code'];
842
			}
843
844
			if ( isset( $coupon['discount'] ) ) {
845
				$coupon_args['discount_amount'] = floatval( $coupon['discount'] );
846
			}
847
848
			$coupon_id = $order->update_coupon( $coupon['id'], $coupon_args );
849
850
			if ( ! $coupon_id ) {
851
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_order_coupon', __( 'Cannot update coupon, try again.', 'woocommerce' ), 500 );
852
			}
853
		}
854
	}
855
856
	/**
857
	 * Helper method to add/update meta data, with two restrictions:
858
	 *
859
	 * 1) Only non-protected meta (no leading underscore) can be set
860
	 * 2) Meta values must be scalar (int, string, bool)
861
	 *
862
	 * @param int $order_id Order ID.
863
	 * @param array $meta_data Meta data in array( 'meta_key' => 'meta_value' ) format.
864
	 */
865 View Code Duplication
	protected function update_meta_data( $order_id, $meta_data ) {
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...
866
		foreach ( $meta_data as $meta_key => $meta_value ) {
867
			if ( is_string( $meta_key ) && ! is_protected_meta( $meta_key ) && is_scalar( $meta_value ) ) {
868
				update_post_meta( $order_id, $meta_key, $meta_value );
869
			}
870
		}
871
	}
872
873
	/**
874
	 * Create a single item.
875
	 *
876
	 * @param WP_REST_Request $request Full details about the request.
877
	 * @return WP_Error|WP_REST_Response
878
	 */
879
	public function create_item( $request ) {
880
		if ( ! empty( $request['id'] ) ) {
881
			return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
882
		}
883
884
		$order_id = $this->create_order( $request );
885
		if ( is_wp_error( $order_id ) ) {
886
			return $order_id;
887
		}
888
889
		// Clear transients.
890
		wc_delete_shop_order_transients( $order_id );
0 ignored issues
show
Bug introduced by
It seems like $order_id defined by $this->create_order($request) on line 884 can also be of type object<stdClass>; however, wc_delete_shop_order_transients() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
891
892
		$post = get_post( $order_id );
893
		$this->update_additional_fields_for_object( $post, $request );
894
895
		/**
896
		 * Fires after a single item is created or updated via the REST API.
897
		 *
898
		 * @param object          $post      Inserted object (not a WP_Post object).
899
		 * @param WP_REST_Request $request   Request object.
900
		 * @param boolean         $creating  True when creating item, false when updating.
901
		 */
902
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
903
904
		$request->set_param( 'context', 'edit' );
905
		$response = $this->prepare_item_for_response( $post, $request );
906
		$response = rest_ensure_response( $response );
907
		$response->set_status( 201 );
908
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
909
910
		return $response;
911
	}
912
913
	/**
914
	 * Wrapper method to create/update order items.
915
	 * When updating, the item ID provided is checked to ensure it is associated
916
	 * with the order.
917
	 *
918
	 * @param WC_Order $order order
919
	 * @param string $item_type
920
	 * @param array $item item provided in the request body
921
	 * @param string $action either 'create' or 'update'
922
	 * @throws WC_REST_Exception If item ID is not associated with order
923
	 */
924 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...
925
		global $wpdb;
926
927
		$set_method = 'set_' . $item_type;
928
929
		// Verify provided line item ID is associated with order.
930
		if ( 'update' === $action ) {
931
			$result = $wpdb->get_row(
932
				$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d",
933
				absint( $item['id'] ),
934
				absint( $order->id )
935
			) );
936
937
			if ( is_null( $result ) ) {
938
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 );
939
			}
940
		}
941
942
		$this->$set_method( $order, $item, $action );
943
	}
944
945
	/**
946
	 * Helper method to check if the resource ID associated with the provided item is null.
947
	 * Items can be deleted by setting the resource ID to null.
948
	 *
949
	 * @param array $item Item provided in the request body.
950
	 * @return bool True if the item resource ID is null, false otherwise.
951
	 */
952
	protected function item_is_null( $item ) {
953
		$keys = array( 'product_id', 'method_id', 'title', 'code' );
954
955
		foreach ( $keys as $key ) {
956
			if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) {
957
				return true;
958
			}
959
		}
960
961
		return false;
962
	}
963
964
	/**
965
	 * Update order.
966
	 *
967
	 * @param WP_REST_Request $request Full details about the request.
968
	 * @param WP_Post $post Post data.
969
	 * @return int|WP_Error
970
	 */
971
	protected function update_order( $request, $post ) {
972
		try {
973
			$update_totals = false;
974
			$order         = wc_get_order( $post );
975
			$order_args    = array( 'order_id' => $order->id );
976
977
			// Customer note.
978
			if ( isset( $request['customer_note'] ) ) {
979
				$order_args['customer_note'] = $request['customer_note'];
980
			}
981
982
			// Customer ID.
983
			if ( isset( $request['customer_id'] ) && $request['customer_id'] != $order->get_user_id() ) {
984
				// Make sure customer exists.
985
				if ( false === get_user_by( 'id', $request['customer_id'] ) ) {
986
					throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id', __( 'Customer ID is invalid.', 'woocommerce' ), 400 );
987
				}
988
989
				update_post_meta( $order->id, '_customer_user', $request['customer_id'] );
990
			}
991
992
			// Update addresses.
993
			if ( is_array( $request['billing'] ) ) {
994
				$this->update_address( $order, $request['billing'], 'billing' );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
995
			}
996
			if ( is_array( $request['shipping'] ) ) {
997
				$this->update_address( $order, $request['shipping'], 'shipping' );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
998
			}
999
1000
			$lines = array(
1001
				'line_item' => 'line_items',
1002
				'shipping'  => 'shipping_lines',
1003
				'fee'       => 'fee_lines',
1004
				'coupon'    => 'coupon_lines',
1005
			);
1006
1007
			foreach ( $lines as $line_type => $line ) {
1008
				if ( isset( $request[ $line ] ) && is_array( $request[ $line ] ) ) {
1009
					$update_totals = true;
1010
					foreach ( $request[ $line ] as $item ) {
1011
						// Item ID is always required.
1012
						if ( ! array_key_exists( 'id', $item ) ) {
1013
							throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID is required.', 'woocommerce' ), 400 );
1014
						}
1015
1016
						// Create item.
1017
						if ( is_null( $item['id'] ) ) {
1018
							$this->set_item( $order, $line_type, $item, 'create' );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1019
						} elseif ( $this->item_is_null( $item ) ) {
1020
							// Delete item.
1021
							wc_delete_order_item( $item['id'] );
1022
						} else {
1023
							// Update item.
1024
							$this->set_item( $order, $line_type, $item, 'update' );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1025
						}
1026
					}
1027
				}
1028
			}
1029
1030
			// Set payment method.
1031
			if ( ! empty( $request['payment_method'] ) ) {
1032
				update_post_meta( $order->id, '_payment_method', $request['payment_method'] );
1033
			}
1034
			if ( ! empty( $request['payment_method_title'] ) ) {
1035
				update_post_meta( $order->id, '_payment_method_title', $request['payment_method'] );
1036
			}
1037
			if ( $order->needs_payment() && isset( $request['set_paid'] ) && true === $request['set_paid'] ) {
1038
				$order->payment_complete( ! empty( $request['transaction_id'] ) ? $request['transaction_id'] : '' );
1039
			}
1040
1041
			// Set order currency.
1042
			if ( isset( $request['currency'] ) ) {
1043
				update_post_meta( $order->id, '_order_currency', $request['currency'] );
1044
			}
1045
1046
			// If items have changed, recalculate order totals.
1047
			if ( $update_totals ) {
1048
				$order->calculate_totals();
1049
			}
1050
1051
			// Update meta data.
1052 View Code Duplication
			if ( ! empty( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) {
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...
1053
				$this->update_meta_data( $order->id, $request['meta_data'] );
1054
			}
1055
1056
			// Update the order post to set customer note/modified date.
1057
			wc_update_order( $order_args );
1058
1059
			// Order status.
1060 View Code Duplication
			if ( ! empty( $request['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...
1061
				$order->update_status( $request['status'], isset( $request['status_note'] ) ? $request['status_note'] : '' );
1062
			}
1063
1064
			return $order->id;
1065
		} catch ( WC_REST_Exception $e ) {
1066
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
1067
		}
1068
	}
1069
1070
	/**
1071
	 * Update a single order.
1072
	 *
1073
	 * @param WP_REST_Request $request Full details about the request.
1074
	 * @return WP_Error|WP_REST_Response
1075
	 */
1076
	public function update_item( $request ) {
1077
		$id   = (int) $request['id'];
1078
		$post = get_post( $id );
1079
1080
		if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
1081
			return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
1082
		}
1083
1084
		$order_id = $this->update_order( $request, $post );
1085
		if ( is_wp_error( $order_id ) ) {
1086
			return $order_id;
1087
		}
1088
1089
		// Clear transients.
1090
		wc_delete_shop_order_transients( $order_id );
1091
1092
		$post = get_post( $order_id );
1093
		$this->update_additional_fields_for_object( $post, $request );
1094
1095
		/**
1096
		 * Fires after a single item is created or updated via the REST API.
1097
		 *
1098
		 * @param object          $post      Inserted object (not a WP_Post object).
1099
		 * @param WP_REST_Request $request   Request object.
1100
		 * @param boolean         $creating  True when creating item, false when updating.
1101
		 */
1102
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
1103
1104
		$request->set_param( 'context', 'edit' );
1105
		$response = $this->prepare_item_for_response( $post, $request );
1106
		return rest_ensure_response( $response );
1107
	}
1108
1109
	/**
1110
	 * Get order statuses.
1111
	 *
1112
	 * @return array
1113
	 */
1114
	protected function get_order_statuses() {
1115
		$order_statuses = array();
1116
1117
		foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
1118
			$order_statuses[] = str_replace( 'wc-', '', $status );
1119
		}
1120
1121
		return $order_statuses;
1122
	}
1123
1124
	/**
1125
	 * Get the Order's schema, conforming to JSON Schema.
1126
	 *
1127
	 * @return array
1128
	 */
1129
	public function get_item_schema() {
1130
		$schema = array(
1131
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
1132
			'title'      => $this->post_type,
1133
			'type'       => 'object',
1134
			'properties' => array(
1135
				'id' => array(
1136
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
1137
					'type'        => 'integer',
1138
					'context'     => array( 'view', 'edit' ),
1139
					'readonly'    => true,
1140
				),
1141
				'parent_id' => array(
1142
					'description' => __( 'Parent order ID.', 'woocommerce' ),
1143
					'type'        => 'integer',
1144
					'context'     => array( 'view', 'edit' ),
1145
				),
1146
				'status' => array(
1147
					'description' => __( 'Order status.', 'woocommerce' ),
1148
					'type'        => 'string',
1149
					'default'     => 'pending',
1150
					'enum'        => $this->get_order_statuses(),
1151
					'context'     => array( 'view', 'edit' ),
1152
				),
1153
				'order_key' => array(
1154
					'description' => __( 'Order key.', 'woocommerce' ),
1155
					'type'        => 'string',
1156
					'context'     => array( 'view', 'edit' ),
1157
					'readonly'    => true,
1158
				),
1159
				'currency' => array(
1160
					'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ),
1161
					'type'        => 'string',
1162
					'default'     => get_woocommerce_currency(),
1163
					'enum'        => array_keys( get_woocommerce_currencies() ),
1164
					'context'     => array( 'view', 'edit' ),
1165
				),
1166
				'version' => array(
1167
					'description' => __( 'Version of WooCommerce when the order was made.', 'woocommerce' ),
1168
					'type'        => 'integer',
1169
					'context'     => array( 'view', 'edit' ),
1170
					'readonly'    => true,
1171
				),
1172
				'prices_include_tax' => array(
1173
					'description' => __( 'Shows if the prices included tax during checkout.', 'woocommerce' ),
1174
					'type'        => 'boolean',
1175
					'context'     => array( 'view', 'edit' ),
1176
					'readonly'    => true,
1177
				),
1178
				'date_created' => array(
1179
					'description' => __( "The date the order was created, in the site's timezone.", 'woocommerce' ),
1180
					'type'        => 'date-time',
1181
					'context'     => array( 'view', 'edit' ),
1182
					'readonly'    => true,
1183
				),
1184
				'date_modified' => array(
1185
					'description' => __( "The date the order was last modified, in the site's timezone.", 'woocommerce' ),
1186
					'type'        => 'date-time',
1187
					'context'     => array( 'view', 'edit' ),
1188
					'readonly'    => true,
1189
				),
1190
				'customer_id' => array(
1191
					'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ),
1192
					'type'        => 'integer',
1193
					'default'     => 0,
1194
					'context'     => array( 'view', 'edit' ),
1195
				),
1196
				'discount_total' => array(
1197
					'description' => __( 'Total discount amount for the order.', 'woocommerce' ),
1198
					'type'        => 'string',
1199
					'context'     => array( 'view', 'edit' ),
1200
					'readonly'    => true,
1201
				),
1202
				'discount_tax' => array(
1203
					'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ),
1204
					'type'        => 'string',
1205
					'context'     => array( 'view', 'edit' ),
1206
					'readonly'    => true,
1207
				),
1208
				'shipping_total' => array(
1209
					'description' => __( 'Total shipping amount for the order.', 'woocommerce' ),
1210
					'type'        => 'string',
1211
					'context'     => array( 'view', 'edit' ),
1212
					'readonly'    => true,
1213
				),
1214
				'shipping_tax' => array(
1215
					'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ),
1216
					'type'        => 'string',
1217
					'context'     => array( 'view', 'edit' ),
1218
					'readonly'    => true,
1219
				),
1220
				'cart_tax' => array(
1221
					'description' => __( 'Sum of line item taxes only.', 'woocommerce' ),
1222
					'type'        => 'string',
1223
					'context'     => array( 'view', 'edit' ),
1224
					'readonly'    => true,
1225
				),
1226
				'total' => array(
1227
					'description' => __( 'Grand total.', 'woocommerce' ),
1228
					'type'        => 'string',
1229
					'context'     => array( 'view', 'edit' ),
1230
					'readonly'    => true,
1231
				),
1232
				'total_tax' => array(
1233
					'description' => __( 'Sum of all taxes.', 'woocommerce' ),
1234
					'type'        => 'string',
1235
					'context'     => array( 'view', 'edit' ),
1236
					'readonly'    => true,
1237
				),
1238
				'billing' => array(
1239
					'description' => __( 'Billing address.', 'woocommerce' ),
1240
					'type'        => 'array',
1241
					'context'     => array( 'view', 'edit' ),
1242
					'properties'  => array(
1243
						'first_name' => array(
1244
							'description' => __( 'First name.', 'woocommerce' ),
1245
							'type'        => 'string',
1246
							'context'     => array( 'view', 'edit' ),
1247
						),
1248
						'last_name' => array(
1249
							'description' => __( 'Last name.', 'woocommerce' ),
1250
							'type'        => 'string',
1251
							'context'     => array( 'view', 'edit' ),
1252
						),
1253
						'company' => array(
1254
							'description' => __( 'Company name.', 'woocommerce' ),
1255
							'type'        => 'string',
1256
							'context'     => array( 'view', 'edit' ),
1257
						),
1258
						'address_1' => array(
1259
							'description' => __( 'Address line 1.', 'woocommerce' ),
1260
							'type'        => 'string',
1261
							'context'     => array( 'view', 'edit' ),
1262
						),
1263
						'address_2' => array(
1264
							'description' => __( 'Address line 2.', 'woocommerce' ),
1265
							'type'        => 'string',
1266
							'context'     => array( 'view', 'edit' ),
1267
						),
1268
						'city' => array(
1269
							'description' => __( 'City name.', 'woocommerce' ),
1270
							'type'        => 'string',
1271
							'context'     => array( 'view', 'edit' ),
1272
						),
1273
						'state' => array(
1274
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1275
							'type'        => 'string',
1276
							'context'     => array( 'view', 'edit' ),
1277
						),
1278
						'postcode' => array(
1279
							'description' => __( 'Postal code.', 'woocommerce' ),
1280
							'type'        => 'string',
1281
							'context'     => array( 'view', 'edit' ),
1282
						),
1283
						'country' => array(
1284
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1285
							'type'        => 'string',
1286
							'context'     => array( 'view', 'edit' ),
1287
						),
1288
						'email' => array(
1289
							'description' => __( 'Email address.', 'woocommerce' ),
1290
							'type'        => 'string',
1291
							'format'      => 'email',
1292
							'context'     => array( 'view', 'edit' ),
1293
						),
1294
						'phone' => array(
1295
							'description' => __( 'Phone number.', 'woocommerce' ),
1296
							'type'        => 'string',
1297
							'context'     => array( 'view', 'edit' ),
1298
						),
1299
					),
1300
				),
1301
				'shipping' => array(
1302
					'description' => __( 'Shipping address.', 'woocommerce' ),
1303
					'type'        => 'array',
1304
					'context'     => array( 'view', 'edit' ),
1305
					'properties'  => array(
1306
						'first_name' => array(
1307
							'description' => __( 'First name.', 'woocommerce' ),
1308
							'type'        => 'string',
1309
							'context'     => array( 'view', 'edit' ),
1310
						),
1311
						'last_name' => array(
1312
							'description' => __( 'Last name.', 'woocommerce' ),
1313
							'type'        => 'string',
1314
							'context'     => array( 'view', 'edit' ),
1315
						),
1316
						'company' => array(
1317
							'description' => __( 'Company name.', 'woocommerce' ),
1318
							'type'        => 'string',
1319
							'context'     => array( 'view', 'edit' ),
1320
						),
1321
						'address_1' => array(
1322
							'description' => __( 'Address line 1.', 'woocommerce' ),
1323
							'type'        => 'string',
1324
							'context'     => array( 'view', 'edit' ),
1325
						),
1326
						'address_2' => array(
1327
							'description' => __( 'Address line 2.', 'woocommerce' ),
1328
							'type'        => 'string',
1329
							'context'     => array( 'view', 'edit' ),
1330
						),
1331
						'city' => array(
1332
							'description' => __( 'City name.', 'woocommerce' ),
1333
							'type'        => 'string',
1334
							'context'     => array( 'view', 'edit' ),
1335
						),
1336
						'state' => array(
1337
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1338
							'type'        => 'string',
1339
							'context'     => array( 'view', 'edit' ),
1340
						),
1341
						'postcode' => array(
1342
							'description' => __( 'Postal code.', 'woocommerce' ),
1343
							'type'        => 'string',
1344
							'context'     => array( 'view', 'edit' ),
1345
						),
1346
						'country' => array(
1347
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1348
							'type'        => 'string',
1349
							'context'     => array( 'view', 'edit' ),
1350
						),
1351
					),
1352
				),
1353
				'payment_method' => array(
1354
					'description' => __( 'Payment method ID.', 'woocommerce' ),
1355
					'type'        => 'string',
1356
					'context'     => array( 'view', 'edit' ),
1357
				),
1358
				'payment_method_title' => array(
1359
					'description' => __( 'Payment method title.', 'woocommerce' ),
1360
					'type'        => 'string',
1361
					'context'     => array( 'view', 'edit' ),
1362
				),
1363
				'set_paid' => array(
1364
					'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ),
1365
					'type'        => 'boolean',
1366
					'default'     => false,
1367
					'context'     => array( 'edit' ),
1368
				),
1369
				'transaction_id' => array(
1370
					'description' => __( 'Unique transaction ID.', 'woocommerce' ),
1371
					'type'        => 'string',
1372
					'context'     => array( 'view', 'edit' ),
1373
				),
1374
				'customer_ip_address' => array(
1375
					'description' => __( "Customer's IP address.", 'woocommerce' ),
1376
					'type'        => 'string',
1377
					'context'     => array( 'view', 'edit' ),
1378
					'readonly'    => true,
1379
				),
1380
				'customer_user_agent' => array(
1381
					'description' => __( 'User agent of the customer.', 'woocommerce' ),
1382
					'type'        => 'string',
1383
					'context'     => array( 'view', 'edit' ),
1384
					'readonly'    => true,
1385
				),
1386
				'created_via' => array(
1387
					'description' => __( 'Shows where the order was created.', 'woocommerce' ),
1388
					'type'        => 'string',
1389
					'context'     => array( 'view', 'edit' ),
1390
					'readonly'    => true,
1391
				),
1392
				'customer_note' => array(
1393
					'description' => __( 'Note left by customer during checkout.', 'woocommerce' ),
1394
					'type'        => 'string',
1395
					'context'     => array( 'view', 'edit' ),
1396
				),
1397
				'date_completed' => array(
1398
					'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ),
1399
					'type'        => 'date-time',
1400
					'context'     => array( 'view', 'edit' ),
1401
					'readonly'    => true,
1402
				),
1403
				'date_paid' => array(
1404
					'description' => __( "The date the order has been paid, in the site's timezone.", 'woocommerce' ),
1405
					'type'        => 'date-time',
1406
					'context'     => array( 'view', 'edit' ),
1407
					'readonly'    => true,
1408
				),
1409
				'cart_hash' => array(
1410
					'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ),
1411
					'type'        => 'string',
1412
					'context'     => array( 'view', 'edit' ),
1413
					'readonly'    => true,
1414
				),
1415
				'line_items' => array(
1416
					'description' => __( 'Line items data.', 'woocommerce' ),
1417
					'type'        => 'array',
1418
					'context'     => array( 'view', 'edit' ),
1419
					'properties'  => array(
1420
						'id' => array(
1421
							'description' => __( 'Item ID.', 'woocommerce' ),
1422
							'type'        => 'integer',
1423
							'context'     => array( 'view', 'edit' ),
1424
							'readonly'    => true,
1425
						),
1426
						'name' => array(
1427
							'description' => __( 'Product name.', 'woocommerce' ),
1428
							'type'        => 'string',
1429
							'context'     => array( 'view', 'edit' ),
1430
							'readonly'    => true,
1431
						),
1432
						'sku' => array(
1433
							'description' => __( 'Product SKU.', 'woocommerce' ),
1434
							'type'        => 'string',
1435
							'context'     => array( 'view', 'edit' ),
1436
							'readonly'    => true,
1437
						),
1438
						'product_id' => array(
1439
							'description' => __( 'Product ID.', 'woocommerce' ),
1440
							'type'        => 'integer',
1441
							'context'     => array( 'view', 'edit' ),
1442
						),
1443
						'variation_id' => array(
1444
							'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
1445
							'type'        => 'integer',
1446
							'context'     => array( 'view', 'edit' ),
1447
						),
1448
						'quantity' => array(
1449
							'description' => __( 'Quantity ordered.', 'woocommerce' ),
1450
							'type'        => 'integer',
1451
							'context'     => array( 'view', 'edit' ),
1452
						),
1453
						'tax_class' => array(
1454
							'description' => __( 'Tax class of product.', 'woocommerce' ),
1455
							'type'        => 'integer',
1456
							'context'     => array( 'view', 'edit' ),
1457
							'readonly'    => true,
1458
						),
1459
						'price' => array(
1460
							'description' => __( 'Product price.', 'woocommerce' ),
1461
							'type'        => 'string',
1462
							'context'     => array( 'view', 'edit' ),
1463
							'readonly'    => true,
1464
						),
1465
						'subtotal' => array(
1466
							'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
1467
							'type'        => 'string',
1468
							'context'     => array( 'view', 'edit' ),
1469
						),
1470
						'subtotal_tax' => array(
1471
							'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
1472
							'type'        => 'string',
1473
							'context'     => array( 'view', 'edit' ),
1474
						),
1475
						'total' => array(
1476
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1477
							'type'        => 'string',
1478
							'context'     => array( 'view', 'edit' ),
1479
						),
1480
						'total_tax' => array(
1481
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1482
							'type'        => 'string',
1483
							'context'     => array( 'view', 'edit' ),
1484
						),
1485
						'taxes' => array(
1486
							'description' => __( 'Line taxes.', 'woocommerce' ),
1487
							'type'        => 'array',
1488
							'context'     => array( 'view', 'edit' ),
1489
							'readonly'    => true,
1490
							'properties'  => array(
1491
								'id' => array(
1492
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1493
									'type'        => 'integer',
1494
									'context'     => array( 'view', 'edit' ),
1495
									'readonly'    => true,
1496
								),
1497
								'total' => array(
1498
									'description' => __( 'Tax total.', 'woocommerce' ),
1499
									'type'        => 'string',
1500
									'context'     => array( 'view', 'edit' ),
1501
									'readonly'    => true,
1502
								),
1503
								'subtotal' => array(
1504
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1505
									'type'        => 'string',
1506
									'context'     => array( 'view', 'edit' ),
1507
									'readonly'    => true,
1508
								),
1509
							),
1510
						),
1511
						'meta' => array(
1512
							'description' => __( 'Line item meta data.', 'woocommerce' ),
1513
							'type'        => 'array',
1514
							'context'     => array( 'view', 'edit' ),
1515
							'readonly'    => true,
1516
							'properties'  => array(
1517
								'key' => array(
1518
									'description' => __( 'Meta key.', 'woocommerce' ),
1519
									'type'        => 'string',
1520
									'context'     => array( 'view', 'edit' ),
1521
									'readonly'    => true,
1522
								),
1523
								'label' => array(
1524
									'description' => __( 'Meta label.', 'woocommerce' ),
1525
									'type'        => 'string',
1526
									'context'     => array( 'view', 'edit' ),
1527
									'readonly'    => true,
1528
								),
1529
								'value' => array(
1530
									'description' => __( 'Meta value.', 'woocommerce' ),
1531
									'type'        => 'string',
1532
									'context'     => array( 'view', 'edit' ),
1533
									'readonly'    => true,
1534
								),
1535
							),
1536
						),
1537
					),
1538
				),
1539
				'tax_lines' => array(
1540
					'description' => __( 'Tax lines data.', 'woocommerce' ),
1541
					'type'        => 'array',
1542
					'context'     => array( 'view', 'edit' ),
1543
					'readonly'    => true,
1544
					'properties'  => array(
1545
						'id' => array(
1546
							'description' => __( 'Item ID.', 'woocommerce' ),
1547
							'type'        => 'integer',
1548
							'context'     => array( 'view', 'edit' ),
1549
							'readonly'    => true,
1550
						),
1551
						'rate_code' => array(
1552
							'description' => __( 'Tax rate code.', 'woocommerce' ),
1553
							'type'        => 'string',
1554
							'context'     => array( 'view', 'edit' ),
1555
							'readonly'    => true,
1556
						),
1557
						'rate_id' => array(
1558
							'description' => __( 'Tax rate ID.', 'woocommerce' ),
1559
							'type'        => 'string',
1560
							'context'     => array( 'view', 'edit' ),
1561
							'readonly'    => true,
1562
						),
1563
						'label' => array(
1564
							'description' => __( 'Tax rate label.', 'woocommerce' ),
1565
							'type'        => 'string',
1566
							'context'     => array( 'view', 'edit' ),
1567
							'readonly'    => true,
1568
						),
1569
						'compound' => array(
1570
							'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ),
1571
							'type'        => 'boolean',
1572
							'context'     => array( 'view', 'edit' ),
1573
							'readonly'    => true,
1574
						),
1575
						'tax_total' => array(
1576
							'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ),
1577
							'type'        => 'string',
1578
							'context'     => array( 'view', 'edit' ),
1579
							'readonly'    => true,
1580
						),
1581
						'shipping_tax_total' => array(
1582
							'description' => __( 'Shipping tax total.', 'woocommerce' ),
1583
							'type'        => 'string',
1584
							'context'     => array( 'view', 'edit' ),
1585
							'readonly'    => true,
1586
						),
1587
					),
1588
				),
1589
				'shipping_lines' => array(
1590
					'description' => __( 'Shipping lines data.', 'woocommerce' ),
1591
					'type'        => 'array',
1592
					'context'     => array( 'view', 'edit' ),
1593
					'properties'  => array(
1594
						'id' => array(
1595
							'description' => __( 'Item ID.', 'woocommerce' ),
1596
							'type'        => 'integer',
1597
							'context'     => array( 'view', 'edit' ),
1598
							'readonly'    => true,
1599
						),
1600
						'method_title' => array(
1601
							'description' => __( 'Shipping method name.', 'woocommerce' ),
1602
							'type'        => 'string',
1603
							'context'     => array( 'view', 'edit' ),
1604
						),
1605
						'method_id' => array(
1606
							'description' => __( 'Shipping method ID.', 'woocommerce' ),
1607
							'type'        => 'string',
1608
							'context'     => array( 'view', 'edit' ),
1609
						),
1610
						'total' => array(
1611
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1612
							'type'        => 'string',
1613
							'context'     => array( 'view', 'edit' ),
1614
						),
1615
						'total_tax' => array(
1616
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1617
							'type'        => 'string',
1618
							'context'     => array( 'view', 'edit' ),
1619
							'readonly'    => true,
1620
						),
1621
						'taxes' => array(
1622
							'description' => __( 'Line taxes.', 'woocommerce' ),
1623
							'type'        => 'array',
1624
							'context'     => array( 'view', 'edit' ),
1625
							'readonly'    => true,
1626
							'properties'  => array(
1627
								'id' => array(
1628
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1629
									'type'        => 'integer',
1630
									'context'     => array( 'view', 'edit' ),
1631
									'readonly'    => true,
1632
								),
1633
								'total' => array(
1634
									'description' => __( 'Tax total.', 'woocommerce' ),
1635
									'type'        => 'string',
1636
									'context'     => array( 'view', 'edit' ),
1637
									'readonly'    => true,
1638
								),
1639
							),
1640
						),
1641
					),
1642
				),
1643
				'fee_lines' => array(
1644
					'description' => __( 'Fee lines data.', 'woocommerce' ),
1645
					'type'        => 'array',
1646
					'context'     => array( 'view', 'edit' ),
1647
					'properties'  => array(
1648
						'id' => array(
1649
							'description' => __( 'Item ID.', 'woocommerce' ),
1650
							'type'        => 'integer',
1651
							'context'     => array( 'view', 'edit' ),
1652
							'readonly'    => true,
1653
						),
1654
						'name' => array(
1655
							'description' => __( 'Fee name.', 'woocommerce' ),
1656
							'type'        => 'string',
1657
							'context'     => array( 'view', 'edit' ),
1658
						),
1659
						'tax_class' => array(
1660
							'description' => __( 'Tax class of fee.', 'woocommerce' ),
1661
							'type'        => 'string',
1662
							'context'     => array( 'view', 'edit' ),
1663
						),
1664
						'tax_status' => array(
1665
							'description' => __( 'Tax status of fee.', 'woocommerce' ),
1666
							'type'        => 'string',
1667
							'context'     => array( 'view', 'edit' ),
1668
						),
1669
						'total' => array(
1670
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1671
							'type'        => 'string',
1672
							'context'     => array( 'view', 'edit' ),
1673
						),
1674
						'total_tax' => array(
1675
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1676
							'type'        => 'string',
1677
							'context'     => array( 'view', 'edit' ),
1678
						),
1679
						'taxes' => array(
1680
							'description' => __( 'Line taxes.', 'woocommerce' ),
1681
							'type'        => 'array',
1682
							'context'     => array( 'view', 'edit' ),
1683
							'readonly'    => true,
1684
							'properties'  => array(
1685
								'id' => array(
1686
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1687
									'type'        => 'integer',
1688
									'context'     => array( 'view', 'edit' ),
1689
									'readonly'    => true,
1690
								),
1691
								'total' => array(
1692
									'description' => __( 'Tax total.', 'woocommerce' ),
1693
									'type'        => 'string',
1694
									'context'     => array( 'view', 'edit' ),
1695
									'readonly'    => true,
1696
								),
1697
								'subtotal' => array(
1698
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1699
									'type'        => 'string',
1700
									'context'     => array( 'view', 'edit' ),
1701
									'readonly'    => true,
1702
								),
1703
							),
1704
						),
1705
					),
1706
				),
1707
				'coupon_lines' => array(
1708
					'description' => __( 'Coupons line data.', 'woocommerce' ),
1709
					'type'        => 'array',
1710
					'context'     => array( 'view', 'edit' ),
1711
					'properties'  => array(
1712
						'id' => array(
1713
							'description' => __( 'Item ID.', 'woocommerce' ),
1714
							'type'        => 'integer',
1715
							'context'     => array( 'view', 'edit' ),
1716
							'readonly'    => true,
1717
						),
1718
						'code' => array(
1719
							'description' => __( 'Coupon code.', 'woocommerce' ),
1720
							'type'        => 'string',
1721
							'context'     => array( 'view', 'edit' ),
1722
						),
1723
						'discount' => array(
1724
							'description' => __( 'Discount total.', 'woocommerce' ),
1725
							'type'        => 'string',
1726
							'context'     => array( 'view', 'edit' ),
1727
						),
1728
						'discount_tax' => array(
1729
							'description' => __( 'Discount total tax.', 'woocommerce' ),
1730
							'type'        => 'string',
1731
							'context'     => array( 'view', 'edit' ),
1732
							'readonly'    => true,
1733
						),
1734
					),
1735
				),
1736
				'refunds' => array(
1737
					'description' => __( 'List of refunds.', 'woocommerce' ),
1738
					'type'        => 'array',
1739
					'context'     => array( 'view', 'edit' ),
1740
					'readonly'    => true,
1741
					'properties'  => array(
1742
						'id' => array(
1743
							'description' => __( 'Refund ID.', 'woocommerce' ),
1744
							'type'        => 'integer',
1745
							'context'     => array( 'view', 'edit' ),
1746
							'readonly'    => true,
1747
						),
1748
						'reason' => array(
1749
							'description' => __( 'Refund reason.', 'woocommerce' ),
1750
							'type'        => 'string',
1751
							'context'     => array( 'view', 'edit' ),
1752
							'readonly'    => true,
1753
						),
1754
						'total' => array(
1755
							'description' => __( 'Refund total.', 'woocommerce' ),
1756
							'type'        => 'string',
1757
							'context'     => array( 'view', 'edit' ),
1758
							'readonly'    => true,
1759
						),
1760
					),
1761
				),
1762
			),
1763
		);
1764
1765
		return $this->add_additional_fields_schema( $schema );
1766
	}
1767
1768
	/**
1769
	 * Get the query params for collections.
1770
	 *
1771
	 * @return array
1772
	 */
1773
	public function get_collection_params() {
1774
		$params = parent::get_collection_params();
1775
1776
		$params['status'] = array(
1777
			'default'           => 'any',
1778
			'description'       => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ),
1779
			'type'              => 'string',
1780
			'enum'              => array_merge( array( 'any' ), $this->get_order_statuses() ),
1781
			'sanitize_callback' => 'sanitize_key',
1782
			'validate_callback' => 'rest_validate_request_arg',
1783
		);
1784
		$params['customer'] = array(
1785
			'description'       => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ),
1786
			'type'              => 'integer',
1787
			'sanitize_callback' => 'absint',
1788
			'validate_callback' => 'rest_validate_request_arg',
1789
		);
1790
		$params['product'] = array(
1791
			'description'       => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ),
1792
			'type'              => 'integer',
1793
			'sanitize_callback' => 'absint',
1794
			'validate_callback' => 'rest_validate_request_arg',
1795
		);
1796
		$params['dp'] = array(
1797
			'default'           => 2,
1798
			'description'       => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
1799
			'type'              => 'integer',
1800
			'sanitize_callback' => 'absint',
1801
			'validate_callback' => 'rest_validate_request_arg',
1802
		);
1803
1804
		return $params;
1805
	}
1806
}
1807