Test Failed
Push — master ( a5e4c3...7495fa )
by Mike
43:00
created

WC_REST_Orders_V1_Controller   F

Complexity

Total Complexity 127

Size/Duplication

Total Lines 1605
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 965
dl 0
loc 1605
rs 1.635
c 0
b 0
f 0
wmc 127

25 Methods

Rating   Name   Duplication   Size   Complexity  
A set_item() 0 37 5
A prepare_shipping_lines() 0 12 4
B query_args() 0 48 8
F prepare_item_for_response() 0 231 25
A item_is_null() 0 10 4
A get_collection_params() 0 32 1
A create_item() 0 29 3
A register_routes() 0 61 1
A prepare_line_items() 0 18 5
A update_address() 0 4 3
B update_order() 0 20 11
A get_product_id() 0 11 5
B create_order() 0 28 9
A get_order_statuses() 0 8 2
C prepare_item_for_database() 0 51 17
A maybe_set_item_props() 0 3 2
B get_item_schema() 0 677 1
A filter_writable_props() 0 2 1
A maybe_set_item_prop() 0 3 2
A __construct() 0 2 1
A prepare_coupon_lines() 0 12 4
A prepare_links() 0 20 3
A create_base_order() 0 2 1
A prepare_fee_lines() 0 12 4
A update_item() 0 30 5

How to fix   Complexity   

Complex Class

Complex classes like WC_REST_Orders_V1_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_REST_Orders_V1_Controller, and based on these observations, apply Extract Interface, too.

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/RestApi
10
 * @since    3.0.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Orders controller class.
19
 *
20
 * @package WooCommerce/RestApi
21
 * @extends WC_REST_Posts_Controller
22
 */
23
class WC_REST_Orders_V1_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,
0 ignored issues
show
Bug introduced by
The type WP_REST_Server was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
			'args' => array(
75
				'id' => array(
76
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
77
					'type'        => 'integer',
78
				),
79
			),
80
			array(
81
				'methods'             => WP_REST_Server::READABLE,
82
				'callback'            => array( $this, 'get_item' ),
83
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
84
				'args'                => array(
85
					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
86
				),
87
			),
88
			array(
89
				'methods'             => WP_REST_Server::EDITABLE,
90
				'callback'            => array( $this, 'update_item' ),
91
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
92
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
93
			),
94
			array(
95
				'methods'             => WP_REST_Server::DELETABLE,
96
				'callback'            => array( $this, 'delete_item' ),
97
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
98
				'args'                => array(
99
					'force' => array(
100
						'default'     => false,
101
						'type'        => 'boolean',
102
						'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
103
					),
104
				),
105
			),
106
			'schema' => array( $this, 'get_public_item_schema' ),
107
		) );
108
109
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
110
			array(
111
				'methods'             => WP_REST_Server::EDITABLE,
112
				'callback'            => array( $this, 'batch_items' ),
113
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
114
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
115
			),
116
			'schema' => array( $this, 'get_public_batch_schema' ),
117
		) );
118
	}
119
120
	/**
121
	 * Prepare a single order output for response.
122
	 *
123
	 * @param WP_Post $post Post object.
0 ignored issues
show
Bug introduced by
The type WP_Post was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
124
	 * @param WP_REST_Request $request Request object.
0 ignored issues
show
Bug introduced by
The type WP_REST_Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
125
	 * @return WP_REST_Response $data
0 ignored issues
show
Bug introduced by
The type WP_REST_Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
126
	 */
127
	public function prepare_item_for_response( $post, $request ) {
128
		$order = wc_get_order( $post );
129
		$dp    = is_null( $request['dp'] ) ? wc_get_price_decimals() : absint( $request['dp'] );
130
131
		$data = array(
132
			'id'                   => $order->get_id(),
133
			'parent_id'            => $order->get_parent_id(),
134
			'status'               => $order->get_status(),
135
			'order_key'            => $order->get_order_key(),
136
			'number'               => $order->get_order_number(),
137
			'currency'             => $order->get_currency(),
138
			'version'              => $order->get_version(),
139
			'prices_include_tax'   => $order->get_prices_include_tax(),
140
			'date_created'         => wc_rest_prepare_date_response( $order->get_date_created() ),  // v1 API used UTC.
141
			'date_modified'        => wc_rest_prepare_date_response( $order->get_date_modified() ), // v1 API used UTC.
142
			'customer_id'          => $order->get_customer_id(),
143
			'discount_total'       => wc_format_decimal( $order->get_total_discount(), $dp ),
144
			'discount_tax'         => wc_format_decimal( $order->get_discount_tax(), $dp ),
145
			'shipping_total'       => wc_format_decimal( $order->get_shipping_total(), $dp ),
146
			'shipping_tax'         => wc_format_decimal( $order->get_shipping_tax(), $dp ),
147
			'cart_tax'             => wc_format_decimal( $order->get_cart_tax(), $dp ),
148
			'total'                => wc_format_decimal( $order->get_total(), $dp ),
149
			'total_tax'            => wc_format_decimal( $order->get_total_tax(), $dp ),
150
			'billing'              => array(),
151
			'shipping'             => array(),
152
			'payment_method'       => $order->get_payment_method(),
153
			'payment_method_title' => $order->get_payment_method_title(),
154
			'transaction_id'       => $order->get_transaction_id(),
155
			'customer_ip_address'  => $order->get_customer_ip_address(),
156
			'customer_user_agent'  => $order->get_customer_user_agent(),
157
			'created_via'          => $order->get_created_via(),
158
			'customer_note'        => $order->get_customer_note(),
159
			'date_completed'       => wc_rest_prepare_date_response( $order->get_date_completed(), false ), // v1 API used local time.
160
			'date_paid'            => wc_rest_prepare_date_response( $order->get_date_paid(), false ), // v1 API used local time.
161
			'cart_hash'            => $order->get_cart_hash(),
162
			'line_items'           => array(),
163
			'tax_lines'            => array(),
164
			'shipping_lines'       => array(),
165
			'fee_lines'            => array(),
166
			'coupon_lines'         => array(),
167
			'refunds'              => array(),
168
		);
169
170
		// Add addresses.
171
		$data['billing']  = $order->get_address( 'billing' );
172
		$data['shipping'] = $order->get_address( 'shipping' );
173
174
		// Add line items.
175
		foreach ( $order->get_items() as $item_id => $item ) {
176
			$product      = $order->get_product_from_item( $item );
177
			$product_id   = 0;
178
			$variation_id = 0;
179
			$product_sku  = null;
180
181
			// Check if the product exists.
182
			if ( is_object( $product ) ) {
183
				$product_id   = $item->get_product_id();
184
				$variation_id = $item->get_variation_id();
185
				$product_sku  = $product->get_sku();
186
			}
187
188
			$item_meta = array();
189
190
			$hideprefix = 'true' === $request['all_item_meta'] ? null : '_';
191
192
			foreach ( $item->get_formatted_meta_data( $hideprefix, true ) as $meta_key => $formatted_meta ) {
193
				$item_meta[] = array(
194
					'key'   => $formatted_meta->key,
195
					'label' => $formatted_meta->display_key,
196
					'value' => wc_clean( $formatted_meta->display_value ),
197
				);
198
			}
199
200
			$line_item = array(
201
				'id'           => $item_id,
202
				'name'         => $item['name'],
203
				'sku'          => $product_sku,
204
				'product_id'   => (int) $product_id,
205
				'variation_id' => (int) $variation_id,
206
				'quantity'     => wc_stock_amount( $item['qty'] ),
207
				'tax_class'    => ! empty( $item['tax_class'] ) ? $item['tax_class'] : '',
208
				'price'        => wc_format_decimal( $order->get_item_total( $item, false, false ), $dp ),
209
				'subtotal'     => wc_format_decimal( $order->get_line_subtotal( $item, false, false ), $dp ),
210
				'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),
211
				'total'        => wc_format_decimal( $order->get_line_total( $item, false, false ), $dp ),
212
				'total_tax'    => wc_format_decimal( $item['line_tax'], $dp ),
213
				'taxes'        => array(),
214
				'meta'         => $item_meta,
215
			);
216
217
			$item_line_taxes = maybe_unserialize( $item['line_tax_data'] );
218
			if ( isset( $item_line_taxes['total'] ) ) {
219
				$line_tax = array();
220
221
				foreach ( $item_line_taxes['total'] as $tax_rate_id => $tax ) {
222
					$line_tax[ $tax_rate_id ] = array(
223
						'id'       => $tax_rate_id,
224
						'total'    => $tax,
225
						'subtotal' => '',
226
					);
227
				}
228
229
				foreach ( $item_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
230
					$line_tax[ $tax_rate_id ]['subtotal'] = $tax;
231
				}
232
233
				$line_item['taxes'] = array_values( $line_tax );
234
			}
235
236
			$data['line_items'][] = $line_item;
237
		}
238
239
		// Add taxes.
240
		foreach ( $order->get_items( 'tax' ) as $key => $tax ) {
241
			$tax_line = array(
242
				'id'                 => $key,
243
				'rate_code'          => $tax['name'],
244
				'rate_id'            => $tax['rate_id'],
245
				'label'              => isset( $tax['label'] ) ? $tax['label'] : $tax['name'],
246
				'compound'           => (bool) $tax['compound'],
247
				'tax_total'          => wc_format_decimal( $tax['tax_amount'], $dp ),
248
				'shipping_tax_total' => wc_format_decimal( $tax['shipping_tax_amount'], $dp ),
249
			);
250
251
			$data['tax_lines'][] = $tax_line;
252
		}
253
254
		// Add shipping.
255
		foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) {
256
			$shipping_line = array(
257
				'id'           => $shipping_item_id,
258
				'method_title' => $shipping_item['name'],
259
				'method_id'    => $shipping_item['method_id'],
260
				'total'        => wc_format_decimal( $shipping_item['cost'], $dp ),
261
				'total_tax'    => wc_format_decimal( '', $dp ),
262
				'taxes'        => array(),
263
			);
264
265
			$shipping_taxes = $shipping_item->get_taxes();
266
267
			if ( ! empty( $shipping_taxes['total'] ) ) {
268
				$shipping_line['total_tax'] = wc_format_decimal( array_sum( $shipping_taxes['total'] ), $dp );
269
270
				foreach ( $shipping_taxes['total'] as $tax_rate_id => $tax ) {
271
					$shipping_line['taxes'][] = array(
272
						'id'       => $tax_rate_id,
273
						'total'    => $tax,
274
					);
275
				}
276
			}
277
278
			$data['shipping_lines'][] = $shipping_line;
279
		}
280
281
		// Add fees.
282
		foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
283
			$fee_line = array(
284
				'id'         => $fee_item_id,
285
				'name'       => $fee_item['name'],
286
				'tax_class'  => ! empty( $fee_item['tax_class'] ) ? $fee_item['tax_class'] : '',
287
				'tax_status' => 'taxable',
288
				'total'      => wc_format_decimal( $order->get_line_total( $fee_item ), $dp ),
289
				'total_tax'  => wc_format_decimal( $order->get_line_tax( $fee_item ), $dp ),
290
				'taxes'      => array(),
291
			);
292
293
			$fee_line_taxes = maybe_unserialize( $fee_item['line_tax_data'] );
294
			if ( isset( $fee_line_taxes['total'] ) ) {
295
				$fee_tax = array();
296
297
				foreach ( $fee_line_taxes['total'] as $tax_rate_id => $tax ) {
298
					$fee_tax[ $tax_rate_id ] = array(
299
						'id'       => $tax_rate_id,
300
						'total'    => $tax,
301
						'subtotal' => '',
302
					);
303
				}
304
305
				if ( isset( $fee_line_taxes['subtotal'] ) ) {
306
					foreach ( $fee_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
307
						$fee_tax[ $tax_rate_id ]['subtotal'] = $tax;
308
					}
309
				}
310
311
				$fee_line['taxes'] = array_values( $fee_tax );
312
			}
313
314
			$data['fee_lines'][] = $fee_line;
315
		}
316
317
		// Add coupons.
318
		foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
319
			$coupon_line = array(
320
				'id'           => $coupon_item_id,
321
				'code'         => $coupon_item['name'],
322
				'discount'     => wc_format_decimal( $coupon_item['discount_amount'], $dp ),
323
				'discount_tax' => wc_format_decimal( $coupon_item['discount_amount_tax'], $dp ),
324
			);
325
326
			$data['coupon_lines'][] = $coupon_line;
327
		}
328
329
		// Add refunds.
330
		foreach ( $order->get_refunds() as $refund ) {
331
			$data['refunds'][] = array(
332
				'id'     => $refund->get_id(),
333
				'refund' => $refund->get_reason() ? $refund->get_reason() : '',
334
				'total'  => '-' . wc_format_decimal( $refund->get_amount(), $dp ),
335
			);
336
		}
337
338
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
339
		$data    = $this->add_additional_fields_to_object( $data, $request );
340
		$data    = $this->filter_response_by_context( $data, $context );
341
342
		// Wrap the data in a response object.
343
		$response = rest_ensure_response( $data );
344
345
		$response->add_links( $this->prepare_links( $order, $request ) );
346
347
		/**
348
		 * Filter the data for a response.
349
		 *
350
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
351
		 * prepared for the response.
352
		 *
353
		 * @param WP_REST_Response   $response   The response object.
354
		 * @param WP_Post            $post       Post object.
355
		 * @param WP_REST_Request    $request    Request object.
356
		 */
357
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
358
	}
359
360
	/**
361
	 * Prepare links for the request.
362
	 *
363
	 * @param WC_Order $order Order object.
0 ignored issues
show
Bug introduced by
The type WC_Order was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
364
	 * @param WP_REST_Request $request Request object.
365
	 * @return array Links for the given order.
366
	 */
367
	protected function prepare_links( $order, $request ) {
368
		$links = array(
369
			'self' => array(
370
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $order->get_id() ) ),
371
			),
372
			'collection' => array(
373
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
374
			),
375
		);
376
		if ( 0 !== (int) $order->get_user_id() ) {
377
			$links['customer'] = array(
378
				'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $order->get_user_id() ) ),
379
			);
380
		}
381
		if ( 0 !== (int) $order->get_parent_id() ) {
382
			$links['up'] = array(
383
				'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order->get_parent_id() ) ),
384
			);
385
		}
386
		return $links;
387
	}
388
389
	/**
390
	 * Query args.
391
	 *
392
	 * @param array $args
393
	 * @param WP_REST_Request $request
394
	 * @return array
395
	 */
396
	public function query_args( $args, $request ) {
397
		global $wpdb;
398
399
		// Set post_status.
400
		if ( 'any' !== $request['status'] ) {
401
			$args['post_status'] = 'wc-' . $request['status'];
402
		} else {
403
			$args['post_status'] = 'any';
404
		}
405
406
		if ( isset( $request['customer'] ) ) {
407
			if ( ! empty( $args['meta_query'] ) ) {
408
				$args['meta_query'] = array();
409
			}
410
411
			$args['meta_query'][] = array(
412
				'key'   => '_customer_user',
413
				'value' => $request['customer'],
414
				'type'  => 'NUMERIC',
415
			);
416
		}
417
418
		// Search by product.
419
		if ( ! empty( $request['product'] ) ) {
420
			$order_ids = $wpdb->get_col( $wpdb->prepare( "
421
				SELECT order_id
422
				FROM {$wpdb->prefix}woocommerce_order_items
423
				WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d )
424
				AND order_item_type = 'line_item'
425
			 ", $request['product'] ) );
426
427
			// Force WP_Query return empty if don't found any order.
428
			$order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 );
429
430
			$args['post__in'] = $order_ids;
431
		}
432
433
		// Search.
434
		if ( ! empty( $args['s'] ) ) {
435
			$order_ids = wc_order_search( $args['s'] );
436
437
			if ( ! empty( $order_ids ) ) {
438
				unset( $args['s'] );
439
				$args['post__in'] = array_merge( $order_ids, array( 0 ) );
440
			}
441
		}
442
443
		return $args;
444
	}
445
446
	/**
447
	 * Prepare a single order for create.
448
	 *
449
	 * @param  WP_REST_Request $request Request object.
450
	 * @return WP_Error|WC_Order $data Object.
0 ignored issues
show
Bug introduced by
The type WP_Error was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
451
	 */
452
	protected function prepare_item_for_database( $request ) {
453
		$id        = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
454
		$order     = new WC_Order( $id );
455
		$schema    = $this->get_item_schema();
456
		$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
457
458
		// Handle all writable props
459
		foreach ( $data_keys as $key ) {
460
			$value = $request[ $key ];
461
462
			if ( ! is_null( $value ) ) {
463
				switch ( $key ) {
464
					case 'billing' :
465
					case 'shipping' :
466
						$this->update_address( $order, $value, $key );
467
						break;
468
					case 'line_items' :
469
					case 'shipping_lines' :
470
					case 'fee_lines' :
471
					case 'coupon_lines' :
472
						if ( is_array( $value ) ) {
473
							foreach ( $value as $item ) {
474
								if ( is_array( $item ) ) {
475
									if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) {
476
										$order->remove_item( $item['id'] );
477
									} else {
478
										$this->set_item( $order, $key, $item );
479
									}
480
								}
481
							}
482
						}
483
						break;
484
					default :
485
						if ( is_callable( array( $order, "set_{$key}" ) ) ) {
486
							$order->{"set_{$key}"}( $value );
487
						}
488
						break;
489
				}
490
			}
491
		}
492
493
		/**
494
		 * Filter the data for the insert.
495
		 *
496
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
497
		 * prepared for the response.
498
		 *
499
		 * @param WC_Order           $order      The order object.
500
		 * @param WP_REST_Request    $request    Request object.
501
		 */
502
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $order, $request );
503
	}
504
505
	/**
506
	 * Create base WC Order object.
507
	 * @deprecated 3.0.0
508
	 * @param array $data
509
	 * @return WC_Order
510
	 */
511
	protected function create_base_order( $data ) {
512
		return wc_create_order( $data );
513
	}
514
515
	/**
516
	 * Only return writable props from schema.
517
	 * @param  array $schema
518
	 * @return bool
519
	 */
520
	protected function filter_writable_props( $schema ) {
521
		return empty( $schema['readonly'] );
522
	}
523
524
	/**
525
	 * Create order.
526
	 *
527
	 * @param WP_REST_Request $request Full details about the request.
528
	 * @return int|WP_Error
529
	 */
530
	protected function create_order( $request ) {
531
		try {
532
			// Make sure customer exists.
533
			if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) {
534
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce' ), 400 );
0 ignored issues
show
Bug introduced by
The type WC_REST_Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
535
			}
536
537
			// Make sure customer is part of blog.
538
			if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) {
539
				add_user_to_blog( get_current_blog_id(), $request['customer_id'], 'customer' );
540
			}
541
542
			$order = $this->prepare_item_for_database( $request );
543
			$order->set_created_via( 'rest-api' );
544
			$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
545
			$order->calculate_totals();
546
			$order->save();
547
548
			// Handle set paid.
549
			if ( true === $request['set_paid'] ) {
550
				$order->payment_complete( $request['transaction_id'] );
551
			}
552
553
			return $order->get_id();
554
		} catch ( WC_Data_Exception $e ) {
0 ignored issues
show
Bug introduced by
The type WC_Data_Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
555
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
556
		} catch ( WC_REST_Exception $e ) {
557
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
558
		}
559
	}
560
561
	/**
562
	 * Update order.
563
	 *
564
	 * @param WP_REST_Request $request Full details about the request.
565
	 * @return int|WP_Error
566
	 */
567
	protected function update_order( $request ) {
568
		try {
569
			$order = $this->prepare_item_for_database( $request );
570
			$order->save();
571
572
			// Handle set paid.
573
			if ( $order->needs_payment() && true === $request['set_paid'] ) {
574
				$order->payment_complete( $request['transaction_id'] );
575
			}
576
577
			// If items have changed, recalculate order totals.
578
			if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) {
579
				$order->calculate_totals( true );
580
			}
581
582
			return $order->get_id();
583
		} catch ( WC_Data_Exception $e ) {
584
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
585
		} catch ( WC_REST_Exception $e ) {
586
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
587
		}
588
	}
589
590
	/**
591
	 * Update address.
592
	 *
593
	 * @param WC_Order $order
594
	 * @param array $posted
595
	 * @param string $type
596
	 */
597
	protected function update_address( $order, $posted, $type = 'billing' ) {
598
		foreach ( $posted as $key => $value ) {
599
			if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) {
600
				$order->{"set_{$type}_{$key}"}( $value );
601
			}
602
		}
603
	}
604
605
	/**
606
	 * Gets the product ID from the SKU or posted ID.
607
	 *
608
	 * @param array $posted Request data
609
	 *
610
	 * @return int
611
	 * @throws WC_REST_Exception
612
	 */
613
	protected function get_product_id( $posted ) {
614
		if ( ! empty( $posted['sku'] ) ) {
615
			$product_id = (int) wc_get_product_id_by_sku( $posted['sku'] );
616
		} elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) {
617
			$product_id = (int) $posted['product_id'];
618
		} elseif ( ! empty( $posted['variation_id'] ) ) {
619
			$product_id = (int) $posted['variation_id'];
620
		} else {
621
			throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 );
622
		}
623
		return $product_id;
624
	}
625
626
	/**
627
	 * Maybe set an item prop if the value was posted.
628
	 * @param WC_Order_Item $item
0 ignored issues
show
Bug introduced by
The type WC_Order_Item was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
629
	 * @param string $prop
630
	 * @param array $posted Request data.
631
	 */
632
	protected function maybe_set_item_prop( $item, $prop, $posted ) {
633
		if ( isset( $posted[ $prop ] ) ) {
634
			$item->{"set_$prop"}( $posted[ $prop ] );
635
		}
636
	}
637
638
	/**
639
	 * Maybe set item props if the values were posted.
640
	 * @param WC_Order_Item $item
641
	 * @param string[] $props
642
	 * @param array $posted Request data.
643
	 */
644
	protected function maybe_set_item_props( $item, $props, $posted ) {
645
		foreach ( $props as $prop ) {
646
			$this->maybe_set_item_prop( $item, $prop, $posted );
647
		}
648
	}
649
650
	/**
651
	 * Create or update a line item.
652
	 *
653
	 * @param array $posted Line item data.
654
	 * @param string $action 'create' to add line item or 'update' to update it.
655
	 *
656
	 * @return WC_Order_Item_Product
657
	 * @throws WC_REST_Exception Invalid data, server error.
658
	 */
659
	protected function prepare_line_items( $posted, $action = 'create' ) {
660
		$item    = new WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' );
0 ignored issues
show
Bug introduced by
The type WC_Order_Item_Product was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
661
		$product = wc_get_product( $this->get_product_id( $posted ) );
662
663
		if ( $product !== $item->get_product() ) {
664
			$item->set_product( $product );
665
666
			if ( 'create' === $action ) {
667
				$quantity = isset( $posted['quantity'] ) ? $posted['quantity'] : 1;
668
				$total    = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) );
669
				$item->set_total( $total );
670
				$item->set_subtotal( $total );
671
			}
672
		}
673
674
		$this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted );
675
676
		return $item;
677
	}
678
679
	/**
680
	 * Create or update an order shipping method.
681
	 *
682
	 * @param $posted $shipping Item data.
0 ignored issues
show
Documentation Bug introduced by
The doc comment $posted at position 0 could not be parsed: Unknown type name '$posted' at position 0 in $posted.
Loading history...
683
	 * @param string $action 'create' to add shipping or 'update' to update it.
684
	 *
685
	 * @return WC_Order_Item_Shipping
686
	 * @throws WC_REST_Exception Invalid data, server error.
687
	 */
688
	protected function prepare_shipping_lines( $posted, $action ) {
689
		$item = new WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' );
0 ignored issues
show
Bug introduced by
The type WC_Order_Item_Shipping was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
690
691
		if ( 'create' === $action ) {
692
			if ( empty( $posted['method_id'] ) ) {
693
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 );
694
			}
695
		}
696
697
		$this->maybe_set_item_props( $item, array( 'method_id', 'method_title', 'total' ), $posted );
698
699
		return $item;
700
	}
701
702
	/**
703
	 * Create or update an order fee.
704
	 *
705
	 * @param array $posted Item data.
706
	 * @param string $action 'create' to add fee or 'update' to update it.
707
	 *
708
	 * @return WC_Order_Item_Fee
709
	 * @throws WC_REST_Exception Invalid data, server error.
710
	 */
711
	protected function prepare_fee_lines( $posted, $action ) {
712
		$item = new WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' );
0 ignored issues
show
Bug introduced by
The type WC_Order_Item_Fee was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
713
714
		if ( 'create' === $action ) {
715
			if ( empty( $posted['name'] ) ) {
716
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 );
717
			}
718
		}
719
720
		$this->maybe_set_item_props( $item, array( 'name', 'tax_class', 'tax_status', 'total' ), $posted );
721
722
		return $item;
723
	}
724
725
	/**
726
	 * Create or update an order coupon.
727
	 *
728
	 * @param array $posted Item data.
729
	 * @param string $action 'create' to add coupon or 'update' to update it.
730
	 *
731
	 * @return WC_Order_Item_Coupon
732
	 * @throws WC_REST_Exception Invalid data, server error.
733
	 */
734
	protected function prepare_coupon_lines( $posted, $action ) {
735
		$item = new WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' );
0 ignored issues
show
Bug introduced by
The type WC_Order_Item_Coupon was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
736
737
		if ( 'create' === $action ) {
738
			if ( empty( $posted['code'] ) ) {
739
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
740
			}
741
		}
742
743
		$this->maybe_set_item_props( $item, array( 'code', 'discount' ), $posted );
744
745
		return $item;
746
	}
747
748
	/**
749
	 * Wrapper method to create/update order items.
750
	 * When updating, the item ID provided is checked to ensure it is associated
751
	 * with the order.
752
	 *
753
	 * @param WC_Order $order order
754
	 * @param string $item_type
755
	 * @param array $posted item provided in the request body
756
	 * @throws WC_REST_Exception If item ID is not associated with order
757
	 */
758
	protected function set_item( $order, $item_type, $posted ) {
759
		global $wpdb;
760
761
		if ( ! empty( $posted['id'] ) ) {
762
			$action = 'update';
763
		} else {
764
			$action = 'create';
765
		}
766
767
		$method = 'prepare_' . $item_type;
768
769
		// Verify provided line item ID is associated with order.
770
		if ( 'update' === $action ) {
771
			$result = $wpdb->get_row(
772
				$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d",
773
				absint( $posted['id'] ),
774
				absint( $order->get_id() )
775
			) );
776
			if ( is_null( $result ) ) {
777
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 );
778
			}
779
		}
780
781
		// Prepare item data
782
		$item = $this->$method( $posted, $action );
783
784
		/**
785
		 * Action hook to adjust item before save.
786
		 * @since 3.0.0
787
		 */
788
		do_action( 'woocommerce_rest_set_order_item', $item, $posted );
789
790
		// Save or add to order
791
		if ( 'create' === $action ) {
792
			$order->add_item( $item );
793
		} else {
794
			$item->save();
795
		}
796
	}
797
798
	/**
799
	 * Helper method to check if the resource ID associated with the provided item is null.
800
	 * Items can be deleted by setting the resource ID to null.
801
	 *
802
	 * @param array $item Item provided in the request body.
803
	 * @return bool True if the item resource ID is null, false otherwise.
804
	 */
805
	protected function item_is_null( $item ) {
806
		$keys = array( 'product_id', 'method_id', 'method_title', 'name', 'code' );
807
808
		foreach ( $keys as $key ) {
809
			if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) {
810
				return true;
811
			}
812
		}
813
814
		return false;
815
	}
816
817
	/**
818
	 * Create a single item.
819
	 *
820
	 * @param WP_REST_Request $request Full details about the request.
821
	 * @return WP_Error|WP_REST_Response
822
	 */
823
	public function create_item( $request ) {
824
		if ( ! empty( $request['id'] ) ) {
825
			/* translators: %s: post type */
826
			return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
827
		}
828
829
		$order_id = $this->create_order( $request );
830
		if ( is_wp_error( $order_id ) ) {
831
			return $order_id;
832
		}
833
834
		$post = get_post( $order_id );
835
		$this->update_additional_fields_for_object( $post, $request );
836
837
		/**
838
		 * Fires after a single item is created or updated via the REST API.
839
		 *
840
		 * @param WP_Post         $post      Post object.
841
		 * @param WP_REST_Request $request   Request object.
842
		 * @param boolean         $creating  True when creating item, false when updating.
843
		 */
844
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
845
		$request->set_param( 'context', 'edit' );
846
		$response = $this->prepare_item_for_response( $post, $request );
847
		$response = rest_ensure_response( $response );
848
		$response->set_status( 201 );
849
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
850
851
		return $response;
852
	}
853
854
	/**
855
	 * Update a single order.
856
	 *
857
	 * @param WP_REST_Request $request Full details about the request.
858
	 * @return WP_Error|WP_REST_Response
859
	 */
860
	public function update_item( $request ) {
861
		try {
862
			$post_id = (int) $request['id'];
863
864
			if ( empty( $post_id ) || get_post_type( $post_id ) !== $this->post_type ) {
865
				return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
866
			}
867
868
			$order_id = $this->update_order( $request );
869
			if ( is_wp_error( $order_id ) ) {
870
				return $order_id;
871
			}
872
873
			$post = get_post( $order_id );
874
			$this->update_additional_fields_for_object( $post, $request );
875
876
			/**
877
			 * Fires after a single item is created or updated via the REST API.
878
			 *
879
			 * @param WP_Post         $post      Post object.
880
			 * @param WP_REST_Request $request   Request object.
881
			 * @param boolean         $creating  True when creating item, false when updating.
882
			 */
883
			do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
884
			$request->set_param( 'context', 'edit' );
885
			$response = $this->prepare_item_for_response( $post, $request );
886
			return rest_ensure_response( $response );
887
888
		} catch ( Exception $e ) {
889
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
0 ignored issues
show
Bug introduced by
The method getErrorCode() does not exist on Exception. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

889
			return new WP_Error( $e->/** @scrutinizer ignore-call */ getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
890
		}
891
	}
892
893
	/**
894
	 * Get order statuses without prefixes.
895
	 * @return array
896
	 */
897
	protected function get_order_statuses() {
898
		$order_statuses = array();
899
900
		foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
901
			$order_statuses[] = str_replace( 'wc-', '', $status );
902
		}
903
904
		return $order_statuses;
905
	}
906
907
	/**
908
	 * Get the Order's schema, conforming to JSON Schema.
909
	 *
910
	 * @return array
911
	 */
912
	public function get_item_schema() {
913
		$schema = array(
914
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
915
			'title'      => $this->post_type,
916
			'type'       => 'object',
917
			'properties' => array(
918
				'id' => array(
919
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
920
					'type'        => 'integer',
921
					'context'     => array( 'view', 'edit' ),
922
					'readonly'    => true,
923
				),
924
				'parent_id' => array(
925
					'description' => __( 'Parent order ID.', 'woocommerce' ),
926
					'type'        => 'integer',
927
					'context'     => array( 'view', 'edit' ),
928
				),
929
				'status' => array(
930
					'description' => __( 'Order status.', 'woocommerce' ),
931
					'type'        => 'string',
932
					'default'     => 'pending',
933
					'enum'        => $this->get_order_statuses(),
934
					'context'     => array( 'view', 'edit' ),
935
				),
936
				'order_key' => array(
937
					'description' => __( 'Order key.', 'woocommerce' ),
938
					'type'        => 'string',
939
					'context'     => array( 'view', 'edit' ),
940
					'readonly'    => true,
941
				),
942
				'number' => array(
943
					'description' => __( 'Order number.', 'woocommerce' ),
944
					'type'        => 'string',
945
					'context'     => array( 'view', 'edit' ),
946
					'readonly'    => true,
947
				),
948
				'currency' => array(
949
					'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ),
950
					'type'        => 'string',
951
					'default'     => get_woocommerce_currency(),
952
					'enum'        => array_keys( get_woocommerce_currencies() ),
953
					'context'     => array( 'view', 'edit' ),
954
				),
955
				'version' => array(
956
					'description' => __( 'Version of WooCommerce which last updated the order.', 'woocommerce' ),
957
					'type'        => 'integer',
958
					'context'     => array( 'view', 'edit' ),
959
					'readonly'    => true,
960
				),
961
				'prices_include_tax' => array(
962
					'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ),
963
					'type'        => 'boolean',
964
					'context'     => array( 'view', 'edit' ),
965
					'readonly'    => true,
966
				),
967
				'date_created' => array(
968
					'description' => __( "The date the order was created, as GMT.", 'woocommerce' ),
969
					'type'        => 'date-time',
970
					'context'     => array( 'view', 'edit' ),
971
					'readonly'    => true,
972
				),
973
				'date_modified' => array(
974
					'description' => __( "The date the order was last modified, as GMT.", 'woocommerce' ),
975
					'type'        => 'date-time',
976
					'context'     => array( 'view', 'edit' ),
977
					'readonly'    => true,
978
				),
979
				'customer_id' => array(
980
					'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ),
981
					'type'        => 'integer',
982
					'default'     => 0,
983
					'context'     => array( 'view', 'edit' ),
984
				),
985
				'discount_total' => array(
986
					'description' => __( 'Total discount amount for the order.', 'woocommerce' ),
987
					'type'        => 'string',
988
					'context'     => array( 'view', 'edit' ),
989
					'readonly'    => true,
990
				),
991
				'discount_tax' => array(
992
					'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ),
993
					'type'        => 'string',
994
					'context'     => array( 'view', 'edit' ),
995
					'readonly'    => true,
996
				),
997
				'shipping_total' => array(
998
					'description' => __( 'Total shipping amount for the order.', 'woocommerce' ),
999
					'type'        => 'string',
1000
					'context'     => array( 'view', 'edit' ),
1001
					'readonly'    => true,
1002
				),
1003
				'shipping_tax' => array(
1004
					'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ),
1005
					'type'        => 'string',
1006
					'context'     => array( 'view', 'edit' ),
1007
					'readonly'    => true,
1008
				),
1009
				'cart_tax' => array(
1010
					'description' => __( 'Sum of line item taxes only.', 'woocommerce' ),
1011
					'type'        => 'string',
1012
					'context'     => array( 'view', 'edit' ),
1013
					'readonly'    => true,
1014
				),
1015
				'total' => array(
1016
					'description' => __( 'Grand total.', 'woocommerce' ),
1017
					'type'        => 'string',
1018
					'context'     => array( 'view', 'edit' ),
1019
					'readonly'    => true,
1020
				),
1021
				'total_tax' => array(
1022
					'description' => __( 'Sum of all taxes.', 'woocommerce' ),
1023
					'type'        => 'string',
1024
					'context'     => array( 'view', 'edit' ),
1025
					'readonly'    => true,
1026
				),
1027
				'billing' => array(
1028
					'description' => __( 'Billing address.', 'woocommerce' ),
1029
					'type'        => 'object',
1030
					'context'     => array( 'view', 'edit' ),
1031
					'properties'  => array(
1032
						'first_name' => array(
1033
							'description' => __( 'First name.', 'woocommerce' ),
1034
							'type'        => 'string',
1035
							'context'     => array( 'view', 'edit' ),
1036
						),
1037
						'last_name' => array(
1038
							'description' => __( 'Last name.', 'woocommerce' ),
1039
							'type'        => 'string',
1040
							'context'     => array( 'view', 'edit' ),
1041
						),
1042
						'company' => array(
1043
							'description' => __( 'Company name.', 'woocommerce' ),
1044
							'type'        => 'string',
1045
							'context'     => array( 'view', 'edit' ),
1046
						),
1047
						'address_1' => array(
1048
							'description' => __( 'Address line 1.', 'woocommerce' ),
1049
							'type'        => 'string',
1050
							'context'     => array( 'view', 'edit' ),
1051
						),
1052
						'address_2' => array(
1053
							'description' => __( 'Address line 2.', 'woocommerce' ),
1054
							'type'        => 'string',
1055
							'context'     => array( 'view', 'edit' ),
1056
						),
1057
						'city' => array(
1058
							'description' => __( 'City name.', 'woocommerce' ),
1059
							'type'        => 'string',
1060
							'context'     => array( 'view', 'edit' ),
1061
						),
1062
						'state' => array(
1063
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1064
							'type'        => 'string',
1065
							'context'     => array( 'view', 'edit' ),
1066
						),
1067
						'postcode' => array(
1068
							'description' => __( 'Postal code.', 'woocommerce' ),
1069
							'type'        => 'string',
1070
							'context'     => array( 'view', 'edit' ),
1071
						),
1072
						'country' => array(
1073
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1074
							'type'        => 'string',
1075
							'context'     => array( 'view', 'edit' ),
1076
						),
1077
						'email' => array(
1078
							'description' => __( 'Email address.', 'woocommerce' ),
1079
							'type'        => 'string',
1080
							'format'      => 'email',
1081
							'context'     => array( 'view', 'edit' ),
1082
						),
1083
						'phone' => array(
1084
							'description' => __( 'Phone number.', 'woocommerce' ),
1085
							'type'        => 'string',
1086
							'context'     => array( 'view', 'edit' ),
1087
						),
1088
					),
1089
				),
1090
				'shipping' => array(
1091
					'description' => __( 'Shipping address.', 'woocommerce' ),
1092
					'type'        => 'object',
1093
					'context'     => array( 'view', 'edit' ),
1094
					'properties'  => array(
1095
						'first_name' => array(
1096
							'description' => __( 'First name.', 'woocommerce' ),
1097
							'type'        => 'string',
1098
							'context'     => array( 'view', 'edit' ),
1099
						),
1100
						'last_name' => array(
1101
							'description' => __( 'Last name.', 'woocommerce' ),
1102
							'type'        => 'string',
1103
							'context'     => array( 'view', 'edit' ),
1104
						),
1105
						'company' => array(
1106
							'description' => __( 'Company name.', 'woocommerce' ),
1107
							'type'        => 'string',
1108
							'context'     => array( 'view', 'edit' ),
1109
						),
1110
						'address_1' => array(
1111
							'description' => __( 'Address line 1.', 'woocommerce' ),
1112
							'type'        => 'string',
1113
							'context'     => array( 'view', 'edit' ),
1114
						),
1115
						'address_2' => array(
1116
							'description' => __( 'Address line 2.', 'woocommerce' ),
1117
							'type'        => 'string',
1118
							'context'     => array( 'view', 'edit' ),
1119
						),
1120
						'city' => array(
1121
							'description' => __( 'City name.', 'woocommerce' ),
1122
							'type'        => 'string',
1123
							'context'     => array( 'view', 'edit' ),
1124
						),
1125
						'state' => array(
1126
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1127
							'type'        => 'string',
1128
							'context'     => array( 'view', 'edit' ),
1129
						),
1130
						'postcode' => array(
1131
							'description' => __( 'Postal code.', 'woocommerce' ),
1132
							'type'        => 'string',
1133
							'context'     => array( 'view', 'edit' ),
1134
						),
1135
						'country' => array(
1136
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1137
							'type'        => 'string',
1138
							'context'     => array( 'view', 'edit' ),
1139
						),
1140
					),
1141
				),
1142
				'payment_method' => array(
1143
					'description' => __( 'Payment method ID.', 'woocommerce' ),
1144
					'type'        => 'string',
1145
					'context'     => array( 'view', 'edit' ),
1146
				),
1147
				'payment_method_title' => array(
1148
					'description' => __( 'Payment method title.', 'woocommerce' ),
1149
					'type'        => 'string',
1150
					'context'     => array( 'view', 'edit' ),
1151
					'arg_options' => array(
1152
						'sanitize_callback' => 'sanitize_text_field',
1153
					),
1154
				),
1155
				'set_paid' => array(
1156
					'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ),
1157
					'type'        => 'boolean',
1158
					'default'     => false,
1159
					'context'     => array( 'edit' ),
1160
				),
1161
				'transaction_id' => array(
1162
					'description' => __( 'Unique transaction ID.', 'woocommerce' ),
1163
					'type'        => 'string',
1164
					'context'     => array( 'view', 'edit' ),
1165
				),
1166
				'customer_ip_address' => array(
1167
					'description' => __( "Customer's IP address.", 'woocommerce' ),
1168
					'type'        => 'string',
1169
					'context'     => array( 'view', 'edit' ),
1170
					'readonly'    => true,
1171
				),
1172
				'customer_user_agent' => array(
1173
					'description' => __( 'User agent of the customer.', 'woocommerce' ),
1174
					'type'        => 'string',
1175
					'context'     => array( 'view', 'edit' ),
1176
					'readonly'    => true,
1177
				),
1178
				'created_via' => array(
1179
					'description' => __( 'Shows where the order was created.', 'woocommerce' ),
1180
					'type'        => 'string',
1181
					'context'     => array( 'view', 'edit' ),
1182
					'readonly'    => true,
1183
				),
1184
				'customer_note' => array(
1185
					'description' => __( 'Note left by customer during checkout.', 'woocommerce' ),
1186
					'type'        => 'string',
1187
					'context'     => array( 'view', 'edit' ),
1188
				),
1189
				'date_completed' => array(
1190
					'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ),
1191
					'type'        => 'date-time',
1192
					'context'     => array( 'view', 'edit' ),
1193
					'readonly'    => true,
1194
				),
1195
				'date_paid' => array(
1196
					'description' => __( "The date the order was paid, in the site's timezone.", 'woocommerce' ),
1197
					'type'        => 'date-time',
1198
					'context'     => array( 'view', 'edit' ),
1199
					'readonly'    => true,
1200
				),
1201
				'cart_hash' => array(
1202
					'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ),
1203
					'type'        => 'string',
1204
					'context'     => array( 'view', 'edit' ),
1205
					'readonly'    => true,
1206
				),
1207
				'line_items' => array(
1208
					'description' => __( 'Line items data.', 'woocommerce' ),
1209
					'type'        => 'array',
1210
					'context'     => array( 'view', 'edit' ),
1211
					'items'       => array(
1212
						'type'       => 'object',
1213
						'properties' => array(
1214
							'id' => array(
1215
								'description' => __( 'Item ID.', 'woocommerce' ),
1216
								'type'        => 'integer',
1217
								'context'     => array( 'view', 'edit' ),
1218
								'readonly'    => true,
1219
							),
1220
							'name' => array(
1221
								'description' => __( 'Product name.', 'woocommerce' ),
1222
								'type'        => 'mixed',
1223
								'context'     => array( 'view', 'edit' ),
1224
								'readonly'    => true,
1225
							),
1226
							'sku' => array(
1227
								'description' => __( 'Product SKU.', 'woocommerce' ),
1228
								'type'        => 'string',
1229
								'context'     => array( 'view', 'edit' ),
1230
								'readonly'    => true,
1231
							),
1232
							'product_id' => array(
1233
								'description' => __( 'Product ID.', 'woocommerce' ),
1234
								'type'        => 'mixed',
1235
								'context'     => array( 'view', 'edit' ),
1236
							),
1237
							'variation_id' => array(
1238
								'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
1239
								'type'        => 'integer',
1240
								'context'     => array( 'view', 'edit' ),
1241
							),
1242
							'quantity' => array(
1243
								'description' => __( 'Quantity ordered.', 'woocommerce' ),
1244
								'type'        => 'integer',
1245
								'context'     => array( 'view', 'edit' ),
1246
							),
1247
							'tax_class' => array(
1248
								'description' => __( 'Tax class of product.', 'woocommerce' ),
1249
								'type'        => 'string',
1250
								'context'     => array( 'view', 'edit' ),
1251
								'readonly'    => true,
1252
							),
1253
							'price' => array(
1254
								'description' => __( 'Product price.', 'woocommerce' ),
1255
								'type'        => 'string',
1256
								'context'     => array( 'view', 'edit' ),
1257
								'readonly'    => true,
1258
							),
1259
							'subtotal' => array(
1260
								'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
1261
								'type'        => 'string',
1262
								'context'     => array( 'view', 'edit' ),
1263
							),
1264
							'subtotal_tax' => array(
1265
								'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
1266
								'type'        => 'string',
1267
								'context'     => array( 'view', 'edit' ),
1268
							),
1269
							'total' => array(
1270
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1271
								'type'        => 'string',
1272
								'context'     => array( 'view', 'edit' ),
1273
							),
1274
							'total_tax' => array(
1275
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1276
								'type'        => 'string',
1277
								'context'     => array( 'view', 'edit' ),
1278
							),
1279
							'taxes' => array(
1280
								'description' => __( 'Line taxes.', 'woocommerce' ),
1281
								'type'        => 'array',
1282
								'context'     => array( 'view', 'edit' ),
1283
								'readonly'    => true,
1284
								'items'       => array(
1285
									'type'       => 'object',
1286
									'properties' => array(
1287
										'id' => array(
1288
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1289
											'type'        => 'integer',
1290
											'context'     => array( 'view', 'edit' ),
1291
											'readonly'    => true,
1292
										),
1293
										'total' => array(
1294
											'description' => __( 'Tax total.', 'woocommerce' ),
1295
											'type'        => 'string',
1296
											'context'     => array( 'view', 'edit' ),
1297
											'readonly'    => true,
1298
										),
1299
										'subtotal' => array(
1300
											'description' => __( 'Tax subtotal.', 'woocommerce' ),
1301
											'type'        => 'string',
1302
											'context'     => array( 'view', 'edit' ),
1303
											'readonly'    => true,
1304
										),
1305
									),
1306
								),
1307
							),
1308
							'meta' => array(
1309
								'description' => __( 'Line item meta data.', 'woocommerce' ),
1310
								'type'        => 'array',
1311
								'context'     => array( 'view', 'edit' ),
1312
								'readonly'    => true,
1313
								'items'       => array(
1314
									'type'       => 'object',
1315
									'properties' => array(
1316
										'key' => array(
1317
											'description' => __( 'Meta key.', 'woocommerce' ),
1318
											'type'        => 'string',
1319
											'context'     => array( 'view', 'edit' ),
1320
											'readonly'    => true,
1321
										),
1322
										'label' => array(
1323
											'description' => __( 'Meta label.', 'woocommerce' ),
1324
											'type'        => 'string',
1325
											'context'     => array( 'view', 'edit' ),
1326
											'readonly'    => true,
1327
										),
1328
										'value' => array(
1329
											'description' => __( 'Meta value.', 'woocommerce' ),
1330
											'type'        => 'mixed',
1331
											'context'     => array( 'view', 'edit' ),
1332
											'readonly'    => true,
1333
										),
1334
									),
1335
								),
1336
							),
1337
						),
1338
					),
1339
				),
1340
				'tax_lines' => array(
1341
					'description' => __( 'Tax lines data.', 'woocommerce' ),
1342
					'type'        => 'array',
1343
					'context'     => array( 'view', 'edit' ),
1344
					'readonly'    => true,
1345
					'items'       => array(
1346
						'type'       => 'object',
1347
						'properties' => array(
1348
							'id' => array(
1349
								'description' => __( 'Item ID.', 'woocommerce' ),
1350
								'type'        => 'integer',
1351
								'context'     => array( 'view', 'edit' ),
1352
								'readonly'    => true,
1353
							),
1354
							'rate_code' => array(
1355
								'description' => __( 'Tax rate code.', 'woocommerce' ),
1356
								'type'        => 'string',
1357
								'context'     => array( 'view', 'edit' ),
1358
								'readonly'    => true,
1359
							),
1360
							'rate_id' => array(
1361
								'description' => __( 'Tax rate ID.', 'woocommerce' ),
1362
								'type'        => 'string',
1363
								'context'     => array( 'view', 'edit' ),
1364
								'readonly'    => true,
1365
							),
1366
							'label' => array(
1367
								'description' => __( 'Tax rate label.', 'woocommerce' ),
1368
								'type'        => 'string',
1369
								'context'     => array( 'view', 'edit' ),
1370
								'readonly'    => true,
1371
							),
1372
							'compound' => array(
1373
								'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ),
1374
								'type'        => 'boolean',
1375
								'context'     => array( 'view', 'edit' ),
1376
								'readonly'    => true,
1377
							),
1378
							'tax_total' => array(
1379
								'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ),
1380
								'type'        => 'string',
1381
								'context'     => array( 'view', 'edit' ),
1382
								'readonly'    => true,
1383
							),
1384
							'shipping_tax_total' => array(
1385
								'description' => __( 'Shipping tax total.', 'woocommerce' ),
1386
								'type'        => 'string',
1387
								'context'     => array( 'view', 'edit' ),
1388
								'readonly'    => true,
1389
							),
1390
						),
1391
					),
1392
				),
1393
				'shipping_lines' => array(
1394
					'description' => __( 'Shipping lines data.', 'woocommerce' ),
1395
					'type'        => 'array',
1396
					'context'     => array( 'view', 'edit' ),
1397
					'items'       => array(
1398
						'type'       => 'object',
1399
						'properties' => array(
1400
							'id' => array(
1401
								'description' => __( 'Item ID.', 'woocommerce' ),
1402
								'type'        => 'integer',
1403
								'context'     => array( 'view', 'edit' ),
1404
								'readonly'    => true,
1405
							),
1406
							'method_title' => array(
1407
								'description' => __( 'Shipping method name.', 'woocommerce' ),
1408
								'type'        => 'mixed',
1409
								'context'     => array( 'view', 'edit' ),
1410
							),
1411
							'method_id' => array(
1412
								'description' => __( 'Shipping method ID.', 'woocommerce' ),
1413
								'type'        => 'mixed',
1414
								'context'     => array( 'view', 'edit' ),
1415
							),
1416
							'total' => array(
1417
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1418
								'type'        => 'string',
1419
								'context'     => array( 'view', 'edit' ),
1420
							),
1421
							'total_tax' => array(
1422
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1423
								'type'        => 'string',
1424
								'context'     => array( 'view', 'edit' ),
1425
								'readonly'    => true,
1426
							),
1427
							'taxes' => array(
1428
								'description' => __( 'Line taxes.', 'woocommerce' ),
1429
								'type'        => 'array',
1430
								'context'     => array( 'view', 'edit' ),
1431
								'readonly'    => true,
1432
								'items'       => array(
1433
									'type'       => 'object',
1434
									'properties' => array(
1435
										'id' => array(
1436
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1437
											'type'        => 'integer',
1438
											'context'     => array( 'view', 'edit' ),
1439
											'readonly'    => true,
1440
										),
1441
										'total' => array(
1442
											'description' => __( 'Tax total.', 'woocommerce' ),
1443
											'type'        => 'string',
1444
											'context'     => array( 'view', 'edit' ),
1445
											'readonly'    => true,
1446
										),
1447
									),
1448
								),
1449
							),
1450
						),
1451
					),
1452
				),
1453
				'fee_lines' => array(
1454
					'description' => __( 'Fee lines data.', 'woocommerce' ),
1455
					'type'        => 'array',
1456
					'context'     => array( 'view', 'edit' ),
1457
					'items'       => array(
1458
						'type'       => 'object',
1459
						'properties' => array(
1460
							'id' => array(
1461
								'description' => __( 'Item ID.', 'woocommerce' ),
1462
								'type'        => 'integer',
1463
								'context'     => array( 'view', 'edit' ),
1464
								'readonly'    => true,
1465
							),
1466
							'name' => array(
1467
								'description' => __( 'Fee name.', 'woocommerce' ),
1468
								'type'        => 'mixed',
1469
								'context'     => array( 'view', 'edit' ),
1470
							),
1471
							'tax_class' => array(
1472
								'description' => __( 'Tax class of fee.', 'woocommerce' ),
1473
								'type'        => 'string',
1474
								'context'     => array( 'view', 'edit' ),
1475
							),
1476
							'tax_status' => array(
1477
								'description' => __( 'Tax status of fee.', 'woocommerce' ),
1478
								'type'        => 'string',
1479
								'context'     => array( 'view', 'edit' ),
1480
								'enum'        => array( 'taxable', 'none' ),
1481
							),
1482
							'total' => array(
1483
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1484
								'type'        => 'string',
1485
								'context'     => array( 'view', 'edit' ),
1486
							),
1487
							'total_tax' => array(
1488
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1489
								'type'        => 'string',
1490
								'context'     => array( 'view', 'edit' ),
1491
							),
1492
							'taxes' => array(
1493
								'description' => __( 'Line taxes.', 'woocommerce' ),
1494
								'type'        => 'array',
1495
								'context'     => array( 'view', 'edit' ),
1496
								'readonly'    => true,
1497
								'items'       => array(
1498
									'type'       => 'object',
1499
									'properties' => array(
1500
										'id' => array(
1501
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1502
											'type'        => 'integer',
1503
											'context'     => array( 'view', 'edit' ),
1504
											'readonly'    => true,
1505
										),
1506
										'total' => array(
1507
											'description' => __( 'Tax total.', 'woocommerce' ),
1508
											'type'        => 'string',
1509
											'context'     => array( 'view', 'edit' ),
1510
											'readonly'    => true,
1511
										),
1512
										'subtotal' => array(
1513
											'description' => __( 'Tax subtotal.', 'woocommerce' ),
1514
											'type'        => 'string',
1515
											'context'     => array( 'view', 'edit' ),
1516
											'readonly'    => true,
1517
										),
1518
									),
1519
								),
1520
							),
1521
						),
1522
					),
1523
				),
1524
				'coupon_lines' => array(
1525
					'description' => __( 'Coupons line data.', 'woocommerce' ),
1526
					'type'        => 'array',
1527
					'context'     => array( 'view', 'edit' ),
1528
					'items'       => array(
1529
						'type'       => 'object',
1530
						'properties' => array(
1531
							'id' => array(
1532
								'description' => __( 'Item ID.', 'woocommerce' ),
1533
								'type'        => 'integer',
1534
								'context'     => array( 'view', 'edit' ),
1535
								'readonly'    => true,
1536
							),
1537
							'code' => array(
1538
								'description' => __( 'Coupon code.', 'woocommerce' ),
1539
								'type'        => 'mixed',
1540
								'context'     => array( 'view', 'edit' ),
1541
							),
1542
							'discount' => array(
1543
								'description' => __( 'Discount total.', 'woocommerce' ),
1544
								'type'        => 'string',
1545
								'context'     => array( 'view', 'edit' ),
1546
							),
1547
							'discount_tax' => array(
1548
								'description' => __( 'Discount total tax.', 'woocommerce' ),
1549
								'type'        => 'string',
1550
								'context'     => array( 'view', 'edit' ),
1551
								'readonly'    => true,
1552
							),
1553
						),
1554
					),
1555
				),
1556
				'refunds' => array(
1557
					'description' => __( 'List of refunds.', 'woocommerce' ),
1558
					'type'        => 'array',
1559
					'context'     => array( 'view', 'edit' ),
1560
					'readonly'    => true,
1561
					'items'       => array(
1562
						'type'       => 'object',
1563
						'properties' => array(
1564
							'id' => array(
1565
								'description' => __( 'Refund ID.', 'woocommerce' ),
1566
								'type'        => 'integer',
1567
								'context'     => array( 'view', 'edit' ),
1568
								'readonly'    => true,
1569
							),
1570
							'reason' => array(
1571
								'description' => __( 'Refund reason.', 'woocommerce' ),
1572
								'type'        => 'string',
1573
								'context'     => array( 'view', 'edit' ),
1574
								'readonly'    => true,
1575
							),
1576
							'total' => array(
1577
								'description' => __( 'Refund total.', 'woocommerce' ),
1578
								'type'        => 'string',
1579
								'context'     => array( 'view', 'edit' ),
1580
								'readonly'    => true,
1581
							),
1582
						),
1583
					),
1584
				),
1585
			),
1586
		);
1587
1588
		return $this->add_additional_fields_schema( $schema );
1589
	}
1590
1591
	/**
1592
	 * Get the query params for collections.
1593
	 *
1594
	 * @return array
1595
	 */
1596
	public function get_collection_params() {
1597
		$params = parent::get_collection_params();
1598
1599
		$params['status'] = array(
1600
			'default'           => 'any',
1601
			'description'       => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ),
1602
			'type'              => 'string',
1603
			'enum'              => array_merge( array( 'any' ), $this->get_order_statuses() ),
1604
			'sanitize_callback' => 'sanitize_key',
1605
			'validate_callback' => 'rest_validate_request_arg',
1606
		);
1607
		$params['customer'] = array(
1608
			'description'       => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ),
1609
			'type'              => 'integer',
1610
			'sanitize_callback' => 'absint',
1611
			'validate_callback' => 'rest_validate_request_arg',
1612
		);
1613
		$params['product'] = array(
1614
			'description'       => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ),
1615
			'type'              => 'integer',
1616
			'sanitize_callback' => 'absint',
1617
			'validate_callback' => 'rest_validate_request_arg',
1618
		);
1619
		$params['dp'] = array(
1620
			'default'           => wc_get_price_decimals(),
1621
			'description'       => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
1622
			'type'              => 'integer',
1623
			'sanitize_callback' => 'absint',
1624
			'validate_callback' => 'rest_validate_request_arg',
1625
		);
1626
1627
		return $params;
1628
	}
1629
}
1630