diff --git a/plugins/woocommerce-admin/includes/wc-admin-order-functions.php b/plugins/woocommerce-admin/includes/wc-admin-order-functions.php index c1dd7981545..a9eedb98485 100644 --- a/plugins/woocommerce-admin/includes/wc-admin-order-functions.php +++ b/plugins/woocommerce-admin/includes/wc-admin-order-functions.php @@ -16,39 +16,87 @@ function wc_admin_order_product_lookup_entry( $order_id ) { global $wpdb; $order = wc_get_order( $order_id ); - if ( ! $order ) { + + // This hook gets called on refunds as well, so return early to avoid errors. + if ( ! $order || 'shop_order_refund' === $order->get_type() ) { return; } - foreach ( $order->get_items() as $order_item ) { - $wpdb->replace( + if ( 'refunded' === $order->get_status() ) { + $wpdb->delete( $wpdb->prefix . 'wc_order_product_lookup', - array( - 'order_item_id' => $order_item->get_id(), - 'order_id' => $order->get_id(), - 'product_id' => $order_item->get_product_id( 'edit' ), - 'variation_id' => $order_item->get_variation_id( 'edit' ), - 'customer_id' => ( 0 < $order->get_customer_id( 'edit' ) ) ? $order->get_customer_id( 'edit' ) : null, - 'product_qty' => $order_item->get_quantity( 'edit' ), - 'product_net_revenue' => $order_item->get_subtotal( 'edit' ), - 'date_created' => date( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ), - ), - array( - '%d', - '%d', - '%d', - '%d', - '%d', - '%d', - '%f', - '%s', - ) + array( 'order_id' => $order->get_id() ), + array( '%d' ) ); + return; + } + + $refunds = wc_admin_get_order_refund_items( $order ); + + foreach ( $order->get_items() as $order_item ) { + $order_item_id = $order_item->get_id(); + $quantity_refunded = isset( $refunds[ $order_item_id ] ) ? $refunds[ $order_item_id ]['quantity'] : 0; + $amount_refunded = isset( $refunds[ $order_item_id ] ) ? $refunds[ $order_item_id ]['subtotal'] : 0; + if ( $quantity_refunded >= $order_item->get_quantity( 'edit' ) ) { + $wpdb->delete( + $wpdb->prefix . 'wc_order_product_lookup', + array( 'order_item_id' => $order_item_id ), + array( '%d' ) + ); + } else { + $wpdb->replace( + $wpdb->prefix . 'wc_order_product_lookup', + array( + 'order_item_id' => $order_item_id, + 'order_id' => $order->get_id(), + 'product_id' => $order_item->get_product_id( 'edit' ), + 'variation_id' => $order_item->get_variation_id( 'edit' ), + 'customer_id' => ( 0 < $order->get_customer_id( 'edit' ) ) ? $order->get_customer_id( 'edit' ) : null, + 'product_qty' => $order_item->get_quantity( 'edit' ) - $quantity_refunded, + 'product_net_revenue' => $order_item->get_subtotal( 'edit' ) - $amount_refunded, + 'date_created' => date( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ), + ), + array( + '%d', + '%d', + '%d', + '%d', + '%d', + '%d', + '%f', + '%s', + ) + ); + } } } // TODO: maybe replace these with woocommerce_create_order, woocommerce_update_order, woocommerce_trash_order, woocommerce_delete_order, as clean_post_cache might be called in other circumstances and trigger too many updates? -add_action( 'save_post', 'wc_admin_order_product_lookup_entry', 10, 1 ); -add_action( 'clean_post_cache', 'wc_admin_order_product_lookup_entry', 10, 1 ); +add_action( 'save_post', 'wc_admin_order_product_lookup_entry' ); +add_action( 'woocommerce_order_refunded', 'wc_admin_order_product_lookup_entry' ); +add_action( 'clean_post_cache', 'wc_admin_order_product_lookup_entry' ); + +/** + * Get total refund amount and line items refunded. + * + * @param object $order WC_Order. + * @return array Refunded line items with line item ID as key. + */ +function wc_admin_get_order_refund_items( $order ) { + $refunds = $order->get_refunds(); + $refunded_line_items = array(); + foreach ( $refunds as $refund ) { + foreach ( $refund->get_items() as $refunded_item ) { + $line_item_id = wc_get_order_item_meta( $refunded_item->get_id(), '_refunded_item_id', true ); + if ( ! isset( $refunded_line_items[ $line_item_id ] ) ) { + $refunded_line_items[ $line_item_id ]['quantity'] = 0; + $refunded_line_items[ $line_item_id ]['subtotal'] = 0; + } + $refunded_line_items[ $line_item_id ]['quantity'] += absint( $refunded_item['quantity'] ); + $refunded_line_items[ $line_item_id ]['subtotal'] += abs( $refunded_item['subtotal'] ); + } + } + return $refunded_line_items; +} /** * Make an entry in the wc_order_tax_lookup table for an order.