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
* @ version 2.1 . 0
*/
2014-09-20 19:10:04 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ; // Exit if accessed directly
}
2013-08-09 16:11:15 +00:00
2016-03-09 13:25:36 +00:00
/**
* Wrapper for get_posts specific to orders .
*
* This function should be used for order retrieval so that when we move to
* custom tables , functions still work .
*
* Args :
2016-09-14 19:00:14 +00:00
* status array | string List of order statuses to find
* type array | string Order type , e . g . shop_order or shop_order_refund
* parent int post / order parent
* 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 )
* limit int Maximum of orders to retrieve .
* offset int Offset of orders to retrieve .
* page int Page of orders to retrieve . Ignored when using the 'offset' arg .
* exclude array Order IDs to exclude from the query .
* orderby string Order by date , title , id , modified , rand etc
* order string ASC or DESC
* return string Type of data to return . Allowed values :
* ids array of order ids
* objects array of order objects ( default )
* paginate bool If true , the return value will be an array with values :
* 'orders' => array of data ( return value above ),
* 'total' => total number of orders matching the query
* 'max_num_pages' => max number of pages found
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 ) {
$args = wp_parse_args ( $args , array (
'status' => array_keys ( wc_get_order_statuses () ),
'type' => wc_get_order_types ( 'view-orders' ),
'parent' => null ,
2016-03-14 16:08:42 +00:00
'customer' => null ,
2016-03-09 14:50:34 +00:00
'email' => '' ,
2016-03-14 16:08:42 +00:00
'limit' => get_option ( 'posts_per_page' ),
2016-03-09 13:25:36 +00:00
'offset' => null ,
'page' => 1 ,
'exclude' => array (),
'orderby' => 'date' ,
'order' => 'DESC' ,
2016-03-09 16:11:05 +00:00
'return' => 'objects' ,
'paginate' => false ,
2016-03-09 13:25:36 +00:00
) );
// Handle some BW compatibility arg names where wp_query args differ in naming.
2016-03-14 16:08:42 +00:00
$map_legacy = array (
'numberposts' => 'limit' ,
'post_type' => 'type' ,
'post_status' => 'status' ,
'post_parent' => 'parent' ,
'author' => 'customer' ,
'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
/**
* Generate WP_Query args . This logic will change if orders are moved to
* custom tables in the future .
*/
$wp_query_args = array (
2016-03-09 16:11:05 +00:00
'post_type' => $args [ 'type' ] ? $args [ 'type' ] : 'shop_order' ,
2016-03-09 13:25:36 +00:00
'post_status' => $args [ 'status' ],
'posts_per_page' => $args [ 'limit' ],
'meta_query' => array (),
'fields' => 'ids' ,
'orderby' => $args [ 'orderby' ],
'order' => $args [ 'order' ],
);
if ( ! is_null ( $args [ 'parent' ] ) ) {
$wp_query_args [ 'post_parent' ] = absint ( $args [ 'parent' ] );
}
if ( ! is_null ( $args [ 'offset' ] ) ) {
$wp_query_args [ 'offset' ] = absint ( $args [ 'offset' ] );
} else {
$wp_query_args [ 'paged' ] = absint ( $args [ 'page' ] );
}
if ( ! empty ( $args [ 'customer' ] ) ) {
2016-03-09 14:50:34 +00:00
$values = is_array ( $args [ 'customer' ] ) ? $args [ 'customer' ] : array ( $args [ 'customer' ] );
$wp_query_args [ 'meta_query' ][] = _wc_get_orders_generate_customer_meta_query ( $values );
2016-03-09 13:25:36 +00:00
}
if ( ! empty ( $args [ 'exclude' ] ) ) {
$wp_query_args [ 'post__not_in' ] = array_map ( 'absint' , $args [ 'exclude' ] );
}
2016-08-27 03:23:21 +00:00
if ( ! $args [ 'paginate' ] ) {
2016-03-09 16:11:05 +00:00
$wp_query_args [ 'no_found_rows' ] = true ;
}
2016-03-09 13:25:36 +00:00
// Get results.
$orders = new WP_Query ( $wp_query_args );
if ( 'objects' === $args [ 'return' ] ) {
$return = array_map ( 'wc_get_order' , $orders -> posts );
} else {
$return = $orders -> posts ;
}
2016-08-27 03:23:21 +00:00
if ( $args [ 'paginate' ] ) {
2016-03-09 16:11:05 +00:00
return ( object ) array (
'orders' => $return ,
'total' => $orders -> found_posts ,
'max_num_pages' => $orders -> max_num_pages ,
);
} else {
return $return ;
}
2016-03-09 13:25:36 +00:00
}
2016-03-09 14:50:34 +00:00
/**
* Generate meta query for wc_get_orders . Used internally only .
* @ since 2.6 . 0
* @ param array $values
* @ param string $relation
* @ return array
*/
function _wc_get_orders_generate_customer_meta_query ( $values , $relation = 'or' ) {
$meta_query = array (
'relation' => strtoupper ( $relation ),
'customer_emails' => array (
'key' => '_billing_email' ,
'value' => array (),
2016-03-14 16:08:42 +00:00
'compare' => 'IN' ,
2016-03-09 14:50:34 +00:00
),
'customer_ids' => array (
'key' => '_customer_user' ,
'value' => array (),
2016-03-14 16:08:42 +00:00
'compare' => 'IN' ,
2016-08-27 01:46:45 +00:00
),
2016-03-09 14:50:34 +00:00
);
foreach ( $values as $value ) {
if ( is_array ( $value ) ) {
$meta_query [] = _wc_get_orders_generate_customer_meta_query ( $value , 'and' );
} elseif ( is_email ( $value ) ) {
$meta_query [ 'customer_emails' ][ 'value' ][] = sanitize_email ( $value );
} else {
$meta_query [ 'customer_ids' ][ 'value' ][] = strval ( absint ( $value ) );
}
}
if ( empty ( $meta_query [ 'customer_emails' ][ 'value' ] ) ) {
unset ( $meta_query [ 'customer_emails' ] );
unset ( $meta_query [ 'relation' ] );
}
if ( empty ( $meta_query [ 'customer_ids' ][ 'value' ] ) ) {
unset ( $meta_query [ 'customer_ids' ] );
unset ( $meta_query [ 'relation' ] );
}
return $meta_query ;
}
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
* @ 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' .
* @ since 2.7 . 0
* @ return array
*/
function wc_get_is_paid_statuses () {
return apply_filters ( 'woocommerce_order_is_paid_statuses' , array ( 'processing' , 'completed' ) );
}
2014-06-13 13:21:10 +00:00
/**
* Main function for returning orders , uses the WC_Order_Factory class .
*
2014-09-12 13:01:49 +00:00
* @ since 2.2
* @ param mixed $the_order Post object or post ID of the order .
2016-06-06 17:57:24 +00:00
* @ return WC_Order | WC_Refund
2014-06-13 13:21:10 +00:00
*/
2014-08-15 12:56:43 +00:00
function wc_get_order ( $the_order = false ) {
2016-01-04 10:43:48 +00:00
if ( ! did_action ( 'woocommerce_init' ) ) {
_doing_it_wrong ( __FUNCTION__ , __ ( 'wc_get_order should not be called before the woocommerce_init action.' , 'woocommerce' ), '2.5' );
return false ;
}
2014-08-15 12:56:43 +00:00
return WC () -> order_factory -> get_order ( $the_order );
2014-06-13 13:21:10 +00:00
}
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 .
*
* @ access public
* @ 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 ) {
2013-08-09 16:11:15 +00:00
global $wpdb ;
// Faster than get_posts()
2014-09-16 09:45:28 +00:00
$order_id = $wpdb -> get_var ( $wpdb -> prepare ( " SELECT post_id FROM { $wpdb -> prefix } postmeta WHERE meta_key = '_order_key' AND meta_value = %s " , $order_key ) );
2013-08-09 16:11:15 +00:00
return $order_id ;
}
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
*
* $for optionally define what you are getting order types for so only relevent types are returned .
*
* 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
/**
2015-11-03 13:31:20 +00:00
* Grant downloadable product access to the file identified by $download_id .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param string $download_id file identifier
* @ param int $product_id product identifier
* @ param WC_Order $order the order
2015-02-17 14:03:42 +00:00
* @ param int $qty purchased
2013-11-29 18:50:31 +00:00
* @ return int | bool insert id or false on failure
2013-08-09 16:11:15 +00:00
*/
2015-02-17 14:03:42 +00:00
function wc_downloadable_file_permission ( $download_id , $product_id , $order , $qty = 1 ) {
2013-08-09 16:11:15 +00:00
global $wpdb ;
2016-08-05 14:57:40 +00:00
$user_email = sanitize_email ( $order -> get_billing_email () );
2013-08-09 16:11:15 +00:00
$limit = trim ( get_post_meta ( $product_id , '_download_limit' , true ) );
$expiry = trim ( get_post_meta ( $product_id , '_download_expiry' , true ) );
2015-02-17 14:03:42 +00:00
$limit = empty ( $limit ) ? '' : absint ( $limit ) * $qty ;
2013-08-09 16:11:15 +00:00
// Default value is NULL in the table schema
$expiry = empty ( $expiry ) ? null : absint ( $expiry );
2013-12-17 05:07:19 +00:00
if ( $expiry ) {
$order_completed_date = date_i18n ( " Y-m-d " , strtotime ( $order -> completed_date ) );
$expiry = date_i18n ( " Y-m-d " , strtotime ( $order_completed_date . ' + ' . $expiry . ' DAY' ) );
}
2013-08-09 16:11:15 +00:00
2013-09-10 11:26:31 +00:00
$data = apply_filters ( 'woocommerce_downloadable_file_permission_data' , array (
2016-09-14 19:00:14 +00:00
'download_id' => $download_id ,
'product_id' => $product_id ,
'user_id' => absint ( $order -> get_user_id () ),
'user_email' => $user_email ,
'order_id' => $order -> get_id (),
'order_key' => $order -> get_order_key (),
'downloads_remaining' => $limit ,
'access_granted' => current_time ( 'mysql' ),
'download_count' => 0 ,
2013-09-10 11:26:31 +00:00
));
2013-08-09 16:11:15 +00:00
2013-09-10 11:26:31 +00:00
$format = apply_filters ( 'woocommerce_downloadable_file_permission_format' , array (
'%s' ,
2013-08-09 16:11:15 +00:00
'%s' ,
'%s' ,
'%s' ,
'%s' ,
'%s' ,
'%s' ,
'%s' ,
2016-08-27 02:08:49 +00:00
'%d' ,
2013-09-24 08:10:46 +00:00
), $data );
2013-08-09 16:11:15 +00:00
2013-09-10 11:26:31 +00:00
if ( ! is_null ( $expiry ) ) {
$data [ 'access_expires' ] = $expiry ;
$format [] = '%s' ;
}
2013-08-09 16:11:15 +00:00
// Downloadable product - give access to the customer
$result = $wpdb -> insert ( $wpdb -> prefix . 'woocommerce_downloadable_product_permissions' ,
2013-09-10 11:26:31 +00:00
$data ,
2013-09-10 14:08:20 +00:00
$format
2013-09-10 11:26:31 +00:00
);
do_action ( 'woocommerce_grant_product_download_access' , $data );
2013-08-09 16:11:15 +00:00
2013-09-10 11:26:31 +00:00
return $result ? $wpdb -> insert_id : false ;
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
*
* @ access public
* @ param int $order_id
*/
2013-11-25 13:54:52 +00:00
function wc_downloadable_product_permissions ( $order_id ) {
2014-02-17 10:50:36 +00:00
if ( get_post_meta ( $order_id , '_download_permissions_granted' , true ) == 1 ) {
2013-08-09 16:11:15 +00:00
return ; // Only do this once
2014-02-17 10:50:36 +00:00
}
2013-08-09 16:11:15 +00:00
2014-08-15 12:29:21 +00:00
$order = wc_get_order ( $order_id );
2013-08-09 16:11:15 +00:00
2015-08-12 16:21:07 +00:00
if ( $order && $order -> has_status ( 'processing' ) && get_option ( 'woocommerce_downloads_grant_access_after_payment' ) == 'no' ) {
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-08-22 15:51:24 +00:00
$_product = $item -> get_product ();
2013-08-09 16:11:15 +00:00
2013-09-20 16:01:09 +00:00
if ( $_product && $_product -> exists () && $_product -> is_downloadable () ) {
2016-11-11 15:31:00 +00:00
$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 ) {
2015-02-17 14:03:42 +00:00
wc_downloadable_file_permission ( $download_id , $item [ 'variation_id' ] > 0 ? $item [ 'variation_id' ] : $item [ 'product_id' ], $order , $item [ 'qty' ] );
2013-12-16 23:27:57 +00:00
}
2013-09-20 16:01:09 +00:00
}
}
}
2013-08-09 16:11:15 +00:00
2013-09-20 16:01:09 +00:00
update_post_meta ( $order_id , '_download_permissions_granted' , 1 );
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' );
2013-08-09 16:11:15 +00:00
/**
* Add a item to an order ( for example a line item ) .
*
* @ access public
* @ param int $order_id
* @ return mixed
*/
2013-11-25 13:54:52 +00:00
function wc_add_order_item ( $order_id , $item ) {
2013-08-09 16:11:15 +00:00
global $wpdb ;
$order_id = absint ( $order_id );
if ( ! $order_id )
return false ;
$defaults = array (
2016-09-14 19:00:14 +00:00
'order_item_name' => '' ,
'order_item_type' => 'line_item' ,
2013-08-09 16:11:15 +00:00
);
$item = wp_parse_args ( $item , $defaults );
$wpdb -> insert (
$wpdb -> prefix . " woocommerce_order_items " ,
array (
2016-09-14 19:00:14 +00:00
'order_item_name' => $item [ 'order_item_name' ],
'order_item_type' => $item [ 'order_item_type' ],
'order_id' => $order_id ,
2013-08-09 16:11:15 +00:00
),
array (
2016-08-27 03:04:10 +00:00
'%s' ,
'%s' ,
'%d' ,
2013-08-09 16:11:15 +00:00
)
);
$item_id = absint ( $wpdb -> insert_id );
do_action ( 'woocommerce_new_order_item' , $item_id , $item , $order_id );
return $item_id ;
}
2014-07-28 03:21:16 +00:00
/**
2015-11-03 13:31:20 +00:00
* Update an item for an order .
2014-07-28 03:21:16 +00:00
*
* @ since 2.2
* @ param int $item_id
* @ param array $args either `order_item_type` or `order_item_name`
* @ return bool true if successfully updated , false otherwise
*/
function wc_update_order_item ( $item_id , $args ) {
global $wpdb ;
$update = $wpdb -> update ( $wpdb -> prefix . 'woocommerce_order_items' , $args , array ( 'order_item_id' => $item_id ) );
if ( false === $update ) {
return false ;
}
do_action ( 'woocommerce_update_order_item' , $item_id , $args );
return true ;
}
2013-08-09 16:11:15 +00:00
/**
2015-11-03 13:31:20 +00:00
* Delete an item from the order it belongs to based on item id .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param int $item_id
* @ return bool
*/
2013-11-25 13:54:52 +00:00
function wc_delete_order_item ( $item_id ) {
2013-08-09 16:11:15 +00:00
global $wpdb ;
$item_id = absint ( $item_id );
if ( ! $item_id )
return false ;
do_action ( 'woocommerce_before_delete_order_item' , $item_id );
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> prefix } woocommerce_order_items WHERE order_item_id = %d " , $item_id ) );
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> prefix } woocommerce_order_itemmeta WHERE order_item_id = %d " , $item_id ) );
do_action ( 'woocommerce_delete_order_item' , $item_id );
return true ;
}
/**
2015-11-03 13:31:20 +00:00
* WooCommerce Order Item Meta API - Update term meta .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param mixed $item_id
* @ param mixed $meta_key
* @ param mixed $meta_value
* @ param string $prev_value ( default : '' )
* @ return bool
*/
2013-11-25 13:54:52 +00:00
function wc_update_order_item_meta ( $item_id , $meta_key , $meta_value , $prev_value = '' ) {
2016-05-09 09:01:12 +00:00
if ( update_metadata ( 'order_item' , $item_id , $meta_key , $meta_value , $prev_value ) ) {
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'orders' ) . 'item_meta_array_' . $item_id ;
wp_cache_delete ( $cache_key , 'orders' );
return true ;
}
return false ;
2013-08-09 16:11:15 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* WooCommerce Order Item Meta API - Add term meta .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param mixed $item_id
* @ param mixed $meta_key
* @ param mixed $meta_value
* @ param bool $unique ( default : false )
2016-05-11 14:28:40 +00:00
* @ return int New row ID or 0
2013-08-09 16:11:15 +00:00
*/
2013-11-25 13:54:52 +00:00
function wc_add_order_item_meta ( $item_id , $meta_key , $meta_value , $unique = false ) {
2016-05-11 14:28:40 +00:00
if ( $meta_id = add_metadata ( 'order_item' , $item_id , $meta_key , $meta_value , $unique ) ) {
2016-05-09 09:01:12 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'orders' ) . 'item_meta_array_' . $item_id ;
wp_cache_delete ( $cache_key , 'orders' );
2016-05-11 14:28:40 +00:00
return $meta_id ;
2016-05-09 09:01:12 +00:00
}
2016-05-11 14:28:40 +00:00
return 0 ;
2013-08-09 16:11:15 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* WooCommerce Order Item Meta API - Delete term meta .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param mixed $item_id
* @ param mixed $meta_key
* @ param string $meta_value ( default : '' )
* @ param bool $delete_all ( default : false )
* @ return bool
*/
2013-11-25 13:54:52 +00:00
function wc_delete_order_item_meta ( $item_id , $meta_key , $meta_value = '' , $delete_all = false ) {
2016-05-09 09:01:12 +00:00
if ( delete_metadata ( 'order_item' , $item_id , $meta_key , $meta_value , $delete_all ) ) {
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'orders' ) . 'item_meta_array_' . $item_id ;
wp_cache_delete ( $cache_key , 'orders' );
return true ;
}
return false ;
2013-08-09 16:11:15 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* WooCommerce Order Item Meta API - Get term meta .
2013-08-09 16:11:15 +00:00
*
* @ access public
* @ param mixed $item_id
* @ param mixed $key
* @ param bool $single ( default : true )
* @ return mixed
*/
2013-11-25 13:54:52 +00:00
function wc_get_order_item_meta ( $item_id , $key , $single = true ) {
2013-08-09 16:11:15 +00:00
return get_metadata ( 'order_item' , $item_id , $key , $single );
}
/**
* Return the count of processing orders .
*
* @ access public
* @ return int
*/
2013-11-25 13:54:52 +00:00
function wc_processing_order_count () {
2015-03-04 08:51:06 +00:00
return wc_orders_count ( 'processing' );
}
/**
* Return the orders count of a specific order status .
*
* @ access public
2015-11-05 16:05:03 +00:00
* @ param string $status
2015-03-04 08:51:06 +00:00
* @ return int
*/
function wc_orders_count ( $status ) {
2016-06-29 18:04:59 +00:00
global $wpdb ;
2014-07-11 11:43:42 +00:00
2016-06-29 18:04:59 +00:00
$count = 0 ;
$status = 'wc-' . $status ;
2015-03-04 08:51:06 +00:00
$order_statuses = array_keys ( wc_get_order_statuses () );
2016-06-29 18:04:59 +00:00
if ( ! in_array ( $status , $order_statuses ) ) {
2015-03-04 08:51:06 +00:00
return 0 ;
}
2016-06-29 19:19:47 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'orders' ) . $status ;
2016-06-29 18:04:59 +00:00
$cached_count = wp_cache_get ( $cache_key , 'counts' );
if ( false !== $cached_count ) {
return $cached_count ;
}
2014-07-11 11:43:42 +00:00
foreach ( wc_get_order_types ( 'order-count' ) as $type ) {
2016-06-29 18:04:59 +00:00
$query = " SELECT COUNT( * ) FROM { $wpdb -> posts } WHERE post_type = %s AND post_status = %s " ;
$count += $wpdb -> get_var ( $wpdb -> prepare ( $query , $type , $status ) );
2014-07-11 11:43:42 +00:00
}
2016-06-29 18:04:59 +00:00
wp_cache_set ( $cache_key , $count , 'counts' );
2014-07-11 11:43:42 +00:00
return $count ;
2013-08-09 16:11:15 +00:00
}
2014-02-26 15:43:19 +00:00
/**
* Clear all transients cache for order data .
*
* @ param int $post_id ( default : 0 )
*/
function wc_delete_shop_order_transients ( $post_id = 0 ) {
2014-10-07 10:01:22 +00:00
$post_id = absint ( $post_id );
$transients_to_clear = array ();
2014-02-26 15:43:19 +00:00
2014-03-20 11:10:25 +00:00
// Clear report transients
$reports = WC_Admin_Reports :: get_reports ();
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
}
}
2014-04-22 19:01:08 +00:00
// clear API report transient
$transients_to_clear [] = 'wc_admin_report' ;
2014-03-18 10:48:18 +00:00
// Clear transients where we have names
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
if ( $post_id && ( $user_id = get_post_meta ( $post_id , '_customer_user' , true ) ) ) {
delete_user_meta ( $user_id , '_money_spent' );
delete_user_meta ( $user_id , '_order_count' );
}
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
2014-02-26 15:43:19 +00:00
do_action ( 'woocommerce_delete_shop_order_transients' , $post_id );
}
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 (
2016-08-22 10:00:31 +00:00
'amount' => 0 ,
2014-07-24 14:34:14 +00:00
'reason' => null ,
'order_id' => 0 ,
'refund_id' => 0 ,
2015-05-15 15:27:11 +00:00
'line_items' => array (),
2014-07-08 14:32:47 +00:00
);
2016-08-24 14:26:35 +00:00
try {
$args = wp_parse_args ( $args , $default_args );
$order = wc_get_order ( $args [ 'order_id' ] );
$refund = new WC_Order_Refund ( $args [ 'refund_id' ] );
2016-08-22 10:00:31 +00:00
2016-08-24 14:26:35 +00:00
if ( ! $order ) {
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
2016-08-24 14:26:35 +00:00
// 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 );
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-09-07 09:04:56 +00:00
$qty = $args [ 'line_items' ][ $item_id ][ 'qty' ];
$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' ) ) ) {
$refunded_item -> set_quantity ( $qty );
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 );
}
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 );
$refund -> save ();
} 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
/**
* 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 ;
$tax_class = $wpdb -> get_var ( $wpdb -> prepare ( " SELECT tax_rate_class FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %d " , $tax_id ) );
return wc_clean ( $tax_class );
}
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
}
2016-08-05 15:04:41 +00:00
return 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
) );
wc_delete_shop_order_transients ( $order_id );
}
add_action ( 'woocommerce_order_status_refunded' , 'wc_order_fully_refunded' );
2016-06-13 13:42:10 +00:00
/**
* Search in orders .
*
* @ since 2.6 . 0
* @ param string $term Term to search .
* @ return array List of orders ID .
*/
function wc_order_search ( $term ) {
global $wpdb ;
$term = str_replace ( 'Order #' , '' , wc_clean ( $term ) );
2016-06-13 18:26:14 +00:00
$post_ids = array ();
2016-06-13 13:42:10 +00:00
// Search fields.
$search_fields = array_map ( 'wc_clean' , apply_filters ( 'woocommerce_shop_order_search_fields' , array (
'_order_key' ,
'_billing_company' ,
'_billing_address_1' ,
'_billing_address_2' ,
'_billing_city' ,
'_billing_postcode' ,
'_billing_country' ,
'_billing_state' ,
'_billing_email' ,
'_billing_phone' ,
'_shipping_address_1' ,
'_shipping_address_2' ,
'_shipping_city' ,
'_shipping_postcode' ,
'_shipping_country' ,
2016-08-27 02:08:49 +00:00
'_shipping_state' ,
2016-06-13 13:42:10 +00:00
) ) );
// Search orders.
if ( is_numeric ( $term ) ) {
$post_ids = array_unique ( array_merge (
$wpdb -> get_col (
2016-06-23 11:32:20 +00:00
$wpdb -> prepare ( " SELECT DISTINCT p1.post_id FROM { $wpdb -> postmeta } p1 WHERE p1.meta_key IN (' " . implode ( " ',' " , array_map ( 'esc_sql' , $search_fields ) ) . " ') AND p1.meta_value LIKE '%%%s%%'; " , wc_clean ( $term ) )
2016-06-13 13:42:10 +00:00
),
array ( absint ( $term ) )
) );
} elseif ( ! empty ( $search_fields ) ) {
$post_ids = array_unique ( array_merge (
$wpdb -> get_col (
$wpdb -> prepare ( "
SELECT DISTINCT p1 . post_id
FROM { $wpdb -> postmeta } p1
INNER JOIN { $wpdb -> postmeta } p2 ON p1 . post_id = p2 . post_id
WHERE
( p1 . meta_key = '_billing_first_name' AND p2 . meta_key = '_billing_last_name' AND CONCAT ( p1 . meta_value , ' ' , p2 . meta_value ) LIKE '%%%s%%' )
OR
( p1 . meta_key = '_shipping_first_name' AND p2 . meta_key = '_shipping_last_name' AND CONCAT ( p1 . meta_value , ' ' , p2 . meta_value ) LIKE '%%%s%%' )
OR
( p1 . meta_key IN ( '" . implode( "' , '", array_map( ' esc_sql ', $search_fields ) ) . "' ) AND p1 . meta_value LIKE '%%%s%%' )
" ,
$term , $term , $term
)
),
$wpdb -> get_col (
$wpdb -> prepare ( "
SELECT order_id
FROM { $wpdb -> prefix } woocommerce_order_items as order_items
WHERE order_item_name LIKE '%%%s%%'
" ,
$term
)
)
) );
}
return $post_ids ;
}
2016-06-21 19:26:35 +00:00
/**
* Update total sales amount for each product within a paid order .
*
2016-06-29 11:58:38 +00:00
* @ since 2.7 . 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 );
if ( ! $order || 'yes' === get_post_meta ( $order_id , '_recorded_sales' , true ) ) {
return ;
}
if ( sizeof ( $order -> get_items () ) > 0 ) {
foreach ( $order -> get_items () as $item ) {
if ( $item [ 'product_id' ] > 0 ) {
update_post_meta ( $item [ 'product_id' ], 'total_sales' , absint ( get_post_meta ( $item [ 'product_id' ], 'total_sales' , true ) ) + absint ( $item [ 'qty' ] ) );
}
}
}
update_post_meta ( $order_id , '_recorded_sales' , 'yes' );
/**
* 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 .
*
2016-06-29 11:58:38 +00:00
* @ since 2.7 . 0
2016-06-21 19:26:35 +00:00
* @ param int $order_id
*/
function wc_update_coupon_usage_counts ( $order_id ) {
$order = wc_get_order ( $order_id );
$has_recorded = get_post_meta ( $order_id , '_recorded_coupon_usage_counts' , true );
if ( ! $order ) {
return ;
}
if ( $order -> has_status ( 'cancelled' ) && 'yes' === $has_recorded ) {
$action = 'reduce' ;
delete_post_meta ( $order_id , '_recorded_coupon_usage_counts' );
} elseif ( ! $order -> has_status ( 'cancelled' ) && 'yes' !== $has_recorded ) {
$action = 'increase' ;
update_post_meta ( $order_id , '_recorded_coupon_usage_counts' , 'yes' );
} 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' :
$coupon -> dcr_usage_count ( $used_by );
break ;
case 'increase' :
$coupon -> inc_usage_count ( $used_by );
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 .
*
* @ access public
2016-06-21 19:26:35 +00:00
*/
2016-11-02 18:50:42 +00:00
function wc_cancel_unpaid_orders () {
global $wpdb ;
$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-02 18:50:42 +00:00
$date = date ( " Y-m-d H:i:s " , strtotime ( '-' . absint ( $held_duration ) . ' MINUTES' , current_time ( 'timestamp' ) ) );
2016-06-21 19:26:35 +00:00
2016-11-02 18:50:42 +00:00
$unpaid_orders = $wpdb -> get_col ( $wpdb -> prepare ( "
SELECT posts . ID
FROM { $wpdb -> posts } AS posts
WHERE posts . post_type IN ( '" . implode( "' , '", wc_get_order_types() ) . "' )
AND posts . post_status = 'wc-pending'
AND posts . post_modified < % s
" , $date ) );
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 );
if ( apply_filters ( 'woocommerce_cancel_unpaid_order' , 'checkout' === get_post_meta ( $unpaid_order , '_created_via' , true ), $order ) ) {
$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' );