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

Orders::prepare_shipping_lines()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 6
nop 3
dl 0
loc 13
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * REST API Orders controller
4
 *
5
 * Handles requests to the /orders endpoint.
6
 *
7
 * @package WooCommerce/RestApi
8
 */
9
10
namespace WooCommerce\RestApi\Controllers\Version4;
11
12
defined( 'ABSPATH' ) || exit;
13
14
/**
15
 * REST API Orders controller class.
16
 */
17
class Orders extends AbstractObjectsController {
18
19
	/**
20
	 * Route base.
21
	 *
22
	 * @var string
23
	 */
24
	protected $rest_base = 'orders';
25
26
	/**
27
	 * Post type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $post_type = 'shop_order';
32
33
	/**
34
	 * If object is hierarchical.
35
	 *
36
	 * @var bool
37
	 */
38
	protected $hierarchical = true;
39
40
	/**
41
	 * Stores the request.
42
	 *
43
	 * @var array
44
	 */
45
	protected $request = array();
46
47
	/**
48
	 * Register the routes for orders.
49
	 */
50
	public function register_routes() {
51
		register_rest_route(
52
			$this->namespace,
53
			'/' . $this->rest_base,
54
			array(
55
				array(
56
					'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...
57
					'callback'            => array( $this, 'get_items' ),
58
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
59
					'args'                => $this->get_collection_params(),
60
				),
61
				array(
62
					'methods'             => \WP_REST_Server::CREATABLE,
63
					'callback'            => array( $this, 'create_item' ),
64
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
65
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
66
				),
67
				'schema' => array( $this, 'get_public_item_schema' ),
68
			),
69
			true
70
		);
71
72
		register_rest_route(
73
			$this->namespace,
74
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
75
			array(
76
				'args'   => array(
77
					'id' => array(
78
						'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
79
						'type'        => 'integer',
80
					),
81
				),
82
				array(
83
					'methods'             => \WP_REST_Server::READABLE,
84
					'callback'            => array( $this, 'get_item' ),
85
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
86
					'args'                => array(
87
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
88
					),
89
				),
90
				array(
91
					'methods'             => \WP_REST_Server::EDITABLE,
92
					'callback'            => array( $this, 'update_item' ),
93
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
94
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
95
				),
96
				array(
97
					'methods'             => \WP_REST_Server::DELETABLE,
98
					'callback'            => array( $this, 'delete_item' ),
99
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
100
					'args'                => array(
101
						'force' => array(
102
							'default'     => false,
103
							'type'        => 'boolean',
104
							'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
105
						),
106
					),
107
				),
108
				'schema' => array( $this, 'get_public_item_schema' ),
109
			),
110
			true
111
		);
112
113
		register_rest_route(
114
			$this->namespace,
115
			'/' . $this->rest_base . '/batch',
116
			array(
117
				array(
118
					'methods'             => \WP_REST_Server::EDITABLE,
119
					'callback'            => array( $this, 'batch_items' ),
120
					'permission_callback' => array( $this, 'batch_items_permissions_check' ),
121
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
122
				),
123
				'schema' => array( $this, 'get_public_batch_schema' ),
124
			),
125
			true
126
		);
127
	}
128
129
	/**
130
	 * Get object. Return false if object is not of required type.
131
	 *
132
	 * @since  3.0.0
133
	 * @param  int $id Object ID.
134
	 * @return WC_Data|bool
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Controllers\Version4\WC_Data 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...
135
	 */
136
	protected function get_object( $id ) {
137
		$order = wc_get_order( $id );
138
		// In case id is a refund's id (or it's not an order at all), don't expose it via /orders/ path.
139
		if ( ! $order || 'shop_order_refund' === $order->get_type() ) {
140
			return false;
141
		}
142
143
		return $order;
144
	}
145
146
	/**
147
	 * Expands an order item to get its data.
148
	 *
149
	 * @param WC_Order_item $item Order item data.
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...\Version4\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...
150
	 * @return array
151
	 */
152
	protected function get_order_item_data( $item ) {
153
		$data           = $item->get_data();
154
		$format_decimal = array( 'subtotal', 'subtotal_tax', 'total', 'total_tax', 'tax_total', 'shipping_tax_total' );
155
156
		// Format decimal values.
157
		foreach ( $format_decimal as $key ) {
158
			if ( isset( $data[ $key ] ) ) {
159
				$data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] );
160
			}
161
		}
162
163
		// Add SKU and PRICE to products.
164
		if ( is_callable( array( $item, 'get_product' ) ) ) {
165
			$data['sku']   = $item->get_product() ? $item->get_product()->get_sku() : null;
166
			$data['price'] = $item->get_quantity() ? $item->get_total() / $item->get_quantity() : 0;
167
		}
168
169
		// Format taxes.
170
		if ( ! empty( $data['taxes']['total'] ) ) {
171
			$taxes = array();
172
173
			foreach ( $data['taxes']['total'] as $tax_rate_id => $tax ) {
174
				$taxes[] = array(
175
					'id'       => $tax_rate_id,
176
					'total'    => $tax,
177
					'subtotal' => isset( $data['taxes']['subtotal'][ $tax_rate_id ] ) ? $data['taxes']['subtotal'][ $tax_rate_id ] : '',
178
				);
179
			}
180
			$data['taxes'] = $taxes;
181
		} elseif ( isset( $data['taxes'] ) ) {
182
			$data['taxes'] = array();
183
		}
184
185
		// Remove names for coupons, taxes and shipping.
186
		if ( isset( $data['code'] ) || isset( $data['rate_code'] ) || isset( $data['method_title'] ) ) {
187
			unset( $data['name'] );
188
		}
189
190
		// Remove props we don't want to expose.
191
		unset( $data['order_id'] );
192
		unset( $data['type'] );
193
194
		return $data;
195
	}
196
197
	/**
198
	 * Get formatted item data.
199
	 *
200
	 * @since  3.0.0
201
	 * @param  WC_Data $object WC_Data instance.
202
	 * @return array
203
	 */
204
	protected function get_formatted_item_data( $object ) {
205
		$data              = $object->get_data();
206
		$format_decimal    = array( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'shipping_total', 'shipping_tax', 'cart_tax', 'total', 'total_tax' );
207
		$format_date       = array( 'date_created', 'date_modified', 'date_completed', 'date_paid' );
208
		$format_line_items = array( 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines' );
209
210
		// Format decimal values.
211
		foreach ( $format_decimal as $key ) {
212
			$data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] );
213
		}
214
215
		// Format date values.
216
		foreach ( $format_date as $key ) {
217
			$datetime              = $data[ $key ];
218
			$data[ $key ]          = wc_rest_prepare_date_response( $datetime, false );
219
			$data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime );
220
		}
221
222
		// Format the order status.
223
		$data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status'];
224
225
		// Format line items.
226
		foreach ( $format_line_items as $key ) {
227
			$data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) );
228
		}
229
230
		// Refunds.
231
		$data['refunds'] = array();
232
		foreach ( $object->get_refunds() as $refund ) {
233
			$data['refunds'][] = array(
234
				'id'     => $refund->get_id(),
235
				'reason' => $refund->get_reason() ? $refund->get_reason() : '',
236
				'total'  => '-' . wc_format_decimal( $refund->get_amount(), $this->request['dp'] ),
237
			);
238
		}
239
240
		// Currency symbols.
241
		$currency_symbol         = get_woocommerce_currency_symbol( $data['currency'] );
242
		$data['currency_symbol'] = html_entity_decode( $currency_symbol );
243
244
		return array(
245
			'id'                   => $object->get_id(),
246
			'parent_id'            => $data['parent_id'],
247
			'number'               => $data['number'],
248
			'order_key'            => $data['order_key'],
249
			'created_via'          => $data['created_via'],
250
			'version'              => $data['version'],
251
			'status'               => $data['status'],
252
			'currency'             => $data['currency'],
253
			'currency_symbol'      => $data['currency_symbol'],
254
			'date_created'         => $data['date_created'],
255
			'date_created_gmt'     => $data['date_created_gmt'],
256
			'date_modified'        => $data['date_modified'],
257
			'date_modified_gmt'    => $data['date_modified_gmt'],
258
			'discount_total'       => $data['discount_total'],
259
			'discount_tax'         => $data['discount_tax'],
260
			'shipping_total'       => $data['shipping_total'],
261
			'shipping_tax'         => $data['shipping_tax'],
262
			'cart_tax'             => $data['cart_tax'],
263
			'total'                => $data['total'],
264
			'total_tax'            => $data['total_tax'],
265
			'prices_include_tax'   => $data['prices_include_tax'],
266
			'customer_id'          => $data['customer_id'],
267
			'customer_ip_address'  => $data['customer_ip_address'],
268
			'customer_user_agent'  => $data['customer_user_agent'],
269
			'customer_note'        => $data['customer_note'],
270
			'billing'              => $data['billing'],
271
			'shipping'             => $data['shipping'],
272
			'payment_method'       => $data['payment_method'],
273
			'payment_method_title' => $data['payment_method_title'],
274
			'transaction_id'       => $data['transaction_id'],
275
			'date_paid'            => $data['date_paid'],
276
			'date_paid_gmt'        => $data['date_paid_gmt'],
277
			'date_completed'       => $data['date_completed'],
278
			'date_completed_gmt'   => $data['date_completed_gmt'],
279
			'cart_hash'            => $data['cart_hash'],
280
			'meta_data'            => $data['meta_data'],
281
			'line_items'           => $data['line_items'],
282
			'tax_lines'            => $data['tax_lines'],
283
			'shipping_lines'       => $data['shipping_lines'],
284
			'fee_lines'            => $data['fee_lines'],
285
			'coupon_lines'         => $data['coupon_lines'],
286
			'refunds'              => $data['refunds'],
287
		);
288
	}
289
290
	/**
291
	 * Prepare a single order output for response.
292
	 *
293
	 * @since  3.0.0
294
	 * @param  \WC_Data         $object  Object data.
0 ignored issues
show
Bug introduced by
The type WC_Data 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...
295
	 * @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...
296
	 * @return \WP_REST_Response
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...
297
	 */
298
	public function prepare_object_for_response( $object, $request ) {
299
		$this->request       = $request;
0 ignored issues
show
Documentation Bug introduced by
It seems like $request of type WP_REST_Request is incompatible with the declared type array of property $request.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
300
		$this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] );
301
		$data                = $this->get_formatted_item_data( $object );
302
		$context             = ! empty( $request['context'] ) ? $request['context'] : 'view';
303
		$data                = $this->add_additional_fields_to_object( $data, $request );
304
		$data                = $this->filter_response_by_context( $data, $context );
305
		$response            = rest_ensure_response( $data );
306
		$response->add_links( $this->prepare_links( $object, $request ) );
307
308
		/**
309
		 * Filter the data for a response.
310
		 *
311
		 * The dynamic portion of the hook name, $this->post_type,
312
		 * refers to object type being prepared for the response.
313
		 *
314
		 * @param \WP_REST_Response $response The response object.
315
		 * @param WC_Data          $object   Object data.
316
		 * @param \WP_REST_Request  $request  Request object.
317
		 */
318
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request );
319
	}
320
321
	/**
322
	 * Prepare links for the request.
323
	 *
324
	 * @param WC_Data          $object  Object data.
325
	 * @param \WP_REST_Request $request Request object.
326
	 * @return array                   Links for the given post.
327
	 */
328
	protected function prepare_links( $object, $request ) {
329
		$links = array(
330
			'self'       => array(
331
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ),
332
			),
333
			'collection' => array(
334
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
335
			),
336
		);
337
338
		if ( 0 !== (int) $object->get_customer_id() ) {
339
			$links['customer'] = array(
340
				'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $object->get_customer_id() ) ),
341
			);
342
		}
343
344
		if ( 0 !== (int) $object->get_parent_id() ) {
345
			$links['up'] = array(
346
				'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object->get_parent_id() ) ),
347
			);
348
		}
349
350
		return $links;
351
	}
352
353
	/**
354
	 * Prepare objects query.
355
	 *
356
	 * @since  3.0.0
357
	 * @param  \WP_REST_Request $request Full details about the request.
358
	 * @return array
359
	 */
360
	protected function prepare_objects_query( $request ) {
361
		global $wpdb;
362
363
		// This is needed to get around an array to string notice in \WC_REST_Orders_V2_Controller::prepare_objects_query.
364
		$statuses = $request['status'];
365
		unset( $request['status'] );
366
		$args = parent::prepare_objects_query( $request );
367
368
		$args['post_status'] = array();
369
		foreach ( $statuses as $status ) {
370
			if ( in_array( $status, $this->get_order_statuses(), true ) ) {
371
				$args['post_status'][] = 'wc-' . $status;
372
			} elseif ( 'any' === $status ) {
373
				// Set status to "any" and short-circuit out.
374
				$args['post_status'] = 'any';
375
				break;
376
			} else {
377
				$args['post_status'][] = $status;
378
			}
379
		}
380
381
		// Put the statuses back for further processing (next/prev links, etc).
382
		$request['status'] = $statuses;
383
384
		// Customer.
385
		if ( isset( $request['customer'] ) ) {
386
			if ( ! empty( $args['meta_query'] ) ) {
387
				$args['meta_query'] = array(); // WPCS: slow query ok.
388
			}
389
390
			$args['meta_query'][] = array(
391
				'key'   => '_customer_user',
392
				'value' => $request['customer'],
393
				'type'  => 'NUMERIC',
394
			);
395
		}
396
397
		// Search by product.
398
		if ( ! empty( $request['product'] ) ) {
399
			$order_ids = $wpdb->get_col(
400
				$wpdb->prepare(
401
					"SELECT order_id
402
					FROM {$wpdb->prefix}woocommerce_order_items
403
					WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d )
404
					AND order_item_type = 'line_item'",
405
					$request['product']
406
				)
407
			);
408
409
			// Force WP_Query return empty if don't found any order.
410
			$order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 );
411
412
			$args['post__in'] = $order_ids;
413
		}
414
415
		// Search.
416
		if ( ! empty( $args['s'] ) ) {
417
			$order_ids = wc_order_search( $args['s'] );
418
419
			if ( ! empty( $order_ids ) ) {
420
				unset( $args['s'] );
421
				$args['post__in'] = array_merge( $order_ids, array( 0 ) );
422
			}
423
		}
424
425
		// Search by partial order number.
426
		if ( ! empty( $request['number'] ) ) {
427
			$partial_number = trim( $request['number'] );
428
			$limit          = intval( $args['posts_per_page'] );
429
			$order_ids      = $wpdb->get_col(
430
				$wpdb->prepare(
431
					"SELECT ID
432
					FROM {$wpdb->prefix}posts
433
					WHERE post_type = 'shop_order'
434
					AND ID LIKE %s
435
					LIMIT %d",
436
					$wpdb->esc_like( absint( $partial_number ) ) . '%',
437
					$limit
438
				)
439
			);
440
441
			// Force \WP_Query return empty if don't found any order.
442
			$order_ids        = empty( $order_ids ) ? array( 0 ) : $order_ids;
443
			$args['post__in'] = $order_ids;
444
		}
445
446
		return $args;
447
	}
448
449
	/**
450
	 * Only return writable props from schema.
451
	 *
452
	 * @param  array $schema Schema.
453
	 * @return bool
454
	 */
455
	protected function filter_writable_props( $schema ) {
456
		return empty( $schema['readonly'] );
457
	}
458
459
460
	/**
461
	 * Prepare a single order for create or update.
462
	 *
463
	 * @throws WC_REST_Exception When fails to set any item.
464
	 * @param  \WP_REST_Request $request Request object.
465
	 * @param  bool             $creating If is creating a new object.
466
	 * @return \WP_Error|WC_Data
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...
467
	 */
468
	protected function prepare_object_for_database( $request, $creating = false ) {
469
		$id        = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
470
		$order     = new \WC_Order( $id );
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...
471
		$schema    = $this->get_item_schema();
472
		$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
473
474
		// Handle all writable props.
475
		foreach ( $data_keys as $key ) {
476
			$value = $request[ $key ];
477
478
			if ( ! is_null( $value ) ) {
479
				switch ( $key ) {
480
					case 'coupon_lines':
481
					case 'status':
482
						// Change should be done later so transitions have new data.
483
						break;
484
					case 'billing':
485
					case 'shipping':
486
						$this->update_address( $order, $value, $key );
487
						break;
488
					case 'line_items':
489
					case 'shipping_lines':
490
					case 'fee_lines':
491
						if ( is_array( $value ) ) {
492
							foreach ( $value as $item ) {
493
								if ( is_array( $item ) ) {
494
									if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) {
495
										$order->remove_item( $item['id'] );
496
									} else {
497
										$this->set_item( $order, $key, $item );
498
									}
499
								}
500
							}
501
						}
502
						break;
503
					case 'meta_data':
504
						if ( is_array( $value ) ) {
505
							foreach ( $value as $meta ) {
506
								$order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
507
							}
508
						}
509
						break;
510
					default:
511
						if ( is_callable( array( $order, "set_{$key}" ) ) ) {
512
							$order->{"set_{$key}"}( $value );
513
						}
514
						break;
515
				}
516
			}
517
		}
518
519
		/**
520
		 * Filters an object before it is inserted via the REST API.
521
		 *
522
		 * The dynamic portion of the hook name, `$this->post_type`,
523
		 * refers to the object type slug.
524
		 *
525
		 * @param WC_Data         $order    Object object.
526
		 * @param \WP_REST_Request $request  Request object.
527
		 * @param bool            $creating If is creating a new object.
528
		 */
529
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $order, $request, $creating );
530
	}
531
532
	/**
533
	 * Save an object data.
534
	 *	 *
535
	 * @param  \WP_REST_Request $request  Full details about the request.
536
	 * @param  bool             $creating If is creating a new object.
537
	 * @return WC_Data|\WP_Error
538
	 * @throws \WC_REST_Exception But all errors are validated before returning any data.
539
	 */
540
	protected function save_object( $request, $creating = false ) {
541
		try {
542
			$object = $this->prepare_object_for_database( $request, $creating );
543
544
			if ( is_wp_error( $object ) ) {
545
				return $object;
546
			}
547
548
			// Make sure gateways are loaded so hooks from gateways fire on save/create.
549
			WC()->payment_gateways();
550
551
			if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] ) {
552
				// Make sure customer exists.
553
				if ( false === get_user_by( 'id', $request['customer_id'] ) ) {
554
					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...
555
				}
556
557
				// Make sure customer is part of blog.
558
				if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) {
559
					add_user_to_blog( get_current_blog_id(), $request['customer_id'], 'customer' );
560
				}
561
			}
562
563
			if ( $creating ) {
564
				$object->set_created_via( 'rest-api' );
565
				$object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
566
				$object->calculate_totals();
567
			} else {
568
				// If items have changed, recalculate order totals.
569
				if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) {
570
					$object->calculate_totals( true );
571
				}
572
			}
573
574
			// Set coupons.
575
			$this->calculate_coupons( $request, $object );
576
577
			// Set status.
578
			if ( ! empty( $request['status'] ) ) {
579
				$object->set_status( $request['status'] );
580
			}
581
582
			$object->save();
583
584
			// Actions for after the order is saved.
585
			if ( true === $request['set_paid'] ) {
586
				if ( $creating || $object->needs_payment() ) {
587
					$object->payment_complete( $request['transaction_id'] );
588
				}
589
			}
590
591
			return $this->get_object( $object->get_id() );
592
		} 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...
593
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
594
		} catch ( \WC_REST_Exception $e ) {
595
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
596
		}
597
	}
598
599
	/**
600
	 * Update address.
601
	 *
602
	 * @param WC_Order $order  Order data.
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Controllers\Version4\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...
603
	 * @param array    $posted Posted data.
604
	 * @param string   $type   Address type.
605
	 */
606
	protected function update_address( $order, $posted, $type = 'billing' ) {
607
		foreach ( $posted as $key => $value ) {
608
			if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) {
609
				$order->{"set_{$type}_{$key}"}( $value );
610
			}
611
		}
612
	}
613
614
	/**
615
	 * Gets the product ID from the SKU or posted ID.
616
	 *
617
	 * @param array $posted Request data.
618
	 * @return int
619
	 * @throws \WC_REST_Exception When SKU or ID is not valid.
620
	 */
621
	protected function get_product_id( $posted ) {
622
		if ( ! empty( $posted['sku'] ) ) {
623
			$product_id = (int) wc_get_product_id_by_sku( $posted['sku'] );
624
		} elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) {
625
			$product_id = (int) $posted['product_id'];
626
		} elseif ( ! empty( $posted['variation_id'] ) ) {
627
			$product_id = (int) $posted['variation_id'];
628
		} else {
629
			throw new \WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 );
630
		}
631
		return $product_id;
632
	}
633
634
	/**
635
	 * Maybe set an item prop if the value was posted.
636
	 *
637
	 * @param WC_Order_Item $item   Order item.
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...\Version4\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...
638
	 * @param string        $prop   Order property.
639
	 * @param array         $posted Request data.
640
	 */
641
	protected function maybe_set_item_prop( $item, $prop, $posted ) {
642
		if ( isset( $posted[ $prop ] ) ) {
643
			$item->{"set_$prop"}( $posted[ $prop ] );
644
		}
645
	}
646
647
	/**
648
	 * Maybe set item props if the values were posted.
649
	 *
650
	 * @param WC_Order_Item $item   Order item data.
651
	 * @param string[]      $props  Properties.
652
	 * @param array         $posted Request data.
653
	 */
654
	protected function maybe_set_item_props( $item, $props, $posted ) {
655
		foreach ( $props as $prop ) {
656
			$this->maybe_set_item_prop( $item, $prop, $posted );
657
		}
658
	}
659
660
	/**
661
	 * Maybe set item meta if posted.
662
	 *
663
	 * @param WC_Order_Item $item   Order item data.
664
	 * @param array         $posted Request data.
665
	 */
666
	protected function maybe_set_item_meta_data( $item, $posted ) {
667
		if ( ! empty( $posted['meta_data'] ) && is_array( $posted['meta_data'] ) ) {
668
			foreach ( $posted['meta_data'] as $meta ) {
669
				if ( isset( $meta['key'] ) ) {
670
					$value = isset( $meta['value'] ) ? $meta['value'] : null;
671
					$item->update_meta_data( $meta['key'], $value, isset( $meta['id'] ) ? $meta['id'] : '' );
672
				}
673
			}
674
		}
675
	}
676
677
	/**
678
	 * Create or update a line item.
679
	 *
680
	 * @param array  $posted Line item data.
681
	 * @param string $action 'create' to add line item or 'update' to update it.
682
	 * @param object $item Passed when updating an item. Null during creation.
683
	 * @return WC_Order_Item_Product
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...4\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...
684
	 * @throws WC_REST_Exception Invalid data, server error.
685
	 */
686
	protected function prepare_line_items( $posted, $action = 'create', $item = null ) {
687
		$item    = is_null( $item ) ? new \WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item;
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...
688
		$product = wc_get_product( $this->get_product_id( $posted ) );
689
690
		if ( $product !== $item->get_product() ) {
691
			$item->set_product( $product );
692
693
			if ( 'create' === $action ) {
694
				$quantity = isset( $posted['quantity'] ) ? $posted['quantity'] : 1;
695
				$total    = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) );
696
				$item->set_total( $total );
697
				$item->set_subtotal( $total );
698
			}
699
		}
700
701
		$this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted );
702
		$this->maybe_set_item_meta_data( $item, $posted );
703
704
		return $item;
705
	}
706
707
	/**
708
	 * Create or update an order shipping method.
709
	 *
710
	 * @param array  $posted $shipping Item data.
711
	 * @param string $action 'create' to add shipping or 'update' to update it.
712
	 * @param object $item Passed when updating an item. Null during creation.
713
	 * @return WC_Order_Item_Shipping
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...\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...
714
	 * @throws WC_REST_Exception Invalid data, server error.
715
	 */
716
	protected function prepare_shipping_lines( $posted, $action = 'create', $item = null ) {
717
		$item = is_null( $item ) ? new \WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item;
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...
718
719
		if ( 'create' === $action ) {
720
			if ( empty( $posted['method_id'] ) ) {
721
				throw new \WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 );
722
			}
723
		}
724
725
		$this->maybe_set_item_props( $item, array( 'method_id', 'method_title', 'total' ), $posted );
726
		$this->maybe_set_item_meta_data( $item, $posted );
727
728
		return $item;
729
	}
730
731
	/**
732
	 * Create or update an order fee.
733
	 *
734
	 * @param array  $posted Item data.
735
	 * @param string $action 'create' to add fee or 'update' to update it.
736
	 * @param object $item Passed when updating an item. Null during creation.
737
	 * @return WC_Order_Item_Fee
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...sion4\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...
738
	 * @throws WC_REST_Exception Invalid data, server error.
739
	 */
740
	protected function prepare_fee_lines( $posted, $action = 'create', $item = null ) {
741
		$item = is_null( $item ) ? new \WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item;
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...
742
743
		if ( 'create' === $action ) {
744
			if ( empty( $posted['name'] ) ) {
745
				throw new \WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 );
746
			}
747
		}
748
749
		$this->maybe_set_item_props( $item, array( 'name', 'tax_class', 'tax_status', 'total' ), $posted );
750
		$this->maybe_set_item_meta_data( $item, $posted );
751
752
		return $item;
753
	}
754
755
	/**
756
	 * Create or update an order coupon.
757
	 *
758
	 * @param array  $posted Item data.
759
	 * @param string $action 'create' to add coupon or 'update' to update it.
760
	 * @param object $item Passed when updating an item. Null during creation.
761
	 * @return WC_Order_Item_Coupon
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...n4\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...
762
	 * @throws WC_REST_Exception Invalid data, server error.
763
	 */
764
	protected function prepare_coupon_lines( $posted, $action = 'create', $item = null ) {
765
		$item = is_null( $item ) ? new \WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item;
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...
766
767
		if ( 'create' === $action ) {
768
			if ( empty( $posted['code'] ) ) {
769
				throw new \WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
770
			}
771
		}
772
773
		$this->maybe_set_item_props( $item, array( 'code', 'discount' ), $posted );
774
		$this->maybe_set_item_meta_data( $item, $posted );
775
776
		return $item;
777
	}
778
779
	/**
780
	 * Wrapper method to create/update order items.
781
	 * When updating, the item ID provided is checked to ensure it is associated
782
	 * with the order.
783
	 *
784
	 * @param WC_Order $order order object.
785
	 * @param string   $item_type The item type.
786
	 * @param array    $posted item provided in the request body.
787
	 * @throws WC_REST_Exception If item ID is not associated with order.
788
	 */
789
	protected function set_item( $order, $item_type, $posted ) {
790
		global $wpdb;
791
792
		if ( ! empty( $posted['id'] ) ) {
793
			$action = 'update';
794
		} else {
795
			$action = 'create';
796
		}
797
798
		$method = 'prepare_' . $item_type;
799
		$item   = null;
800
801
		// Verify provided line item ID is associated with order.
802
		if ( 'update' === $action ) {
803
			$item = $order->get_item( absint( $posted['id'] ), false );
804
805
			if ( ! $item ) {
806
				throw new \WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 );
807
			}
808
		}
809
810
		// Prepare item data.
811
		$item = $this->$method( $posted, $action, $item );
812
813
		do_action( 'woocommerce_rest_set_order_item', $item, $posted );
814
815
		// If creating the order, add the item to it.
816
		if ( 'create' === $action ) {
817
			$order->add_item( $item );
818
		} else {
819
			$item->save();
820
		}
821
	}
822
823
	/**
824
	 * Helper method to check if the resource ID associated with the provided item is null.
825
	 * Items can be deleted by setting the resource ID to null.
826
	 *
827
	 * @param array $item Item provided in the request body.
828
	 * @return bool True if the item resource ID is null, false otherwise.
829
	 */
830
	protected function item_is_null( $item ) {
831
		$keys = array( 'product_id', 'method_id', 'method_title', 'name', 'code' );
832
833
		foreach ( $keys as $key ) {
834
			if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) {
835
				return true;
836
			}
837
		}
838
839
		return false;
840
	}
841
842
	/**
843
	 * Get order statuses without prefixes.
844
	 *
845
	 * @return array
846
	 */
847
	protected function get_order_statuses() {
848
		$order_statuses = array();
849
850
		foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
851
			$order_statuses[] = str_replace( 'wc-', '', $status );
852
		}
853
854
		return $order_statuses;
855
	}
856
857
	/**
858
	 * Get the Order's schema, conforming to JSON Schema.
859
	 *
860
	 * @return array
861
	 */
862
	public function get_item_schema() {
863
		$schema = array(
864
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
865
			'title'      => $this->post_type,
866
			'type'       => 'object',
867
			'properties' => array(
868
				'id'                   => array(
869
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
870
					'type'        => 'integer',
871
					'context'     => array( 'view', 'edit' ),
872
					'readonly'    => true,
873
				),
874
				'parent_id'            => array(
875
					'description' => __( 'Parent order ID.', 'woocommerce' ),
876
					'type'        => 'integer',
877
					'context'     => array( 'view', 'edit' ),
878
				),
879
				'number'               => array(
880
					'description' => __( 'Order number.', 'woocommerce' ),
881
					'type'        => 'string',
882
					'context'     => array( 'view', 'edit' ),
883
					'readonly'    => true,
884
				),
885
				'order_key'            => array(
886
					'description' => __( 'Order key.', 'woocommerce' ),
887
					'type'        => 'string',
888
					'context'     => array( 'view', 'edit' ),
889
					'readonly'    => true,
890
				),
891
				'created_via'          => array(
892
					'description' => __( 'Shows where the order was created.', 'woocommerce' ),
893
					'type'        => 'string',
894
					'context'     => array( 'view', 'edit' ),
895
					'readonly'    => true,
896
				),
897
				'version'              => array(
898
					'description' => __( 'Version of WooCommerce which last updated the order.', 'woocommerce' ),
899
					'type'        => 'integer',
900
					'context'     => array( 'view', 'edit' ),
901
					'readonly'    => true,
902
				),
903
				'status'               => array(
904
					'description' => __( 'Order status.', 'woocommerce' ),
905
					'type'        => 'string',
906
					'default'     => 'pending',
907
					'enum'        => $this->get_order_statuses(),
908
					'context'     => array( 'view', 'edit' ),
909
				),
910
				'currency'             => array(
911
					'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ),
912
					'type'        => 'string',
913
					'default'     => get_woocommerce_currency(),
914
					'enum'        => array_keys( get_woocommerce_currencies() ),
915
					'context'     => array( 'view', 'edit' ),
916
				),
917
				'currency_symbol'      => array(
918
					'description' => __( 'Currency symbol.', 'woocommerce' ),
919
					'type'        => 'string',
920
					'context'     => array( 'view', 'edit' ),
921
					'readonly'    => true,
922
				),
923
				'date_created'         => array(
924
					'description' => __( "The date the order was created, in the site's timezone.", 'woocommerce' ),
925
					'type'        => 'date-time',
926
					'context'     => array( 'view', 'edit' ),
927
					'readonly'    => true,
928
				),
929
				'date_created_gmt'     => array(
930
					'description' => __( 'The date the order was created, as GMT.', 'woocommerce' ),
931
					'type'        => 'date-time',
932
					'context'     => array( 'view', 'edit' ),
933
					'readonly'    => true,
934
				),
935
				'date_modified'        => array(
936
					'description' => __( "The date the order was last modified, in the site's timezone.", 'woocommerce' ),
937
					'type'        => 'date-time',
938
					'context'     => array( 'view', 'edit' ),
939
					'readonly'    => true,
940
				),
941
				'date_modified_gmt'    => array(
942
					'description' => __( 'The date the order was last modified, as GMT.', 'woocommerce' ),
943
					'type'        => 'date-time',
944
					'context'     => array( 'view', 'edit' ),
945
					'readonly'    => true,
946
				),
947
				'discount_total'       => array(
948
					'description' => __( 'Total discount amount for the order.', 'woocommerce' ),
949
					'type'        => 'string',
950
					'context'     => array( 'view', 'edit' ),
951
					'readonly'    => true,
952
				),
953
				'discount_tax'         => array(
954
					'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ),
955
					'type'        => 'string',
956
					'context'     => array( 'view', 'edit' ),
957
					'readonly'    => true,
958
				),
959
				'shipping_total'       => array(
960
					'description' => __( 'Total shipping amount for the order.', 'woocommerce' ),
961
					'type'        => 'string',
962
					'context'     => array( 'view', 'edit' ),
963
					'readonly'    => true,
964
				),
965
				'shipping_tax'         => array(
966
					'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ),
967
					'type'        => 'string',
968
					'context'     => array( 'view', 'edit' ),
969
					'readonly'    => true,
970
				),
971
				'cart_tax'             => array(
972
					'description' => __( 'Sum of line item taxes only.', 'woocommerce' ),
973
					'type'        => 'string',
974
					'context'     => array( 'view', 'edit' ),
975
					'readonly'    => true,
976
				),
977
				'total'                => array(
978
					'description' => __( 'Grand total.', 'woocommerce' ),
979
					'type'        => 'string',
980
					'context'     => array( 'view', 'edit' ),
981
					'readonly'    => true,
982
				),
983
				'total_tax'            => array(
984
					'description' => __( 'Sum of all taxes.', 'woocommerce' ),
985
					'type'        => 'string',
986
					'context'     => array( 'view', 'edit' ),
987
					'readonly'    => true,
988
				),
989
				'prices_include_tax'   => array(
990
					'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ),
991
					'type'        => 'boolean',
992
					'context'     => array( 'view', 'edit' ),
993
					'readonly'    => true,
994
				),
995
				'customer_id'          => array(
996
					'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ),
997
					'type'        => 'integer',
998
					'default'     => 0,
999
					'context'     => array( 'view', 'edit' ),
1000
				),
1001
				'customer_ip_address'  => array(
1002
					'description' => __( "Customer's IP address.", 'woocommerce' ),
1003
					'type'        => 'string',
1004
					'context'     => array( 'view', 'edit' ),
1005
					'readonly'    => true,
1006
				),
1007
				'customer_user_agent'  => array(
1008
					'description' => __( 'User agent of the customer.', 'woocommerce' ),
1009
					'type'        => 'string',
1010
					'context'     => array( 'view', 'edit' ),
1011
					'readonly'    => true,
1012
				),
1013
				'customer_note'        => array(
1014
					'description' => __( 'Note left by customer during checkout.', 'woocommerce' ),
1015
					'type'        => 'string',
1016
					'context'     => array( 'view', 'edit' ),
1017
				),
1018
				'billing'              => array(
1019
					'description' => __( 'Billing address.', 'woocommerce' ),
1020
					'type'        => 'object',
1021
					'context'     => array( 'view', 'edit' ),
1022
					'properties'  => array(
1023
						'first_name' => array(
1024
							'description' => __( 'First name.', 'woocommerce' ),
1025
							'type'        => 'string',
1026
							'context'     => array( 'view', 'edit' ),
1027
						),
1028
						'last_name'  => array(
1029
							'description' => __( 'Last name.', 'woocommerce' ),
1030
							'type'        => 'string',
1031
							'context'     => array( 'view', 'edit' ),
1032
						),
1033
						'company'    => array(
1034
							'description' => __( 'Company name.', 'woocommerce' ),
1035
							'type'        => 'string',
1036
							'context'     => array( 'view', 'edit' ),
1037
						),
1038
						'address_1'  => array(
1039
							'description' => __( 'Address line 1', 'woocommerce' ),
1040
							'type'        => 'string',
1041
							'context'     => array( 'view', 'edit' ),
1042
						),
1043
						'address_2'  => array(
1044
							'description' => __( 'Address line 2', 'woocommerce' ),
1045
							'type'        => 'string',
1046
							'context'     => array( 'view', 'edit' ),
1047
						),
1048
						'city'       => array(
1049
							'description' => __( 'City name.', 'woocommerce' ),
1050
							'type'        => 'string',
1051
							'context'     => array( 'view', 'edit' ),
1052
						),
1053
						'state'      => array(
1054
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1055
							'type'        => 'string',
1056
							'context'     => array( 'view', 'edit' ),
1057
						),
1058
						'postcode'   => array(
1059
							'description' => __( 'Postal code.', 'woocommerce' ),
1060
							'type'        => 'string',
1061
							'context'     => array( 'view', 'edit' ),
1062
						),
1063
						'country'    => array(
1064
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1065
							'type'        => 'string',
1066
							'context'     => array( 'view', 'edit' ),
1067
						),
1068
						'email'      => array(
1069
							'description' => __( 'Email address.', 'woocommerce' ),
1070
							'type'        => 'string',
1071
							'format'      => 'email',
1072
							'context'     => array( 'view', 'edit' ),
1073
						),
1074
						'phone'      => array(
1075
							'description' => __( 'Phone number.', 'woocommerce' ),
1076
							'type'        => 'string',
1077
							'context'     => array( 'view', 'edit' ),
1078
						),
1079
					),
1080
				),
1081
				'shipping'             => array(
1082
					'description' => __( 'Shipping address.', 'woocommerce' ),
1083
					'type'        => 'object',
1084
					'context'     => array( 'view', 'edit' ),
1085
					'properties'  => array(
1086
						'first_name' => array(
1087
							'description' => __( 'First name.', 'woocommerce' ),
1088
							'type'        => 'string',
1089
							'context'     => array( 'view', 'edit' ),
1090
						),
1091
						'last_name'  => array(
1092
							'description' => __( 'Last name.', 'woocommerce' ),
1093
							'type'        => 'string',
1094
							'context'     => array( 'view', 'edit' ),
1095
						),
1096
						'company'    => array(
1097
							'description' => __( 'Company name.', 'woocommerce' ),
1098
							'type'        => 'string',
1099
							'context'     => array( 'view', 'edit' ),
1100
						),
1101
						'address_1'  => array(
1102
							'description' => __( 'Address line 1', 'woocommerce' ),
1103
							'type'        => 'string',
1104
							'context'     => array( 'view', 'edit' ),
1105
						),
1106
						'address_2'  => array(
1107
							'description' => __( 'Address line 2', 'woocommerce' ),
1108
							'type'        => 'string',
1109
							'context'     => array( 'view', 'edit' ),
1110
						),
1111
						'city'       => array(
1112
							'description' => __( 'City name.', 'woocommerce' ),
1113
							'type'        => 'string',
1114
							'context'     => array( 'view', 'edit' ),
1115
						),
1116
						'state'      => array(
1117
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1118
							'type'        => 'string',
1119
							'context'     => array( 'view', 'edit' ),
1120
						),
1121
						'postcode'   => array(
1122
							'description' => __( 'Postal code.', 'woocommerce' ),
1123
							'type'        => 'string',
1124
							'context'     => array( 'view', 'edit' ),
1125
						),
1126
						'country'    => array(
1127
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1128
							'type'        => 'string',
1129
							'context'     => array( 'view', 'edit' ),
1130
						),
1131
					),
1132
				),
1133
				'payment_method'       => array(
1134
					'description' => __( 'Payment method ID.', 'woocommerce' ),
1135
					'type'        => 'string',
1136
					'context'     => array( 'view', 'edit' ),
1137
				),
1138
				'payment_method_title' => array(
1139
					'description' => __( 'Payment method title.', 'woocommerce' ),
1140
					'type'        => 'string',
1141
					'context'     => array( 'view', 'edit' ),
1142
					'arg_options' => array(
1143
						'sanitize_callback' => 'sanitize_text_field',
1144
					),
1145
				),
1146
				'transaction_id'       => array(
1147
					'description' => __( 'Unique transaction ID.', 'woocommerce' ),
1148
					'type'        => 'string',
1149
					'context'     => array( 'view', 'edit' ),
1150
				),
1151
				'date_paid'            => array(
1152
					'description' => __( "The date the order was paid, in the site's timezone.", 'woocommerce' ),
1153
					'type'        => 'date-time',
1154
					'context'     => array( 'view', 'edit' ),
1155
					'readonly'    => true,
1156
				),
1157
				'date_paid_gmt'        => array(
1158
					'description' => __( 'The date the order was paid, as GMT.', 'woocommerce' ),
1159
					'type'        => 'date-time',
1160
					'context'     => array( 'view', 'edit' ),
1161
					'readonly'    => true,
1162
				),
1163
				'date_completed'       => array(
1164
					'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ),
1165
					'type'        => 'date-time',
1166
					'context'     => array( 'view', 'edit' ),
1167
					'readonly'    => true,
1168
				),
1169
				'date_completed_gmt'   => array(
1170
					'description' => __( 'The date the order was completed, as GMT.', 'woocommerce' ),
1171
					'type'        => 'date-time',
1172
					'context'     => array( 'view', 'edit' ),
1173
					'readonly'    => true,
1174
				),
1175
				'cart_hash'            => array(
1176
					'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ),
1177
					'type'        => 'string',
1178
					'context'     => array( 'view', 'edit' ),
1179
					'readonly'    => true,
1180
				),
1181
				'meta_data'            => array(
1182
					'description' => __( 'Meta data.', 'woocommerce' ),
1183
					'type'        => 'array',
1184
					'context'     => array( 'view', 'edit' ),
1185
					'items'       => array(
1186
						'type'       => 'object',
1187
						'properties' => array(
1188
							'id'    => array(
1189
								'description' => __( 'Meta ID.', 'woocommerce' ),
1190
								'type'        => 'integer',
1191
								'context'     => array( 'view', 'edit' ),
1192
								'readonly'    => true,
1193
							),
1194
							'key'   => array(
1195
								'description' => __( 'Meta key.', 'woocommerce' ),
1196
								'type'        => 'string',
1197
								'context'     => array( 'view', 'edit' ),
1198
							),
1199
							'value' => array(
1200
								'description' => __( 'Meta value.', 'woocommerce' ),
1201
								'type'        => 'mixed',
1202
								'context'     => array( 'view', 'edit' ),
1203
							),
1204
						),
1205
					),
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
							),
1225
							'product_id'   => array(
1226
								'description' => __( 'Product ID.', 'woocommerce' ),
1227
								'type'        => 'mixed',
1228
								'context'     => array( 'view', 'edit' ),
1229
							),
1230
							'variation_id' => array(
1231
								'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
1232
								'type'        => 'integer',
1233
								'context'     => array( 'view', 'edit' ),
1234
							),
1235
							'quantity'     => array(
1236
								'description' => __( 'Quantity ordered.', 'woocommerce' ),
1237
								'type'        => 'integer',
1238
								'context'     => array( 'view', 'edit' ),
1239
							),
1240
							'tax_class'    => array(
1241
								'description' => __( 'Tax class of product.', 'woocommerce' ),
1242
								'type'        => 'string',
1243
								'context'     => array( 'view', 'edit' ),
1244
							),
1245
							'subtotal'     => array(
1246
								'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
1247
								'type'        => 'string',
1248
								'context'     => array( 'view', 'edit' ),
1249
							),
1250
							'subtotal_tax' => array(
1251
								'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
1252
								'type'        => 'string',
1253
								'context'     => array( 'view', 'edit' ),
1254
								'readonly'    => true,
1255
							),
1256
							'total'        => array(
1257
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1258
								'type'        => 'string',
1259
								'context'     => array( 'view', 'edit' ),
1260
							),
1261
							'total_tax'    => array(
1262
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1263
								'type'        => 'string',
1264
								'context'     => array( 'view', 'edit' ),
1265
								'readonly'    => true,
1266
							),
1267
							'taxes'        => array(
1268
								'description' => __( 'Line taxes.', 'woocommerce' ),
1269
								'type'        => 'array',
1270
								'context'     => array( 'view', 'edit' ),
1271
								'readonly'    => true,
1272
								'items'       => array(
1273
									'type'       => 'object',
1274
									'properties' => array(
1275
										'id'       => array(
1276
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1277
											'type'        => 'integer',
1278
											'context'     => array( 'view', 'edit' ),
1279
										),
1280
										'total'    => array(
1281
											'description' => __( 'Tax total.', 'woocommerce' ),
1282
											'type'        => 'string',
1283
											'context'     => array( 'view', 'edit' ),
1284
										),
1285
										'subtotal' => array(
1286
											'description' => __( 'Tax subtotal.', 'woocommerce' ),
1287
											'type'        => 'string',
1288
											'context'     => array( 'view', 'edit' ),
1289
										),
1290
									),
1291
								),
1292
							),
1293
							'meta_data'    => array(
1294
								'description' => __( 'Meta data.', 'woocommerce' ),
1295
								'type'        => 'array',
1296
								'context'     => array( 'view', 'edit' ),
1297
								'items'       => array(
1298
									'type'       => 'object',
1299
									'properties' => array(
1300
										'id'    => array(
1301
											'description' => __( 'Meta ID.', 'woocommerce' ),
1302
											'type'        => 'integer',
1303
											'context'     => array( 'view', 'edit' ),
1304
											'readonly'    => true,
1305
										),
1306
										'key'   => array(
1307
											'description' => __( 'Meta key.', 'woocommerce' ),
1308
											'type'        => 'string',
1309
											'context'     => array( 'view', 'edit' ),
1310
										),
1311
										'value' => array(
1312
											'description' => __( 'Meta value.', 'woocommerce' ),
1313
											'type'        => 'mixed',
1314
											'context'     => array( 'view', 'edit' ),
1315
										),
1316
									),
1317
								),
1318
							),
1319
							'sku'          => array(
1320
								'description' => __( 'Product SKU.', 'woocommerce' ),
1321
								'type'        => 'string',
1322
								'context'     => array( 'view', 'edit' ),
1323
								'readonly'    => true,
1324
							),
1325
							'price'        => array(
1326
								'description' => __( 'Product price.', 'woocommerce' ),
1327
								'type'        => 'number',
1328
								'context'     => array( 'view', 'edit' ),
1329
								'readonly'    => true,
1330
							),
1331
						),
1332
					),
1333
				),
1334
				'tax_lines'            => array(
1335
					'description' => __( 'Tax lines data.', 'woocommerce' ),
1336
					'type'        => 'array',
1337
					'context'     => array( 'view', 'edit' ),
1338
					'readonly'    => true,
1339
					'items'       => array(
1340
						'type'       => 'object',
1341
						'properties' => array(
1342
							'id'                 => array(
1343
								'description' => __( 'Item ID.', 'woocommerce' ),
1344
								'type'        => 'integer',
1345
								'context'     => array( 'view', 'edit' ),
1346
								'readonly'    => true,
1347
							),
1348
							'rate_code'          => array(
1349
								'description' => __( 'Tax rate code.', 'woocommerce' ),
1350
								'type'        => 'string',
1351
								'context'     => array( 'view', 'edit' ),
1352
								'readonly'    => true,
1353
							),
1354
							'rate_id'            => array(
1355
								'description' => __( 'Tax rate ID.', 'woocommerce' ),
1356
								'type'        => 'string',
1357
								'context'     => array( 'view', 'edit' ),
1358
								'readonly'    => true,
1359
							),
1360
							'label'              => array(
1361
								'description' => __( 'Tax rate label.', 'woocommerce' ),
1362
								'type'        => 'string',
1363
								'context'     => array( 'view', 'edit' ),
1364
								'readonly'    => true,
1365
							),
1366
							'compound'           => array(
1367
								'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ),
1368
								'type'        => 'boolean',
1369
								'context'     => array( 'view', 'edit' ),
1370
								'readonly'    => true,
1371
							),
1372
							'tax_total'          => array(
1373
								'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ),
1374
								'type'        => 'string',
1375
								'context'     => array( 'view', 'edit' ),
1376
								'readonly'    => true,
1377
							),
1378
							'shipping_tax_total' => array(
1379
								'description' => __( 'Shipping tax total.', 'woocommerce' ),
1380
								'type'        => 'string',
1381
								'context'     => array( 'view', 'edit' ),
1382
								'readonly'    => true,
1383
							),
1384
							'meta_data'          => array(
1385
								'description' => __( 'Meta data.', 'woocommerce' ),
1386
								'type'        => 'array',
1387
								'context'     => array( 'view', 'edit' ),
1388
								'items'       => array(
1389
									'type'       => 'object',
1390
									'properties' => array(
1391
										'id'    => array(
1392
											'description' => __( 'Meta ID.', 'woocommerce' ),
1393
											'type'        => 'integer',
1394
											'context'     => array( 'view', 'edit' ),
1395
											'readonly'    => true,
1396
										),
1397
										'key'   => array(
1398
											'description' => __( 'Meta key.', 'woocommerce' ),
1399
											'type'        => 'string',
1400
											'context'     => array( 'view', 'edit' ),
1401
										),
1402
										'value' => array(
1403
											'description' => __( 'Meta value.', 'woocommerce' ),
1404
											'type'        => 'mixed',
1405
											'context'     => array( 'view', 'edit' ),
1406
										),
1407
									),
1408
								),
1409
							),
1410
						),
1411
					),
1412
				),
1413
				'shipping_lines'       => array(
1414
					'description' => __( 'Shipping lines data.', 'woocommerce' ),
1415
					'type'        => 'array',
1416
					'context'     => array( 'view', 'edit' ),
1417
					'items'       => array(
1418
						'type'       => 'object',
1419
						'properties' => array(
1420
							'id'           => array(
1421
								'description' => __( 'Item ID.', 'woocommerce' ),
1422
								'type'        => 'integer',
1423
								'context'     => array( 'view', 'edit' ),
1424
								'readonly'    => true,
1425
							),
1426
							'method_title' => array(
1427
								'description' => __( 'Shipping method name.', 'woocommerce' ),
1428
								'type'        => 'mixed',
1429
								'context'     => array( 'view', 'edit' ),
1430
							),
1431
							'method_id'    => array(
1432
								'description' => __( 'Shipping method ID.', 'woocommerce' ),
1433
								'type'        => 'mixed',
1434
								'context'     => array( 'view', 'edit' ),
1435
							),
1436
							'instance_id'  => array(
1437
								'description' => __( 'Shipping instance ID.', 'woocommerce' ),
1438
								'type'        => 'string',
1439
								'context'     => array( 'view', 'edit' ),
1440
							),
1441
							'total'        => array(
1442
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1443
								'type'        => 'string',
1444
								'context'     => array( 'view', 'edit' ),
1445
							),
1446
							'total_tax'    => array(
1447
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1448
								'type'        => 'string',
1449
								'context'     => array( 'view', 'edit' ),
1450
								'readonly'    => true,
1451
							),
1452
							'taxes'        => array(
1453
								'description' => __( 'Line taxes.', 'woocommerce' ),
1454
								'type'        => 'array',
1455
								'context'     => array( 'view', 'edit' ),
1456
								'readonly'    => true,
1457
								'items'       => array(
1458
									'type'       => 'object',
1459
									'properties' => array(
1460
										'id'    => array(
1461
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1462
											'type'        => 'integer',
1463
											'context'     => array( 'view', 'edit' ),
1464
											'readonly'    => true,
1465
										),
1466
										'total' => array(
1467
											'description' => __( 'Tax total.', 'woocommerce' ),
1468
											'type'        => 'string',
1469
											'context'     => array( 'view', 'edit' ),
1470
											'readonly'    => true,
1471
										),
1472
									),
1473
								),
1474
							),
1475
							'meta_data'    => array(
1476
								'description' => __( 'Meta data.', 'woocommerce' ),
1477
								'type'        => 'array',
1478
								'context'     => array( 'view', 'edit' ),
1479
								'items'       => array(
1480
									'type'       => 'object',
1481
									'properties' => array(
1482
										'id'    => array(
1483
											'description' => __( 'Meta ID.', 'woocommerce' ),
1484
											'type'        => 'integer',
1485
											'context'     => array( 'view', 'edit' ),
1486
											'readonly'    => true,
1487
										),
1488
										'key'   => array(
1489
											'description' => __( 'Meta key.', 'woocommerce' ),
1490
											'type'        => 'string',
1491
											'context'     => array( 'view', 'edit' ),
1492
										),
1493
										'value' => array(
1494
											'description' => __( 'Meta value.', 'woocommerce' ),
1495
											'type'        => 'mixed',
1496
											'context'     => array( 'view', 'edit' ),
1497
										),
1498
									),
1499
								),
1500
							),
1501
						),
1502
					),
1503
				),
1504
				'fee_lines'            => array(
1505
					'description' => __( 'Fee lines data.', 'woocommerce' ),
1506
					'type'        => 'array',
1507
					'context'     => array( 'view', 'edit' ),
1508
					'items'       => array(
1509
						'type'       => 'object',
1510
						'properties' => array(
1511
							'id'         => array(
1512
								'description' => __( 'Item ID.', 'woocommerce' ),
1513
								'type'        => 'integer',
1514
								'context'     => array( 'view', 'edit' ),
1515
								'readonly'    => true,
1516
							),
1517
							'name'       => array(
1518
								'description' => __( 'Fee name.', 'woocommerce' ),
1519
								'type'        => 'mixed',
1520
								'context'     => array( 'view', 'edit' ),
1521
							),
1522
							'tax_class'  => array(
1523
								'description' => __( 'Tax class of fee.', 'woocommerce' ),
1524
								'type'        => 'string',
1525
								'context'     => array( 'view', 'edit' ),
1526
							),
1527
							'tax_status' => array(
1528
								'description' => __( 'Tax status of fee.', 'woocommerce' ),
1529
								'type'        => 'string',
1530
								'context'     => array( 'view', 'edit' ),
1531
								'enum'        => array( 'taxable', 'none' ),
1532
							),
1533
							'total'      => array(
1534
								'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1535
								'type'        => 'string',
1536
								'context'     => array( 'view', 'edit' ),
1537
							),
1538
							'total_tax'  => array(
1539
								'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1540
								'type'        => 'string',
1541
								'context'     => array( 'view', 'edit' ),
1542
								'readonly'    => true,
1543
							),
1544
							'taxes'      => array(
1545
								'description' => __( 'Line taxes.', 'woocommerce' ),
1546
								'type'        => 'array',
1547
								'context'     => array( 'view', 'edit' ),
1548
								'readonly'    => true,
1549
								'items'       => array(
1550
									'type'       => 'object',
1551
									'properties' => array(
1552
										'id'       => array(
1553
											'description' => __( 'Tax rate ID.', 'woocommerce' ),
1554
											'type'        => 'integer',
1555
											'context'     => array( 'view', 'edit' ),
1556
											'readonly'    => true,
1557
										),
1558
										'total'    => array(
1559
											'description' => __( 'Tax total.', 'woocommerce' ),
1560
											'type'        => 'string',
1561
											'context'     => array( 'view', 'edit' ),
1562
											'readonly'    => true,
1563
										),
1564
										'subtotal' => array(
1565
											'description' => __( 'Tax subtotal.', 'woocommerce' ),
1566
											'type'        => 'string',
1567
											'context'     => array( 'view', 'edit' ),
1568
											'readonly'    => true,
1569
										),
1570
									),
1571
								),
1572
							),
1573
							'meta_data'  => array(
1574
								'description' => __( 'Meta data.', 'woocommerce' ),
1575
								'type'        => 'array',
1576
								'context'     => array( 'view', 'edit' ),
1577
								'items'       => array(
1578
									'type'       => 'object',
1579
									'properties' => array(
1580
										'id'    => array(
1581
											'description' => __( 'Meta ID.', 'woocommerce' ),
1582
											'type'        => 'integer',
1583
											'context'     => array( 'view', 'edit' ),
1584
											'readonly'    => true,
1585
										),
1586
										'key'   => array(
1587
											'description' => __( 'Meta key.', 'woocommerce' ),
1588
											'type'        => 'string',
1589
											'context'     => array( 'view', 'edit' ),
1590
										),
1591
										'value' => array(
1592
											'description' => __( 'Meta value.', 'woocommerce' ),
1593
											'type'        => 'mixed',
1594
											'context'     => array( 'view', 'edit' ),
1595
										),
1596
									),
1597
								),
1598
							),
1599
						),
1600
					),
1601
				),
1602
				'coupon_lines'         => array(
1603
					'description' => __( 'Coupons line data.', 'woocommerce' ),
1604
					'type'        => 'array',
1605
					'context'     => array( 'view', 'edit' ),
1606
					'items'       => array(
1607
						'type'       => 'object',
1608
						'properties' => array(
1609
							'id'           => array(
1610
								'description' => __( 'Item ID.', 'woocommerce' ),
1611
								'type'        => 'integer',
1612
								'context'     => array( 'view', 'edit' ),
1613
								'readonly'    => true,
1614
							),
1615
							'code'         => array(
1616
								'description' => __( 'Coupon code.', 'woocommerce' ),
1617
								'type'        => 'mixed',
1618
								'context'     => array( 'view', 'edit' ),
1619
							),
1620
							'discount'     => array(
1621
								'description' => __( 'Discount total.', 'woocommerce' ),
1622
								'type'        => 'string',
1623
								'context'     => array( 'view', 'edit' ),
1624
								'readonly'    => true,
1625
							),
1626
							'discount_tax' => array(
1627
								'description' => __( 'Discount total tax.', 'woocommerce' ),
1628
								'type'        => 'string',
1629
								'context'     => array( 'view', 'edit' ),
1630
								'readonly'    => true,
1631
							),
1632
							'meta_data'    => array(
1633
								'description' => __( 'Meta data.', 'woocommerce' ),
1634
								'type'        => 'array',
1635
								'context'     => array( 'view', 'edit' ),
1636
								'items'       => array(
1637
									'type'       => 'object',
1638
									'properties' => array(
1639
										'id'    => array(
1640
											'description' => __( 'Meta ID.', 'woocommerce' ),
1641
											'type'        => 'integer',
1642
											'context'     => array( 'view', 'edit' ),
1643
											'readonly'    => true,
1644
										),
1645
										'key'   => array(
1646
											'description' => __( 'Meta key.', 'woocommerce' ),
1647
											'type'        => 'string',
1648
											'context'     => array( 'view', 'edit' ),
1649
										),
1650
										'value' => array(
1651
											'description' => __( 'Meta value.', 'woocommerce' ),
1652
											'type'        => 'mixed',
1653
											'context'     => array( 'view', 'edit' ),
1654
										),
1655
									),
1656
								),
1657
							),
1658
						),
1659
					),
1660
				),
1661
				'refunds'              => array(
1662
					'description' => __( 'List of refunds.', 'woocommerce' ),
1663
					'type'        => 'array',
1664
					'context'     => array( 'view', 'edit' ),
1665
					'readonly'    => true,
1666
					'items'       => array(
1667
						'type'       => 'object',
1668
						'properties' => array(
1669
							'id'     => array(
1670
								'description' => __( 'Refund ID.', 'woocommerce' ),
1671
								'type'        => 'integer',
1672
								'context'     => array( 'view', 'edit' ),
1673
								'readonly'    => true,
1674
							),
1675
							'reason' => array(
1676
								'description' => __( 'Refund reason.', 'woocommerce' ),
1677
								'type'        => 'string',
1678
								'context'     => array( 'view', 'edit' ),
1679
								'readonly'    => true,
1680
							),
1681
							'total'  => array(
1682
								'description' => __( 'Refund total.', 'woocommerce' ),
1683
								'type'        => 'string',
1684
								'context'     => array( 'view', 'edit' ),
1685
								'readonly'    => true,
1686
							),
1687
						),
1688
					),
1689
				),
1690
				'set_paid'             => array(
1691
					'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ),
1692
					'type'        => 'boolean',
1693
					'default'     => false,
1694
					'context'     => array( 'edit' ),
1695
				),
1696
			),
1697
		);
1698
1699
		return $this->add_additional_fields_schema( $schema );
1700
	}
1701
1702
	/**
1703
	 * Get the query params for collections.
1704
	 *
1705
	 * @return array
1706
	 */
1707
	public function get_collection_params() {
1708
		$params = parent::get_collection_params();
1709
1710
		$params['status'] = array(
1711
			'default'           => 'any',
1712
			'description'       => __( 'Limit result set to orders which have specific statuses.', 'woocommerce' ),
1713
			'type'              => 'array',
1714
			'items'             => array(
1715
				'type' => 'string',
1716
				'enum' => array_merge( array( 'any', 'trash' ), $this->get_order_statuses() ),
1717
			),
1718
			'validate_callback' => 'rest_validate_request_arg',
1719
		);
1720
		$params['customer'] = array(
1721
			'description'       => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ),
1722
			'type'              => 'integer',
1723
			'sanitize_callback' => 'absint',
1724
			'validate_callback' => 'rest_validate_request_arg',
1725
		);
1726
		$params['product']  = array(
1727
			'description'       => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ),
1728
			'type'              => 'integer',
1729
			'sanitize_callback' => 'absint',
1730
			'validate_callback' => 'rest_validate_request_arg',
1731
		);
1732
		$params['dp']       = array(
1733
			'default'           => wc_get_price_decimals(),
1734
			'description'       => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
1735
			'type'              => 'integer',
1736
			'sanitize_callback' => 'absint',
1737
			'validate_callback' => 'rest_validate_request_arg',
1738
		);
1739
		// This needs to remain a string to support extensions that filter Order Number.
1740
		$params['number'] = array(
1741
			'description'       => __( 'Limit result set to orders matching part of an order number.', 'woocommerce' ),
1742
			'type'              => 'string',
1743
			'validate_callback' => 'rest_validate_request_arg',
1744
		);
1745
1746
		return $params;
1747
	}
1748
1749
	/**
1750
	 * Calculate coupons.
1751
	 *
1752
	 * @throws \WC_REST_Exception When fails to set any item.
1753
	 *
1754
	 * @param \WP_REST_Request $request Request object.
1755
	 * @param \WC_Order        $order   Order data.
1756
	 * @return bool
1757
	 */
1758
	protected function calculate_coupons( $request, $order ) {
1759
		if ( ! isset( $request['coupon_lines'] ) || ! is_array( $request['coupon_lines'] ) ) {
1760
			return false;
1761
		}
1762
1763
		// Remove all coupons first to ensure calculation is correct.
1764
		foreach ( $order->get_items( 'coupon' ) as $coupon ) {
1765
			$order->remove_coupon( $coupon->get_code() );
1766
		}
1767
1768
		foreach ( $request['coupon_lines'] as $item ) {
1769
			if ( is_array( $item ) ) {
1770
				if ( empty( $item['id'] ) ) {
1771
					if ( empty( $item['code'] ) ) {
1772
						throw new \WC_REST_Exception( 'woocommerce_rest_invalid_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
1773
					}
1774
1775
					$results = $order->apply_coupon( wc_clean( $item['code'] ) );
1776
1777
					if ( is_wp_error( $results ) ) {
1778
						throw new \WC_REST_Exception( 'woocommerce_rest_' . $results->get_error_code(), $results->get_error_message(), 400 );
1779
					}
1780
				}
1781
			}
1782
		}
1783
1784
		return true;
1785
	}
1786
}
1787