• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql-woocommerce / 23675172456

28 Mar 2026 02:10AM UTC coverage: 70.983% (-18.4%) from 89.424%
23675172456

Pull #1003

github

web-flow
Merge 05339093d into 6fb7b226f
Pull Request #1003: devops: WC email template tests, COT cursor HPOS fix, checkout account auth

71 of 81 new or added lines in 5 files covered. (87.65%)

3346 existing lines in 124 files now uncovered.

12576 of 17717 relevant lines covered (70.98%)

55.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.2
/includes/data/mutation/class-order-mutation.php
1
<?php
2
/**
3
 * Defines helper functions for executing mutations related to the orders.
4
 *
5
 * @package WPGraphQL\WooCommerce\Data\Mutation
6
 * @since 0.2.0
7
 */
8

9
namespace WPGraphQL\WooCommerce\Data\Mutation;
10

11
use GraphQL\Error\UserError;
12
use WPGraphQL\Utils\Utils;
13

14

15
/**
16
 * Class - Order_Mutation
17
 */
18
class Order_Mutation {
19
        /**
20
         * Filterable authentication function.
21
         *
22
         * @param array                                $input     Input data describing order.
23
         * @param \WPGraphQL\AppContext                $context   AppContext instance.
24
         * @param \GraphQL\Type\Definition\ResolveInfo $info      ResolveInfo instance.
25
         * @param string                               $mutation  Mutation being executed.
26
         * @param integer|null|false                   $order_id  Order ID.
27
         * @throws \GraphQL\Error\UserError  Error locating order.
28
         *
29
         * @return boolean
30
         */
31
        public static function authorized( $input, $context, $info, $mutation = 'create', $order_id = null ) {
32
                /**
33
                 * Get order post type.
34
                 *
35
                 * @var \WP_Post_Type $post_type_object
36
                 */
37
                $post_type_object = get_post_type_object( 'shop_order' );
1✔
38

39
                if ( ! $order_id ) {
1✔
40
                        return apply_filters(
1✔
41
                                "graphql_woocommerce_authorized_to_{$mutation}_orders",
1✔
42
                                current_user_can( $post_type_object->cap->edit_posts ),
1✔
43
                                $order_id,
1✔
44
                                $input,
1✔
45
                                $context,
1✔
46
                                $info
1✔
47
                        );
1✔
48
                }
49

50
                /** @var false|\WC_Order $order */
UNCOV
51
                $order = \wc_get_order( $order_id );
×
UNCOV
52
                if ( false === $order ) {
×
UNCOV
53
                        throw new UserError(
×
UNCOV
54
                                sprintf(
×
55
                                        /* translators: %d: Order ID */
UNCOV
56
                                        __( 'Failed to find order with ID of %d.', 'wp-graphql-woocommerce' ),
×
UNCOV
57
                                        $order_id
×
UNCOV
58
                                )
×
UNCOV
59
                        );
×
60
                }
61

UNCOV
62
                $post_type = get_post_type( $order_id );
×
UNCOV
63
                if ( false === $post_type ) {
×
64
                        throw new UserError( __( 'Failed to identify the post type of the order.', 'wp-graphql-woocommerce' ) );
×
65
                }
66

67
                // Return true if user is owner or admin.
UNCOV
68
                $is_owner = 0 !== get_current_user_id() && $order->get_customer_id() === get_current_user_id();
×
UNCOV
69
                $is_admin = \wc_rest_check_post_permissions( $post_type, 'edit', $order_id );
×
UNCOV
70
                return $is_owner || $is_admin;
×
71
        }
72

73
        /**
74
         * Create an order.
75
         *
76
         * @param array                                $input    Input data describing order.
77
         * @param \WPGraphQL\AppContext                $context  AppContext instance.
78
         * @param \GraphQL\Type\Definition\ResolveInfo $info     ResolveInfo instance.
79
         *
80
         * @return integer
81
         *
82
         * @throws \GraphQL\Error\UserError  Error creating order.
83
         */
84
        public static function create_order( $input, $context, $info ) {
85
                $order = new \WC_Order();
1✔
86

87
                $order->set_currency( ! empty( $input['currency'] ) ? $input['currency'] : get_woocommerce_currency() );
1✔
88
                $order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
1✔
89
                $order->set_customer_ip_address( \WC_Geolocation::get_ip_address() );
1✔
90
                $order->set_customer_user_agent( wc_get_user_agent() );
1✔
91

92
                $order_id = $order->save();
1✔
93

94
                $order_keys = [
1✔
95
                        'status'       => 'status',
1✔
96
                        'customerId'   => 'customer_id',
1✔
97
                        'customerNote' => 'customer_note',
1✔
98
                        'parent'       => 'parent',
1✔
99
                        'createdVia'   => 'created_via',
1✔
100
                ];
1✔
101

102
                $args = [ 'order_id' => $order_id ];
1✔
103
                foreach ( $input as $key => $value ) {
1✔
104
                        if ( array_key_exists( $key, $order_keys ) ) {
1✔
105
                                $args[ $order_keys[ $key ] ] = $value;
1✔
106
                        }
107
                }
108

109
                /**
110
                 * Action called before order is created.
111
                 *
112
                 * @param array                                $input   Input data describing order.
113
                 * @param \WPGraphQL\AppContext                $context Request AppContext instance.
114
                 * @param \GraphQL\Type\Definition\ResolveInfo $info    Request ResolveInfo instance.
115
                 */
116
                do_action( 'graphql_woocommerce_before_order_create', $input, $context, $info );
1✔
117

118
                $order = \wc_create_order( $args );
1✔
119
                if ( is_wp_error( $order ) ) {
1✔
120
                        throw new UserError( $order->get_error_code() . $order->get_error_message() );
×
121
                }
122

123
                /**
124
                 * Action called after order is created.
125
                 *
126
                 * @param \WC_Order    $order   WC_Order instance.
127
                 * @param array       $input   Input data describing order.
128
                 * @param \WPGraphQL\AppContext  $context Request AppContext instance.
129
                 * @param \GraphQL\Type\Definition\ResolveInfo $info    Request ResolveInfo instance.
130
                 */
131
                do_action( 'graphql_woocommerce_after_order_create', $order, $input, $context, $info );
1✔
132

133
                return $order->get_id();
1✔
134
        }
135

136
        /**
137
         * Add items to order.
138
         *
139
         * @param array                                $input     Input data describing order.
140
         * @param int                                  $order_id  Order object.
141
         * @param \WPGraphQL\AppContext                $context   AppContext instance.
142
         * @param \GraphQL\Type\Definition\ResolveInfo $info      ResolveInfo instance.
143
         *
144
         * @throws \Exception  Failed to retrieve order.
145
         *
146
         * @return void
147
         */
148
        public static function add_items( $input, $order_id, $context, $info ) {
149
                /** @var \WC_Order|false $order */
150
                $order = \WC_Order_Factory::get_order( $order_id );
1✔
151
                if ( false === $order ) {
1✔
152
                        throw new \Exception( __( 'Failed to retrieve order.', 'wp-graphql-woocommerce' ) );
×
153
                }
154

155
                $item_group_keys = [
1✔
156
                        'lineItems'     => 'line_item',
1✔
157
                        'shippingLines' => 'shipping',
1✔
158
                        'feeLines'      => 'fee',
1✔
159
                ];
1✔
160

161
                $order_items = [];
1✔
162
                foreach ( $input as $key => $group_items ) {
1✔
163
                        if ( array_key_exists( $key, $item_group_keys ) ) {
1✔
164
                                $type                 = $item_group_keys[ $key ];
1✔
165
                                $order_items[ $type ] = [];
1✔
166

167
                                /**
168
                                 * Action called before an item group is added to an order.
169
                                 *
170
                                 * @param array                                $group_items  Items data being added.
171
                                 * @param \WC_Order                            $order        Order object.
172
                                 * @param \WPGraphQL\AppContext                $context      Request AppContext instance.
173
                                 * @param \GraphQL\Type\Definition\ResolveInfo $info         Request ResolveInfo instance.
174
                                 */
175
                                do_action( "graphql_woocommerce_before_{$type}s_added_to_order", $group_items, $order, $context, $info );
1✔
176

177
                                foreach ( $group_items as $item_data ) {
1✔
178
                                        $item = self::set_item(
1✔
179
                                                $item_data,
1✔
180
                                                $type,
1✔
181
                                                $order,
1✔
182
                                                $context,
1✔
183
                                                $info
1✔
184
                                        );
1✔
185

186
                                        /**
187
                                         * Action called before an item group is added to an order.
188
                                         *
189
                                         * @param \WC_Order_Item                       $item      Order item object.
190
                                         * @param array                                $item_data Item data being added.
191
                                         * @param \WC_Order                            $order     Order object.
192
                                         * @param \WPGraphQL\AppContext                $context   Request AppContext instance.
193
                                         * @param \GraphQL\Type\Definition\ResolveInfo $info      Request ResolveInfo instance.
194
                                         */
195
                                        do_action( "graphql_woocommerce_before_{$type}_added_to_order", $item, $item_data, $order, $context, $info );
1✔
196

197
                                        if ( 0 === $item->get_id() ) {
1✔
198
                                                $order->add_item( $item );
1✔
199
                                                $order_items[ $type ][] = $item;
1✔
200
                                        } else {
UNCOV
201
                                                $item->save();
×
UNCOV
202
                                                $order_items[ $type ][] = $item;
×
203
                                        }
204
                                }
205

206
                                /**
207
                                 * Action called after an item group is added to an order, and before the order has been saved with the new items.
208
                                 *
209
                                 * @param array                                $group_items  Item data being added.
210
                                 * @param \WC_Order                            $order        Order object.
211
                                 * @param \WPGraphQL\AppContext                $context      Request AppContext instance.
212
                                 * @param \GraphQL\Type\Definition\ResolveInfo $info         Request ResolveInfo instance.
213
                                 */
214
                                do_action( "graphql_woocommerce_after_{$type}s_added_to_order", $group_items, $order, $context, $info );
1✔
215
                        }//end if
216
                }//end foreach
217

218
                /**
219
                 * Action called after all items have been added and right before the new items have been saved.
220
                 *
221
                 * @param array<string, array<\WC_Order_Item>> $order_items Order items.
222
                 * @param \WC_Order                            $order       WC_Order instance.
223
                 * @param array                                $input       Input data describing order.
224
                 * @param \WPGraphQL\AppContext                $context     Request AppContext instance.
225
                 * @param \GraphQL\Type\Definition\ResolveInfo $info        Request ResolveInfo instance.
226
                 */
227
                do_action( 'graphql_woocommerce_before_new_order_items_save', $order_items, $order, $input, $context, $info );
1✔
228

229
                $order->save();
1✔
230
        }
231

232
        /**
233
         *
234
         * @param array<string, mixed>                 $item_data  Item data.
235
         * @param string                               $type       Item type.
236
         * @param \WC_Order                            $order      Order object.
237
         * @param \WPGraphQL\AppContext                $context    AppContext instance.
238
         * @param \GraphQL\Type\Definition\ResolveInfo $info       ResolveInfo instance.
239
         *
240
         * @return \WC_Order_Item
241
         */
242
        public static function set_item( $item_data, $type, $order, $context, $info ) {
243
                $item_id    = ! empty( $item_data['id'] ) ? $item_data['id'] : 0;
1✔
244
                $item_class = self::get_order_item_classname( $type, $item_id );
1✔
245

246
                /** @var \WC_Order_Item $item */
247
                $item = new $item_class( $item_id );
1✔
248

249
                /**
250
                 * Filter the order item object before it is created.
251
                 *
252
                 * @param \WC_Order_Item                       $item       Order item object.
253
                 * @param array                                $item_data  Item data.
254
                 * @param \WC_Order                            $order      Order object.
255
                 * @param \WPGraphQL\AppContext                $context    AppContext instance.
256
                 * @param \GraphQL\Type\Definition\ResolveInfo $info       ResolveInfo instance.
257
                 */
258
                $item = apply_filters( "graphql_create_order_{$type}_object", $item, $item_data, $order, $context, $info );
1✔
259

260
                self::map_input_to_item( $item, $item_data, $type );
1✔
261

262
                /**
263
                 * Action called after an order item is created.
264
                 *
265
                 * @param \WC_Order_Item                       $item       Order item object.
266
                 * @param array                                $item_data  Item data.
267
                 * @param \WC_Order                            $order      Order object.
268
                 * @param \WPGraphQL\AppContext                $context    AppContext instance.
269
                 * @param \GraphQL\Type\Definition\ResolveInfo $info       ResolveInfo instance.
270
                 */
271
                do_action( "graphql_create_order_{$type}", $item, $item_data, $order, $context, $info );
1✔
272

273
                return $item;
1✔
274
        }
275

276
        /**
277
         * Get order item class name.
278
         *
279
         * @param string $type Order item type.
280
         * @param int    $id  Order item ID.
281
         *
282
         * @return string
283
         */
284
        public static function get_order_item_classname( $type, $id = 0 ) {
285
                $classname = false;
1✔
286
                switch ( $type ) {
287
                        case 'line_item':
1✔
288
                        case 'product':
1✔
289
                                $classname = 'WC_Order_Item_Product';
1✔
290
                                break;
1✔
291
                        case 'coupon':
1✔
292
                                $classname = 'WC_Order_Item_Coupon';
×
293
                                break;
×
294
                        case 'fee':
1✔
295
                                $classname = 'WC_Order_Item_Fee';
1✔
296
                                break;
1✔
297
                        case 'shipping':
1✔
298
                                $classname = 'WC_Order_Item_Shipping';
1✔
299
                                break;
1✔
300
                        case 'tax':
×
301
                                $classname = 'WC_Order_Item_Tax';
×
302
                                break;
×
303
                }
304

305
                $classname = apply_filters( 'woocommerce_get_order_item_classname', $classname, $type, $id ); // phpcs:ignore WordPress.NamingConventions
1✔
306

307
                return $classname;
1✔
308
        }
309

310
        /**
311
         * Return array of item mapped with the provided $item_keys and extracts $meta_data
312
         *
313
         * @param \WC_Order_Item &$item      Order item.
314
         * @param array          $input      Item input data.
315
         * @param string         $type       Item type.
316
         *
317
         * @throws \Exception Failed to retrieve connected product.
318
         *
319
         * @return void
320
         */
321
        protected static function map_input_to_item( &$item, $input, $type ) {
322
                $item_keys = self::get_order_item_keys( $type );
1✔
323

324
                $args      = [];
1✔
325
                $meta_data = null;
1✔
326
                foreach ( $input as $key => $value ) {
1✔
327
                        if ( array_key_exists( $key, $item_keys ) ) {
1✔
328
                                $args[ $item_keys[ $key ] ] = $value;
1✔
329
                        } elseif ( 'metaData' === $key ) {
1✔
330
                                $meta_data = $value;
1✔
331
                        } else {
332
                                $args[ $key ] = $value;
1✔
333
                        }
334
                }
335

336
                // Auto-fill line item name, subtotal, and total from the product when not provided.
337
                $has_product_id   = ! empty( $args['product_id'] ) || ! empty( $args['variation_id'] );
1✔
338
                $missing_defaults = ! isset( $args['subtotal'] ) || ! isset( $args['total'] ) || ! isset( $args['name'] );
1✔
339
                if ( 'line_item' === $type && $has_product_id && $missing_defaults ) {
1✔
340
                        $product_id = self::get_product_id( $args );
1✔
341
                        $product    = ! empty( $product_id ) ? wc_get_product( $product_id ) : null;
1✔
342
                        if ( ! is_object( $product ) ) {
1✔
343
                                throw new \Exception( __( 'Failed to retrieve product connected to order item.', 'wp-graphql-woocommerce' ) );
×
344
                        }
345

346
                        $total            = wc_get_price_excluding_tax( $product, [ 'qty' => $args['quantity'] ?? 1 ] );
1✔
347
                        $args['subtotal'] = $args['subtotal'] ?? $total;
1✔
348
                        $args['total']    = $args['total'] ?? $total;
1✔
349
                        $args['name']     = $args['name'] ?? $product->get_name();
1✔
350
                }
351

352
                // Set item props.
353
                foreach ( $args as $key => $value ) {
1✔
354
                        if ( is_callable( [ $item, "set_{$key}" ] ) ) {
1✔
355
                                $item->{"set_{$key}"}( $value );
1✔
356
                        }
357
                }
358

359
                // Update item meta data if any is found.
360
                if ( empty( $meta_data ) ) {
1✔
361
                        return;
1✔
362
                }
363

364
                foreach ( $meta_data as $entry ) {
1✔
365
                        $exists = $item->get_meta( $entry['key'], true, 'edit' );
1✔
366
                        if ( '' !== $exists && $exists !== $entry['value'] ) {
1✔
UNCOV
367
                                $item->update_meta_data( $entry['key'], $entry['value'] );
×
368
                        } else {
369
                                $item->add_meta_data( $entry['key'], $entry['value'] );
1✔
370
                        }
371
                }
372
        }
373

374
        /**
375
         * Returns array of item keys by item type.
376
         *
377
         * @param string $type  Order item type.
378
         *
379
         * @return array
380
         */
381
        protected static function get_order_item_keys( $type ) {
382
                switch ( $type ) {
383
                        case 'line_item':
1✔
384
                                return [
1✔
385
                                        'productId'   => 'product_id',
1✔
386
                                        'variationId' => 'variation_id',
1✔
387
                                        'taxClass'    => 'tax_class',
1✔
388
                                ];
1✔
389

390
                        case 'shipping':
1✔
391
                                return [
1✔
392
                                        'name'        => 'order_item_name',
1✔
393
                                        'methodTitle' => 'method_title',
1✔
394
                                        'methodId'    => 'method_id',
1✔
395
                                        'instanceId'  => 'instance_id',
1✔
396
                                ];
1✔
397

398
                        case 'fee':
1✔
399
                                return [
1✔
400
                                        'name'      => 'name',
1✔
401
                                        'taxClass'  => 'tax_class',
1✔
402
                                        'taxStatus' => 'tax_status',
1✔
403
                                ];
1✔
404
                        default:
405
                                /**
406
                                 * Allow filtering of order item keys for unknown item types.
407
                                 *
408
                                 * @param array  $item_keys  Order item keys.
409
                                 * @param string $type       Order item type slug.
410
                                 */
411
                                return apply_filters( 'woographql_get_order_item_keys', [], $type );
×
412
                }//end switch
413
        }
414

415
        /**
416
         * Gets the product ID from the SKU or line item data ID.
417
         *
418
         * @param array $data  Line item data.
419
         *
420
         * @return integer
421
         * @throws \GraphQL\Error\UserError When SKU or ID is not valid.
422
         */
423
        protected static function get_product_id( $data ) {
424
                if ( ! empty( $data['sku'] ) ) {
1✔
425
                        $product_id = (int) wc_get_product_id_by_sku( $data['sku'] );
×
426
                } elseif ( ! empty( $data['variation_id'] ) ) {
1✔
427
                        $product_id = (int) $data['variation_id'];
1✔
428
                } elseif ( ! empty( $data['product_id'] ) ) {
1✔
429
                        $product_id = (int) $data['product_id'];
1✔
430
                } else {
431
                        throw new UserError( __( 'Product ID or SKU is required.', 'wp-graphql-woocommerce' ) );
×
432
                }
433

434
                return $product_id;
1✔
435
        }
436

437
        /**
438
         * Create/Update order item meta data.
439
         *
440
         * @param int                                  $item_id    Order item ID.
441
         * @param array                                $meta_data  Array of meta data.
442
         * @param \WPGraphQL\AppContext                $context    AppContext instance.
443
         * @param \GraphQL\Type\Definition\ResolveInfo $info       ResolveInfo instance.
444
         *
445
         * @throws \GraphQL\Error\UserError|\Exception  Invalid item input | Failed to retrieve order item.
446
         *
447
         * @return void
448
         */
449
        protected static function update_item_meta_data( $item_id, $meta_data, $context, $info ) {
450
                $item = \WC_Order_Factory::get_order_item( $item_id );
×
451
                if ( ! is_object( $item ) ) {
×
452
                        throw new \Exception( __( 'Failed to retrieve order item.', 'wp-graphql-woocommerce' ) );
×
453
                }
454

455
                foreach ( $meta_data as $entry ) {
×
456
                        $exists = $item->get_meta( $entry['key'], true, 'edit' );
×
457
                        if ( '' !== $exists && $exists !== $entry['value'] ) {
×
458
                                \wc_update_order_item_meta( $item_id, $entry['key'], $entry['value'] );
×
459
                        } else {
460
                                \wc_add_order_item_meta( $item_id, $entry['key'], $entry['value'] );
×
461
                        }
462
                }
463
        }
464

465
        /**
466
         * Add meta data not set in self::create_order().
467
         *
468
         * @param int                                  $order_id  Order ID.
469
         * @param array                                $input     Order properties.
470
         * @param \WPGraphQL\AppContext                $context   AppContext instance.
471
         * @param \GraphQL\Type\Definition\ResolveInfo $info      ResolveInfo instance.
472
         *
473
         * @throws \Exception  Failed to retrieve order.
474
         *
475
         * @return void
476
         */
477
        public static function add_order_meta( $order_id, $input, $context, $info ) {
478
                $order = \WC_Order_Factory::get_order( $order_id );
1✔
479
                if ( ! is_object( $order ) ) {
1✔
480
                        throw new \Exception( __( 'Failed to retrieve order.', 'wp-graphql-woocommerce' ) );
×
481
                }
482

483
                foreach ( $input as $key => $value ) {
1✔
484
                        switch ( $key ) {
485
                                case 'coupons':
1✔
486
                                case 'lineItems':
1✔
487
                                case 'shippingLines':
1✔
488
                                case 'feeLines':
1✔
489
                                case 'status':
1✔
490
                                        break;
1✔
491
                                case 'billing':
1✔
492
                                case 'shipping':
1✔
493
                                        self::update_address( $value, $order_id, $key );
1✔
494
                                        $order->apply_changes();
1✔
495
                                        break;
1✔
496
                                case 'metaData':
1✔
497
                                        if ( is_array( $value ) ) {
1✔
498
                                                foreach ( $value as $meta ) {
1✔
499
                                                        $order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
1✔
500
                                                }
501
                                        }
502
                                        break;
1✔
503
                                default:
504
                                        $prop = \wc_graphql_camel_case_to_underscore( $key );
1✔
505
                                        if ( is_callable( [ $order, "set_{$prop}" ] ) ) {
1✔
506
                                                $order->{"set_{$prop}"}( $value );
1✔
507
                                        }
508
                                        break;
1✔
509
                        }//end switch
510
                }//end foreach
511

512
                /**
513
                 * Action called before changes to order meta are saved.
514
                 *
515
                 * @param \WC_Order                            $order   WC_Order instance.
516
                 * @param array                                $props   Order props array.
517
                 * @param \WPGraphQL\AppContext                $context Request AppContext instance.
518
                 * @param \GraphQL\Type\Definition\ResolveInfo $info    Request ResolveInfo instance.
519
                 */
520
                do_action( 'graphql_woocommerce_before_order_meta_save', $order, $input, $context, $info );
1✔
521

522
                $order->save_meta_data();
1✔
523
                $order->save();
1✔
524
        }
525

526
        /**
527
         * Update address.
528
         *
529
         * @param array   $address   Address data.
530
         * @param integer $order_id  WC_Order instance.
531
         * @param string  $type      Address type.
532
         *
533
         * @throws \Exception  Failed to retrieve order.
534
         *
535
         * @return void
536
         */
537
        protected static function update_address( $address, $order_id, $type = 'billing' ) {
538
                $order = \WC_Order_Factory::get_order( $order_id );
1✔
539
                if ( ! is_object( $order ) ) {
1✔
540
                        throw new \Exception( __( 'Failed to retrieve order.', 'wp-graphql-woocommerce' ) );
×
541
                }
542

543
                $formatted_address = Customer_Mutation::address_input_mapping( $address, $type );
1✔
544
                foreach ( $formatted_address as $key => $value ) {
1✔
545
                        if ( is_callable( [ $order, "set_{$type}_{$key}" ] ) ) {
1✔
546
                                $order->{"set_{$type}_{$key}"}( $value );
1✔
547
                        }
548
                }
549
                $order->save();
1✔
550
        }
551

552
        /**
553
         * Applies coupons to WC_Order instance
554
         *
555
         * @param int   $order_id  Order ID.
556
         * @param array $coupons   Coupon codes to be applied to order.
557
         *
558
         * @throws \Exception  Failed to retrieve order.
559
         *
560
         * @return void
561
         */
562
        public static function apply_coupons( $order_id, $coupons ) {
563
                $order = \WC_Order_Factory::get_order( $order_id );
1✔
564
                if ( ! is_object( $order ) ) {
1✔
565
                        throw new \Exception( __( 'Failed to retrieve order.', 'wp-graphql-woocommerce' ) );
×
566
                }
567

568
                // Remove all coupons first to ensure calculation is correct.
569
                foreach ( $order->get_items( 'coupon' ) as $coupon ) {
1✔
570
                        /**
571
                         * Order item coupon.
572
                         *
573
                         * @var \WC_Order_Item_Coupon $coupon
574
                         */
575

UNCOV
576
                        $order->remove_coupon( $coupon->get_code() );
×
577
                }
578

579
                foreach ( $coupons as $code ) {
1✔
580
                        $results = $order->apply_coupon( sanitize_text_field( $code ) );
1✔
581
                        if ( is_wp_error( $results ) ) {
1✔
582
                                do_action( 'graphql_woocommerce_' . $results->get_error_code(), $results, $code, $coupons, $order );
×
583
                        }
584
                }
585

586
                $order->save();
1✔
587
        }
588

589
        /**
590
         * Validates order customer
591
         *
592
         * @param string $customer_id  ID of customer for order.
593
         *
594
         * @return bool
595
         */
596
        public static function validate_customer( $customer_id ) {
597
                $id = Utils::get_database_id_from_id( $customer_id );
1✔
598
                if ( ! $id ) {
1✔
599
                        return false;
×
600
                }
601

602
                if ( false === get_user_by( 'id', $id ) ) {
1✔
603
                        return false;
×
604
                }
605

606
                // Make sure customer is part of blog.
607
                if ( is_multisite() && ! is_user_member_of_blog( $id ) ) {
1✔
608
                        add_user_to_blog( get_current_blog_id(), $id, 'customer' );
×
609
                }
610

611
                return true;
1✔
612
        }
613

614
        /**
615
         * Purge object when creating.
616
         *
617
         * @param null|\WC_Order|\WPGraphQL\WooCommerce\Model\Order $order         Object data.
618
         * @param boolean                                           $force_delete  Delete or put in trash.
619
         *
620
         * @return bool
621
         * @throws \GraphQL\Error\UserError  Failed to delete order.
622
         */
623
        public static function purge( $order, $force_delete = true ) {
UNCOV
624
                if ( ! empty( $order ) ) {
×
UNCOV
625
                        return $order->delete( $force_delete );
×
626
                }
627

628
                return false;
×
629
        }
630
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc