Skip to content

Commit

Permalink
fix: General improvements and bugfixes (#899)
Browse files Browse the repository at this point in the history
  • Loading branch information
kidunot89 authored Oct 29, 2024
1 parent 5f924bb commit 594bc29
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 69 deletions.
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion includes/class-woocommerce-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,14 @@ public static function add_session_header_to_allow_headers( array $allowed_heade
* @return array
*/
public static function woographql_stripe_gateway_args( $gateway_args, $payment_method ) {
if ( 'stripe' === $payment_method ) {
/** @var false|\WC_Order|\WC_Order_Refund $order */
$order = wc_get_order( $gateway_args[0] );
if ( false === $order ) {
return $gateway_args;
}

$stripe_source_id = $order->get_meta( '_stripe_source_id' );
if ( 'stripe' === $payment_method && ! empty( $stripe_source_id ) ) {
$gateway_args = [
$gateway_args[0],
true,
Expand Down
1 change: 1 addition & 0 deletions includes/data/mutation/class-checkout-mutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ protected static function validate_checkout( &$data ) {
if ( WC()->cart->needs_payment() ) {
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();

\codecept_debug( $available_gateways );
if ( ! isset( $available_gateways[ $data['payment_method'] ] ) ) {
throw new UserError( __( 'Invalid payment method.', 'wp-graphql-woocommerce' ) );
} else {
Expand Down
216 changes: 165 additions & 51 deletions includes/data/mutation/class-order-mutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use GraphQL\Error\UserError;


/**
* Class - Order_Mutation
*/
Expand Down Expand Up @@ -86,8 +87,8 @@ public static function create_order( $input, $context, $info ) {
/**
* Action called before order is created.
*
* @param array $input Input data describing order.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param array $input Input data describing order.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
*/
do_action( 'graphql_woocommerce_before_order_create', $input, $context, $info );
Expand Down Expand Up @@ -118,78 +119,186 @@ public static function create_order( $input, $context, $info ) {
* @param \WPGraphQL\AppContext $context AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
*
* @throws \Exception Failed to retrieve order.
*
* @return void
*/
public static function add_items( $input, $order_id, $context, $info ) {
/** @var \WC_Order|false $order */
$order = \WC_Order_Factory::get_order( $order_id );
if ( false === $order ) {
throw new \Exception( __( 'Failed to retrieve order.', 'wp-graphql-woocommerce' ) );
}

$item_group_keys = [
'lineItems' => 'line_item',
'shippingLines' => 'shipping',
'feeLines' => 'fee',
];

$item_groups = [];
foreach ( $input as $key => $items ) {
$order_items = [];
foreach ( $input as $key => $group_items ) {
if ( array_key_exists( $key, $item_group_keys ) ) {
$type = $item_group_keys[ $key ];
$type = $item_group_keys[ $key ];
$order_items[ $type ] = [];

/**
* Action called before an item group is added to an order.
*
* @param array $items Item data being added.
* @param integer $order_id ID of target order.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
* @param array $group_items Items data being added.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
*/
do_action( "graphql_woocommerce_before_{$type}s_added_to_order", $items, $order_id, $context, $info );

foreach ( $items as $item_data ) {
// Create Order item.
$item_id = ( ! empty( $item_data['id'] ) && \WC_Order_Factory::get_order_item( $item_data['id'] ) )
? $item_data['id']
: \wc_add_order_item( $order_id, [ 'order_item_type' => $type ] );

// Continue if order item creation failed.
if ( ! $item_id ) {
continue;
do_action( "graphql_woocommerce_before_{$type}s_added_to_order", $group_items, $order, $context, $info );

foreach ( $group_items as $item_data ) {
$item = self::set_item(
$item_data,
$type,
$order,
$context,
$info
);

/**
* Action called before an item group is added to an order.
*
* @param \WC_Order_Item $item Order item object.
* @param array $item_data Item data being added.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
*/
do_action( "graphql_woocommerce_before_{$type}_added_to_order", $item, $item_data, $order, $context, $info );

if ( 0 === $item->get_id() ) {
$order->add_item( $item );
$order_items[ $type ][] = $item;
} else {
$item->save();
$order_items[ $type ][] = $item;
}

// Add input item data to order item.
$item_keys = self::get_order_item_keys( $type );
self::map_input_to_item( $item_id, $item_data, $item_keys, $context, $info );
}

/**
* Action called after an item group is added to an order.
* Action called after an item group is added to an order, and before the order has been saved with the new items.
*
* @param array $items Item data being added.
* @param integer $order_id ID of target order.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
* @param array $group_items Item data being added.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
*/
do_action( "graphql_woocommerce_after_{$type}s_added_to_order", $items, $order_id, $context, $info );
do_action( "graphql_woocommerce_after_{$type}s_added_to_order", $group_items, $order, $context, $info );
}//end if
}//end foreach

/**
* Action called after all items have been added and right before the new items have been saved.
*
* @param array<string, array<\WC_Order_Item>> $order_items Order items.
* @param \WC_Order $order WC_Order instance.
* @param array $input Input data describing order.
* @param \WPGraphQL\AppContext $context Request AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
*/
do_action( 'graphql_woocommerce_before_new_order_items_save', $order_items, $order, $input, $context, $info );

$order->save();
}

/**
* Return array of item mapped with the provided $item_keys and extracts $meta_data
*
* @param integer $item_id Order item ID.
* @param array $input Item input data.
* @param array $item_keys Item key map.
* @param array<string, mixed> $item_data Item data.
* @param string $type Item type.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
*
* @throws \Exception Failed to retrieve order item | Failed to retrieve connected product.
* @return \WC_Order_Item
*/
public static function set_item( $item_data, $type, $order, $context, $info ) {
$item_id = ! empty( $item_data['id'] ) ? $item_data['id'] : 0;
$item_class = self::get_order_item_classname( $type, $item_id );

/** @var \WC_Order_Item $item */
$item = new $item_class( $item_id );

/**
* Filter the order item object before it is created.
*
* @param \WC_Order_Item $item Order item object.
* @param array $item_data Item data.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
*/
$item = apply_filters( "graphql_create_order_{$type}_object", $item, $item_data, $order, $context, $info );

self::map_input_to_item( $item, $item_data, $type );

/**
* Action called after an order item is created.
*
* @param \WC_Order_Item $item Order item object.
* @param array $item_data Item data.
* @param \WC_Order $order Order object.
* @param \WPGraphQL\AppContext $context AppContext instance.
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
*/
do_action( "graphql_create_order_{$type}", $item, $item_data, $order, $context, $info );

return $item;
}

/**
* Get order item class name.
*
* @return int
* @param string $type Order item type.
* @param int $id Order item ID.
*
* @return string
*/
protected static function map_input_to_item( $item_id, $input, $item_keys, $context, $info ) {
$order_item = \WC_Order_Factory::get_order_item( $item_id );
if ( ! is_object( $order_item ) ) {
throw new \Exception( __( 'Failed to retrieve order item.', 'wp-graphql-woocommerce' ) );
public static function get_order_item_classname( $type, $id = 0 ) {
$classname = false;
switch ( $type ) {
case 'line_item':
case 'product':
$classname = 'WC_Order_Item_Product';
break;
case 'coupon':
$classname = 'WC_Order_Item_Coupon';
break;
case 'fee':
$classname = 'WC_Order_Item_Fee';
break;
case 'shipping':
$classname = 'WC_Order_Item_Shipping';
break;
case 'tax':
$classname = 'WC_Order_Item_Tax';
break;
}

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

return $classname;
}

/**
* Return array of item mapped with the provided $item_keys and extracts $meta_data
*
* @param \WC_Order_Item &$item Order item.
* @param array $input Item input data.
* @param string $type Item type.
*
* @throws \Exception Failed to retrieve connected product.
*
* @return void
*/
protected static function map_input_to_item( &$item, $input, $type ) {
$item_keys = self::get_order_item_keys( $type );

$args = [];
$meta_data = null;
foreach ( $input as $key => $value ) {
Expand All @@ -203,10 +312,9 @@ protected static function map_input_to_item( $item_id, $input, $item_keys, $cont
}

// Calculate to subtotal/total for line items.

if ( isset( $args['quantity'] ) ) {
$product = ( ! empty( $order_item['product_id'] ) )
? wc_get_product( $order_item['product_id'] )
$product = ( ! empty( $item['product_id'] ) )
? wc_get_product( $item['product_id'] )
: wc_get_product( self::get_product_id( $args ) );
if ( ! is_object( $product ) ) {
throw new \Exception( __( 'Failed to retrieve product connected to order item.', 'wp-graphql-woocommerce' ) );
Expand All @@ -219,18 +327,24 @@ protected static function map_input_to_item( $item_id, $input, $item_keys, $cont

// Set item props.
foreach ( $args as $key => $value ) {
if ( is_callable( [ $order_item, "set_{$key}" ] ) ) {
$order_item->{"set_{$key}"}( $value );
if ( is_callable( [ $item, "set_{$key}" ] ) ) {
$item->{"set_{$key}"}( $value );
}
}

// Update item meta data if any is found.
if ( 0 !== $item_id && ! empty( $meta_data ) ) {
// Update item meta data.
self::update_item_meta_data( $item_id, $meta_data, $context, $info );
if ( empty( $meta_data ) ) {
return;
}

return $order_item->save();
foreach ( $meta_data as $entry ) {
$exists = $item->get_meta( $entry['key'], true, 'edit' );
if ( '' !== $exists && $exists !== $entry['value'] ) {
$item->update_meta_data( $entry['key'], $entry['value'] );
} else {
$item->add_meta_data( $entry['key'], $entry['value'] );
}
}
}

/**
Expand Down Expand Up @@ -285,10 +399,10 @@ protected static function get_order_item_keys( $type ) {
protected static function get_product_id( $data ) {
if ( ! empty( $data['sku'] ) ) {
$product_id = (int) wc_get_product_id_by_sku( $data['sku'] );
} elseif ( ! empty( $data['product_id'] ) && empty( $data['variation_id'] ) ) {
$product_id = (int) $data['product_id'];
} elseif ( ! empty( $data['variation_id'] ) ) {
$product_id = (int) $data['variation_id'];
} elseif ( ! empty( $data['product_id'] ) ) {
$product_id = (int) $data['product_id'];
} else {
throw new UserError( __( 'Product ID or SKU is required.', 'wp-graphql-woocommerce' ) );
}
Expand Down
2 changes: 1 addition & 1 deletion includes/model/class-order.php
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ protected function order_fields() {
return ! empty( $this->data->get_date_paid() ) ? $this->data->get_date_paid() : null;
},
'subtotal' => function () {
return ! empty( $this->data->get_subtotal() )
return ! is_null( $this->data->get_subtotal() )
? wc_graphql_price( $this->data->get_subtotal(), [ 'currency' => $this->data->get_currency() ] )
: null;
},
Expand Down
10 changes: 7 additions & 3 deletions tests/_support/Factory/OrderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,14 @@ public function createNew( $args = [], $items = [] ) {
public function add_line_item( $order, $args = [], $save = true ) {
$order = $save ? \wc_get_order( $order ) : $order;

if ( empty( $args['product'] ) ) {
$product = \wc_get_product( $this->factory->product->createSimple() );
} else {
if ( ! empty( $args['variation_id'] ) ) {
$product = \wc_get_product( $args['variation_id'] );
} elseif ( ! empty( $args['product_id'] ) ) {
$product = \wc_get_product( $args['product_id'] );
} elseif ( ! empty( $args['product'] ) ) {
$product = \wc_get_product( $args['product'] );
} else {
$product = \wc_get_product( $this->factory->product->createSimple() );
}

if ( empty( $args['qty'] ) ) {
Expand Down
Loading

0 comments on commit 594bc29

Please sign in to comment.