2013-08-09 16:11:15 +00:00
< ? php
/**
2015-11-03 13:53:50 +00:00
* WooCommerce Order Functions
2013-08-09 16:11:15 +00:00
*
* Functions for order specific things .
*
2016-09-14 19:00:14 +00:00
* @ author WooThemes
* @ category Core
* @ package WooCommerce / Functions
2013-08-09 16:11:15 +00:00
*/
2014-09-20 19:10:04 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
2016-11-18 11:14:09 +00:00
exit ;
2014-09-20 19:10:04 +00:00
}
2013-08-09 16:11:15 +00:00
2016-03-09 13:25:36 +00:00
/**
2017-05-12 18:45:01 +00:00
* Standard way of retrieving orders based on certain parameters .
2016-03-09 13:25:36 +00:00
*
* This function should be used for order retrieval so that when we move to
* custom tables , functions still work .
*
* Args :
2017-05-12 18:45:01 +00:00
* 'name'
* 'parent' int post / order parent
* 'parent_exclude'
* 'exclude' array Order IDs to exclude from the query .
* 'limit' int Maximum of orders to retrieve .
* 'page' int Page of orders to retrieve . Ignored when using the 'offset' arg .
* 'offset' int Offset of orders to retrieve .
* 'paginate' bool If true , the return value will be an array with values :
2016-09-14 19:00:14 +00:00
* 'orders' => array of data ( return value above ),
* 'total' => total number of orders matching the query
* 'max_num_pages' => max number of pages found
2017-05-12 18:45:01 +00:00
* 'order' string ASC or DESC .
* 'orderby' string Order by date , title , id , modified , rand etc .
* 'return' string Type of data to return . Allowed values :
* ids array of order ids
* objects array of order objects ( default )
*
* 'status' array | string List of order statuses to find
* 'type' array | string Order type , e . g . shop_order or shop_order_refund
* 'currency'
* 'version'
* 'prices_include_tax'
* 'date_created'
* 'date_modified'
* 'date_completed'
* 'date_paid'
* 'discount_total'
* 'discount_tax'
* 'shipping_total'
* 'shipping_tax'
* 'cart_tax'
* 'total'
* 'total_tax'
* 'customer_id'
* 'customer' int | string | array User ID or billing email to limit orders to a
* particular user . Accepts array of values . Array of values is OR 'ed. If array of array is passed, each array will be AND' ed .
* e . g . test @ test . com , 1 , array ( 1 , 2 , 3 ), array ( array ( 1 , 'test@test.com' ), 2 , 3 )
* 'order_key'
* 'billing_first_name'
* 'billing_last_name'
* 'billing_company'
* 'billing_address_1'
* 'billing_address_2'
* 'billing_city'
* 'billing_state'
* 'billing_postcode'
* 'billing_country'
* 'billing_email'
* 'billing_phone'
* 'shipping_first_name'
* 'shipping_last_name'
* 'shipping_company'
* 'shipping_address_1'
* 'shipping_address_2'
* 'shipping_city'
* 'shipping_state'
* 'shipping_postcode'
* 'shipping_country'
* 'payment_method'
* 'payment_method_title'
* 'transaction_id'
* 'customer_ip_address'
* 'customer_user_agent'
* 'created_via'
* 'customer_note'
2016-03-09 13:25:36 +00:00
*
* @ since 2.6 . 0
* @ param array $args Array of args ( above )
2016-03-14 16:08:42 +00:00
* @ return array | stdClass Number of pages and an array of order objects if
2016-03-09 16:11:05 +00:00
* paginate is true , or just an array of values .
2016-03-09 13:25:36 +00:00
*/
function wc_get_orders ( $args ) {
2016-03-14 16:08:42 +00:00
$map_legacy = array (
'numberposts' => 'limit' ,
'post_type' => 'type' ,
'post_status' => 'status' ,
'post_parent' => 'parent' ,
'author' => 'customer' ,
2017-05-12 18:45:01 +00:00
'email' => 'billing_email' ,
2016-03-14 16:08:42 +00:00
'posts_per_page' => 'limit' ,
'paged' => 'page' ,
);
foreach ( $map_legacy as $from => $to ) {
if ( isset ( $args [ $from ] ) ) {
$args [ $to ] = $args [ $from ];
}
}
2016-03-09 13:25:36 +00:00
2017-05-12 18:45:01 +00:00
// Map legacy date args to modern date args.
$date_before = ! empty ( $args [ 'date_before' ] ) ? strtotime ( $args [ 'date_before' ] ) : false ;
$date_after = ! empty ( $args [ 'date_after' ] ) ? strtotime ( $args [ 'date_after' ] ) : false ;
if ( $date_before && $date_after ) {
$args [ 'date_created' ] = $date_before . '...' . $date_after ;
} elseif ( $date_before ) {
$args [ 'date_created' ] = '<' . $date_before ;
} elseif ( $date_after ) {
$args [ 'date_created' ] = '>' . $date_after ;
}
$query = new WC_Order_Query ( $args );
return $query -> get_orders ();
2016-03-09 13:25:36 +00:00
}
2016-03-09 14:50:34 +00:00
/**
2016-11-18 14:07:21 +00:00
* Main function for returning orders , uses the WC_Order_Factory class .
*
* @ since 2.2
* @ param mixed $the_order Post object or post ID of the order .
* @ return WC_Order | WC_Refund
2016-03-09 14:50:34 +00:00
*/
2016-11-18 14:07:21 +00:00
function wc_get_order ( $the_order = false ) {
2017-03-28 11:37:17 +00:00
if ( ! did_action ( 'woocommerce_after_register_post_type' ) ) {
wc_doing_it_wrong ( __FUNCTION__ , __ ( 'wc_get_order should not be called before post types are registered (woocommerce_after_register_post_type action).' , 'woocommerce' ), '2.5' );
2016-11-18 14:07:21 +00:00
return false ;
2016-03-09 14:50:34 +00:00
}
2016-11-18 14:07:21 +00:00
return WC () -> order_factory -> get_order ( $the_order );
2016-03-09 14:50:34 +00:00
}
2014-05-30 15:03:11 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get all order statuses .
2014-05-30 15:03:11 +00:00
*
* @ since 2.2
2017-04-04 10:52:54 +00:00
* @ used - by WC_Order :: set_status
2014-05-30 15:03:11 +00:00
* @ return array
*/
function wc_get_order_statuses () {
$order_statuses = array (
2016-10-12 10:16:30 +00:00
'wc-pending' => _x ( 'Pending payment' , 'Order status' , 'woocommerce' ),
2014-06-03 10:04:56 +00:00
'wc-processing' => _x ( 'Processing' , 'Order status' , 'woocommerce' ),
2016-10-12 10:16:30 +00:00
'wc-on-hold' => _x ( 'On hold' , 'Order status' , 'woocommerce' ),
2014-06-03 10:04:56 +00:00
'wc-completed' => _x ( 'Completed' , 'Order status' , 'woocommerce' ),
'wc-cancelled' => _x ( 'Cancelled' , 'Order status' , 'woocommerce' ),
'wc-refunded' => _x ( 'Refunded' , 'Order status' , 'woocommerce' ),
'wc-failed' => _x ( 'Failed' , 'Order status' , 'woocommerce' ),
2014-05-30 15:03:11 +00:00
);
return apply_filters ( 'wc_order_statuses' , $order_statuses );
}
2014-11-27 15:37:42 +00:00
/**
* See if a string is an order status .
* @ param string $maybe_status Status , including any wc - prefix
* @ return bool
*/
function wc_is_order_status ( $maybe_status ) {
$order_statuses = wc_get_order_statuses ();
return isset ( $order_statuses [ $maybe_status ] );
}
2016-11-03 11:27:03 +00:00
/**
* Get list of statuses which are consider 'paid' .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-03 11:27:03 +00:00
* @ return array
*/
function wc_get_is_paid_statuses () {
return apply_filters ( 'woocommerce_order_is_paid_statuses' , array ( 'processing' , 'completed' ) );
}
2014-05-30 16:43:21 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get the nice name for an order status .
2014-09-12 13:01:49 +00:00
*
* @ since 2.2
2014-05-30 16:43:21 +00:00
* @ param string $status
* @ return string
*/
function wc_get_order_status_name ( $status ) {
$statuses = wc_get_order_statuses ();
2014-09-12 13:01:49 +00:00
$status = 'wc-' === substr ( $status , 0 , 3 ) ? substr ( $status , 3 ) : $status ;
$status = isset ( $statuses [ 'wc-' . $status ] ) ? $statuses [ 'wc-' . $status ] : $status ;
2014-10-29 11:15:00 +00:00
return $status ;
2014-05-30 16:43:21 +00:00
}
2013-08-09 16:11:15 +00:00
/**
* Finds an Order ID based on an order key .
*
* @ param string $order_key An order key has generated by
* @ return int The ID of an order , or 0 if the order could not be found
*/
2013-11-25 13:54:52 +00:00
function wc_get_order_id_by_order_key ( $order_key ) {
2016-11-18 14:07:21 +00:00
$data_store = WC_Data_Store :: load ( 'order' );
2016-11-21 14:30:56 +00:00
return $data_store -> get_order_id_by_order_key ( $order_key );
2013-08-09 16:11:15 +00:00
}
2014-07-11 11:43:42 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get all registered order types .
2014-07-11 11:43:42 +00:00
*
2017-03-28 17:58:51 +00:00
* $for optionally define what you are getting order types for so only relevant types are returned .
2014-07-11 11:43:42 +00:00
*
* e . g . for 'order-meta-boxes' , 'order-count'
*
* @ since 2.2
* @ param string $for
* @ return array
*/
function wc_get_order_types ( $for = '' ) {
global $wc_order_types ;
if ( ! is_array ( $wc_order_types ) ) {
$wc_order_types = array ();
}
$order_types = array ();
switch ( $for ) {
case 'order-count' :
foreach ( $wc_order_types as $type => $args ) {
if ( ! $args [ 'exclude_from_order_count' ] ) {
$order_types [] = $type ;
}
}
break ;
case 'order-meta-boxes' :
foreach ( $wc_order_types as $type => $args ) {
if ( $args [ 'add_order_meta_boxes' ] ) {
$order_types [] = $type ;
}
}
break ;
case 'view-orders' :
foreach ( $wc_order_types as $type => $args ) {
if ( ! $args [ 'exclude_from_order_views' ] ) {
$order_types [] = $type ;
}
}
break ;
case 'reports' :
foreach ( $wc_order_types as $type => $args ) {
if ( ! $args [ 'exclude_from_order_reports' ] ) {
$order_types [] = $type ;
}
}
break ;
2014-10-06 12:39:49 +00:00
case 'sales-reports' :
foreach ( $wc_order_types as $type => $args ) {
if ( ! $args [ 'exclude_from_order_sales_reports' ] ) {
$order_types [] = $type ;
}
}
break ;
2015-05-12 04:01:23 +00:00
case 'order-webhooks' :
foreach ( $wc_order_types as $type => $args ) {
if ( ! $args [ 'exclude_from_order_webhooks' ] ) {
$order_types [] = $type ;
}
}
break ;
2014-07-11 11:43:42 +00:00
default :
$order_types = array_keys ( $wc_order_types );
break ;
}
return apply_filters ( 'wc_order_types' , $order_types , $for );
}
/**
2015-11-03 13:31:20 +00:00
* Get an order type by post type name .
2014-07-11 11:43:42 +00:00
* @ param string post type name
* @ return bool | array of datails about the order type
*/
function wc_get_order_type ( $type ) {
global $wc_order_types ;
if ( isset ( $wc_order_types [ $type ] ) ) {
return $wc_order_types [ $type ];
} else {
return false ;
}
}
/**
2014-07-22 13:30:42 +00:00
* Register order type . Do not use before init .
2014-07-11 11:43:42 +00:00
*
2015-11-03 13:31:20 +00:00
* Wrapper for register post type , as well as a method of telling WC which .
2014-07-11 11:43:42 +00:00
* post types are types of orders , and having them treated as such .
*
* $args are passed to register_post_type , but there are a few specific to this function :
2016-09-14 19:00:14 +00:00
* - exclude_from_orders_screen ( bool ) Whether or not this order type also get shown in the main .
* orders screen .
* - add_order_meta_boxes ( bool ) Whether or not the order type gets shop_order meta boxes .
* - exclude_from_order_count ( bool ) Whether or not this order type is excluded from counts .
* - exclude_from_order_views ( bool ) Whether or not this order type is visible by customers when .
* viewing orders e . g . on the my account page .
* - exclude_from_order_reports ( bool ) Whether or not to exclude this type from core reports .
* - exclude_from_order_sales_reports ( bool ) Whether or not to exclude this type from core sales reports .
2014-07-11 11:43:42 +00:00
*
* @ since 2.2
* @ see register_post_type for $args used in that function
* @ param string $type Post type . ( max . 20 characters , can not contain capital letters or spaces )
* @ param array $args An array of arguments .
* @ return bool Success or failure
*/
function wc_register_order_type ( $type , $args = array () ) {
if ( post_type_exists ( $type ) ) {
return false ;
}
global $wc_order_types ;
if ( ! is_array ( $wc_order_types ) ) {
$wc_order_types = array ();
}
// Register as a post type
if ( is_wp_error ( register_post_type ( $type , $args ) ) ) {
return false ;
}
// Register for WC usage
$order_type_args = array (
2014-10-06 12:39:49 +00:00
'exclude_from_orders_screen' => false ,
'add_order_meta_boxes' => true ,
'exclude_from_order_count' => false ,
'exclude_from_order_views' => false ,
2015-05-12 04:01:23 +00:00
'exclude_from_order_webhooks' => false ,
2014-10-06 12:39:49 +00:00
'exclude_from_order_reports' => false ,
'exclude_from_order_sales_reports' => false ,
2016-08-27 01:46:45 +00:00
'class_name' => 'WC_Order' ,
2014-07-11 11:43:42 +00:00
);
$args = array_intersect_key ( $args , $order_type_args );
$args = wp_parse_args ( $args , $order_type_args );
$wc_order_types [ $type ] = $args ;
return true ;
}
2013-08-09 16:11:15 +00:00
/**
2016-11-18 14:07:21 +00:00
* Return the count of processing orders .
2013-08-09 16:11:15 +00:00
*
* @ access public
2016-11-18 14:07:21 +00:00
* @ return int
2013-08-09 16:11:15 +00:00
*/
2016-11-18 14:07:21 +00:00
function wc_processing_order_count () {
return wc_orders_count ( 'processing' );
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
/**
* Return the orders count of a specific order status .
*
* @ param string $status
* @ return int
*/
function wc_orders_count ( $status ) {
$count = 0 ;
$status = 'wc-' . $status ;
$order_statuses = array_keys ( wc_get_order_statuses () );
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
if ( ! in_array ( $status , $order_statuses ) ) {
return 0 ;
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'orders' ) . $status ;
$cached_count = wp_cache_get ( $cache_key , 'counts' );
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
if ( false !== $cached_count ) {
return $cached_count ;
2013-12-17 05:07:19 +00:00
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
foreach ( wc_get_order_types ( 'order-count' ) as $type ) {
$data_store = WC_Data_Store :: load ( 'shop_order' === $type ? 'order' : $type );
if ( $data_store ) {
$count += $data_store -> get_order_count ( $status );
}
2013-09-10 11:26:31 +00:00
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
wp_cache_set ( $cache_key , $count , 'counts' );
2013-09-10 11:26:31 +00:00
2016-11-18 14:07:21 +00:00
return $count ;
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
/**
* Grant downloadable product access to the file identified by $download_id .
*
2016-11-18 17:13:02 +00:00
* @ param string $download_id file identifier
* @ param int | WC_Product $product
* @ param WC_Order $order the order
2016-11-18 14:07:21 +00:00
* @ param int $qty purchased
* @ return int | bool insert id or false on failure
*/
function wc_downloadable_file_permission ( $download_id , $product , $order , $qty = 1 ) {
if ( is_numeric ( $product ) ) {
$product = wc_get_product ( $product );
}
2016-11-18 17:13:02 +00:00
$download = new WC_Customer_Download ();
$download -> set_download_id ( $download_id );
$download -> set_product_id ( $product -> get_id () );
$download -> set_user_id ( $order -> get_customer_id () );
$download -> set_order_id ( $order -> get_id () );
2016-11-18 19:29:37 +00:00
$download -> set_user_email ( $order -> get_billing_email () );
2016-11-18 17:13:02 +00:00
$download -> set_order_key ( $order -> get_order_key () );
$download -> set_downloads_remaining ( 0 > $product -> get_download_limit () ? '' : $product -> get_download_limit () * $qty );
2017-03-13 23:54:33 +00:00
$download -> set_access_granted ( current_time ( 'timestamp' , true ) );
2016-11-18 17:13:02 +00:00
$download -> set_download_count ( 0 );
$expiry = $product -> get_download_expiry ();
if ( $expiry > 0 ) {
2017-03-10 16:35:47 +00:00
$from_date = $order -> get_date_completed () ? $order -> get_date_completed () -> format ( 'Y-m-d' ) : current_time ( 'mysql' , true );
$download -> set_access_expires ( strtotime ( $from_date . ' + ' . $expiry . ' DAY' ) );
2016-11-18 17:13:02 +00:00
}
return $download -> save ();
2013-08-09 16:11:15 +00:00
}
2013-12-16 23:27:57 +00:00
2013-08-09 16:11:15 +00:00
/**
2015-11-03 13:31:20 +00:00
* Order Status completed - GIVE DOWNLOADABLE PRODUCT ACCESS TO CUSTOMER .
2013-08-09 16:11:15 +00:00
*
* @ param int $order_id
*/
2016-11-18 17:13:02 +00:00
function wc_downloadable_product_permissions ( $order_id , $force = false ) {
2014-08-15 12:29:21 +00:00
$order = wc_get_order ( $order_id );
2013-08-09 16:11:15 +00:00
2016-11-18 17:13:02 +00:00
if ( ! $order || ( $order -> get_data_store () -> get_download_permissions_granted ( $order ) && ! $force ) ) {
2016-11-18 14:07:21 +00:00
return ;
}
if ( $order -> has_status ( 'processing' ) && 'no' === get_option ( 'woocommerce_downloads_grant_access_after_payment' ) ) {
2014-02-17 10:49:55 +00:00
return ;
2014-02-17 10:50:36 +00:00
}
2014-02-17 10:49:55 +00:00
2013-09-20 16:01:09 +00:00
if ( sizeof ( $order -> get_items () ) > 0 ) {
foreach ( $order -> get_items () as $item ) {
2016-11-18 14:07:21 +00:00
$product = $item -> get_product ();
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
if ( $product && $product -> exists () && $product -> is_downloadable () ) {
$downloads = $product -> get_downloads ();
2013-08-09 16:11:15 +00:00
2013-12-16 23:27:57 +00:00
foreach ( array_keys ( $downloads ) as $download_id ) {
2016-11-18 14:07:21 +00:00
wc_downloadable_file_permission ( $download_id , $product , $order , $item -> get_quantity () );
2013-12-16 23:27:57 +00:00
}
2013-09-20 16:01:09 +00:00
}
}
}
2013-08-09 16:11:15 +00:00
2016-11-18 14:07:21 +00:00
$order -> get_data_store () -> set_download_permissions_granted ( $order , true );
2013-09-10 11:26:31 +00:00
do_action ( 'woocommerce_grant_product_download_permissions' , $order_id );
2013-08-09 16:11:15 +00:00
}
2014-02-17 10:49:55 +00:00
add_action ( 'woocommerce_order_status_completed' , 'wc_downloadable_product_permissions' );
add_action ( 'woocommerce_order_status_processing' , 'wc_downloadable_product_permissions' );
2014-02-26 15:43:19 +00:00
/**
* Clear all transients cache for order data .
*
2016-11-18 11:14:09 +00:00
* @ param int | WC_Order $order
2014-02-26 15:43:19 +00:00
*/
2016-11-18 11:14:09 +00:00
function wc_delete_shop_order_transients ( $order = 0 ) {
if ( is_numeric ( $order ) ) {
$order = wc_get_order ( $order );
}
$reports = WC_Admin_Reports :: get_reports ();
$transients_to_clear = array (
'wc_admin_report'
);
2014-03-20 11:10:25 +00:00
foreach ( $reports as $report_group ) {
foreach ( $report_group [ 'reports' ] as $report_key => $report ) {
$transients_to_clear [] = 'wc_report_' . $report_key ;
2014-03-18 10:48:18 +00:00
}
}
2016-08-27 04:23:02 +00:00
foreach ( $transients_to_clear as $transient ) {
2014-02-26 15:43:19 +00:00
delete_transient ( $transient );
}
2016-02-08 12:26:46 +00:00
// Clear money spent for user associated with order
2016-11-18 11:14:09 +00:00
if ( is_a ( $order , 'WC_Order' ) ) {
$order_id = $order -> get_id ();
delete_user_meta ( $order -> get_customer_id (), '_money_spent' );
delete_user_meta ( $order -> get_customer_id (), '_order_count' );
} else {
$order_id = 0 ;
2016-02-08 12:26:46 +00:00
}
2015-03-27 13:17:54 +00:00
// Increments the transient version to invalidate cache
WC_Cache_Helper :: get_transient_version ( 'orders' , true );
2015-11-05 15:21:28 +00:00
// Do the same for regular cache
2015-11-13 23:11:05 +00:00
WC_Cache_Helper :: incr_cache_prefix ( 'orders' );
2015-11-05 15:21:28 +00:00
2016-11-18 11:14:09 +00:00
do_action ( 'woocommerce_delete_shop_order_transients' , $order_id );
2014-02-26 15:43:19 +00:00
}
2014-06-17 20:39:02 +00:00
/**
2015-11-03 13:31:20 +00:00
* See if we only ship to billing addresses .
2014-06-17 20:39:02 +00:00
* @ return bool
*/
function wc_ship_to_billing_address_only () {
return 'billing_only' === get_option ( 'woocommerce_ship_to_destination' );
}
2014-07-08 14:32:47 +00:00
/**
2015-11-03 13:31:20 +00:00
* Create a new order refund programmatically .
2014-07-08 14:32:47 +00:00
*
2015-02-11 14:14:42 +00:00
* Returns a new refund object on success which can then be used to add additional data .
2014-07-08 14:32:47 +00:00
*
* @ since 2.2
* @ param array $args
2015-02-03 15:08:36 +00:00
* @ return WC_Order_Refund | WP_Error
2014-07-08 14:32:47 +00:00
*/
function wc_create_refund ( $args = array () ) {
$default_args = array (
2017-02-08 16:19:47 +00:00
'amount' => 0 ,
'reason' => null ,
'order_id' => 0 ,
'refund_id' => 0 ,
'line_items' => array (),
'refund_payment' => false ,
'restock_items' => false ,
2014-07-08 14:32:47 +00:00
);
2016-08-24 14:26:35 +00:00
try {
2017-02-08 16:19:47 +00:00
$args = wp_parse_args ( $args , $default_args );
2016-08-22 10:00:31 +00:00
2017-02-08 16:19:47 +00:00
if ( ! $order = wc_get_order ( $args [ 'order_id' ] ) ) {
2016-08-24 15:02:19 +00:00
throw new Exception ( __ ( 'Invalid order ID.' , 'woocommerce' ) );
2016-08-24 14:26:35 +00:00
}
2014-07-08 14:32:47 +00:00
2017-02-08 16:19:47 +00:00
$remaining_refund_amount = $order -> get_remaining_refund_amount ();
$remaining_refund_items = $order -> get_remaining_refund_items ();
$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' ) );
2016-08-24 14:26:35 +00:00
}
2017-02-08 16:19:47 +00:00
2017-01-10 14:14:03 +00:00
$refund -> set_currency ( $order -> get_currency () );
2016-08-24 14:26:35 +00:00
$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 );
2014-07-08 14:32:47 +00:00
2016-08-24 14:26:35 +00:00
if ( ! is_null ( $args [ 'reason' ] ) ) {
$refund -> set_reason ( $args [ 'reason' ] );
}
2014-07-08 14:32:47 +00:00
2016-08-24 14:26:35 +00:00
// Negative line items
if ( sizeof ( $args [ 'line_items' ] ) > 0 ) {
$items = $order -> get_items ( array ( 'line_item' , 'fee' , 'shipping' ) );
2016-08-22 10:00:31 +00:00
2016-08-24 14:26:35 +00:00
foreach ( $items as $item_id => $item ) {
2016-09-07 09:04:56 +00:00
if ( ! isset ( $args [ 'line_items' ][ $item_id ] ) ) {
2016-08-24 14:26:35 +00:00
continue ;
}
2016-08-22 10:00:31 +00:00
2016-11-17 16:53:13 +00:00
$qty = isset ( $args [ 'line_items' ][ $item_id ][ 'qty' ] ) ? $args [ 'line_items' ][ $item_id ][ 'qty' ] : 0 ;
2016-09-07 09:04:56 +00:00
$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 ;
2016-08-24 14:26:35 +00:00
}
2014-10-06 12:39:49 +00:00
2016-08-24 14:26:35 +00:00
$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 );
2016-09-07 09:04:56 +00:00
$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 ) ) );
2014-10-06 12:39:49 +00:00
2016-08-24 14:26:35 +00:00
if ( is_callable ( array ( $refunded_item , 'set_subtotal' ) ) ) {
2016-09-07 09:04:56 +00:00
$refunded_item -> set_subtotal ( wc_format_refund_total ( $refund_total ) );
}
if ( is_callable ( array ( $refunded_item , 'set_quantity' ) ) ) {
2016-11-23 15:10:18 +00:00
$refunded_item -> set_quantity ( $qty * - 1 );
2016-08-24 14:26:35 +00:00
}
2014-10-07 22:58:21 +00:00
2016-08-24 14:26:35 +00:00
$refund -> add_item ( $refunded_item );
2017-02-08 16:19:47 +00:00
$refund_item_count += $qty ;
2016-08-24 14:26:35 +00:00
}
2016-08-22 12:04:57 +00:00
}
2016-08-24 14:26:35 +00:00
$refund -> update_taxes ();
$refund -> calculate_totals ( false );
$refund -> set_total ( $args [ 'amount' ] * - 1 );
2017-01-10 14:14:03 +00:00
/**
* Action hook to adjust refund before save .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-01-10 14:14:03 +00:00
*/
do_action ( 'woocommerce_create_refund' , $refund , $args );
2017-02-08 16:19:47 +00:00
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 () );
2017-02-27 11:55:09 +00:00
$parent_status = apply_filters ( 'woocommerce_order_fully_refunded_status' , 'refunded' , $order -> get_id (), $refund -> get_id () );
if ( $parent_status ) {
$order -> update_status ( $parent_status );
}
2017-02-08 16:19:47 +00:00
}
}
2016-08-24 14:26:35 +00:00
2017-01-10 14:14:03 +00:00
do_action ( 'woocommerce_refund_created' , $refund -> get_id (), $args );
2017-02-08 16:19:47 +00:00
do_action ( 'woocommerce_order_refunded' , $order -> get_id (), $refund -> get_id () );
2017-01-10 14:14:03 +00:00
2016-08-24 14:26:35 +00:00
} catch ( Exception $e ) {
2016-08-24 15:02:19 +00:00
return new WP_Error ( 'error' , $e -> getMessage () );
2014-07-23 16:41:35 +00:00
}
2014-07-23 16:56:43 +00:00
2016-08-22 10:00:31 +00:00
return $refund ;
2014-07-08 14:32:47 +00:00
}
2014-07-19 04:08:02 +00:00
2017-02-08 16:19:47 +00:00
/**
* Try to refund the payment for an order via the gateway .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-02-08 16:19:47 +00:00
* @ 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 .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-02-08 16:19:47 +00:00
* @ 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 );
}
}
}
2014-07-19 04:08:02 +00:00
/**
* Get tax class by tax id .
*
* @ since 2.2
* @ param int $tax_id
* @ return string
*/
function wc_get_tax_class_by_tax_id ( $tax_id ) {
global $wpdb ;
2016-11-18 14:07:21 +00:00
return $wpdb -> get_var ( $wpdb -> prepare ( " SELECT tax_rate_class FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %d " , $tax_id ) );
2014-07-19 04:08:02 +00:00
}
2014-07-22 15:13:35 +00:00
/**
* Get payment gateway class by order data .
*
* @ since 2.2
* @ param int | WC_Order $order
* @ return WC_Payment_Gateway | bool
*/
function wc_get_payment_gateway_by_order ( $order ) {
if ( WC () -> payment_gateways () ) {
$payment_gateways = WC () -> payment_gateways -> payment_gateways ();
} else {
$payment_gateways = array ();
}
2014-08-25 19:11:39 +00:00
if ( ! is_object ( $order ) ) {
$order_id = absint ( $order );
2015-07-30 18:07:34 +00:00
$order = wc_get_order ( $order_id );
2014-07-22 15:13:35 +00:00
}
2017-05-10 18:54:20 +00:00
return is_a ( $order , 'WC_Order' ) && isset ( $payment_gateways [ $order -> get_payment_method () ] ) ? $payment_gateways [ $order -> get_payment_method () ] : false ;
2014-07-22 15:13:35 +00:00
}
2015-04-24 14:58:13 +00:00
/**
* When refunding an order , create a refund line item if the partial refunds do not match order total .
*
* This is manual ; no gateway refund will be performed .
*
* @ since 2.4
* @ param int $order_id
*/
function wc_order_fully_refunded ( $order_id ) {
$order = wc_get_order ( $order_id );
$max_refund = wc_format_decimal ( $order -> get_total () - $order -> get_total_refunded () );
if ( ! $max_refund ) {
return ;
}
// Create the refund object
2016-06-06 17:57:24 +00:00
wc_create_refund ( array (
2015-04-24 14:58:13 +00:00
'amount' => $max_refund ,
2016-10-12 10:16:30 +00:00
'reason' => __ ( 'Order fully refunded' , 'woocommerce' ),
2015-04-24 14:58:13 +00:00
'order_id' => $order_id ,
2016-08-27 01:46:45 +00:00
'line_items' => array (),
2015-04-24 14:58:13 +00:00
) );
}
add_action ( 'woocommerce_order_status_refunded' , 'wc_order_fully_refunded' );
2016-06-13 13:42:10 +00:00
/**
2016-11-18 14:07:21 +00:00
* Search orders .
2016-06-13 13:42:10 +00:00
*
* @ since 2.6 . 0
* @ param string $term Term to search .
* @ return array List of orders ID .
*/
function wc_order_search ( $term ) {
2016-11-18 14:07:21 +00:00
$data_store = WC_Data_Store :: load ( 'order' );
return $data_store -> search_orders ( str_replace ( 'Order #' , '' , wc_clean ( $term ) ) );
2016-06-13 13:42:10 +00:00
}
2016-06-21 19:26:35 +00:00
/**
* Update total sales amount for each product within a paid order .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-06-21 19:26:35 +00:00
* @ param int $order_id
*/
function wc_update_total_sales_counts ( $order_id ) {
$order = wc_get_order ( $order_id );
2016-11-18 14:07:21 +00:00
if ( ! $order || $order -> get_data_store () -> get_recorded_sales ( $order ) ) {
2016-06-21 19:26:35 +00:00
return ;
}
if ( sizeof ( $order -> get_items () ) > 0 ) {
foreach ( $order -> get_items () as $item ) {
2017-02-10 23:41:53 +00:00
if ( $product_id = $item -> get_product_id () ) {
$data_store = WC_Data_Store :: load ( 'product' );
$data_store -> update_product_sales ( $product_id , absint ( $item [ 'qty' ] ), 'increase' );
2016-06-21 19:26:35 +00:00
}
}
}
2016-11-18 14:07:21 +00:00
$order -> get_data_store () -> set_recorded_sales ( $order , true );
2016-06-21 19:26:35 +00:00
/**
* Called when sales for an order are recorded
*
* @ param int $order_id order id
*/
do_action ( 'woocommerce_recorded_sales' , $order_id );
}
add_action ( 'woocommerce_order_status_completed' , 'wc_update_total_sales_counts' );
add_action ( 'woocommerce_order_status_processing' , 'wc_update_total_sales_counts' );
add_action ( 'woocommerce_order_status_on-hold' , 'wc_update_total_sales_counts' );
/**
* Update used coupon amount for each coupon within an order .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-06-21 19:26:35 +00:00
* @ param int $order_id
*/
function wc_update_coupon_usage_counts ( $order_id ) {
2016-11-18 14:07:21 +00:00
if ( ! $order = wc_get_order ( $order_id ) ) {
2016-06-21 19:26:35 +00:00
return ;
}
2016-11-18 14:07:21 +00:00
$has_recorded = $order -> get_data_store () -> get_recorded_coupon_usage_counts ( $order );
if ( $order -> has_status ( 'cancelled' ) && $has_recorded ) {
2016-06-21 19:26:35 +00:00
$action = 'reduce' ;
2016-11-18 14:07:21 +00:00
$order -> get_data_store () -> set_recorded_coupon_usage_counts ( $order , false );
} elseif ( ! $order -> has_status ( 'cancelled' ) && ! $has_recorded ) {
2016-06-21 19:26:35 +00:00
$action = 'increase' ;
2016-11-18 14:07:21 +00:00
$order -> get_data_store () -> set_recorded_coupon_usage_counts ( $order , true );
2016-06-21 19:26:35 +00:00
} else {
return ;
}
if ( sizeof ( $order -> get_used_coupons () ) > 0 ) {
foreach ( $order -> get_used_coupons () as $code ) {
if ( ! $code ) {
continue ;
}
$coupon = new WC_Coupon ( $code );
if ( ! $used_by = $order -> get_user_id () ) {
$used_by = $order -> get_billing_email ();
}
switch ( $action ) {
case 'reduce' :
2017-03-08 23:27:37 +00:00
$coupon -> decrease_usage_count ( $used_by );
2016-06-21 19:26:35 +00:00
break ;
case 'increase' :
2017-03-08 23:27:37 +00:00
$coupon -> increase_usage_count ( $used_by );
2016-06-21 19:26:35 +00:00
break ;
}
}
}
}
2016-10-18 11:27:56 +00:00
add_action ( 'woocommerce_order_status_pending' , 'wc_update_coupon_usage_counts' );
add_action ( 'woocommerce_order_status_completed' , 'wc_update_coupon_usage_counts' );
add_action ( 'woocommerce_order_status_processing' , 'wc_update_coupon_usage_counts' );
add_action ( 'woocommerce_order_status_on-hold' , 'wc_update_coupon_usage_counts' );
add_action ( 'woocommerce_order_status_cancelled' , 'wc_update_coupon_usage_counts' );
2016-06-21 19:26:35 +00:00
/**
2016-11-02 18:50:42 +00:00
* Cancel all unpaid orders after held duration to prevent stock lock for those products .
2016-06-21 19:26:35 +00:00
*/
2016-11-02 18:50:42 +00:00
function wc_cancel_unpaid_orders () {
$held_duration = get_option ( 'woocommerce_hold_stock_minutes' );
if ( $held_duration < 1 || 'yes' !== get_option ( 'woocommerce_manage_stock' ) ) {
return ;
2016-06-21 19:26:35 +00:00
}
2016-11-18 14:07:21 +00:00
$data_store = WC_Data_Store :: load ( 'order' );
$unpaid_orders = $data_store -> get_unpaid_orders ( strtotime ( '-' . absint ( $held_duration ) . ' MINUTES' , current_time ( 'timestamp' ) ) );
2016-06-21 19:26:35 +00:00
2016-11-02 18:50:42 +00:00
if ( $unpaid_orders ) {
foreach ( $unpaid_orders as $unpaid_order ) {
$order = wc_get_order ( $unpaid_order );
2016-11-18 14:07:21 +00:00
if ( apply_filters ( 'woocommerce_cancel_unpaid_order' , 'checkout' === $order -> get_created_via (), $order ) ) {
2016-11-02 18:50:42 +00:00
$order -> update_status ( 'cancelled' , __ ( 'Unpaid order cancelled - time limit reached.' , 'woocommerce' ) );
2016-06-21 19:26:35 +00:00
}
}
}
2016-11-02 18:50:42 +00:00
wp_clear_scheduled_hook ( 'woocommerce_cancel_unpaid_orders' );
wp_schedule_single_event ( time () + ( absint ( $held_duration ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
2016-06-21 19:26:35 +00:00
}
2016-11-02 18:50:42 +00:00
add_action ( 'woocommerce_cancel_unpaid_orders' , 'wc_cancel_unpaid_orders' );