/** * Create a new order refund programmatically * * Returns a new refund object on success which can then be used to add additional data. * * @since 2.2 * @param array $args * @return WC_Order_Refund|WP_Error */ function wc_create_refund($args = array()) { $default_args = array('amount' => '', 'reason' => null, 'order_id' => 0, 'refund_id' => 0, 'line_items' => array()); $args = wp_parse_args($args, $default_args); $refund_data = array(); if ($args['refund_id'] > 0) { $updating = true; $refund_data['ID'] = $args['refund_id']; } else { $updating = false; $refund_data['post_type'] = 'shop_order_refund'; $refund_data['post_status'] = 'wc-completed'; $refund_data['ping_status'] = 'closed'; $refund_data['post_author'] = get_current_user_id() ? get_current_user_id() : 1; $refund_data['post_password'] = uniqid('refund_'); $refund_data['post_parent'] = absint($args['order_id']); $refund_data['post_title'] = sprintf(__('Refund – %s', 'woocommerce'), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce'))); } if (!is_null($args['reason'])) { $refund_data['post_excerpt'] = $args['reason']; } if ($updating) { $refund_id = wp_update_post($refund_data); } else { $refund_id = wp_insert_post(apply_filters('woocommerce_new_refund_data', $refund_data), true); } if (is_wp_error($refund_id)) { return $refund_id; } if (!$updating) { // Default refund meta data update_post_meta($refund_id, '_refund_amount', wc_format_decimal($args['amount'])); // Get refund object $refund = wc_get_order($refund_id); // Negative line items if (sizeof($args['line_items']) > 0) { $order = wc_get_order($args['order_id']); $order_items = $order->get_items(array('line_item', 'fee', 'shipping')); foreach ($args['line_items'] as $refund_item_id => $refund_item) { if (isset($order_items[$refund_item_id])) { if (empty($refund_item['qty']) && empty($refund_item['refund_total']) && empty($refund_item['refund_tax'])) { continue; } // Prevents errors when the order has no taxes if (!isset($refund_item['refund_tax'])) { $refund_item['refund_tax'] = array(); } switch ($order_items[$refund_item_id]['type']) { case 'line_item': $line_item_args = array('totals' => array('subtotal' => wc_format_refund_total($refund_item['refund_total']), 'total' => wc_format_refund_total($refund_item['refund_total']), 'subtotal_tax' => wc_format_refund_total(array_sum($refund_item['refund_tax'])), 'tax' => wc_format_refund_total(array_sum($refund_item['refund_tax'])), 'tax_data' => array('total' => array_map('wc_format_refund_total', $refund_item['refund_tax']), 'subtotal' => array_map('wc_format_refund_total', $refund_item['refund_tax'])))); $new_item_id = $refund->add_product($order->get_product_from_item($order_items[$refund_item_id]), isset($refund_item['qty']) ? $refund_item['qty'] : 0, $line_item_args); wc_add_order_item_meta($new_item_id, '_refunded_item_id', $refund_item_id); break; case 'shipping': $shipping = new stdClass(); $shipping->label = $order_items[$refund_item_id]['name']; $shipping->id = $order_items[$refund_item_id]['method_id']; $shipping->cost = wc_format_refund_total($refund_item['refund_total']); $shipping->taxes = array_map('wc_format_refund_total', $refund_item['refund_tax']); $new_item_id = $refund->add_shipping($shipping); wc_add_order_item_meta($new_item_id, '_refunded_item_id', $refund_item_id); break; case 'fee': $fee = new stdClass(); $fee->name = $order_items[$refund_item_id]['name']; $fee->tax_class = $order_items[$refund_item_id]['tax_class']; $fee->taxable = $fee->tax_class !== '0'; $fee->amount = wc_format_refund_total($refund_item['refund_total']); $fee->tax = wc_format_refund_total(array_sum($refund_item['refund_tax'])); $fee->tax_data = array_map('wc_format_refund_total', $refund_item['refund_tax']); $new_item_id = $refund->add_fee($fee); wc_add_order_item_meta($new_item_id, '_refunded_item_id', $refund_item_id); break; } } } $refund->update_taxes(); } $refund->calculate_totals(false); // Set total to total refunded which may vary from order items $refund->set_total(wc_format_decimal($args['amount']) * -1, 'total'); do_action('woocommerce_refund_created', $refund_id); } // Clear transients wc_delete_shop_order_transients($args['order_id']); return new WC_Order_Refund($refund_id); }
/** * Create or update an order fee. * * @param WC_Order $order Order data. * @param array $fee Item data. * @param string $action 'create' to add fee or 'update' to update it. * @throws WC_REST_Exception Invalid data, server error. */ protected function set_fee($order, $fee, $action) { if ('create' === $action) { // Fee name is required. if (empty($fee['name'])) { throw new WC_REST_Exception('woocommerce_rest_invalid_fee_item', __('Fee name is required.', 'woocommerce'), 400); } $fee_data = new stdClass(); $fee_data->id = sanitize_title($fee['name']); $fee_data->name = $fee['name']; $fee_data->amount = isset($fee['total']) ? floatval($fee['total']) : 0; $fee_data->taxable = false; $fee_data->tax = 0; $fee_data->tax_data = array(); $fee_data->tax_class = ''; // If taxable, tax class and total are required. if (isset($fee['tax_status']) && 'taxable' === $fee['tax_status']) { if (!isset($fee['tax_class'])) { throw new WC_REST_Exception('woocommerce_rest_invalid_fee_item', __('Fee tax class is required when fee is taxable.', 'woocommerce'), 400); } $fee_data->taxable = true; $fee_data->tax_class = $fee['tax_class']; if (isset($fee['total_tax'])) { $fee_data->tax = isset($fee['total_tax']) ? wc_format_refund_total($fee['total_tax']) : 0; } } $fee_id = $order->add_fee($fee_data); if (!$fee_id) { throw new WC_REST_Exception('woocommerce_rest_cannot_create_fee', __('Cannot create fee, try again.', 'woocommerce'), 500); } } else { $fee_args = array(); if (isset($fee['name'])) { $fee_args['name'] = $fee['name']; } if (isset($fee['tax_class'])) { $fee_args['tax_class'] = $fee['tax_class']; } if (isset($fee['total'])) { $fee_args['line_total'] = floatval($fee['total']); } if (isset($fee['total_tax'])) { $fee_args['line_tax'] = floatval($fee['total_tax']); } $fee_id = $order->update_fee($fee['id'], $fee_args); if (!$fee_id) { throw new WC_REST_Exception('woocommerce_rest_cannot_update_fee', __('Cannot update fee, try again.', 'woocommerce'), 500); } } }
/** * Create or update an order fee * * @since 2.2 * @param \WC_Order $order * @param array $fee item data * @param string $action 'create' to add fee or 'update' to update it * @throws WC_API_Exception invalid data, server error */ protected function set_fee($order, $fee, $action) { if ('create' === $action) { // fee title is required if (!isset($fee['title'])) { throw new WC_API_Exception('woocommerce_invalid_fee_item', __('Fee title is required', 'woocommerce'), 400); } $item = new WC_Order_Item_Fee(); $item->set_name(sanitize_title($fee['title'])); $item->set_total(isset($fee['total']) ? floatval($fee['total']) : 0); // if taxable, tax class and total are required if (!empty($fee['taxable'])) { if (!isset($fee['tax_class'])) { throw new WC_API_Exception('woocommerce_invalid_fee_item', __('Fee tax class is required when fee is taxable', 'woocommerce'), 400); } $item->set_tax_status('taxable'); $item->set_tax_class($fee['tax_class']); if (isset($fee['total_tax'])) { $item->set_total_tax(isset($fee['total_tax']) ? wc_format_refund_total($fee['total_tax']) : 0); } if (isset($fee['tax_data'])) { $item->set_total_tax(wc_format_refund_total(array_sum($fee['tax_data']))); $item->set_taxes(array_map('wc_format_refund_total', $fee['tax_data'])); } } $fee_id = $item->save(); if (!$fee_id) { throw new WC_API_Exception('woocommerce_cannot_create_fee', __('Cannot create fee, try again', 'woocommerce'), 500); } } else { $item = new WC_Order_Item_Fee($fee['id']); if (isset($fee['title'])) { $item->set_name(sanitize_title($fee['title'])); } if (isset($fee['tax_class'])) { $item->set_tax_class($fee['tax_class']); } if (isset($fee['total'])) { $item->set_total(floatval($fee['total'])); } if (isset($fee['total_tax'])) { $item->set_total_tax(floatval($fee['total_tax'])); } $fee_id = $item->save(); if (!$fee_id) { throw new WC_API_Exception('woocommerce_cannot_update_fee', __('Cannot update fee, try again', 'woocommerce'), 500); } } }
/** * Create or update an order fee. * * @since 2.5.0 * @param \WC_Order $order * @param array $fee item data * @param string $action 'create' to add fee or 'update' to update it * @throws WC_CLI_Exception invalid data, server error */ protected function set_fee($order, $fee, $action) { if ('create' === $action) { // fee title is required if (!isset($fee['title'])) { throw new WC_CLI_Exception('woocommerce_invalid_fee_item', __('Fee title is required', 'woocommerce')); } $order_fee = new stdClass(); $order_fee->id = sanitize_title($fee['title']); $order_fee->name = $fee['title']; $order_fee->amount = isset($fee['total']) ? floatval($fee['total']) : 0; $order_fee->taxable = false; $order_fee->tax = 0; $order_fee->tax_data = array(); $order_fee->tax_class = ''; // if taxable, tax class and total are required if (isset($fee['taxable']) && $fee['taxable']) { if (!isset($fee['tax_class'])) { throw new WC_CLI_Exception('woocommerce_invalid_fee_item', __('Fee tax class is required when fee is taxable', 'woocommerce')); } $order_fee->taxable = true; $order_fee->tax_class = $fee['tax_class']; if (isset($fee['total_tax'])) { $order_fee->tax = isset($fee['total_tax']) ? wc_format_refund_total($fee['total_tax']) : 0; } if (isset($fee['tax_data'])) { $order_fee->tax = wc_format_refund_total(array_sum($fee['tax_data'])); $order_fee->tax_data = array_map('wc_format_refund_total', $fee['tax_data']); } } $fee_id = $order->add_fee($order_fee); if (!$fee_id) { throw new WC_CLI_Exception('woocommerce_cannot_create_fee', __('Cannot create fee, try again', 'woocommerce')); } } else { $fee_args = array(); if (isset($fee['title'])) { $fee_args['name'] = $fee['title']; } if (isset($fee['tax_class'])) { $fee_args['tax_class'] = $fee['tax_class']; } if (isset($fee['total'])) { $fee_args['line_total'] = floatval($fee['total']); } if (isset($fee['total_tax'])) { $fee_args['line_tax'] = floatval($fee['total_tax']); } $fee_id = $order->update_fee($fee['id'], $fee_args); if (!$fee_id) { throw new WC_CLI_Exception('woocommerce_cannot_update_fee', __('Cannot update fee, try again', 'woocommerce')); } } }
/** * Test wc_format_refund_total(). * * @since 2.2 */ public function test_wc_format_refund_total() { $this->assertEquals(-10, wc_format_refund_total(10)); $this->assertEquals(10, wc_format_refund_total(-10)); }
/** * Create a new order refund programmatically. * * Returns a new refund object on success which can then be used to add additional data. * * @since 2.2 * @param array $args * @return WC_Order_Refund|WP_Error */ function wc_create_refund($args = array()) { $default_args = array('amount' => 0, 'reason' => null, 'order_id' => 0, 'refund_id' => 0, 'line_items' => array()); try { $args = wp_parse_args($args, $default_args); $order = wc_get_order($args['order_id']); $refund = new WC_Order_Refund($args['refund_id']); if (!$order) { throw new Exception(__('Invalid order ID.', 'woocommerce')); } // prevent negative refunds if (0 > $args['amount']) { $args['amount'] = 0; } $refund->set_amount($args['amount']); $refund->set_parent_id(absint($args['order_id'])); $refund->set_refunded_by(get_current_user_id() ? get_current_user_id() : 1); if (!is_null($args['reason'])) { $refund->set_reason($args['reason']); } // Negative line items if (sizeof($args['line_items']) > 0) { $items = $order->get_items(array('line_item', 'fee', 'shipping')); foreach ($items as $item_id => $item) { if (!isset($args['line_items'][$item_id])) { continue; } $qty = isset($args['line_items'][$item_id]['qty']) ? $args['line_items'][$item_id]['qty'] : 0; $refund_total = $args['line_items'][$item_id]['refund_total']; $refund_tax = isset($args['line_items'][$item_id]['refund_tax']) ? array_filter((array) $args['line_items'][$item_id]['refund_tax']) : array(); if (empty($qty) && empty($refund_total) && empty($args['line_items'][$item_id]['refund_tax'])) { continue; } $class = get_class($item); $refunded_item = new $class($item); $refunded_item->set_id(0); $refunded_item->add_meta_data('_refunded_item_id', $item_id, true); $refunded_item->set_total(wc_format_refund_total($refund_total)); $refunded_item->set_taxes(array('total' => array_map('wc_format_refund_total', $refund_tax), 'subtotal' => array_map('wc_format_refund_total', $refund_tax))); if (is_callable(array($refunded_item, 'set_subtotal'))) { $refunded_item->set_subtotal(wc_format_refund_total($refund_total)); } if (is_callable(array($refunded_item, 'set_quantity'))) { $refunded_item->set_quantity($qty * -1); } $refund->add_item($refunded_item); } } $refund->update_taxes(); $refund->calculate_totals(false); $refund->set_total($args['amount'] * -1); $refund->save(); } catch (Exception $e) { return new WP_Error('error', $e->getMessage()); } return $refund; }
/** * Add an item to the provided order * * @since 3.0.0 * @param \WC_Order $order * @param array $item Parsed item data from CSV * @param string $type Line item type * @return int|false ID of the inserted order item, false on failure */ private function add_order_item(WC_Order $order, $item, $type) { $result = false; switch ($type) { case 'line_item': $product = $this->get_product_for_item($item); $args = $this->prepare_product_args($item); $result = $order->add_product($product, $args['qty'], $args); if (!$result) { wc_csv_import_suite()->log(sprintf(__('> > Warning: cannot add order item "%s".', 'woocommerce-csv-import-suite'), esc_html($identifier))); } break; case 'shipping': $args = array('order_item_name' => $item['method_title'], 'order_item_type' => 'shipping'); // we're using wc_add_order_item instead of $order->add_shipping because // we do not want the order total to be recalculated $result = wc_add_order_item($order->id, $args); if (!$result) { wc_csv_import_suite()->log(sprintf(__('> > Warning: cannot add shipping method "%s".', 'woocommerce-csv-import-suite'), esc_html($item['title']))); } break; case 'tax': $args = array('order_item_name' => $item['code'], 'order_item_type' => 'tax'); $result = wc_add_order_item($order->id, $args); if (!$result) { wc_csv_import_suite()->log(sprintf(__('> > Warning: cannot add tax "%s".', 'woocommerce-csv-import-suite'), esc_html($item['label']))); } break; case 'coupon': $result = $order->add_coupon($item['code'], $item['amount']); if (!$result) { wc_csv_import_suite()->log(sprintf(__('> > Warning: cannot add coupon "%s".', 'woocommerce-csv-import-suite'), esc_html($item['code']))); } break; case 'fee': $order_fee = new stdClass(); $order_fee->id = sanitize_title($item['title']); $order_fee->name = $item['title']; $order_fee->amount = isset($item['total']) ? floatval($item['total']) : 0; $order_fee->taxable = false; $order_fee->tax = 0; $order_fee->tax_data = array(); $order_fee->tax_class = ''; // if taxable, tax class and total are required if (isset($item['taxable']) && $item['taxable']) { $order_fee->taxable = true; $order_fee->tax_class = $item['tax_class']; if (isset($item['total_tax'])) { $order_fee->tax = isset($item['total_tax']) ? wc_format_refund_total($item['total_tax']) : 0; } if (isset($item['tax_data'])) { $tax_data = isset($item['tax_data']['total']) ? $item['tax_data']['total'] : $item['tax_data']; $order_fee->tax = wc_format_refund_total(array_sum($tax_data)); $order_fee->tax_data = array_map('wc_format_refund_total', $tax_data); } } $result = $order->add_fee($order_fee); if (!$result) { wc_csv_import_suite()->log(sprintf(__('> > Warning: cannot add fee "%s".', 'woocommerce-csv-import-suite'), esc_html($item['title']))); } break; } // store original order item ID if ($result && isset($item['order_item_id']) && $item['order_item_id'] > 0) { wc_update_order_item_meta($result, '_original_order_item_id', $item['order_item_id']); } return $result; }