Completed
Push — master ( fdc177...34f2bd )
by Claudio
10:41
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
		if ( ! empty( $request['product'] ) ) {
416
			$order_ids = $wpdb->get_col( $wpdb->prepare( "
417
				SELECT order_id
418
				FROM {$wpdb->prefix}woocommerce_order_items
419
				WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d )
420
				AND order_item_type = 'line_item'
421
			 ", $request['product'] ) );
422
423
			// Force WP_Query return empty if don't found any order.
424
			$order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 );
425
426
			$args['post__in'] = $order_ids;
427
		}
428
429
		return $args;
430
	}
431
432
	/**
433
	 * Prepare a single order for create.
434
	 *
435
	 * @param WP_REST_Request $request Request object.
436
	 * @return WP_Error|stdClass $data Object.
437
	 */
438
	protected function prepare_item_for_database( $request ) {
439
		$data = new stdClass;
440
441
		// Set default order args.
442
		$data->status        = $request['status'];
443
		$data->customer_id   = $request['customer_id'];
444
		$data->customer_note = $request['customer_note'];
445
446
		/**
447
		 * Filter the query_vars used in `get_items` for the constructed query.
448
		 *
449
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
450
		 * prepared for insertion.
451
		 *
452
		 * @param stdClass        $data    An object representing a single item prepared
453
		 *                                 for inserting the database.
454
		 * @param WP_REST_Request $request Request object.
455
		 */
456
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $data, $request );
457
	}
458
459
	/**
460
	 * Create base WC Order object.
461
	 *
462
	 * @param array $data
463
	 * @return WC_Order
464
	 */
465
	protected function create_base_order( $data ) {
466
		return wc_create_order( $data );
467
	}
468
469
	/**
470
	 * Create order.
471
	 *
472
	 * @param WP_REST_Request $request Full details about the request.
473
	 * @return int|WP_Error
474
	 */
475
	protected function create_order( $request ) {
476
		wc_transaction_query( 'start' );
477
478
		try {
479
			// Make sure customer exists.
480
			if ( 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) {
481
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce' ), 400 );
482
			}
483
484
			$data = $this->prepare_item_for_database( $request );
485
			if ( is_wp_error( $data ) ) {
486
				return $data;
487
			}
488
489
			$data->created_via = 'rest-api';
490
491
			$order = $this->create_base_order( (array) $data );
492
493 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...
494
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_order', sprintf( __( 'Cannot create order: %s.', 'woocommerce' ), implode( ', ', $order->get_error_messages() ) ), 400 );
495
			}
496
497
			// Set addresses.
498
			if ( is_array( $request['billing'] ) ) {
499
				$this->update_address( $order, $request['billing'], 'billing' );
500
			}
501
			if ( is_array( $request['shipping'] ) ) {
502
				$this->update_address( $order, $request['shipping'], 'shipping' );
503
			}
504
505
			// Set currency.
506
			update_post_meta( $order->id, '_order_currency', $request['currency'] );
507
508
			// Set lines.
509
			$lines = array(
510
				'line_item' => 'line_items',
511
				'shipping'  => 'shipping_lines',
512
				'fee'       => 'fee_lines',
513
				'coupon'    => 'coupon_lines',
514
			);
515
516
			foreach ( $lines as $line_type => $line ) {
517
				if ( is_array( $request[ $line ] ) ) {
518
					foreach ( $request[ $line ] as $item ) {
519
						$set_item = 'set_' . $line_type;
520
						$this->$set_item( $order, $item, 'create' );
521
					}
522
				}
523
			}
524
525
			// Calculate totals and set them.
526
			$order->calculate_totals();
527
528
			// Set payment method.
529
			if ( ! empty( $request['payment_method'] ) ) {
530
				update_post_meta( $order->id, '_payment_method', $request['payment_method'] );
531
			}
532
			if ( ! empty( $request['payment_method_title'] ) ) {
533
				update_post_meta( $order->id, '_payment_method_title', $request['payment_method'] );
534
			}
535
			if ( true === $request['set_paid'] ) {
536
				$order->payment_complete( $request['transaction_id'] );
537
			}
538
539
			// Set meta data.
540 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...
541
				$this->update_meta_data( $order->id, $request['meta_data'] );
542
			}
543
544
			wc_transaction_query( 'commit' );
545
546
			return $order->id;
547
		} catch ( WC_REST_Exception $e ) {
548
			wc_transaction_query( 'rollback' );
549
550
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
551
		}
552
	}
553
554
	/**
555
	 * Update address.
556
	 *
557
	 * @param WC_Order $order
558
	 * @param array $posted
559
	 * @param string $type
560
	 */
561
	protected function update_address( $order, $posted, $type = 'billing' ) {
562
		$fields = $order->get_address( $type );
563
564
		foreach ( array_keys( $fields ) as $field ) {
565
			if ( isset( $posted[ $field ] ) ) {
566
				$fields[ $field ] = $posted[ $field ];
567
			}
568
		}
569
570
		// Set address.
571
		$order->set_address( $fields, $type );
572
573
		// Update user meta.
574
		if ( $order->get_user_id() ) {
575
			foreach ( $fields as $key => $value ) {
576
				update_user_meta( $order->get_user_id(), $type . '_' . $key, $value );
577
			}
578
		}
579
	}
580
581
	/**
582
	 * Create or update a line item.
583
	 *
584
	 * @param WC_Order $order Order data.
585
	 * @param array $item Line item data.
586
	 * @param string $action 'create' to add line item or 'update' to update it.
587
	 * @throws WC_REST_Exception Invalid data, server error.
588
	 */
589
	protected function set_line_item( $order, $item, $action = 'create' ) {
590
		$creating  = 'create' === $action;
591
		$item_args = array();
592
593
		// Product is always required.
594
		if ( empty( $item['product_id'] ) && empty( $item['sku'] ) && empty( $item['variation_id'] ) ) {
595
			throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 );
596
		}
597
598
		if ( ! empty( $item['sku'] ) ) {
599
			$product_id = (int) wc_get_product_id_by_sku( $item['sku'] );
600
		} elseif ( ! empty( $item['product_id'] ) && empty( $item['variation_id'] ) ) {
601
			$product_id = (int) $item['product_id'];
602
		} elseif ( ! empty( $item['variation_id'] ) ) {
603
			$product_id = (int) $item['variation_id'];
604
		}
605
606
		// When updating, ensure product ID provided matches.
607
		if ( 'update' === $action && ! empty( $item['id'] ) ) {
608
			$item_product_id   = (int) wc_get_order_item_meta( $item['id'], '_product_id' );
609
			$item_variation_id = (int) wc_get_order_item_meta( $item['id'], '_variation_id' );
610
611
			if ( $product_id !== $item_product_id && $product_id !== $item_variation_id ) {
612
				throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or variation ID provided does not match this line item.', 'woocommerce' ), 400 );
613
			}
614
		}
615
616
		$product = wc_get_product( $product_id );
617
618
		// Must be a valid WC_Product.
619
		if ( ! is_object( $product ) ) {
620
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product', __( 'Product is invalid.', 'woocommerce' ), 400 );
621
		}
622
623
		// Quantity must be positive float.
624
		if ( isset( $item['quantity'] ) && 0 >= floatval( $item['quantity'] ) ) {
625
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product_quantity', __( 'Product quantity must be a positive float.', 'woocommerce' ), 400 );
626
		}
627
628
		// Quantity is required when creating.
629
		if ( $creating && ! isset( $item['quantity'] ) ) {
630
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_product_quantity', __( 'Product quantity is required.', 'woocommerce' ), 400 );
631
		}
632
633
		// Get variation attributes.
634
		if ( method_exists( $product, 'get_variation_attributes' ) ) {
635
			$item_args['variation'] = $product->get_variation_attributes();
636
		}
637
638
		// Quantity.
639
		if ( isset( $item['quantity'] ) ) {
640
			$item_args['qty'] = $item['quantity'];
641
		}
642
643
		// Total.
644
		if ( isset( $item['total'] ) ) {
645
			$item_args['totals']['total'] = floatval( $item['total'] );
646
		}
647
648
		// Total tax.
649
		if ( isset( $item['total_tax'] ) ) {
650
			$item_args['totals']['tax'] = floatval( $item['total_tax'] );
651
		}
652
653
		// Subtotal.
654
		if ( isset( $item['subtotal'] ) ) {
655
			$item_args['totals']['subtotal'] = floatval( $item['subtotal'] );
656
		}
657
658
		// Subtotal tax.
659
		if ( isset( $item['subtotal_tax'] ) ) {
660
			$item_args['totals']['subtotal_tax'] = floatval( $item['subtotal_tax'] );
661
		}
662
663 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...
664
			$item_id = $order->add_product( $product, $item_args['qty'], $item_args );
665
			if ( ! $item_id ) {
666
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_line_item', __( 'Cannot create line item, try again.', 'woocommerce' ), 500 );
667
			}
668
		} else {
669
			$item_id = $order->update_product( $item['id'], $product, $item_args );
670
			if ( ! $item_id ) {
671
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_line_item', __( 'Cannot update line item, try again.', 'woocommerce' ), 500 );
672
			}
673
		}
674
	}
675
676
	/**
677
	 * Create or update an order shipping method.
678
	 *
679
	 * @param WC_Order $order Order data.
680
	 * @param array $shipping Item data.
681
	 * @param string $action 'create' to add shipping or 'update' to update it.
682
	 * @throws WC_REST_Exception Invalid data, server error.
683
	 */
684
	protected function set_shipping( $order, $shipping, $action ) {
685
		// Total must be a positive float.
686
		if ( ! empty( $shipping['total'] ) && 0 > floatval( $shipping['total'] ) ) {
687
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_total', __( 'Shipping total must be a positive amount.', 'woocommerce' ), 400 );
688
		}
689
690
		if ( 'create' === $action ) {
691
			// Method ID is required.
692
			if ( empty( $shipping['method_id'] ) ) {
693
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 );
694
			}
695
696
			$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'] );
697
698
			$shipping_id = $order->add_shipping( $rate );
699
700
			if ( ! $shipping_id ) {
701
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_shipping', __( 'Cannot create shipping method, try again.', 'woocommerce' ), 500 );
702
			}
703
704
		} else {
705
			$shipping_args = array();
706
707
			if ( isset( $shipping['method_id'] ) ) {
708
				$shipping_args['method_id'] = $shipping['method_id'];
709
			}
710
711
			if ( isset( $shipping['method_title'] ) ) {
712
				$shipping_args['method_title'] = $shipping['method_title'];
713
			}
714
715
			if ( isset( $shipping['total'] ) ) {
716
				$shipping_args['cost'] = floatval( $shipping['total'] );
717
			}
718
719
			$shipping_id = $order->update_shipping( $shipping['id'], $shipping_args );
720
721
			if ( ! $shipping_id ) {
722
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_shipping', __( 'Cannot update shipping method, try again.', 'woocommerce' ), 500 );
723
			}
724
		}
725
	}
726
727
	/**
728
	 * Create or update an order fee.
729
	 *
730
	 * @param WC_Order $order Order data.
731
	 * @param array $fee Item data.
732
	 * @param string $action 'create' to add fee or 'update' to update it.
733
	 * @throws WC_REST_Exception Invalid data, server error.
734
	 */
735
	protected function set_fee( $order, $fee, $action ) {
736
		if ( 'create' === $action ) {
737
738
			// Fee name is required.
739
			if ( empty( $fee['name'] ) ) {
740
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 );
741
			}
742
743
			$fee_data            = new stdClass();
744
			$fee_data->id        = sanitize_title( $fee['name'] );
745
			$fee_data->name      = $fee['name'];
746
			$fee_data->amount    = isset( $fee['total'] ) ? floatval( $fee['total'] ) : 0;
747
			$fee_data->taxable   = false;
748
			$fee_data->tax       = 0;
749
			$fee_data->tax_data  = array();
750
			$fee_data->tax_class = '';
751
752
			// If taxable, tax class and total are required.
753
			if ( isset( $fee['tax_status'] ) && 'taxable' === $fee['tax_status'] ) {
754
755
				if ( ! isset( $fee['tax_class'] ) ) {
756
					throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee tax class is required when fee is taxable.', 'woocommerce' ), 400 );
757
				}
758
759
				$fee_data->taxable   = true;
760
				$fee_data->tax_class = $fee['tax_class'];
761
762
				if ( isset( $fee['total_tax'] ) ) {
763
					$fee_data->tax = isset( $fee['total_tax'] ) ? wc_format_refund_total( $fee['total_tax'] ) : 0;
764
				}
765
			}
766
767
			$fee_id = $order->add_fee( $fee_data );
768
769
			if ( ! $fee_id ) {
770
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_fee', __( 'Cannot create fee, try again.', 'woocommerce' ), 500 );
771
			}
772
773
		} else {
774
			$fee_args = array();
775
776
			if ( isset( $fee['name'] ) ) {
777
				$fee_args['name'] = $fee['name'];
778
			}
779
780
			if ( isset( $fee['tax_class'] ) ) {
781
				$fee_args['tax_class'] = $fee['tax_class'];
782
			}
783
784
			if ( isset( $fee['total'] ) ) {
785
				$fee_args['line_total'] = floatval( $fee['total'] );
786
			}
787
788
			if ( isset( $fee['total_tax'] ) ) {
789
				$fee_args['line_tax'] = floatval( $fee['total_tax'] );
790
			}
791
792
			$fee_id = $order->update_fee( $fee['id'], $fee_args );
793
794
			if ( ! $fee_id ) {
795
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_fee', __( 'Cannot update fee, try again.', 'woocommerce' ), 500 );
796
			}
797
		}
798
	}
799
800
	/**
801
	 * Create or update an order coupon.
802
	 *
803
	 * @param WC_Order $order Order data.
804
	 * @param array $coupon Item data.
805
	 * @param string $action 'create' to add coupon or 'update' to update it.
806
	 * @throws WC_REST_Exception Invalid data, server error.
807
	 */
808
	protected function set_coupon( $order, $coupon, $action ) {
809
		// Coupon discount must be positive float.
810
		if ( isset( $coupon['discount'] ) && 0 > floatval( $coupon['discount'] ) ) {
811
			throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_total', __( 'Coupon discount must be a positive amount.', 'woocommerce' ), 400 );
812
		}
813
814
		if ( 'create' === $action ) {
815
			// Coupon code is required.
816
			if ( empty( $coupon['code'] ) ) {
817
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
818
			}
819
820
			$coupon_id = $order->add_coupon( $coupon['code'], floatval( $coupon['discount'] ) );
821
822
			if ( ! $coupon_id ) {
823
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_order_coupon', __( 'Cannot create coupon, try again.', 'woocommerce' ), 500 );
824
			}
825
826
		} else {
827
			$coupon_args = array();
828
829
			if ( isset( $coupon['code'] ) ) {
830
				$coupon_args['code'] = $coupon['code'];
831
			}
832
833
			if ( isset( $coupon['discount'] ) ) {
834
				$coupon_args['discount_amount'] = floatval( $coupon['discount'] );
835
			}
836
837
			$coupon_id = $order->update_coupon( $coupon['id'], $coupon_args );
838
839
			if ( ! $coupon_id ) {
840
				throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_order_coupon', __( 'Cannot update coupon, try again.', 'woocommerce' ), 500 );
841
			}
842
		}
843
	}
844
845
	/**
846
	 * Helper method to add/update meta data, with two restrictions:
847
	 *
848
	 * 1) Only non-protected meta (no leading underscore) can be set
849
	 * 2) Meta values must be scalar (int, string, bool)
850
	 *
851
	 * @param int $order_id Order ID.
852
	 * @param array $meta_data Meta data in array( 'meta_key' => 'meta_value' ) format.
853
	 */
854 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...
855
		foreach ( $meta_data as $meta_key => $meta_value ) {
856
			if ( is_string( $meta_key ) && ! is_protected_meta( $meta_key ) && is_scalar( $meta_value ) ) {
857
				update_post_meta( $order_id, $meta_key, $meta_value );
858
			}
859
		}
860
	}
861
862
	/**
863
	 * Create a single item.
864
	 *
865
	 * @param WP_REST_Request $request Full details about the request.
866
	 * @return WP_Error|WP_REST_Response
867
	 */
868
	public function create_item( $request ) {
869
		if ( ! empty( $request['id'] ) ) {
870
			return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
871
		}
872
873
		$order_id = $this->create_order( $request );
874
		if ( is_wp_error( $order_id ) ) {
875
			return $order_id;
876
		}
877
878
		// Clear transients.
879
		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 873 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...
880
881
		$post = get_post( $order_id );
882
		$this->update_additional_fields_for_object( $post, $request );
883
884
		/**
885
		 * Fires after a single item is created or updated via the REST API.
886
		 *
887
		 * @param object          $post      Inserted object (not a WP_Post object).
888
		 * @param WP_REST_Request $request   Request object.
889
		 * @param boolean         $creating  True when creating item, false when updating.
890
		 */
891
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
892
893
		$request->set_param( 'context', 'edit' );
894
		$response = $this->prepare_item_for_response( $post, $request );
895
		$response = rest_ensure_response( $response );
896
		$response->set_status( 201 );
897
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
898
899
		return $response;
900
	}
901
902
	/**
903
	 * Wrapper method to create/update order items.
904
	 * When updating, the item ID provided is checked to ensure it is associated
905
	 * with the order.
906
	 *
907
	 * @param WC_Order $order order
908
	 * @param string $item_type
909
	 * @param array $item item provided in the request body
910
	 * @param string $action either 'create' or 'update'
911
	 * @throws WC_REST_Exception If item ID is not associated with order
912
	 */
913 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...
914
		global $wpdb;
915
916
		$set_method = 'set_' . $item_type;
917
918
		// Verify provided line item ID is associated with order.
919
		if ( 'update' === $action ) {
920
			$result = $wpdb->get_row(
921
				$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d",
922
				absint( $item['id'] ),
923
				absint( $order->id )
924
			) );
925
926
			if ( is_null( $result ) ) {
927
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 );
928
			}
929
		}
930
931
		$this->$set_method( $order, $item, $action );
932
	}
933
934
	/**
935
	 * Helper method to check if the resource ID associated with the provided item is null.
936
	 * Items can be deleted by setting the resource ID to null.
937
	 *
938
	 * @param array $item Item provided in the request body.
939
	 * @return bool True if the item resource ID is null, false otherwise.
940
	 */
941
	protected function item_is_null( $item ) {
942
		$keys = array( 'product_id', 'method_id', 'title', 'code' );
943
944
		foreach ( $keys as $key ) {
945
			if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) {
946
				return true;
947
			}
948
		}
949
950
		return false;
951
	}
952
953
	/**
954
	 * Update order.
955
	 *
956
	 * @param WP_REST_Request $request Full details about the request.
957
	 * @param WP_Post $post Post data.
958
	 * @return int|WP_Error
959
	 */
960
	protected function update_order( $request, $post ) {
961
		try {
962
			$update_totals = false;
963
			$order         = wc_get_order( $post );
964
			$order_args    = array( 'order_id' => $order->id );
965
966
			// Customer note.
967
			if ( isset( $request['customer_note'] ) ) {
968
				$order_args['customer_note'] = $request['customer_note'];
969
			}
970
971
			// Customer ID.
972
			if ( isset( $request['customer_id'] ) && $request['customer_id'] != $order->get_user_id() ) {
973
				// Make sure customer exists.
974
				if ( false === get_user_by( 'id', $request['customer_id'] ) ) {
975
					throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id', __( 'Customer ID is invalid.', 'woocommerce' ), 400 );
976
				}
977
978
				update_post_meta( $order->id, '_customer_user', $request['customer_id'] );
979
			}
980
981
			// Update addresses.
982
			if ( is_array( $request['billing'] ) ) {
983
				$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...
984
			}
985
			if ( is_array( $request['shipping'] ) ) {
986
				$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...
987
			}
988
989
			$lines = array(
990
				'line_item' => 'line_items',
991
				'shipping'  => 'shipping_lines',
992
				'fee'       => 'fee_lines',
993
				'coupon'    => 'coupon_lines',
994
			);
995
996
			foreach ( $lines as $line_type => $line ) {
997
				if ( isset( $request[ $line ] ) && is_array( $request[ $line ] ) ) {
998
					$update_totals = true;
999
					foreach ( $request[ $line ] as $item ) {
1000
						// Item ID is always required.
1001
						if ( ! array_key_exists( 'id', $item ) ) {
1002
							throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID is required.', 'woocommerce' ), 400 );
1003
						}
1004
1005
						// Create item.
1006
						if ( is_null( $item['id'] ) ) {
1007
							$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...
1008
						} elseif ( $this->item_is_null( $item ) ) {
1009
							// Delete item.
1010
							wc_delete_order_item( $item['id'] );
1011
						} else {
1012
							// Update item.
1013
							$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...
1014
						}
1015
					}
1016
				}
1017
			}
1018
1019
			// Set payment method.
1020
			if ( ! empty( $request['payment_method'] ) ) {
1021
				update_post_meta( $order->id, '_payment_method', $request['payment_method'] );
1022
			}
1023
			if ( ! empty( $request['payment_method_title'] ) ) {
1024
				update_post_meta( $order->id, '_payment_method_title', $request['payment_method'] );
1025
			}
1026
			if ( $order->needs_payment() && isset( $request['set_paid'] ) && true === $request['set_paid'] ) {
1027
				$order->payment_complete( ! empty( $request['transaction_id'] ) ? $request['transaction_id'] : '' );
1028
			}
1029
1030
			// Set order currency.
1031
			if ( isset( $request['currency'] ) ) {
1032
				update_post_meta( $order->id, '_order_currency', $request['currency'] );
1033
			}
1034
1035
			// If items have changed, recalculate order totals.
1036
			if ( $update_totals ) {
1037
				$order->calculate_totals();
1038
			}
1039
1040
			// Update meta data.
1041 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...
1042
				$this->update_meta_data( $order->id, $request['meta_data'] );
1043
			}
1044
1045
			// Update the order post to set customer note/modified date.
1046
			wc_update_order( $order_args );
1047
1048
			// Order status.
1049 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...
1050
				$order->update_status( $request['status'], isset( $request['status_note'] ) ? $request['status_note'] : '' );
1051
			}
1052
1053
			return $order->id;
1054
		} catch ( WC_REST_Exception $e ) {
1055
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
1056
		}
1057
	}
1058
1059
	/**
1060
	 * Update a single order.
1061
	 *
1062
	 * @param WP_REST_Request $request Full details about the request.
1063
	 * @return WP_Error|WP_REST_Response
1064
	 */
1065
	public function update_item( $request ) {
1066
		$id   = (int) $request['id'];
1067
		$post = get_post( $id );
1068
1069
		if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
1070
			return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
1071
		}
1072
1073
		$order_id = $this->update_order( $request, $post );
1074
		if ( is_wp_error( $order_id ) ) {
1075
			return $order_id;
1076
		}
1077
1078
		// Clear transients.
1079
		wc_delete_shop_order_transients( $order_id );
1080
1081
		$post = get_post( $order_id );
1082
		$this->update_additional_fields_for_object( $post, $request );
1083
1084
		/**
1085
		 * Fires after a single item is created or updated via the REST API.
1086
		 *
1087
		 * @param object          $post      Inserted object (not a WP_Post object).
1088
		 * @param WP_REST_Request $request   Request object.
1089
		 * @param boolean         $creating  True when creating item, false when updating.
1090
		 */
1091
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
1092
1093
		$request->set_param( 'context', 'edit' );
1094
		$response = $this->prepare_item_for_response( $post, $request );
1095
		return rest_ensure_response( $response );
1096
	}
1097
1098
	/**
1099
	 * Get order statuses.
1100
	 *
1101
	 * @return array
1102
	 */
1103
	protected function get_order_statuses() {
1104
		$order_statuses = array();
1105
1106
		foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
1107
			$order_statuses[] = str_replace( 'wc-', '', $status );
1108
		}
1109
1110
		return $order_statuses;
1111
	}
1112
1113
	/**
1114
	 * Get the Order's schema, conforming to JSON Schema.
1115
	 *
1116
	 * @return array
1117
	 */
1118
	public function get_item_schema() {
1119
		$schema = array(
1120
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
1121
			'title'      => $this->post_type,
1122
			'type'       => 'object',
1123
			'properties' => array(
1124
				'id' => array(
1125
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
1126
					'type'        => 'integer',
1127
					'context'     => array( 'view', 'edit' ),
1128
					'readonly'    => true,
1129
				),
1130
				'parent_id' => array(
1131
					'description' => __( 'Parent order ID.', 'woocommerce' ),
1132
					'type'        => 'integer',
1133
					'context'     => array( 'view', 'edit' ),
1134
				),
1135
				'status' => array(
1136
					'description' => __( 'Order status.', 'woocommerce' ),
1137
					'type'        => 'string',
1138
					'default'     => 'pending',
1139
					'enum'        => $this->get_order_statuses(),
1140
					'context'     => array( 'view', 'edit' ),
1141
				),
1142
				'order_key' => array(
1143
					'description' => __( 'Order key.', 'woocommerce' ),
1144
					'type'        => 'string',
1145
					'context'     => array( 'view', 'edit' ),
1146
					'readonly'    => true,
1147
				),
1148
				'currency' => array(
1149
					'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ),
1150
					'type'        => 'string',
1151
					'default'     => get_woocommerce_currency(),
1152
					'enum'        => array_keys( get_woocommerce_currencies() ),
1153
					'context'     => array( 'view', 'edit' ),
1154
				),
1155
				'version' => array(
1156
					'description' => __( 'Version of WooCommerce when the order was made.', 'woocommerce' ),
1157
					'type'        => 'integer',
1158
					'context'     => array( 'view', 'edit' ),
1159
					'readonly'    => true,
1160
				),
1161
				'prices_include_tax' => array(
1162
					'description' => __( 'Shows if the prices included tax during checkout.', 'woocommerce' ),
1163
					'type'        => 'boolean',
1164
					'context'     => array( 'view', 'edit' ),
1165
					'readonly'    => true,
1166
				),
1167
				'date_created' => array(
1168
					'description' => __( "The date the order was created, in the site's timezone.", 'woocommerce' ),
1169
					'type'        => 'date-time',
1170
					'context'     => array( 'view', 'edit' ),
1171
					'readonly'    => true,
1172
				),
1173
				'date_modified' => array(
1174
					'description' => __( "The date the order was last modified, in the site's timezone.", 'woocommerce' ),
1175
					'type'        => 'date-time',
1176
					'context'     => array( 'view', 'edit' ),
1177
					'readonly'    => true,
1178
				),
1179
				'customer_id' => array(
1180
					'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ),
1181
					'type'        => 'integer',
1182
					'default'     => 0,
1183
					'context'     => array( 'view', 'edit' ),
1184
				),
1185
				'discount_total' => array(
1186
					'description' => __( 'Total discount amount for the order.', 'woocommerce' ),
1187
					'type'        => 'string',
1188
					'context'     => array( 'view', 'edit' ),
1189
					'readonly'    => true,
1190
				),
1191
				'discount_tax' => array(
1192
					'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ),
1193
					'type'        => 'string',
1194
					'context'     => array( 'view', 'edit' ),
1195
					'readonly'    => true,
1196
				),
1197
				'shipping_total' => array(
1198
					'description' => __( 'Total shipping amount for the order.', 'woocommerce' ),
1199
					'type'        => 'string',
1200
					'context'     => array( 'view', 'edit' ),
1201
					'readonly'    => true,
1202
				),
1203
				'shipping_tax' => array(
1204
					'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ),
1205
					'type'        => 'string',
1206
					'context'     => array( 'view', 'edit' ),
1207
					'readonly'    => true,
1208
				),
1209
				'cart_tax' => array(
1210
					'description' => __( 'Sum of line item taxes only.', 'woocommerce' ),
1211
					'type'        => 'string',
1212
					'context'     => array( 'view', 'edit' ),
1213
					'readonly'    => true,
1214
				),
1215
				'total' => array(
1216
					'description' => __( 'Grand total.', 'woocommerce' ),
1217
					'type'        => 'string',
1218
					'context'     => array( 'view', 'edit' ),
1219
					'readonly'    => true,
1220
				),
1221
				'total_tax' => array(
1222
					'description' => __( 'Sum of all taxes.', 'woocommerce' ),
1223
					'type'        => 'string',
1224
					'context'     => array( 'view', 'edit' ),
1225
					'readonly'    => true,
1226
				),
1227
				'billing' => array(
1228
					'description' => __( 'Billing address.', 'woocommerce' ),
1229
					'type'        => 'array',
1230
					'context'     => array( 'view', 'edit' ),
1231
					'properties'  => array(
1232
						'first_name' => array(
1233
							'description' => __( 'First name.', 'woocommerce' ),
1234
							'type'        => 'string',
1235
							'context'     => array( 'view', 'edit' ),
1236
						),
1237
						'last_name' => array(
1238
							'description' => __( 'Last name.', 'woocommerce' ),
1239
							'type'        => 'string',
1240
							'context'     => array( 'view', 'edit' ),
1241
						),
1242
						'company' => array(
1243
							'description' => __( 'Company name.', 'woocommerce' ),
1244
							'type'        => 'string',
1245
							'context'     => array( 'view', 'edit' ),
1246
						),
1247
						'address_1' => array(
1248
							'description' => __( 'Address line 1.', 'woocommerce' ),
1249
							'type'        => 'string',
1250
							'context'     => array( 'view', 'edit' ),
1251
						),
1252
						'address_2' => array(
1253
							'description' => __( 'Address line 2.', 'woocommerce' ),
1254
							'type'        => 'string',
1255
							'context'     => array( 'view', 'edit' ),
1256
						),
1257
						'city' => array(
1258
							'description' => __( 'City name.', 'woocommerce' ),
1259
							'type'        => 'string',
1260
							'context'     => array( 'view', 'edit' ),
1261
						),
1262
						'state' => array(
1263
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1264
							'type'        => 'string',
1265
							'context'     => array( 'view', 'edit' ),
1266
						),
1267
						'postcode' => array(
1268
							'description' => __( 'Postal code.', 'woocommerce' ),
1269
							'type'        => 'string',
1270
							'context'     => array( 'view', 'edit' ),
1271
						),
1272
						'country' => array(
1273
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1274
							'type'        => 'string',
1275
							'context'     => array( 'view', 'edit' ),
1276
						),
1277
						'email' => array(
1278
							'description' => __( 'Email address.', 'woocommerce' ),
1279
							'type'        => 'string',
1280
							'format'      => 'email',
1281
							'context'     => array( 'view', 'edit' ),
1282
						),
1283
						'phone' => array(
1284
							'description' => __( 'Phone number.', 'woocommerce' ),
1285
							'type'        => 'string',
1286
							'context'     => array( 'view', 'edit' ),
1287
						),
1288
					),
1289
				),
1290
				'shipping' => array(
1291
					'description' => __( 'Shipping address.', 'woocommerce' ),
1292
					'type'        => 'array',
1293
					'context'     => array( 'view', 'edit' ),
1294
					'properties'  => array(
1295
						'first_name' => array(
1296
							'description' => __( 'First name.', 'woocommerce' ),
1297
							'type'        => 'string',
1298
							'context'     => array( 'view', 'edit' ),
1299
						),
1300
						'last_name' => array(
1301
							'description' => __( 'Last name.', 'woocommerce' ),
1302
							'type'        => 'string',
1303
							'context'     => array( 'view', 'edit' ),
1304
						),
1305
						'company' => array(
1306
							'description' => __( 'Company name.', 'woocommerce' ),
1307
							'type'        => 'string',
1308
							'context'     => array( 'view', 'edit' ),
1309
						),
1310
						'address_1' => array(
1311
							'description' => __( 'Address line 1.', 'woocommerce' ),
1312
							'type'        => 'string',
1313
							'context'     => array( 'view', 'edit' ),
1314
						),
1315
						'address_2' => array(
1316
							'description' => __( 'Address line 2.', 'woocommerce' ),
1317
							'type'        => 'string',
1318
							'context'     => array( 'view', 'edit' ),
1319
						),
1320
						'city' => array(
1321
							'description' => __( 'City name.', 'woocommerce' ),
1322
							'type'        => 'string',
1323
							'context'     => array( 'view', 'edit' ),
1324
						),
1325
						'state' => array(
1326
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1327
							'type'        => 'string',
1328
							'context'     => array( 'view', 'edit' ),
1329
						),
1330
						'postcode' => array(
1331
							'description' => __( 'Postal code.', 'woocommerce' ),
1332
							'type'        => 'string',
1333
							'context'     => array( 'view', 'edit' ),
1334
						),
1335
						'country' => array(
1336
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1337
							'type'        => 'string',
1338
							'context'     => array( 'view', 'edit' ),
1339
						),
1340
					),
1341
				),
1342
				'payment_method' => array(
1343
					'description' => __( 'Payment method ID.', 'woocommerce' ),
1344
					'type'        => 'string',
1345
					'context'     => array( 'view', 'edit' ),
1346
				),
1347
				'payment_method_title' => array(
1348
					'description' => __( 'Payment method title.', 'woocommerce' ),
1349
					'type'        => 'string',
1350
					'context'     => array( 'view', 'edit' ),
1351
				),
1352
				'set_paid' => array(
1353
					'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ),
1354
					'type'        => 'boolean',
1355
					'default'     => false,
1356
					'context'     => array( 'edit' ),
1357
				),
1358
				'transaction_id' => array(
1359
					'description' => __( 'Unique transaction ID.', 'woocommerce' ),
1360
					'type'        => 'string',
1361
					'context'     => array( 'view', 'edit' ),
1362
				),
1363
				'customer_ip_address' => array(
1364
					'description' => __( "Customer's IP address.", 'woocommerce' ),
1365
					'type'        => 'string',
1366
					'context'     => array( 'view', 'edit' ),
1367
					'readonly'    => true,
1368
				),
1369
				'customer_user_agent' => array(
1370
					'description' => __( 'User agent of the customer.', 'woocommerce' ),
1371
					'type'        => 'string',
1372
					'context'     => array( 'view', 'edit' ),
1373
					'readonly'    => true,
1374
				),
1375
				'created_via' => array(
1376
					'description' => __( 'Shows where the order was created.', 'woocommerce' ),
1377
					'type'        => 'string',
1378
					'context'     => array( 'view', 'edit' ),
1379
					'readonly'    => true,
1380
				),
1381
				'customer_note' => array(
1382
					'description' => __( 'Note left by customer during checkout.', 'woocommerce' ),
1383
					'type'        => 'string',
1384
					'context'     => array( 'view', 'edit' ),
1385
				),
1386
				'date_completed' => array(
1387
					'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ),
1388
					'type'        => 'date-time',
1389
					'context'     => array( 'view', 'edit' ),
1390
					'readonly'    => true,
1391
				),
1392
				'date_paid' => array(
1393
					'description' => __( "The date the order has been paid, in the site's timezone.", 'woocommerce' ),
1394
					'type'        => 'date-time',
1395
					'context'     => array( 'view', 'edit' ),
1396
					'readonly'    => true,
1397
				),
1398
				'cart_hash' => array(
1399
					'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ),
1400
					'type'        => 'string',
1401
					'context'     => array( 'view', 'edit' ),
1402
					'readonly'    => true,
1403
				),
1404
				'line_items' => array(
1405
					'description' => __( 'Line items data.', 'woocommerce' ),
1406
					'type'        => 'array',
1407
					'context'     => array( 'view', 'edit' ),
1408
					'properties'  => array(
1409
						'id' => array(
1410
							'description' => __( 'Item ID.', 'woocommerce' ),
1411
							'type'        => 'integer',
1412
							'context'     => array( 'view', 'edit' ),
1413
							'readonly'    => true,
1414
						),
1415
						'name' => array(
1416
							'description' => __( 'Product name.', 'woocommerce' ),
1417
							'type'        => 'string',
1418
							'context'     => array( 'view', 'edit' ),
1419
							'readonly'    => true,
1420
						),
1421
						'sku' => array(
1422
							'description' => __( 'Product SKU.', 'woocommerce' ),
1423
							'type'        => 'string',
1424
							'context'     => array( 'view', 'edit' ),
1425
							'readonly'    => true,
1426
						),
1427
						'product_id' => array(
1428
							'description' => __( 'Product ID.', 'woocommerce' ),
1429
							'type'        => 'integer',
1430
							'context'     => array( 'view', 'edit' ),
1431
						),
1432
						'variation_id' => array(
1433
							'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
1434
							'type'        => 'integer',
1435
							'context'     => array( 'view', 'edit' ),
1436
						),
1437
						'quantity' => array(
1438
							'description' => __( 'Quantity ordered.', 'woocommerce' ),
1439
							'type'        => 'integer',
1440
							'context'     => array( 'view', 'edit' ),
1441
						),
1442
						'tax_class' => array(
1443
							'description' => __( 'Tax class of product.', 'woocommerce' ),
1444
							'type'        => 'integer',
1445
							'context'     => array( 'view', 'edit' ),
1446
							'readonly'    => true,
1447
						),
1448
						'price' => array(
1449
							'description' => __( 'Product price.', 'woocommerce' ),
1450
							'type'        => 'string',
1451
							'context'     => array( 'view', 'edit' ),
1452
							'readonly'    => true,
1453
						),
1454
						'subtotal' => array(
1455
							'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
1456
							'type'        => 'string',
1457
							'context'     => array( 'view', 'edit' ),
1458
						),
1459
						'subtotal_tax' => array(
1460
							'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
1461
							'type'        => 'string',
1462
							'context'     => array( 'view', 'edit' ),
1463
						),
1464
						'total' => array(
1465
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1466
							'type'        => 'string',
1467
							'context'     => array( 'view', 'edit' ),
1468
						),
1469
						'total_tax' => array(
1470
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1471
							'type'        => 'string',
1472
							'context'     => array( 'view', 'edit' ),
1473
						),
1474
						'taxes' => array(
1475
							'description' => __( 'Line taxes.', 'woocommerce' ),
1476
							'type'        => 'array',
1477
							'context'     => array( 'view', 'edit' ),
1478
							'readonly'    => true,
1479
							'properties'  => array(
1480
								'id' => array(
1481
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1482
									'type'        => 'integer',
1483
									'context'     => array( 'view', 'edit' ),
1484
									'readonly'    => true,
1485
								),
1486
								'total' => array(
1487
									'description' => __( 'Tax total.', 'woocommerce' ),
1488
									'type'        => 'string',
1489
									'context'     => array( 'view', 'edit' ),
1490
									'readonly'    => true,
1491
								),
1492
								'subtotal' => array(
1493
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1494
									'type'        => 'string',
1495
									'context'     => array( 'view', 'edit' ),
1496
									'readonly'    => true,
1497
								),
1498
							),
1499
						),
1500
						'meta' => array(
1501
							'description' => __( 'Line item meta data.', 'woocommerce' ),
1502
							'type'        => 'array',
1503
							'context'     => array( 'view', 'edit' ),
1504
							'readonly'    => true,
1505
							'properties'  => array(
1506
								'key' => array(
1507
									'description' => __( 'Meta key.', 'woocommerce' ),
1508
									'type'        => 'string',
1509
									'context'     => array( 'view', 'edit' ),
1510
									'readonly'    => true,
1511
								),
1512
								'label' => array(
1513
									'description' => __( 'Meta label.', 'woocommerce' ),
1514
									'type'        => 'string',
1515
									'context'     => array( 'view', 'edit' ),
1516
									'readonly'    => true,
1517
								),
1518
								'value' => array(
1519
									'description' => __( 'Meta value.', 'woocommerce' ),
1520
									'type'        => 'string',
1521
									'context'     => array( 'view', 'edit' ),
1522
									'readonly'    => true,
1523
								),
1524
							),
1525
						),
1526
					),
1527
				),
1528
				'tax_lines' => array(
1529
					'description' => __( 'Tax lines data.', 'woocommerce' ),
1530
					'type'        => 'array',
1531
					'context'     => array( 'view', 'edit' ),
1532
					'readonly'    => true,
1533
					'properties'  => array(
1534
						'id' => array(
1535
							'description' => __( 'Item ID.', 'woocommerce' ),
1536
							'type'        => 'integer',
1537
							'context'     => array( 'view', 'edit' ),
1538
							'readonly'    => true,
1539
						),
1540
						'rate_code' => array(
1541
							'description' => __( 'Tax rate code.', 'woocommerce' ),
1542
							'type'        => 'string',
1543
							'context'     => array( 'view', 'edit' ),
1544
							'readonly'    => true,
1545
						),
1546
						'rate_id' => array(
1547
							'description' => __( 'Tax rate ID.', 'woocommerce' ),
1548
							'type'        => 'string',
1549
							'context'     => array( 'view', 'edit' ),
1550
							'readonly'    => true,
1551
						),
1552
						'label' => array(
1553
							'description' => __( 'Tax rate label.', 'woocommerce' ),
1554
							'type'        => 'string',
1555
							'context'     => array( 'view', 'edit' ),
1556
							'readonly'    => true,
1557
						),
1558
						'compound' => array(
1559
							'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ),
1560
							'type'        => 'boolean',
1561
							'context'     => array( 'view', 'edit' ),
1562
							'readonly'    => true,
1563
						),
1564
						'tax_total' => array(
1565
							'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ),
1566
							'type'        => 'string',
1567
							'context'     => array( 'view', 'edit' ),
1568
							'readonly'    => true,
1569
						),
1570
						'shipping_tax_total' => array(
1571
							'description' => __( 'Shipping tax total.', 'woocommerce' ),
1572
							'type'        => 'string',
1573
							'context'     => array( 'view', 'edit' ),
1574
							'readonly'    => true,
1575
						),
1576
					),
1577
				),
1578
				'shipping_lines' => array(
1579
					'description' => __( 'Shipping lines data.', 'woocommerce' ),
1580
					'type'        => 'array',
1581
					'context'     => array( 'view', 'edit' ),
1582
					'properties'  => array(
1583
						'id' => array(
1584
							'description' => __( 'Item ID.', 'woocommerce' ),
1585
							'type'        => 'integer',
1586
							'context'     => array( 'view', 'edit' ),
1587
							'readonly'    => true,
1588
						),
1589
						'method_title' => array(
1590
							'description' => __( 'Shipping method name.', 'woocommerce' ),
1591
							'type'        => 'string',
1592
							'context'     => array( 'view', 'edit' ),
1593
						),
1594
						'method_id' => array(
1595
							'description' => __( 'Shipping method ID.', 'woocommerce' ),
1596
							'type'        => 'string',
1597
							'context'     => array( 'view', 'edit' ),
1598
						),
1599
						'total' => array(
1600
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1601
							'type'        => 'string',
1602
							'context'     => array( 'view', 'edit' ),
1603
						),
1604
						'total_tax' => array(
1605
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1606
							'type'        => 'string',
1607
							'context'     => array( 'view', 'edit' ),
1608
							'readonly'    => true,
1609
						),
1610
						'taxes' => array(
1611
							'description' => __( 'Line taxes.', 'woocommerce' ),
1612
							'type'        => 'array',
1613
							'context'     => array( 'view', 'edit' ),
1614
							'readonly'    => true,
1615
							'properties'  => array(
1616
								'id' => array(
1617
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1618
									'type'        => 'integer',
1619
									'context'     => array( 'view', 'edit' ),
1620
									'readonly'    => true,
1621
								),
1622
								'total' => array(
1623
									'description' => __( 'Tax total.', 'woocommerce' ),
1624
									'type'        => 'string',
1625
									'context'     => array( 'view', 'edit' ),
1626
									'readonly'    => true,
1627
								),
1628
							),
1629
						),
1630
					),
1631
				),
1632
				'fee_lines' => array(
1633
					'description' => __( 'Fee lines data.', 'woocommerce' ),
1634
					'type'        => 'array',
1635
					'context'     => array( 'view', 'edit' ),
1636
					'properties'  => array(
1637
						'id' => array(
1638
							'description' => __( 'Item ID.', 'woocommerce' ),
1639
							'type'        => 'integer',
1640
							'context'     => array( 'view', 'edit' ),
1641
							'readonly'    => true,
1642
						),
1643
						'name' => array(
1644
							'description' => __( 'Fee name.', 'woocommerce' ),
1645
							'type'        => 'string',
1646
							'context'     => array( 'view', 'edit' ),
1647
						),
1648
						'tax_class' => array(
1649
							'description' => __( 'Tax class of fee.', 'woocommerce' ),
1650
							'type'        => 'string',
1651
							'context'     => array( 'view', 'edit' ),
1652
						),
1653
						'tax_status' => array(
1654
							'description' => __( 'Tax status of fee.', 'woocommerce' ),
1655
							'type'        => 'string',
1656
							'context'     => array( 'view', 'edit' ),
1657
						),
1658
						'total' => array(
1659
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1660
							'type'        => 'string',
1661
							'context'     => array( 'view', 'edit' ),
1662
						),
1663
						'total_tax' => array(
1664
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1665
							'type'        => 'string',
1666
							'context'     => array( 'view', 'edit' ),
1667
						),
1668
						'taxes' => array(
1669
							'description' => __( 'Line taxes.', 'woocommerce' ),
1670
							'type'        => 'array',
1671
							'context'     => array( 'view', 'edit' ),
1672
							'readonly'    => true,
1673
							'properties'  => array(
1674
								'id' => array(
1675
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1676
									'type'        => 'integer',
1677
									'context'     => array( 'view', 'edit' ),
1678
									'readonly'    => true,
1679
								),
1680
								'total' => array(
1681
									'description' => __( 'Tax total.', 'woocommerce' ),
1682
									'type'        => 'string',
1683
									'context'     => array( 'view', 'edit' ),
1684
									'readonly'    => true,
1685
								),
1686
								'subtotal' => array(
1687
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1688
									'type'        => 'string',
1689
									'context'     => array( 'view', 'edit' ),
1690
									'readonly'    => true,
1691
								),
1692
							),
1693
						),
1694
					),
1695
				),
1696
				'coupon_lines' => array(
1697
					'description' => __( 'Coupons line data.', 'woocommerce' ),
1698
					'type'        => 'array',
1699
					'context'     => array( 'view', 'edit' ),
1700
					'properties'  => array(
1701
						'id' => array(
1702
							'description' => __( 'Item ID.', 'woocommerce' ),
1703
							'type'        => 'integer',
1704
							'context'     => array( 'view', 'edit' ),
1705
							'readonly'    => true,
1706
						),
1707
						'code' => array(
1708
							'description' => __( 'Coupon code.', 'woocommerce' ),
1709
							'type'        => 'string',
1710
							'context'     => array( 'view', 'edit' ),
1711
						),
1712
						'discount' => array(
1713
							'description' => __( 'Discount total.', 'woocommerce' ),
1714
							'type'        => 'string',
1715
							'context'     => array( 'view', 'edit' ),
1716
						),
1717
						'discount_tax' => array(
1718
							'description' => __( 'Discount total tax.', 'woocommerce' ),
1719
							'type'        => 'string',
1720
							'context'     => array( 'view', 'edit' ),
1721
							'readonly'    => true,
1722
						),
1723
					),
1724
				),
1725
				'refunds' => array(
1726
					'description' => __( 'List of refunds.', 'woocommerce' ),
1727
					'type'        => 'array',
1728
					'context'     => array( 'view', 'edit' ),
1729
					'readonly'    => true,
1730
					'properties'  => array(
1731
						'id' => array(
1732
							'description' => __( 'Refund ID.', 'woocommerce' ),
1733
							'type'        => 'integer',
1734
							'context'     => array( 'view', 'edit' ),
1735
							'readonly'    => true,
1736
						),
1737
						'reason' => array(
1738
							'description' => __( 'Refund reason.', 'woocommerce' ),
1739
							'type'        => 'string',
1740
							'context'     => array( 'view', 'edit' ),
1741
							'readonly'    => true,
1742
						),
1743
						'total' => array(
1744
							'description' => __( 'Refund total.', 'woocommerce' ),
1745
							'type'        => 'string',
1746
							'context'     => array( 'view', 'edit' ),
1747
							'readonly'    => true,
1748
						),
1749
					),
1750
				),
1751
			),
1752
		);
1753
1754
		return $this->add_additional_fields_schema( $schema );
1755
	}
1756
1757
	/**
1758
	 * Get the query params for collections.
1759
	 *
1760
	 * @return array
1761
	 */
1762
	public function get_collection_params() {
1763
		$params = parent::get_collection_params();
1764
1765
		$params['status'] = array(
1766
			'default'           => 'any',
1767
			'description'       => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ),
1768
			'type'              => 'string',
1769
			'enum'              => array_merge( array( 'any' ), $this->get_order_statuses() ),
1770
			'sanitize_callback' => 'sanitize_key',
1771
			'validate_callback' => 'rest_validate_request_arg',
1772
		);
1773
		$params['customer'] = array(
1774
			'description'       => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ),
1775
			'type'              => 'integer',
1776
			'sanitize_callback' => 'absint',
1777
			'validate_callback' => 'rest_validate_request_arg',
1778
		);
1779
		$params['product'] = array(
1780
			'description'       => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ),
1781
			'type'              => 'integer',
1782
			'sanitize_callback' => 'absint',
1783
			'validate_callback' => 'rest_validate_request_arg',
1784
		);
1785
		$params['dp'] = array(
1786
			'default'           => 2,
1787
			'description'       => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
1788
			'type'              => 'integer',
1789
			'sanitize_callback' => 'absint',
1790
			'validate_callback' => 'rest_validate_request_arg',
1791
		);
1792
1793
		return $params;
1794
	}
1795
}
1796