Skip to content

Instantly share code, notes, and snippets.

@YourMark
Last active July 9, 2020 16:46
Show Gist options
  • Save YourMark/cc5c02aa624090c453adbdf05aa7e6c5 to your computer and use it in GitHub Desktop.
Save YourMark/cc5c02aa624090c453adbdf05aa7e6c5 to your computer and use it in GitHub Desktop.
<?php
namespace Patboard\Plugin;
/**
* Class Cli_Command
*
*/
class Cli_Command extends \WP_CLI_Command {
/**
* @var string
*/
private $reason = 'VAT correction';
/**
* Create refund orders for given order id's.
*
* @param $args
* @param $assoc_args
*
* @throws \Exception
*/
function refund( $args, $assoc_args ) {
$orders = $assoc_args['orders'];
$orders = explode( ',', $orders );
if ( isset( $assoc_args['reason'] ) ) {
$this->reason = $assoc_args['reason'];
}
foreach ( $orders as $orderid ) {
$refunded = $this->refund_order( $orderid );
if ( is_wp_error( $refunded ) ) {
echo $orderid . ': ' . $refunded->get_error_message();
}
}
}
/**
* @param $orderid
*
* @return \WC_Order_Refund|\WP_Error
* @throws \Exception
*/
private function refund_order( $orderid ) {
$order = wc_get_order( $orderid );
// If it's something else such as a WC_Order_Refund, we don't want that.
if ( ! is_a( $order, 'WC_Order' ) ) {
return new \WP_Error( 'wc-order', 'Provided ID is not a WC Order' );
}
if ( 'refunded' == $order->get_status() ) {
return new \WP_Error( 'wc-order', 'Order has been already refunded' );
}
// Get Items
$order_items = $order->get_items( array( 'line_item', 'fee', 'shipping' ) );
// Refund Amount
$refund_amount = 0;
// Prepare line items which we are refunding
$line_items = [];
// Iterating through order shipping items
if ( ! $order_items ) {
return new \WP_Error( 'wc-order', 'This order has no items' );
}
foreach ( $order_items as $item_id => $item ) {
$line_total = $item->get_total();
$qty = $item->get_quantity();
$tax_data = $item->get_total_tax();
$refund_tax = 0;
if ( ! empty( $tax_data ) ) {
$refund_tax = wc_format_decimal( $tax_data );
}
$refund_amount = wc_format_decimal( $refund_amount ) + wc_format_decimal( $line_total );
$line_items[ $item_id ] = array(
'qty' => $qty,
'refund_total' => wc_format_decimal( $line_total ),
'refund_tax' => $refund_tax
);
}
$refund = wc_create_refund( array(
'amount' => $refund_amount,
'reason' => $this->reason,
'order_id' => $orderid,
'line_items' => $line_items,
'refund_payment' => false,
) );
return $refund;
}
}
\WP_CLI::add_command( 'refund', array( __namespace__ . '\Cli_Command', 'refund' ) );
@YourMark
Copy link
Author

YourMark commented Jul 30, 2019

Hello @n9yty.

This is the solution that worked for me.

<?php

namespace Patboard\Plugin;

class Cli_Command extends \WP_CLI_Command {

	/**
	 * @var string
	 */
	private $reason = 'VAT correction';

	/**
	 * Create refund orders for given order id's.
	 *
	 * @param $args
	 * @param $assoc_args
	 *
	 * @throws \Exception
	 */
	function refund( $args, $assoc_args ) {
		$orders = $assoc_args['orders'];
		$orders = explode( ',', $orders );

		if ( isset( $assoc_args['reason'] ) ) {
			$this->reason = $assoc_args['reason'];
		}

		foreach ( $orders as $orderid ) {
			if( empty( $orderid ) ) {
				continue;
			}
			$refunded = $this->refund_order( $orderid );

			if ( is_wp_error( $refunded ) ) {
				echo \WP_CLI::log( \WP_CLI::colorize( '%r' . $orderid . ': ' . $refunded->get_error_message() . '%r' ) );
			}
			else {
				echo \WP_CLI::log( \WP_CLI::colorize( '%g' . $orderid . ': Successfully refunded' . '%g' ) );
			}
		}
	}

	/**
	 * @param $orderid
	 *
	 * @return \WC_Order_Refund|\WP_Error
	 * @throws \Exception
	 */
	private function refund_order( $orderid ) {
		$order = wc_get_order( $orderid );

		// If it's something else such as a WC_Order_Refund, we don't want that.
		if ( ! is_a( $order, 'WC_Order' ) ) {
			return new \WP_Error( 'wc-order', 'Provided ID is not a WC Order' );
		}

		if ( 'refunded' == $order->get_status() ) {
			return new \WP_Error( 'wc-order', 'Order has been already refunded' );
		}

		// Get Items
		$order_items = $order->get_items( array( 'line_item', 'fee', 'shipping' ) );

		// Refund Amount
		$refund_amount = 0;

		// Prepare line items which we are refunding
		$line_items = [];

		// Iterating through order shipping items

		if ( ! $order_items ) {
			return new \WP_Error( 'wc-order', 'This order has no items' );
		}

		foreach ( $order_items as $item_id => $item ) {
			$line_total = $order->get_line_total( $item, false, false );
			$qty        = $item->get_quantity();
			$tax_data   = wc_get_order_item_meta( $item_id, '_line_tax_data' );

			$refund_tax = array();

			// Check if it's shipping costs. If so, get shipping taxes.
			if ( $item instanceof \WC_Order_Item_Shipping ) {
				$tax_data = wc_get_order_item_meta( $item_id, 'taxes' );
			}

			// If taxdata is set, format as decimal.
			if ( ! empty( $tax_data['total'] ) ) {
				$refund_tax = array_filter( array_map( 'wc_format_decimal', $tax_data['total'] ) );
			}


			// Calculate line total, including tax.
			$line_total_inc_tax = wc_format_decimal( $line_total ) + ( is_numeric( reset( $refund_tax ) ) ? wc_format_decimal( reset( $refund_tax ) ) : 0 );

			// Add the total for this line tot the grand total.
			$refund_amount = wc_format_decimal( $refund_amount ) + round( $line_total_inc_tax, 2 );

			// Fill item per line.
			$line_items[ $item_id ] = array(
				'qty'          => $qty,
				'refund_total' => wc_format_decimal( $line_total ),
				'refund_tax'   => array_map( 'wc_round_tax_total', $refund_tax )
			);
		}

		// Check if the refund amount matches the actual order amount
		$expected_refund = floatval( $order->get_total() );
		$actual_refund   = round( $refund_amount, 2 );

		if ( $expected_refund !== $actual_refund ) {
			$sum = $expected_refund - $actual_refund;

			return new \WP_Error( 'wc-order', sprintf( 'Return amount is off by %f', $sum ) );
		}
		// And finally, create the refund.
		$refund = wc_create_refund( array(
			'amount'         => $actual_refund,
			'reason'         => $this->reason,
			'order_id'       => $orderid,
			'line_items'     => $line_items,
			'refund_payment' => false,
		) );

		return $refund;
	}
}

\WP_CLI::add_command( 'refund', array( __namespace__ . '\Cli_Command', 'refund' ) );

@mischasigtermans
Copy link

Hi! Not sure where you get the taxes id from. Could you please share this? We're also trying to refund the total VAT in our refund request.

@YourMark
Copy link
Author

@pixelstart
I believe that's given in
$tax_data = wc_get_order_item_meta( $item_id, '_line_tax_data' );

The $tax_data contains the ID and value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment