I need to insert in a custom plugin the code to get the name of the discount codes I enter in the settings, the discount obtained with the code and the total.
Based on Get coupon data from WooCommerce orders answer code, I have inserted the following code:
$order = wc_get_order( $order_id );
// GET THE ORDER COUPON ITEMS
$order_items = $order->get_items('coupon');
// print_r($order_items); // For testing
// LOOP THROUGH ORDER COUPON ITEMS
foreach( $order_items as $item_id => $item ){
// Retrieving the coupon ID reference
$coupon_post_obj = get_page_by_title( $item->get_name(), OBJECT, 'shop_coupon' );
$coupon_id = $coupon_post_obj->ID;
// Get an instance of WC_Coupon object (necessary to use WC_Coupon methods)
$coupon = new WC_Coupon($coupon_id);
## Filtering with your coupon custom types
if( $coupon->is_type( 'fixed' ) || $coupon->is_type( 'percent' ) || $coupon->is_type( 'fixed_product' ) ){
// Get the Coupon discount amounts in the order
$order_discount_amount = wc_get_order_item_meta( $item_id, 'discount_amount', true );
$order_discount_tax_amount = wc_get_order_item_meta( $item_id, 'discount_amount_tax', true );
## Or get the coupon amount object
$coupons_amount = $coupons->get_amount();
}
}
$confirmation = str_ireplace("{order_items}", $order_items, $confirmation);
But the only information it brings back to me, when I do an echo is the word "array".
What am I doing wrong? Any help?
Try the following instead, that will add a coma separated string of applied coupon codes with their respective discount amount:
$order = wc_get_order( $order_id ); // If needed
$output = array(); // Initializing
// loop through order items "coupon"
foreach( $order->get_items('coupon') as $item_id => $item ){
// Get the coupon array data in an unprotected array
$data = $item->get_data();
// Format desired coupon data for output
$output[] = $data['code'] . ': ' . strip_tags( wc_price( $data['discount'] + $data['discount_tax'] ) );
}
$confirmation = str_ireplace("{order_items}", implode(', ', $output), $confirmation);
Related
While im pretty hopeless at php, i feel like im getting close on this one, i just cant quite lock it in
So we have 2 plugins in the mix here WCFM & AST
In WCFM, a vendor adds a tracking number and carrier this is store in the order meta data with a meta keys of
wcfm_tracking_code
AND
wcfm_tracking_url
i found this hook elesewhere that allows me to fire a set of things to happen, once the tracking has been submitted into the system
“wcfm_after_order_mark_shipped” (Parameters -> $order_id, $order_item_id, $tracking_code, $tracking_url)
so now i want to take the above 2 order meta datas and push them into another plugin called AST
They code they provide to push data into thier system is:
<?php if ( class_exists( 'WC_Advanced_Shipment_Tracking_Actions' ) ) {
$order_id = '123'; //Replace with your order id
$tracking_provider = 'USPS'; //Replace with your shipping provider
$tracking_number = '123123'; //Replace with your tracking number
$date_shipped = '2020-06-22'; ////Replace with your shipped date
$status_shipped = 1; // 0=no,1=shipped,2=partial shipped(if partial shipped order status is enabled)
$sku = 't-shirt,blue-jeans'; //the line item (product) SKU
$qty = '1,1'; //the line item (product) quantity
if ( function_exists( 'ast_insert_tracking_number' ) ) {
ast_insert_tracking_number( $order_id, $tracking_number, $tracking_provider, $date_shipped, $status_shipped, $sku, $qty );
}
}
SO using both codes provided, can anyone show me how i can take the metadata input from WCFM and push it into AST?
Ive hired someone on upwork, and they cant even figure it out, but it seems so straightforward.
This is the code we have,it pushes the data in but it comes in looking like this
Instead of this like it should
add_action( 'wcfm_after_order_mark_shipped', 'testwr', 99, 6 );
function testwr( $order_id, $order_item_id, $tracking_code, $tracking_url, $product_id, $wcfm_tracking_data ) {
$order = wc_get_order( $order_id );
$data['order_id'] = $order_id;
$data['tracking_provider'] = $tracking_url;
$data['wcfm_tracking_code'] = $wcfm_tracking_data['wcfm_tracking_code'];
$data['date_shipped'] = $order->order_date;
$data['status_shipped'] = 1;
extract($data);
foreach ($order->get_items() as $item) {
$product = wc_get_product($item->get_product_id());
$item_quantity[] = $item->get_quantity();
$item_sku[] = $product->get_sku();
}
if( $item_sku ) {
$data['sku'] = implode( ",",$item_sku );
}
$tracking_url = $tracking_url . "/" . $tracking_code;
$url = "<a href='".$tracking_url."'>test</a>";
$data['qty'] = implode( ",",$item_quantity );
$test = ast_insert_tracking_number( $order_id, $tracking_url, $tracking_url, $date_shipped, $status_shipped, $sku, $qty);
var_dump($test);
print_r($tracking_code);
print_r($data);
print_r($wcfm_tracking_data);
var_dump($order_item_id);
//ast_insert_tracking_number
die();
}
Kudos to anyone willing to give this a try and some time
Regards
On the checkout page of my WooCommerce based site, users will have a list of shipping methods to choose from depending on what they are purchasing
This is the list I want to capture:
Things like
“Free Shipping” for orders over a certain price
“Freight Shipping” for certain items, and so on.
My goal, is to display ALL available methods for each specific order, and display it on the “Edit Order / Order Details” page in the Admin view.
A small feature that would help us be able to quickly identify what option people are choosing more often, depending on the choices that they have available.
This is what I have so far:
add_action( 'woocommerce_checkout_update_order_meta', 'save_available_shipping_methods' );
function save_available_shipping_methods( $order_id ) {
$shippingmethods = WC()->cart->get_shipping_methods();
update_post_meta( $order_id, '_shipping_methods', $shippingmethods );
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'get_available_shipping_methods', 10, 1 );
function get_available_shipping_methods($order){
$order = wc_get_order( $order_id );
if ( $order ) {
echo '<p><strong>'.__('Available Shipping Methods: ').'</strong> ' . get_post_meta($order->get_shipping_methods(), '_shipping_field_value', true ) . '</p>';
}
}
I'll attach a picture as well to maybe make things a little easier to follow.
This is where I would like to put it. The current hook places the field underneath the shipping address
In the woocommerce_checkout_update_order_meta hook, the available shipping packages are stored via update_post_meta
Comments with explanation added in the code
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Display on the order edit page (backend)
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
// True
if ( $rate_labels ) {
// Loop trough rate labels
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
In case anyone is wondering, this is the final result. It will loop through each order to find the available shipping methods at the time, along with how much it was quoted for.
// Capture the available shipping methods, and costs:
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
$rate_costs = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
$rate_costs[] = $rate->get_cost();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
update_post_meta( $order_id, '_available_shipping_method_cost', $rate_costs );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Make it display on the edit order page:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
I have added shipping cost for the orders that are synced from Amazon. For some reason I had to set custom shipping flat price in woo-orders created for Amazon-order. It is done as follow:
$OrderOBJ = wc_get_order(2343);
$item = new WC_Order_Item_Shipping();
$new_ship_price = 10;
$shippingItem = $OrderOBJ->get_items('shipping');
$item->set_method_title( "Amazon shipping rate" );
$item->set_method_id( "amazon_flat_rate:17" );
$item->set_total( $new_ship_price );
$OrderOBJ->update_item( $item );
$OrderOBJ->calculate_totals();
$OrderOBJ->save()
The problem is, I have to update orders in each time the status is changed in Amazon, there is no problem doing that, problem is I have to update the shipping cost also if it is updated. But I have not found anyway to do so. Can anyone tell me how to update the shipping items of orders set in this way? Or is it the fact that, once shipping item is set then we cannot update or delete it?
To add or update shipping items use the following:
$order_id = 2343;
$order = wc_get_order($order_id);
$cost = 10;
$items = (array) $order->get_items('shipping');
$country = $order->get_shipping_country();
// Set the array for tax calculations
$calculate_tax_for = array(
'country' => $country_code,
'state' => '', // Can be set (optional)
'postcode' => '', // Can be set (optional)
'city' => '', // Can be set (optional)
);
if ( sizeof( $items ) == 0 ) {
$item = new WC_Order_Item_Shipping();
$items = array($item);
$new_item = true;
}
// Loop through shipping items
foreach ( $items as $item ) {
$item->set_method_title( __("Amazon shipping rate") );
$item->set_method_id( "amazon_flat_rate:17" ); // set an existing Shipping method rate ID
$item->set_total( $cost ); // (optional)
$item->calculate_taxes( $calculate_tax_for ); // Calculate taxes
if( isset($new_item) && $new_item ) {
$order->add_item( $item );
} else {
$item->save()
}
}
$order->calculate_totals();
It should better work…
To remove shipping items use te following:
$order_id = 2343;
$order = wc_get_order($order_id);
$items = (array) $order->get_items('shipping');
if ( sizeof( $items ) > 0 ) {
// Loop through shipping items
foreach ( $items as $item_id => $item ) {
$order->remove_item( $item_id );
}
$order->calculate_totals();
}
Related: Add a shipping to an order programmatically in Woocommerce 3
The WC_Order_Item_Shipping object can be added to an order using either of 2 methods.
WC_ORDER->add_shipping( WC_Order_Item_Shipping ) This is deprecated in WooCommerce V3.
WC_ORDER->add_item( WC_Order_Item_Shipping )
If you need to persist this change on the database then use WC_ORDER->save();
References: woocommerce.github.io.../#add_shipping woocommerce.github.io.../#add_item
Just get order and delete item by id
$ordr_id = 4414;
$item_id = 986;
$order = wc_get_order($ordr_id);
$order->remove_item($item_id);
$order->calculate_totals();
In WooCommerce, I would like to send a request to an API once the customer has successfully checked out. Its basically a website where the client is selling online courses (Like udemy).
When the customer checks out, I would like to send an API request and enroll the user for that particular course. I have tried several WooCommerce hooks but none worked for me.
This is the code that I'm using:
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
function enroll_student($order_id)
{
echo $order_id;
echo "Hooked";
}
I am writing this code for a plugin and to make it easier, I am currently using Cash on Delivery method.
Can anyone point me out where I am going wrong because when I checkout I cant see the message "hooked" that I am printing nor the $order_id?
It takes me to the success page and doesn't show these two things that I am printing.
Update 2 Only For Woocommerce 3+ (added restriction to execute the code only once)
add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
if ( ! $order_id )
return;
// Allow code execution only once
if( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get the order key
$order_key = $order->get_order_key();
// Get the order number
$order_key = $order->get_order_number();
if($order->is_paid())
$paid = __('yes');
else
$paid = __('no');
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
// Get the product object
$product = $item->get_product();
// Get the product Id
$product_id = $product->get_id();
// Get the product name
$product_id = $item->get_name();
}
// Output some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
// Flag the action as done (to avoid repetitions on reload for example)
$order->update_meta_data( '_thankyou_action_done', true );
$order->save();
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Related thread:
Get Order items and WC_Order_Item_Product in WooCommerce 3
How to get WooCommerce order details
The code is tested and works.
Updated (to get the product Id from Orders items as asked in your comment)
May be you could use woocommerce_thankyou hook instead, that will display on order-received page your echoed code, this way:
add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
if ( ! $order_id )
return;
// Getting an instance of the order object
$order = wc_get_order( $order_id );
if($order->is_paid())
$paid = 'yes';
else
$paid = 'no';
// iterating through each order items (getting product ID and the product object)
// (work for simple and variable products)
foreach ( $order->get_items() as $item_id => $item ) {
if( $item['variation_id'] > 0 ){
$product_id = $item['variation_id']; // variable product
} else {
$product_id = $item['product_id']; // simple product
}
// Get the product object
$product = wc_get_product( $product_id );
}
// Ouptput some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
The code is tested and works.
Then you can use all class WC_Abstract_Order methods on the $order object.
Related:
How to get WooCommerce order details
Get Order items and WC_Order_Item_Product in WooCommerce 3
How to get Customer details from Order in WooCommerce?
Rather than 'woocommerce_thankyou' hook, 'woocommerce_checkout_order_processed' hook is the relevant hook. 'woocommerce_checkout_order_processed' hook will be called only once and you will not need to add meta for each product and make additional calls to keep check for code to run only once. As, 'woocommerce_thankyou' can be called multiple times that is each time thankyou page loads.
Replace add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
with
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
and remove meta code and checks. Updated code is
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
// Getting an instance of the order object
$order = wc_get_order( $order_id );
if($order->is_paid())
$paid = 'yes';
else
$paid = 'no';
// iterating through each order items (getting product ID and the product object)
// (work for simple and variable products)
foreach ( $order->get_items() as $item_id => $item ) {
if( $item['variation_id'] > 0 ){
$product_id = $item['variation_id']; // variable product
} else {
$product_id = $item['product_id']; // simple product
}
// Get the product object
$product = wc_get_product( $product_id );
}
// Ouptput some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
}
you can get the order items of an order by
// Getting an instance of the order object
$order = new WC_Order( $order_id );
$items = $order->get_items();
//Loop through them, you can get all the relevant data:
foreach ( $items as $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
}
I am creating my order like so:
$order = wc_create_order();
$product = wc_get_product( $_POST["product"] );
$order->add_product( $product, 1 );
$kupon = new WC_Coupon( $_POST["coupon"] );
$amount = $kupon->get_discount_amount( $product->price );
$order->add_coupon( $_POST["coupon"], $amount, $amount );
$order->calculate_shipping();
$order->calculate_totals();
If you take a closer look, I am adding a coupon code dynamicaly with add_coupon function from WC_Order class. Everythings works perfectly, the order is added to database with correct product, quantites, and ALSO the coupon is added - but the problem is that coupon is not "applied" to the total. It is not deducting the totals price. Here is the image:
While adding a product to an Order, we should pass an argument containing subtotal and total like this:
$args = array(
"totals" => array('subtotal' => $item["price"],
'total' => $item["price"] - $coupon_discount)
);
$order->add_product( $product, 1, $args);
Where $product is Woocommerce product. Hope this helps somebody.
Here is the solution that worked in my case for modifying order line items then applying a discount afterward - $order is a WC_Order object:
$order_total = $order->get_total()
$coupon_code = $this->get_coupon_code( $order );
$coupon = new WC_Coupon( $coupon_code );
$coupon_type = $coupon->discount_type;
$coupon_amount = $coupon->coupon_amount;
$final_discount = 0;
// You must calculate the discount yourself! I have not found a convenient method outside the WC_Cart context to do this for you.
$final_discount = $coupon_amount * ( $order_total / 100 );
$order->add_coupon( $coupon_code, $final_discount, 0 );
$order->set_total( $final_discount );
Here is the method to retrieve the coupon code for a WC_Order object. In my case I know there will never be more then 1 coupon per order so you may want to adjust it to accommodate more:
public function get_coupon_code( $subscription ) {
$coupon_used = '';
if( $subscription->get_used_coupons() ) {
$coupons_count = count( $subscription->get_used_coupons() );
foreach( $subscription->get_used_coupons() as $coupon) {
$coupon_used = $coupon;
break;
}
}
if ( $coupon_used !== '' ) {
return $coupon_used;
} else {
return false;
}
}