WooCommerce Create a downloadable product's order programmatically - wordpress

I use wc_create_order to create an order and everything is fine except the user can't have the downloadable product's download link in My Downloads section.
$def_args = array('customer_id' => $user_id, 'status' => 'completed');
$order = wc_create_order( $def_args );
$targs['totals']['subtotal'] = $ord['pay_amount'];
$targs['totals']['total'] = $ord['pay_amount'];
$targs['totals']['subtotal_tax'] = 0;
$targs['totals']['tax'] = 0;
$sku_ = $ord['sku'];
$order->add_product( get_product_by_sku( $sku_ ) , 1, $targs ); //(get_product with id and next is for quantity)
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->set_total( $ord['pay_amount'] );
$order->calculate_totals();
// I took get_product_by_sku function in stackoverflow but I don't remember which question.
function get_product_by_sku( $sku ) {
global $wpdb;
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );
if ( $product_id ) return new WC_Product( $product_id );
return null;
}
$ord variable has some information about the order.
Should I need to call a function or something like that to make order with download link?

I found the solution, when creating an order, make the status processing and after call update_status.
$def_args = array('customer_id' => $user_id, 'status' => 'processing');
...
$order->update_status('completed');
Then user have her/his download links.

Related

Cancel all user orders on subscription cancelled/expired

I want to develop a setup/code in which all the orders made by the user gets cancelled when the his subscription gets cancelled or expired. I am using Wordpress, Woocommerce and Woocommerce Subscriptions plugin.
I am trying to do by using something like this:
function my_cancelled_subscription( $user_id, $subscription_key ) {
//what code should I use here????
}
add_action( 'woocommerce_subscription_status_cancelled', 'my_cancelled_subscription', 10, 2 );
Please help mee.....
I figured it out..........
function my_cancelled_subscription( $subscription ) {
$order_id = $subscription->get_parent_id();
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
$order->update_status( 'cancelled' );
$args = array(
'customer_id' => $user_id,
'status' => 'wc-completed',
);
$arrorders = wc_get_orders( $args );
foreach ($arrorders as $ord) {
$ord->update_status( 'cancelled' );
}
}
add_action( 'woocommerce_subscription_status_cancelled', 'my_cancelled_subscription' );

Large WooCommerce query throws fatal memory errors

I have a site that has tens of thousands of orders, which I need to compare the billing and customer/user emails and show a flag if they don't match. One of the stipulations is that I'm unable to add any metadata to the orders. So my solution is to just add a custom column, and compare the emails on the fly when the orders list is rendered. That works just fine.
add_filter( 'manage_edit-shop_order_columns', 'mismatched_orders_column' );
function mismatched_orders_column( $columns ) {
$columns['mismatched'] = 'Mismatched';
return $columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'mismatched_orders_column_data' );
function mismatched_orders_column_data( $column ) {
global $post;
if ( 'mismatched' === $column ) {
$order = new WC_Order( $post->ID );
$customer = $order->get_user();
$result = '';
$billing_email = strtolower ( $order->get_billing_email() );
$customer_email = '';
if ($customer) $customer_email = strtolower ( $customer->user_email );
if ( $customer && ( $billing_email != $customer_email ) ) {
$result = '<span class="mismatched-order" title="Possible order mismatch">Yes</span>';
}
echo $result;
}
}
My issue is when trying to add sorting. Because I'm not accessing any post metadata, I don't have any easy data to sort via the main query. My solution here was originally to hook into pre_get_posts, grab all the orders in a new WP_Query, then loop through them and add the ones that had mismatched emails to an array for use in post__in.
This works/worked fine on my small dev site, but throws fatal memory errors when trying to loop over any more than about 8 or 9 thousand posts (out of a total of 30-40 thousand). Increasing memory isn't really an option.
add_filter( 'manage_edit-shop_order_sortable_columns', 'mismatched_orders_column_sortable');
function mismatched_orders_column_sortable( $columns ) {
$columns['mismatched'] = 'mismatched';
return $columns;
}
add_action( 'pre_get_posts', 'mismatched_emails_posts_orderby' );
function mismatched_emails_posts_orderby( $query ) {
if( ! is_admin() || ! $query->is_main_query() ) {
return;
}
//Remove the pre_get_posts hook so we don't get stuck in a loop
add_action( 'pre_get_posts', 'mismatched_emails_posts_orderby' );
//Make sure we're only looking at our custom column
if ( 'mismatched' === $query->get( 'orderby') ) {
//Set our initial array for 'post__in'
$mismatched = array();
$orders_list = get_posts(array(
'post_type' => 'shop_order',
'posts_per_page' => -1,
'post_status' => 'any',
'fields' => 'ids'
));
//And here is our problem
foreach( $orders_list as $order_post ) :
//Get our order and customer/user object
$order_object = new WC_Order( $order_post );
$customer = $order_object->get_user();
//Check that billing and customer emails don't match, and also that we're not dealing with a guest order
if ( ( $order_object->get_billing_email() != $customer->user_email ) && $order_object->get_user() != false ) {
$mismatched[] = $order_post;
}
endforeach; wp_reset_postdata();
$query->set( 'post__in', $mismatched );
}
}
I would seriously appreciate any insight into how I could either reduce the expense of the query I'm trying to run, or an alternate approach. Again, just for clarification, adding metadata to the orders isn't an option.
Thanks!

Woocommerce Admin Product Search Not working

I ran into the issue that woocommerce product search in admin not working and giving result as No product found though there are lots of products with my search input.
Also i haven't used relevancy search plugin so there will be no any issue.
I set debug true in wp-congig.php and its throwing one error related to wp_meta with printing whole query.
Also tried disable theme and try with default theme also tried to disable every plugin one by one but still no luck.
I was experiencing the same thing and adding the following code block to my functions.php file fixed it immediately:
function m_request_query( $query_vars ) {
global $typenow;
global $wpdb;
global $pagenow;
if ( 'product' === $typenow && isset( $_GET['s'] ) && 'edit.php' === $pagenow ) {
$search_term = esc_sql( sanitize_text_field( $_GET['s'] ) );
// Split the search term by comma.
$search_terms = explode( ',', $search_term );
// If there are more terms make sure we also search for the whole thing, maybe it's not a list of terms.
if ( count( $search_terms ) > 1 ) {
$search_terms[] = $search_term;
}
// Cleanup the array manually to avoid issues with quote escaping.
array_walk( $search_terms, 'trim' );
array_walk( $search_terms, 'esc_sql' );
$meta_key = '_sku';
$post_types = array( 'product', 'product_variation' );
$query = "SELECT DISTINCT posts.ID as product_id, posts.post_parent as parent_id FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id WHERE postmeta.meta_key = '{$meta_key}' AND postmeta.meta_value IN ('" . implode( "','", $search_terms ) . "') AND posts.post_type IN ('" . implode( "','", $post_types ) . "') ORDER BY posts.post_parent ASC, posts.post_title ASC";
$search_results = $wpdb->get_results( $query );
$product_ids = wp_parse_id_list( array_merge( wp_list_pluck( $search_results, 'product_id' ), wp_list_pluck( $search_results, 'parent_id' ) ) );
$query_vars['post__in'] = array_merge( $product_ids, $query_vars['post__in'] );
}
return $query_vars;
}
add_filter( 'request', 'm_request_query', 20 );
Shout-out to mircian for the snippet.

Updating GEO my WP fields using frontend Advanced custom fields form - WordPress

I’ve created a frontend form using the advanced custom fields (ACF) plugin. Within this form I’ve added some location fields such as postcode and city.
Below is the function used to update the post
global $post_id;
add_filter('acf/pre_save_post' , 'tsm_do_pre_save_post' );
function tsm_do_pre_save_post( $post_id ) {
// Bail if not logged in or not able to post
if ( ! ( is_user_logged_in() ) ) {
return;
}
// check if this is to be a new post
if( $post_id != 'new_job' ) {
return $post_id;
}
$profile_id = um_profile_id();
$userID = 'user_'.$profile_id;
$user_id = get_current_user_id();
// Create a new post
$post = array(
'ID'=> $post_id,
'post_type' => 'members',
'post_status' => 'publish',
'post_title' => $userID,
'post_author' => $user_id,
'category' => $_POST['acf']['field_594d0ffc2a66d'],
);
// insert the post
$post_id = wp_insert_post( $post );
// Save the fields to the post
do_action( 'acf/save_post' , $post_id );
return $post_id;
exit;
}
Once this form has been submitted, the custom post type is updated with no issues.
How can I add the submitted location data (postcode, city etc) into the GEO MY WP information for the post?
I’ve tried to follow the information in the docs secetion (http://docs.geomywp.com/gmw_pt_update_location/) but not having much luck.
GEO my WP function fron docs below:
function gmw_update_post_type_post_location( $post_id ) {
// Return if it's a post revision
if ( false !== wp_is_post_revision( $post_id ) )
return;
// check autosave //
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
//check if user can edit post
if ( !current_user_can( 'edit_post', $post_id ) )
return;
//get the address from the custom field "address"
$address = get_post_meta( $post_id, 'address', true );
//varify that address exists. Otherwise abort the function.
if ( empty( $address ) )
return;
//include the update location file file
include_once( GMW_PT_PATH .'/includes/gmw-pt-update-location.php' );
//make sure the file included and the function exists
if ( !function_exists( 'gmw_pt_update_location' ) )
return;
//Create the array that will pass to the function
$args = array(
'post_id' => $post_id, //Post Id of the post
'address' => $address // the address we pull from the custom field above
);
//run the udpate location function
gmw_pt_update_location( $args );
}
//execute the function whenever post type is being updated
add_action( 'save_post_post', 'gmw_update_post_type_post_location' );
I believe I need to somehow combine the two functions so when the ACF form is updated, it also needs to update the GEO my wp meta data also.
I've tried combining the two functions but not having much luck.
If someone can point me in the right direction it will be very much appreciated
Thanks
Updated code.....
I've tried to add the gmw_update_post_type_post_location() function within the ACF function (code below) but still not having much luck...
global $post_id;
add_filter('acf/pre_save_post' , 'tsm_do_pre_save_post' );
function tsm_do_pre_save_post( $post_id ) {
// Bail if not logged in or not able to post
if ( ! ( is_user_logged_in() ) ) {
return;
}
// check if this is to be a new post
if( $post_id != 'new_job' ) {
return $post_id;
}
$profile_id = um_profile_id();
$userID = 'user_'.$profile_id;
$user_id = get_current_user_id();
$profilePostcode = get_post_meta( $post_id, 'location_postcode', true );
// Create a new post
$post = array(
'ID'=> $post_id,
'post_type' => 'members', // Your post type ( post, page, custom post type )
'post_status' => 'publish', // You can use -> publish, draft, private, etc.
'post_title' => $userID, // Post Title ACF field key
'post_author' => $user_id,
'category' => $_POST['acf']['field_594d0ffc2a66d'], // Post Content ACF field key
);
// insert the post
$post_id = wp_insert_post( $post );
// Save the fields to the post
do_action( 'acf/save_post' , $post_id );
gmw_update_post_type_members_location($post_id);
return $post_id;
exit;
}
function gmw_update_post_type_members_location( $post_id ) {
$profilePostcode = get_post_meta( $post_id, 'profile_location_postcode', true );
if ( empty( $profilePostcode ) )
return;
$address = array(
//'street' => $_POST['location_address'],
//'city' => $_POST['location_town'],
//'state' => $_POST['location_state'],
'zipcode' => $profilePostcode,
//'country' => $_POST['location_country']
);
include_once( GMW_PT_PATH .'/includes/gmw-pt-update-location.php' );
if ( !function_exists( 'gmw_pt_update_location' ) )
return;
$profile_id = um_profile_id();
$userID = 'user_'.$profile_id;
$user_id = get_current_user_id();
$args = array(
'post_id' => $post_id, //Post Id of the post
'post_title' => $userID, // Post Title ACF field key
'post_author' => $user_id,
'post_type' => 'members',
'address' => $address
);
//run the udpate location function
gmw_pt_update_location( $args );
}

How do I get the category name or id of a product of a woocommerce order

I have the function below which works to change the role of a user when they buy certain products, but instead of listing out specific product IDs, I'd like to just use the category name or id of those products. That way, if we add a new product in that category, I don't have to remember to add it to this function. This function is specific to a certain category, so it doesn't matter if people have other products from other categories in the order.
function lgbk_add_member( $order_id ) {
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
if ( $order->user_id > 0 && $product_id == '7' ) {
update_user_meta( $order->user_id, 'paying_customer', 1 );
$user = new WP_User( $order->user_id );
// Remove role
$user->remove_role( 'expired' );
// Add role
$user->add_role( 'customer' );
}
}
}
add_action( 'woocommerce_order_status_completed', 'lgbk_add_member' );
You have to use wp_get_post_terms to get category id of any product like this :
$term_list = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids'));
I am just fetching id's of categories assigned to this particular product, you can fetch name also.
You can get product category name using this function -
function get_product_category_name_by_id( $category_id ) {
$term = get_term_by( 'id', $category_id, 'product_cat', 'ARRAY_A' );
return $term['name'];
}
$product_category = get_product_category_name_by_id( $your_category_id );
Thanks!

Resources