mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
* chore: New feature stubbed out * feat: Implement createRefund and deleteRefund mutations createRefund: Creates a refund on an order with amount, reason, optional payment gateway refund, restock, and meta data support. Requires edit_shop_orders capability. deleteRefund: Deletes a refund by ID with optional force flag. Returns the deleted refund data and parent order. Requires delete_shop_orders capability. Both mutations include before/after action hooks for extensibility and follow the WC REST API refund controller pattern. Closes #17 * chore: Add PHPStan type annotation for wc_get_order in Refund_Delete * devops: Add guard tests for deleteRefund mutation Test invalid refund ID, order ID passed instead of refund ID, with expectedErrorMessage assertions confirming error messages.
225 lines
4.9 KiB
PHP
225 lines
4.9 KiB
PHP
<?php
|
|
|
|
class RefundMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
|
|
private $order_id;
|
|
|
|
public function setUp(): void {
|
|
parent::setUp();
|
|
|
|
update_option( 'woocommerce_prices_include_tax', 'no' );
|
|
update_option( 'woocommerce_calc_taxes', 'yes' );
|
|
|
|
// Create a completed order as shop manager.
|
|
$this->loginAsShopManager();
|
|
|
|
$product_id = $this->factory->product->createSimple( [ 'regular_price' => '100' ] );
|
|
|
|
$order = new \WC_Order();
|
|
$order->set_status( 'completed' );
|
|
$order->set_customer_id( 0 );
|
|
$order->set_payment_method( 'bacs' );
|
|
$order->add_product( wc_get_product( $product_id ), 2 );
|
|
$order->set_total( 200 );
|
|
$order->save();
|
|
|
|
$this->order_id = $order->get_id();
|
|
}
|
|
|
|
public function testCreateRefundMutation() {
|
|
$query = '
|
|
mutation createRefund( $input: CreateRefundInput! ) {
|
|
createRefund( input: $input ) {
|
|
refund {
|
|
databaseId
|
|
amount
|
|
reason
|
|
}
|
|
order {
|
|
databaseId
|
|
total
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
// Assertion One: Customer cannot create refund.
|
|
$this->loginAsCustomer();
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'orderId' => $this->order_id,
|
|
'amount' => '50',
|
|
'reason' => 'Test refund',
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$this->assertQueryError( $response );
|
|
|
|
// Assertion Two: Shop manager can create refund.
|
|
$this->loginAsShopManager();
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'orderId' => $this->order_id,
|
|
'amount' => '50',
|
|
'reason' => 'Test refund',
|
|
],
|
|
],
|
|
]
|
|
);
|
|
|
|
$expected = [
|
|
$this->expectedField( 'createRefund.refund.databaseId', self::NOT_NULL ),
|
|
$this->expectedField( 'createRefund.refund.amount', 50.0 ),
|
|
$this->expectedField( 'createRefund.refund.reason', 'Test refund' ),
|
|
$this->expectedField( 'createRefund.order.databaseId', $this->order_id ),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
// Assertion Three: Invalid amount.
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'orderId' => $this->order_id,
|
|
'amount' => '0',
|
|
'reason' => 'Zero refund',
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$this->assertQueryError( $response );
|
|
}
|
|
|
|
public function testDeleteRefundMutation() {
|
|
$this->loginAsShopManager();
|
|
|
|
// Create a refund first.
|
|
$refund = \wc_create_refund(
|
|
[
|
|
'order_id' => $this->order_id,
|
|
'amount' => '25',
|
|
'reason' => 'Refund to delete',
|
|
]
|
|
);
|
|
$this->assertNotWPError( $refund );
|
|
$refund_id = $refund->get_id();
|
|
|
|
$query = '
|
|
mutation deleteRefund( $input: DeleteRefundInput! ) {
|
|
deleteRefund( input: $input ) {
|
|
refund {
|
|
databaseId
|
|
amount
|
|
}
|
|
order {
|
|
databaseId
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
// Assertion One: Customer cannot delete refund.
|
|
$this->loginAsCustomer();
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'id' => $this->toRelayId( 'order', $refund_id ),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$this->assertQueryError( $response );
|
|
|
|
// Assertion Two: Shop manager can delete refund.
|
|
$this->loginAsShopManager();
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'id' => $this->toRelayId( 'order', $refund_id ),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
|
|
$expected = [
|
|
$this->expectedField( 'deleteRefund.refund.databaseId', $refund_id ),
|
|
$this->expectedField( 'deleteRefund.refund.amount', 25.0 ),
|
|
$this->expectedField( 'deleteRefund.order.databaseId', $this->order_id ),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
// Verify refund is actually deleted.
|
|
$deleted_refund = \wc_get_order( $refund_id );
|
|
$this->assertFalse( $deleted_refund );
|
|
}
|
|
|
|
public function testDeleteRefundWithInvalidIdFails() {
|
|
$this->loginAsShopManager();
|
|
|
|
$query = '
|
|
mutation deleteRefund( $input: DeleteRefundInput! ) {
|
|
deleteRefund( input: $input ) {
|
|
refund { databaseId }
|
|
}
|
|
}
|
|
';
|
|
|
|
// Non-existent ID.
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'id' => $this->toRelayId( 'order', 999999 ),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$this->assertQueryError(
|
|
$response,
|
|
[ $this->expectedErrorMessage( 'Invalid refund ID', self::MESSAGE_CONTAINS ) ]
|
|
);
|
|
}
|
|
|
|
public function testDeleteRefundWithOrderIdFails() {
|
|
$this->loginAsShopManager();
|
|
|
|
$query = '
|
|
mutation deleteRefund( $input: DeleteRefundInput! ) {
|
|
deleteRefund( input: $input ) {
|
|
refund { databaseId }
|
|
}
|
|
}
|
|
';
|
|
|
|
// Pass an order ID instead of a refund ID.
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $query,
|
|
'variables' => [
|
|
'input' => [
|
|
'id' => $this->toRelayId( 'order', $this->order_id ),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$this->assertQueryError(
|
|
$response,
|
|
[ $this->expectedErrorMessage( 'Invalid refund ID', self::MESSAGE_CONTAINS ) ]
|
|
);
|
|
}
|
|
}
|