Skip to content

Instantly share code, notes, and snippets.

@YourMark
Last active July 9, 2020 16:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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' ) );
@n9yty
Copy link

n9yty commented Jul 29, 2019

Hi! Was this updated to fix the issue you had? I saw on GitHub you said you had to pass the Tax ID in, but I'm not sure where you had to do that and if this code reflects that. Looking to use this for a current one-time problem (hopefully).

@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