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();
Related
I follow this thread that works perfectly:
Set discount based on number of orders in WooCommerce.
But in my case I need to apply the discount not to the order total, but to the shipping cost and I would like to do this only for a specific country, specifically UK.
For example:
ONLY First order: 6€ (flat rate shipping cost)
From the second order onwards, always 25€ (flat rate shipping cost)
I already set the flat rate at 6€ for Uk, but I need to understand how to apply the 25€ surcharge only from the second order onwards.
This is my code attempt:
add_filter( 'woocommerce_package_rates', 'free_first_order_shipping', 20, 2 );
function free_first_order_shipping( $rates, $package ) {
if(is_user_logged_in()) {
$user_id = get_current_user_id();
//We want to know how many completed orders customer have or remove status if you dont care.
$args = array(
'customer_id' => 1,
'status' => array('wc-completed'),
);
$orders = wc_get_orders($args);
//If there are no orders returned apply free shipping
if($orders == 0) {
unset( $rates['flat_rate:15'] ); // Shipping 25 euro
}
else{
unset( $rates['betrs_shipping:10-2'] ); // Shipping 6 euro
}
}
return $rates;
}
However, this does not adjust the costs, but removes the shipping method. Any advice?
Some notes on your code attempt/question:
The answer you refer to is about adding a (negative) fee, the woocommerce_package_rates hook is indeed better suited than the woocommerce_cart_calculate_fees hook for your question
You can use $package['destination']['country'] to determine the country
unset( $rates['flat_rate:15'] ) will not adjust the cost but remove the method
To get the total orders by a customer you can use the wc_get_customer_order_count() function
So you get:
function filter_woocommerce_package_rates( $rates, $package ) {
// ONLY for specific countries
$specific_countries = array( 'UK', 'BE' );
// Checks if a value (country) exists in an array, if not return
if ( ! in_array( $package['destination']['country'], $specific_countries ) ) return $rates;
// Only for logged in users
if ( is_user_logged_in() ) {
// Get user ID
$user_id = get_current_user_id();
// Get the total orders by a customer.
$count = wc_get_customer_order_count( $user_id );
// Loop through
foreach ( $rates as $rate_key => $rate ) {
// Initialize
$has_taxes = false;
// Targeting "Flat Rate" shipping method
if ( $rate->method_id == 'flat_rate' ) {
// Get the initial cost
$initial_cost = $new_cost = $rates[$rate_key]->cost;
// Based on order count
if ( $count == 0 ) {
// Set the new rate cost
$new_cost = 6;
} else {
// Set the new rate cost
$new_cost = 25;
}
// Set the new cost
$rates[$rate_key]->cost = $new_cost;
// Taxes rate cost (if enabled)
$taxes = [];
// Loop through the shipping taxes array (as they can be many)
foreach ($rates[$rate_key]->taxes as $key => $tax ) {
if ( $rates[$rate_key]->taxes[$key] > 0 ) {
// Get the initial tax cost
$initial_tax_cost = $new_tax_cost = $rates[$rate_key]->taxes[$key];
// Get the tax rate conversion
$tax_rate = $initial_tax_cost / $initial_cost;
// Set the new tax cost
$taxes[$key] = $new_cost * $tax_rate;
// Enabling tax
$has_taxes = true;
}
}
// When true
if ( $has_taxes ) {
$rates[$rate_key]->taxes = $taxes;
}
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates','filter_woocommerce_package_rates', 10, 2 );
Don't forget to empty your cart to refresh shipping cached data!
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);
Important note: I am not using functions.php for any cart functionality. I am using standalone php files, it has to stay this way.
In Woocommerce, I'm creating a cart dynamically adding a product using:
global $woocommerce;
$cart = $woocommerce->cart;
//set the custom item data
$item_data = array();
$product_id = '121';
$item_data = array(
'plain_data' => 'test data',
'array_data' => array('URL' => 'URL', 'Signals' => 'SIGNALS')
);
//Add it to the cart
$cart->add_to_cart($product_id, 1, null, null, $item_data);
Then I create the order from the cart using:
global $woocommerce;
$cart = $woocommerce->cart;
$order_data = array('payment_method' => 'PayPal');
$checkout = $woocommerce->checkout();
$order_id = $checkout->create_order($order_data);
But the custom item data that I added does not get saved in the order.
What am I doing wrong?
As you don't want to use any hook, you will be oblige to set the custom cart item data afterwards once the order is created… So try the following:
Try the following:
$product_id = '121';
$item_data = array(
'plain_data' => 'test data',
'array_data' => array('URL' => 'URL', 'Signals' => 'SIGNALS')
);
$item_data_keys = array_keys($item_data); // Get array keys
//Add it to the cart
WC()->cart->add_to_cart($product_id, 1, 0, array(), $item_data);
// Create order
$order_id = WC()->checkout->create_order( array('payment_method' => 'PayPal') );
// Get an instance of the WC_Order Object
$order = wc_get_order($order_id);
// Loop through order items
foreach( $order->get_items() as $item ){
// Loop though custom item data
foreach( $item_data_keys as $item_data_key ){
// set custom item data
$item->update_meta_data( $item_data_key, $item_data[$item_data_key] );
}
// Save item data
$item->save();
}
// Save order
$order->save();
Tested and works.
NOTE: global woocommerce is now replaced by WC() since a while.
Im trying to get the correct price for each item variation however it only seems to be getting the first price of that product variation. Not sure how to solve this.
Code:
$query = new WC_Order_Query( array(
'status' => 'on-hold',
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
) );
$order_ids = $query->get_orders();
foreach( $order_ids as $order_id ) {
$order = new WC_Order($order_id);
foreach ($order->get_items() as $item_id => $item_obj) {
$_product = wc_get_product($item_obj['product_id']);
$product = new WC_Product_Variable($item_obj['product_id']);
$product_variations = $product->get_available_variations();
$variation_product_id = $product_variations [0]['variation_id'];
$variation_product = new WC_Product_Variation( $variation_product_id );
$t_dy = $variation_product->get_price();
$item_qty = $item_obj['qty'];
$it_total = $item_qty * $t_dy;
$td = wc_update_order_item_meta($item_id, '_line_total', $it_total);
$order->calculate_totals();
$order->save();
}
}
Updated 3
To get the correct current variation price when the order item is a product variation is much more simple than you are doing. Then you will use Woocommerce 3 CRUD setter and getter methods to set the order item totals, save it and update the order.
The code:
// Loop through order items
foreach ($order->get_items() as $item_id => $item ) {
// Targeting only product variation items
if( $item->get_variation_id() > 0 ){
// Get an instance of the WC_Product_Variation object
$product = $item->get_product();
$price = (float) $product->get_price(); // <=== HERE the variation price
$qty = (int) $item->get_quantity(); // <=== HERE the quantity
// set line totals
$item->set_total( $price * $qty );
$item->set_subtotal( $price * $qty );
$item->save(); // save order item data
}
}
// The following need to be outside the order item loop
$order->calculate_totals(); // Save is included into the method
It should better work this way.
Related:
Get Order items and WC_Order_Item_Product in Woocommerce 3
How to get WooCommerce order details
Found the issue! - it was giving out wrong id
replace:
$variation_product_id = $product_variations [0]['variation_id'];
with this:
$product_variation_id = $item_obj->get_variation_id();
$variation_product_id = $product_variation_id;
$variation_product = new WC_Product_Variation( $variation_product_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;
}
}