Merge pull request #13117 from woocommerce/fix-12973

Moved refund logic
This commit is contained in:
Claudio Sanches 2017-02-08 16:15:54 -02:00 committed by GitHub
commit 4e653139b6
7 changed files with 146 additions and 115 deletions

View File

@ -183,7 +183,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
*/ */
public function reduce_stock( $amount = 1 ) { public function reduce_stock( $amount = 1 ) {
wc_deprecated_function( 'WC_Product::reduce_stock', '2.7', 'wc_update_product_stock' ); wc_deprecated_function( 'WC_Product::reduce_stock', '2.7', 'wc_update_product_stock' );
wc_update_product_stock( $this, $amount, 'decrease' ); return wc_update_product_stock( $this, $amount, 'decrease' );
} }
/** /**
@ -195,7 +195,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
*/ */
public function increase_stock( $amount = 1 ) { public function increase_stock( $amount = 1 ) {
wc_deprecated_function( 'WC_Product::increase_stock', '2.7', 'wc_update_product_stock' ); wc_deprecated_function( 'WC_Product::increase_stock', '2.7', 'wc_update_product_stock' );
wc_update_product_stock( $this, $amount, 'increase' ); return wc_update_product_stock( $this, $amount, 'increase' );
} }
/** /**

View File

@ -252,43 +252,24 @@ class WC_REST_Order_Refunds_Controller extends WC_REST_Orders_Controller {
return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 ); return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 );
} }
$api_refund = is_bool( $request['api_refund'] ) ? $request['api_refund'] : true; // Create the refund.
$refund = wc_create_refund( array(
$data = array(
'order_id' => $order_data->ID, 'order_id' => $order_data->ID,
'amount' => $request['amount'], 'amount' => $request['amount'],
'reason' => empty( $request['reason'] ) ? null : $request['reason'], 'reason' => empty( $request['reason'] ) ? null : $request['reason'],
'line_items' => $request['line_items'], 'line_items' => $request['line_items'],
); 'refund_payment' => is_bool( $request['api_refund'] ) ? $request['api_refund'] : true,
'restock_items' => true,
) );
// Create the refund. if ( is_wp_error( $refund ) ) {
$refund = wc_create_refund( $data ); return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', $refund->get_error_message(), 500 );
}
if ( ! $refund ) { if ( ! $refund ) {
return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 ); return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 );
} }
// Refund via API.
if ( $api_refund ) {
if ( WC()->payment_gateways() ) {
$payment_gateways = WC()->payment_gateways->payment_gateways();
}
$order = wc_get_order( $order_data );
if ( isset( $payment_gateways[ $order->get_payment_method() ] ) && $payment_gateways[ $order->get_payment_method() ]->supports( 'refunds' ) ) {
$result = $payment_gateways[ $order->get_payment_method() ]->process_refund( $order->get_id(), $refund->get_amount(), $refund->get_reason() );
if ( ! $result || is_wp_error( $result ) ) {
$refund->delete();
$message = is_wp_error( $result ) ? $result : new WP_Error( 'woocommerce_rest_create_order_refund_api_failed', __( 'An error occurred while attempting to create the refund using the payment gateway API.', 'woocommerce' ), 500 );
return $message;
}
}
}
$post = get_post( $refund->get_id() ); $post = get_post( $refund->get_id() );
$this->update_additional_fields_for_object( $post, $request ); $this->update_additional_fields_for_object( $post, $request );

View File

@ -1241,7 +1241,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
if ( isset( $request['stock_quantity'] ) ) { if ( isset( $request['stock_quantity'] ) ) {
$product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) );
} elseif ( isset( $request['inventory_delta'] ) ) { } elseif ( isset( $request['inventory_delta'] ) ) {
$stock_quantity = wc_stock_amount( $product->get_stock_amount() ); $stock_quantity = wc_stock_amount( $product->get_stock_quantity() );
$stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] );
$product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) );
} }
@ -1446,7 +1446,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
if ( isset( $data['stock_quantity'] ) ) { if ( isset( $data['stock_quantity'] ) ) {
$variation->set_stock_quantity( $data['stock_quantity'] ); $variation->set_stock_quantity( $data['stock_quantity'] );
} elseif ( isset( $data['inventory_delta'] ) ) { } elseif ( isset( $data['inventory_delta'] ) ) {
$stock_quantity = wc_stock_amount( $variation->get_stock_amount() ); $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() );
$stock_quantity += wc_stock_amount( $data['inventory_delta'] ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] );
$variation->set_stock_quantity( $stock_quantity ); $variation->set_stock_quantity( $stock_quantity );
} }

View File

@ -1618,7 +1618,7 @@ class WC_API_Products extends WC_API_Resource {
if ( isset( $data['stock_quantity'] ) ) { if ( isset( $data['stock_quantity'] ) ) {
$product->set_stock_quantity( wc_stock_amount( $data['stock_quantity'] ) ); $product->set_stock_quantity( wc_stock_amount( $data['stock_quantity'] ) );
} elseif ( isset( $data['inventory_delta'] ) ) { } elseif ( isset( $data['inventory_delta'] ) ) {
$stock_quantity = wc_stock_amount( $product->get_stock_amount() ); $stock_quantity = wc_stock_amount( $product->get_stock_quantity() );
$stock_quantity += wc_stock_amount( $data['inventory_delta'] ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] );
$product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) );
} }
@ -1845,7 +1845,7 @@ class WC_API_Products extends WC_API_Resource {
if ( isset( $data['stock_quantity'] ) ) { if ( isset( $data['stock_quantity'] ) ) {
$variation->set_stock_quantity( $data['stock_quantity'] ); $variation->set_stock_quantity( $data['stock_quantity'] );
} elseif ( isset( $data['inventory_delta'] ) ) { } elseif ( isset( $data['inventory_delta'] ) ) {
$stock_quantity = wc_stock_amount( $variation->get_stock_amount() ); $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() );
$stock_quantity += wc_stock_amount( $data['inventory_delta'] ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] );
$variation->set_stock_quantity( $stock_quantity ); $variation->set_stock_quantity( $stock_quantity );
} }

View File

@ -1365,73 +1365,24 @@ class WC_AJAX {
$line_items[ $item_id ]['refund_tax'] = array_map( 'wc_format_decimal', $tax_totals ); $line_items[ $item_id ]['refund_tax'] = array_map( 'wc_format_decimal', $tax_totals );
} }
// Create the refund object // Create the refund object.
$refund = wc_create_refund( array( $refund = wc_create_refund( array(
'amount' => $refund_amount, 'amount' => $refund_amount,
'reason' => $refund_reason, 'reason' => $refund_reason,
'order_id' => $order_id, 'order_id' => $order_id,
'line_items' => $line_items, 'line_items' => $line_items,
'refund_payment' => $api_refund,
'restock_items' => $restock_refunded_items,
) ); ) );
if ( is_wp_error( $refund ) ) { if ( is_wp_error( $refund ) ) {
throw new Exception( $refund->get_error_message() ); throw new Exception( $refund->get_error_message() );
} }
// Refund via API if ( did_action( 'woocommerce_order_fully_refunded' ) ) {
if ( $api_refund ) {
if ( WC()->payment_gateways() ) {
$payment_gateways = WC()->payment_gateways->payment_gateways();
}
if ( isset( $payment_gateways[ $order->get_payment_method() ] ) && $payment_gateways[ $order->get_payment_method() ]->supports( 'refunds' ) ) {
$result = $payment_gateways[ $order->get_payment_method() ]->process_refund( $order_id, $refund_amount, $refund_reason );
do_action( 'woocommerce_refund_processed', $refund, $result );
if ( is_wp_error( $result ) ) {
throw new Exception( $result->get_error_message() );
} elseif ( ! $result ) {
throw new Exception( __( 'Refund failed', 'woocommerce' ) );
}
}
}
// restock items
foreach ( $line_item_qtys as $item_id => $qty ) {
if ( $restock_refunded_items && $qty && isset( $order_items[ $item_id ] ) ) {
$order_item = $order_items[ $item_id ];
$_product = $order_item->get_product();
if ( $_product && $_product->exists() && $_product->managing_stock() ) {
$old_stock = wc_stock_amount( $_product->stock );
$new_quantity = $_product->increase_stock( $qty );
$order->add_order_note( sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $order_item['product_id'], $old_stock, $new_quantity ) );
do_action( 'woocommerce_restock_refunded_item', $_product->get_id(), $old_stock, $new_quantity, $order, $_product );
}
}
}
// Trigger notifications and status changes
if ( $order->get_remaining_refund_amount() > 0 || ( $order->has_free_item() && $order->get_remaining_refund_items() > 0 ) ) {
/**
* woocommerce_order_partially_refunded.
*
* @since 2.4.0
* Note: 3rd arg was added in err. Kept for bw compat. 2.4.3.
*/
do_action( 'woocommerce_order_partially_refunded', $order_id, $refund->get_id(), $refund->get_id() );
} else {
do_action( 'woocommerce_order_fully_refunded', $order_id, $refund->get_id() );
$order->update_status( apply_filters( 'woocommerce_order_fully_refunded_status', 'refunded', $order_id, $refund->get_id() ) );
$response_data['status'] = 'fully_refunded'; $response_data['status'] = 'fully_refunded';
} }
do_action( 'woocommerce_order_refunded', $order_id, $refund->get_id() );
// Clear transients
wc_delete_shop_order_transients( $order_id );
wp_send_json_success( $response_data ); wp_send_json_success( $response_data );
} catch ( Exception $e ) { } catch ( Exception $e ) {

View File

@ -1469,11 +1469,13 @@ class WC_Order extends WC_Abstract_Order {
* @return array of WC_Order_Refund objects * @return array of WC_Order_Refund objects
*/ */
public function get_refunds() { public function get_refunds() {
if ( empty( $this->refunds ) ) {
$this->refunds = wc_get_orders( array( $this->refunds = wc_get_orders( array(
'type' => 'shop_order_refund', 'type' => 'shop_order_refund',
'parent' => $this->get_id(), 'parent' => $this->get_id(),
'limit' => -1, 'limit' => -1,
) ); ) );
}
return $this->refunds; return $this->refunds;
} }

View File

@ -486,21 +486,26 @@ function wc_create_refund( $args = array() ) {
'order_id' => 0, 'order_id' => 0,
'refund_id' => 0, 'refund_id' => 0,
'line_items' => array(), 'line_items' => array(),
'refund_payment' => false,
'restock_items' => false,
); );
try { try {
$args = wp_parse_args( $args, $default_args ); $args = wp_parse_args( $args, $default_args );
$order = wc_get_order( $args['order_id'] );
$refund = new WC_Order_Refund( $args['refund_id'] );
if ( ! $order ) { if ( ! $order = wc_get_order( $args['order_id'] ) ) {
throw new Exception( __( 'Invalid order ID.', 'woocommerce' ) ); throw new Exception( __( 'Invalid order ID.', 'woocommerce' ) );
} }
// prevent negative refunds $remaining_refund_amount = $order->get_remaining_refund_amount();
if ( 0 > $args['amount'] ) { $remaining_refund_items = $order->get_remaining_refund_items();
$args['amount'] = 0; $refund_item_count = 0;
$refund = new WC_Order_Refund( $args['refund_id'] );
if ( 0 > $args['amount'] || $args['amount'] > $remaining_refund_amount ) {
throw new Exception( __( 'Invalid refund amount.', 'woocommerce' ) );
} }
$refund->set_currency( $order->get_currency() ); $refund->set_currency( $order->get_currency() );
$refund->set_amount( $args['amount'] ); $refund->set_amount( $args['amount'] );
$refund->set_parent_id( absint( $args['order_id'] ) ); $refund->set_parent_id( absint( $args['order_id'] ) );
@ -543,6 +548,7 @@ function wc_create_refund( $args = array() ) {
} }
$refund->add_item( $refunded_item ); $refund->add_item( $refunded_item );
$refund_item_count += $qty;
} }
} }
@ -556,10 +562,31 @@ function wc_create_refund( $args = array() ) {
*/ */
do_action( 'woocommerce_create_refund', $refund, $args ); do_action( 'woocommerce_create_refund', $refund, $args );
$refund->save(); if ( $refund->save() ) {
if ( $args['refund_payment'] ) {
$result = wc_refund_payment( $order, $refund->get_amount(), $refund->get_reason() );
if ( is_wp_error( $result ) ) {
$refund->delete();
return $result;
}
}
if ( $args['restock_items'] ) {
wc_restock_refunded_items( $order, $args['line_items'] );
}
// Trigger notification emails
if ( ( $remaining_refund_amount - $args['amount'] ) > 0 || ( $order->has_free_item() && ( $remaining_refund_items - $refund_item_count ) > 0 ) ) {
do_action( 'woocommerce_order_partially_refunded', $order->get_id(), $refund->get_id() );
} else {
do_action( 'woocommerce_order_fully_refunded', $order->get_id(), $refund->get_id() );
$order->update_status( apply_filters( 'woocommerce_order_fully_refunded_status', 'refunded', $order->get_id(), $refund->get_id() ) );
}
}
// Backwards compatibility hook.
do_action( 'woocommerce_refund_created', $refund->get_id(), $args ); do_action( 'woocommerce_refund_created', $refund->get_id(), $args );
do_action( 'woocommerce_order_refunded', $order->get_id(), $refund->get_id() );
} catch ( Exception $e ) { } catch ( Exception $e ) {
return new WP_Error( 'error', $e->getMessage() ); return new WP_Error( 'error', $e->getMessage() );
@ -568,6 +595,78 @@ function wc_create_refund( $args = array() ) {
return $refund; return $refund;
} }
/**
* Try to refund the payment for an order via the gateway.
*
* @since 2.7.0
* @param WC_Order $order
* @param string $amount
* @param string $reason
* @return bool|WP_Error
*/
function wc_refund_payment( $order, $amount, $reason = '' ) {
try {
if ( ! is_a( $order, 'WC_Order' ) ) {
throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
}
$gateway_controller = WC_Payment_Gateways::instance();
$all_gateways = $gateway_controller->payment_gateways();
$payment_method = $order->get_payment_method();
$gateway = isset( $all_gateways[ $payment_method ] ) ? $all_gateways[ $payment_method ] : false;
if ( ! $gateway ) {
throw new Exception( __( 'The payment gateway for this order does not exist.', 'woocommerce' ) );
}
if ( ! $gateway->supports( 'refunds' ) ) {
throw new Exception( __( 'The payment gateway for this order does not support automatic refunds.', 'woocommerce' ) );
}
$result = $gateway->process_refund( $order->get_id(), $amount, $reason );
if ( ! $result ) {
throw new Exception( __( 'An error occurred while attempting to create the refund using the payment gateway API.', 'woocommerce' ) );
}
if ( is_wp_error( $result ) ) {
throw new Exception( $result->get_error_message() );
}
return true;
} catch ( Exception $e ) {
return new WP_Error( 'error', $e->getMessage() );
}
}
/**
* Restock items during refund.
*
* @since 2.7.0
* @param WC_Order $order
* @param array $refunded_line_items
*/
function wc_restock_refunded_items( $order, $refunded_line_items ) {
$line_items = $order->get_items();
foreach ( $line_items as $item_id => $item ) {
if ( ! isset( $refunded_line_items[ $item_id ], $refunded_line_items[ $item_id ]['qty'] ) ) {
continue;
}
$product = $item->get_product();
if ( $product && $product->managing_stock() ) {
$old_stock = $product->get_stock_quantity();
$new_stock = wc_update_product_stock( $product, $refunded_line_items[ $item_id ]['qty'], 'increase' );
$order->add_order_note( sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $product->get_id(), $old_stock, $new_stock ) );
do_action( 'woocommerce_restock_refunded_item', $product->get_id(), $old_stock, $new_stock, $order, $product );
}
}
}
/** /**
* Get tax class by tax id. * Get tax class by tax id.
* *
@ -625,8 +724,6 @@ function wc_order_fully_refunded( $order_id ) {
'order_id' => $order_id, 'order_id' => $order_id,
'line_items' => array(), 'line_items' => array(),
) ); ) );
wc_delete_shop_order_transients( $order );
} }
add_action( 'woocommerce_order_status_refunded', 'wc_order_fully_refunded' ); add_action( 'woocommerce_order_status_refunded', 'wc_order_fully_refunded' );