feat: configurable createdVia on order mutations + WooCommerce order attribution (#1018)

* Add option to Change the created_via field.

Can be useful when WooCommerce is being used from multiply sources. For example, with plugins like Point of Sale for WooCommerce.

* feat: createdVia on checkout + WooCommerce order attribution origin

Builds on the createOrder createdVia option:

- Add a createdVia input to the checkout mutation. WC_Checkout::create_order()
  hardcodes created_via to 'checkout' after its data loop, so process_checkout()
  overrides it (and tags the attribution source type) only when createdVia is
  provided; otherwise checkout keeps WooCommerce's 'checkout' default.
- createOrder now defaults created_via to WooCommerce::get_order_attribution_source_type()
  ('graphql-api', filterable) and writes that value to the
  _wc_order_attribution_source_type order meta so GraphQL orders are attributable.
- Register a wc_order_attribution_origin_label callback that brands orders
  attributed to 'graphql-api' as the 'GraphQL' origin in WooCommerce admin.
- Cover createdVia + attribution in OrderMutationsTest and CheckoutMutationTest.

---------

Co-authored-by: Scott Kennedy <scottyzen@gmail.com>
This commit is contained in:
Geoff Taylor
2026-06-12 13:16:53 -04:00
committed by GitHub
co-authored by Scott Kennedy
parent 01876f5344
commit 2ce9424e11
6 changed files with 115 additions and 1 deletions
+35
View File
@@ -47,6 +47,41 @@ class WooCommerce {
// WPGraphQL Reset password -> Use woocommerce email password template when requested.
add_filter( 'retrieve_password_message', [ self::class, 'get_reset_password_message' ], 10, 3 );
add_filter( 'retrieve_password_title', [ self::class, 'get_reset_password_title' ] );
// Brand the WooCommerce Order Attribution "Origin" for orders created through WPGraphQL.
add_filter( 'wc_order_attribution_origin_label', [ self::class, 'order_attribution_origin_label' ], 10, 4 );
}
/**
* Returns the WooCommerce Order Attribution source type used to mark orders created through WPGraphQL.
*
* Matches the default `created_via` value so GraphQL-created orders are attributable out of the box.
*
* @return string
*/
public static function get_order_attribution_source_type() {
return apply_filters( 'graphql_woocommerce_order_attribution_source_type', 'graphql-api' );
}
/**
* Provides the WooCommerce Order Attribution "Origin" label for orders created through WPGraphQL.
*
* Connected to WooCommerce's order origin label filter so orders tagged with our attribution
* source type surface a recognizable origin instead of "Unknown".
*
* @param string $label Origin label. May contain a "%s" placeholder for the source.
* @param string $source_type Attribution source type.
* @param string $source Attribution source.
* @param string $formatted_source Formatted attribution source.
*
* @return string
*/
public static function order_attribution_origin_label( $label, $source_type, $source, $formatted_source ) {
if ( self::get_order_attribution_source_type() !== $source_type ) {
return $label;
}
return apply_filters( 'graphql_woocommerce_order_attribution_origin_label', __( 'GraphQL', 'wp-graphql-woocommerce' ), $source, $formatted_source );
}
/**
@@ -649,6 +649,13 @@ class Checkout_Mutation {
throw new UserError( __( 'Unable to create order.', 'wp-graphql-woocommerce' ) );
}
// Override the "created via" source when provided. WC_Checkout::create_order() hardcodes it to "checkout".
if ( ! empty( $input['createdVia'] ) ) {
$order->set_created_via( $input['createdVia'] );
$order->add_meta_data( '_wc_order_attribution_source_type', $input['createdVia'], true );
$order->save();
}
// Add meta data.
if ( ! empty( $input['metaData'] ) ) {
self::update_order_meta( $order_id, $input['metaData'], $input, $context, $info );
+6
View File
@@ -111,6 +111,12 @@ class Checkout {
return __( 'Fees to add to the order.', 'wp-graphql-woocommerce' );
},
],
'createdVia' => [
'type' => 'String',
'description' => static function () {
return __( 'Source of the order. Useful when WooCommerce is driven from multiple sources. Defaults to "checkout".', 'wp-graphql-woocommerce' );
},
],
];
}
+10 -1
View File
@@ -16,6 +16,7 @@ use WC_Order_Factory;
use WPGraphQL\AppContext;
use WPGraphQL\WooCommerce\Data\Mutation\Order_Mutation;
use WPGraphQL\WooCommerce\Model\Order;
use WPGraphQL\WooCommerce\WooCommerce;
/**
* Class Order_Create
@@ -140,6 +141,12 @@ class Order_Create {
return __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'wp-graphql-woocommerce' );
},
],
'createdVia' => [
'type' => 'String',
'description' => static function () {
return __( 'Source of the order. Useful when WooCommerce is driven from multiple sources. Defaults to "graphql-api".', 'wp-graphql-woocommerce' );
},
],
];
}
@@ -203,7 +210,9 @@ class Order_Create {
Order_Mutation::apply_coupons( $order, $input['coupons'] );
}
$order->set_created_via( 'graphql-api' );
$created_via = ! empty( $input['createdVia'] ) ? $input['createdVia'] : WooCommerce::get_order_attribution_source_type();
$order->set_created_via( $created_via );
$order->add_meta_data( '_wc_order_attribution_source_type', $created_via, true );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->calculate_totals( true );
+21
View File
@@ -505,6 +505,27 @@ class CheckoutMutationTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
$this->assertEquals( '555-555-6789', $order->get_shipping_phone() );
}
public function testCheckoutMutationWithCreatedVia() {
$this->loginAsCustomer();
$product_id = $this->factory->product->createSimple();
WC()->cart->add_to_cart( $product_id, 1 );
$variables = [ 'input' => $this->getCheckoutInput( [ 'createdVia' => 'pos' ] ) ];
$query = $this->getCheckoutMutation();
$response = $this->graphql( compact( 'query', 'variables' ) );
$this->assertQuerySuccessful(
$response,
[ $this->expectedField( 'checkout.order.createdVia', 'pos' ) ]
);
// The provided "createdVia" overrides WooCommerce's hardcoded "checkout" source and tags the attribution source type.
$order = \wc_get_order( $response['data']['checkout']['order']['databaseId'] );
$this->assertEquals( 'pos', $order->get_created_via() );
$this->assertEquals( 'pos', $order->get_meta( '_wc_order_attribution_source_type' ) );
}
public function testCheckoutMutationWithNewAccount() {
$variable = $this->factory->product_variation->createSome();
$product_ids = [
+36
View File
@@ -351,8 +351,12 @@ class OrderMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQ
$this->expectedField( 'createOrder.order.shipping.country', 'US' ),
$this->expectedField( 'createOrder.order.paymentMethod', 'bacs' ),
$this->expectedField( 'createOrder.order.paymentMethodTitle', 'Direct Bank Transfer' ),
$this->expectedField( 'createOrder.order.createdVia', 'graphql-api' ),
];
// The order is attributed to the "graphql-api" source so WooCommerce surfaces the "GraphQL" origin.
$this->assertEquals( 'graphql-api', $order->get_meta( '_wc_order_attribution_source_type' ) );
// Validate coupon lines.
$coupon_items = array_values( $order->get_items( 'coupon' ) );
foreach ( $coupon_items as $i => $item ) {
@@ -412,6 +416,38 @@ class OrderMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQ
$this->assertQuerySuccessful( $response, $expected );
}
public function testCreateOrderMutationWithCreatedVia() {
$this->loginAsShopManager();
$product_id = $this->factory->product->createSimple();
$input = [
'createdVia' => 'pos',
'lineItems' => [
[
'productId' => $product_id,
'quantity' => 1,
],
],
];
$response = $this->orderMutation( $input );
$this->assertQuerySuccessful(
$response,
[ $this->expectedField( 'createOrder.order.createdVia', 'pos' ) ]
);
// A provided "createdVia" flows into both the order source and the WooCommerce attribution source type.
$order = \WC_Order_Factory::get_order( $response['data']['createOrder']['order']['databaseId'] );
$this->assertEquals( 'pos', $order->get_created_via() );
$this->assertEquals( 'pos', $order->get_meta( '_wc_order_attribution_source_type' ) );
// Orders attributed to "graphql-api" surface a branded "GraphQL" origin in WooCommerce.
$this->assertEquals(
'GraphQL',
apply_filters( 'wc_order_attribution_origin_label', 'Unknown', 'graphql-api', '', 'Unknown' )
);
}
public function testUpdateOrderMutation() {
// Create products and coupons to be used in order creation.
$variable = $this->factory->product_variation->createSome( $this->factory->product->createVariable() );