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