I'm using woocommerce subscriptions and I'm writing a plugin to update an external system, if the user upgrade or downgrade his subscription it shows as a new order with its ID, but I can't get the subscription ID (which it's constant) related to the order ID, I check the documentation but I couldn't find a solution.
I was able to achieve like this.
$subscriptions = wcs_get_subscriptions_for_order($order_id, array( 'order_type' => 'any' ));
foreach( $subscriptions as $subscription_id => $subscription_obj )
{
$current_subs_id = $subscription_obj->get_id(); // This is current subscription id
$parent_id = $subscription_obj->get_parent_id(); // This is subscription parent id
}
You can access this data by hooking into the processed_subscription_payment action provided by WooCommerce Subscriptions:
add_action( 'processed_subscription_payment', 'se43079522_process_subscription', 10, 2 );
function se43079522_process_subscription($user_id, $subscription_key) {
// here you have access to the $subscription_key (ID) and the $user_id associated
}
Take a look at this link: https://docs.woocommerce.com/document/subscriptions/develop/action-reference/ for more information on the available actions with this plugin.
OR, you can do this manually like so:
Read comments for walk-through
global $woocommerce;
// Get the order ID and save as variable
$order_id = [ORDER_ID];
// Get the order object
$order = new WC_Order( $order_id );
// Loop through the subscription order
foreach ( WC_Subscriptions_Order::get_recurring_items( $order ) as $order_item ) {
// Get the subscription key
$subscription_key = WC_Subscriptions_Manager::get_subscription_key( $order->id, WC_Subscriptions_Order::get_items_product_id( $order_item ) );
}
// This is your subscription key (ID)
echo $subscription_key;
Get Subscription ID from Order. You can use wcs_get_subscriptions_for_order woocommerce function to get an object with the subscription related to that order and get the subscription ID from there.
$subscriptions = wcs_get_subscriptions_for_order($order_id);
var_dump($subscriptions);
Related
We had a legacy system that used Woocommerce but not Woocommerce Subscriptions, we've since migrated to Woocommerce Subscriptions but we have a tonne of standard Woo orders that we'd like to tie to subscriptions.
Via the database, if I find the subscription in the postmeta table, I can manually change _subscription_renewal_order_ids_cache to include the order id so it becomes the following:
a:2:{
i:0;i:143052;
i:1;i:143049;
}
(I've added the second line)
On the order itself if I add the meta _subscription_renewal with the subscription id that then ties the order to the subscription.
Can this be done programmatically? I've done the following but it's not outputting the meta (so therefore I can't amend it).
global $wpdb;
// get all subscriptions IDS
$subscriptions_ids = $wpdb->get_col("
SELECT ID FROM {$wpdb->prefix}posts
WHERE post_type LIKE 'shop_subscription'
");
// Loop through subscriptions Ids
foreach( $subscriptions_ids as $subscription ){
// Get an instance of the WC_Subscription object
$subscription = new WC_Subscription( $subscription );
$subscription_id = $subscription->get_id();
$subscription_email = $subscription->get_billing_email();
echo $subscription->get_meta('_subscription_renewal_order_ids_cache');
echo "<pre>";
print_r( $subscription );
echo "</pre>";
}
I've not gone any further with the code until I'm able to output and amend the post meta.
This can be done by going through orders and adding the meta line:
$order = wc_get_order();
$order->update_meta_data( '_subscription_renewal', $subscription_id );
$order->save();
With $subscription_id being the ID of the subscription you'd like to tie the order to.
I have made a simple webshop with woocommerce, with three payment methods. iDeal and by direct bank transfer and on account. The order ID is created based on the payment method. for example, if payment is made with iDEAL, the order id becomes ID190100; if payment is made on account, the order id becomes RK190100. I get this working with the plugin
"Sequential Order Numbers for WooCommerce" from BeRocket but these are already created before the payment is complete. The order ID must only be finalized once the payment has been made. Now orders that have not yet paid, and may not be paying, will receive a fixed order ID. So is it possible to create a temporary order id and when the order is completed change the order id based on payment method?
Woocommerce by default will use the post ID of the order for the order ID. This is evident when viewing the WC_Order::get_order_number() method. If you want to use a custom order number to display, you'll need to add a filter on woocommerce_order_number to load in a different value.
An example script would be:
add_action( 'woocommerce_order_status_completed', 'wc_change_order_id' );
function wc_change_order_id( $order_id ) {
$order = wc_get_order( $order_id );
$method = $order->get_payment_method(); // need check this
if ( $method === 'account' ) {
$number = 'ID' . $order->get_id();
$order->update_meta_data('_new_order_number', $number );
}
}
add_filter('woocommerce_order_number', function($default_order_number, \WC_Order $order) {
//Load in our meta value. Return it, if it's not empty.
$order_number = $order->get_meta('_new_order_number');
if(!empty($order_number)) {
return $order_number;
}
// use whatever the previous value was, if a plugin modified it already.
return $default_order_number;
},10,2);
Try this. It's very quick example. Hope help.
add_action( 'woocommerce_order_status_completed', 'wc_change_order_id' );
function wc_change_order_id( $order_id ) {
$order = wc_get_order( $order_id );
$method = $order->get_payment_method(); // need check this
if ( $method === 'account' ) {
$number = 'ID' . $order->get_id();
$order->set_id( $number );
$order->save();
}
}
I'm trying to get the subscription id from the action hook woocommerce_order_status_changed.
It gives me the order id which is changed with every switch the customer makes.
For example: If the subscription id is 10, the original order id is 9.
Now every switch the customer made generates a new order id, which the action above gives you. At this point I have the $customer_id, $order_id, and the original post id which is 9,
How I can get the subscription id of the current order?
You can use dedicated function wcs_get_subscriptions_for_order() which will retrieve $subscription IDs.
So this could be your code:
add_action('woocommerce_order_status_changed', 'action_order_status_changed');
function action_order_status_changed( $order_id ){
$subscriptions_ids = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) );
// We get all related subscriptions for this order
foreach( $subscriptions_ids as $subscription_id => $subscription_obj )
if($subscription_obj->order->id == $order_id) break; // Stop the loop
// The subscription ID: $subscription_id
// The An instance of the Subscription object: $subscription_obj
// ...
}
Is there woocommerce function that will return me all related orders (at least the order ID) for the specific subscription that user has bough?
I have found in this official documentation Subscription Function & Property Reference:
WC_Subscription::get_related_orders( $return_fields, $order_type );
But this does not seem to be for specific subscription?
When ever I try to run it I get a fatal error no mater what I pass in:
Fatal error: Uncaught Error: Using $this when not in object context in
C:\xampp\htdocs\mysite.com\wp-content\plugins\woocommerce-subscriptions\includes\class-wc-subscription.php:1413
I am making my own plugin and I select all subscriptions where post status = wc-active from post table. I have looked in the "woocommerce_order_items", "woocommerce_order_itemmeta" and "postmeta" tables but neither of them provide way to get related orders for user bought subscription...
If I only knew where are the relation for user bought subscriptions and its related orders, then i could write some sql but I have no idea and google does not yield any results either.
Any ideas?
My setup:
php version 7.0.4
wordpress version 4.7.3
woocommerce 2.6.8
woocommerce subscriptions: 2.0.18
Updated: Added WooCommerce version 3+ Compatibility
It's very easy to get the order ID from subscription object. I am going to select, just like you, all subscriptions where 'post status' = 'wc-active' from post table.
// Get all customers subscriptions
$customer_subscriptions = get_posts( array(
'numberposts' => -1,
// 'meta_key' => '_customer_user',
// 'meta_value' => get_current_user_id(), // Or $user_id
'post_type' => 'shop_subscription', // WC orders post type
'post_status' => 'wc-active' // Only orders with status "completed"
) );
// Iterating through each post subscription object
foreach( $customer_subscriptions as $customer_subscription ){
// The subscription ID
$subscription_id = $customer_subscription->ID
// IMPORTANT HERE: Get an instance of the WC_Subscription Object
$subscription = new WC_Subscription( $subscription_id );
// Or also you can use
// wc_get_order( $subscription_id );
// Getting the related Order ID (added WC 3+ comaptibility)
$order_id = method_exists( $subscription, 'get_parent_id' ) ? $subscription->get_parent_id() : $subscription->order->id;
// Getting an instance of the related WC_Order Object (added WC 3+ comaptibility)
$order = method_exists( $subscription, 'get_parent' ) ? $subscription->get_parent() : $subscription->order;
// Optional (uncomment below): Displaying the WC_Subscription object raw data
// echo '<pre>';print_r($subscription);echo '</pre>';
}
You can also uncomment in the post query the 'meta_key' and 'meta_value' array lines to get the subscriptions for one customer… This code is tested and works
The most important thing here is:
$subscription = new WC_Subscription($customer_subscription->ID);
…as you will get the WC_Subscription object in which you can apply all WC_Subscription methods without getting errors, with for example:
$subscription = new WC_Subscription($post_id);
$relared_orders_ids_array = $subscription->get_related_orders();
With WooCommerce, I have the following hook in my function.php after the new order is submitted:
add_action( 'woocommerce_new_order', 'create_job_openings');
function create_job_openings($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($order->get_items() as $key => $item) {
$product_name = $item['name'];
var_dump($product_name);
}
}
The above code is not giving me any output i'e it is not entering inside the foreach loop that's why var_dump() not giving me any output, but if I mention the order_id specifically like create_job_openings($order_id=517) it works, even I tried echo $order_id before foreach loop, it is giving me the order_id, then why it is not entering the foreach loop?
note: when I try var_dump($items); before foreach loop its giving me
array(0) {
}
Why it is not able to get the product details even if there are products in it after new order is made?
Update 2 — The working solution (using an email notification hook)
The problem is when using email notification hook can fire an action 2 times, for example when a new order is made (notification for the Shop manager and notification for the customer).
You want to use this "New Order" event for Orders that are in "processing" status.
To avoid your action to be fired 2 times using New order notification WooCommerce event, we use 'customer_processing_order' instead of 'new_order' email ID (notification event).
Here we don't need to get the $order object, as we got it as an argument in this hooked function.
So here is your final functional code:
add_action( 'woocommerce_email_before_order_table', 'custom_action_on_completed_customer_email_notification', 10, 4 );
function custom_action_on_completed_customer_email_notification( $order, $sent_to_admin, $plain_text, $email ) {
if( 'customer_processing_order' == $email->id ){ // for processing order status customer notification…
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
echo $product_name;
break; // (optional) stop loop to first item
}
}
}
This is the validated and working answer to this question
Related Working Answers:
Sending an SMS for specific email notifications and order statuses
Avoid repetitive emails notification on some auto completed orders
Change the user role on purchase for specific products when order status is completed
Update 1 (hook alternative)
Trying using woocommerce_thankyou hook, that is fired on review order after order has been processed:
add_action( 'woocommerce_thankyou', 'create_job_openings', 10, 1 );
function create_job_openings( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stop loop to first item
}
}
(Not working for the OP)
You should try this instead wc_get_order() function this way and your code will be:
add_action( 'woocommerce_new_order', 'create_job_openings', 10, 1);
function create_job_openings($order_id) {
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
foreach ($order_items as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stops on first item
}
}
You can have a look to
How to get WooCommerce order details where a lot of things are explained…
(Not working for the OP)
you can use save_post action when post is added in wordpress. for more visit: Link
function wc_order_add_action($post_id, $post, $update)
{
$post_type = get_post_type($post_id);
// If this isn't a 'shop_order' post, don't update it.
if ("shop_order" != $post_type) return;
$order = wc_get_order($post_id);
foreach($order -> get_items() as $key => $item)
{
$product_name = $item['name'];
var_dump($product_name);
}
}
add_action('save_post', 'wc_order_add_action');
To get the items and product details from the new order, you simply need to request two parameters for woocommerce_new_order. The second passed parameter will be the new order object. Example:
add_action( 'woocommerce_new_order', 'so41605256_new_order', 10, 2 );
function so41605256_new_order( $order_id, $order ) {
// Directly access items from $order variable, do not use wc_get_order...
foreach ($order->get_items() as $item_id => $item) {
//...
}
}
This works for both programmatically created and user created orders, regardless of WC email configuration.
Reference: create and update methods in WC_Order_Data_Store_CPT core class. Kudos to this post which helped me discover this.
Faced the same issue. I fixed it by changing the hook to woocommerce_checkout_order_processed. It works perfectly for both instant payment and cash on delivery since it runs as soon as an order is placed. Note that, this will run before payment completion. So, it doesn't have any relation with payment.
User clicks on proceed to checkout button and then this hook fires.