WooCommerce Admin Create Order & Adding Shipping on Order Pay - php

I am creating order in the admin. I added some functionality to the order pay page which is sent to the customer (form-pay.php). I added ability to remove items from the order as well as update your billing and shipping info (both for the order and account). I use ajax and calculate_totals() after an item is removed, which works.
However, I can't seem to figure out how to get shipping applied to the order. I need this to happen when the order is created in the admin and when someone removes an item on the frontend.
I tried just setting the shipping post meta but that isn't working.
function my_order_update_shipping($order_id, $items) {
$order = wc_get_order($order_id);
$order_subtotal = $order->get_subtotal();
if($order_subtotal > '17.99'){
update_post_meta($order_id, '_order_shipping', '0');
}else{
update_post_meta($order_id, '_order_shipping', '4');
}
}
add_action('woocommerce_before_save_order_items', 'my_order_update_shipping');
How can I achieve this? Or apply a shipping method in this way?

I finally figure this out. Works good but you need to click the "recalculate" button when adding/removing an item in the admin > create order. The below code applies 1 of 2 shipping methods based on a static subtotal amount.
$delivery_zones = WC_Shipping_Zones::get_zones();
foreach ((array) $delivery_zones as $key => $the_zone) {
$shipping_methods = $the_zone['shipping_methods'];
}
// Apply Correct Shipping Method
if ($order_subtotal > '17.99') {
$rate = $shipping_methods[2];
$item = new WC_Order_Item_Shipping();
$item->set_props(array('method_id' => $rate->id, 'total' => wc_format_decimal($rate->cost)));
$order->add_item($item);
} else {
$rate = $shipping_methods[1];
$item = new WC_Order_Item_Shipping();
$item->set_props(array('method_id' => $rate->id, 'total' => wc_format_decimal($rate->cost)));
$order->add_item($item);
}
$order->calculate_totals();
$order->save();

Related

How to properly recalculate totals for existing subscription in custom php function?

I have written the following function in a custom php function that is designed to update a recurring fee amount associated with a woo commerce subscription, where such fee amount is based on a subscription meta field which I have stored the value for in $pickup_location_fee
'''
$updated_fee = 0;
foreach ( $subscription->get_items('fee') as $item_id => $item ) {
$name = $item->get_name();
if ($name == 'Selected Location Pickup Fee') {
wc_update_order_item_meta( $item_id, "_fee_amount", $pickup_location_fee);
wc_update_order_item_meta( $item_id, "_line_total", $pickup_location_fee);
$updated_fee = 1;
}
}
// add new pickup fee if not present
if ($updated_fee == 0) {
$item_id = wc_add_order_item($subscription->get_id(), array('order_item_name' => 'Selected Location Pickup Fee', 'order_item_type' => 'fee'));
if ($item_id) {
wc_update_order_item_meta( $item_id, "_fee_amount", $pickup_location_fee);
wc_update_order_item_meta( $item_id, "_line_total", $pickup_location_fee);
}
}
// END update order item with Selected Location Pickup Fee
'''
This function works as expected to change the amount of _fee_amount and _line_total fields.
My challenge is that the fee is being added, but the subscription cart total is not being updated to reflect it. When I add the following code at the bottom of the previous snippet in an attempt to recalculate the cart, not only does the cart not recalculate, but it seems to somehow inhibit the previous code from working. And the updated _fee_amount and _line_total fields set in the earlier snippet are not saved to the database.
$subscription->calculate_totals();
$subscription->save();
Is someone able to help me figure out the proper way to get the cart to recalculate after modifying these order_line_item_meta?
Thank you,
Josh

Get Local pickup plus details from the order in Woocommerce

In Woocommerce, I just want to get "local pickup" shipping details to display on a custom email. I tried below functions but they don't show anything for "local pickup".
Which function I can use to get "local pickup" info?
I tried without success the following WC_Order methods:
$order->get_shipping_address_1()
$order->get_formatted_shipping_address()
Edit:
Sorry I did not mention that, but I am using Local Pickup Plus plugin
Edit 2:
This is how I got local pickup info for Local Pickups Plus Plugin docs.woocommerce.com/document/local-pickup-plus which puts meta data to main order variable.
$order = wc_get_order( $order_id );
foreach ($order->get_data() as $key => $value):
if ($key==='shipping_lines'):
foreach ($value as $k=>$v):
$a = $v->get_meta_data();
foreach ($a as $x=>$y):
$t = $y->get_data();
$mykey = $t['key'] ;
$pickup["$mykey"] = $t['value'];
endforeach;
endforeach;
endif;
endforeach;
Then you can use the variables below:
$pickup['_pickup_location_name']
$pickup['_pickup_location_address']['address_1']
$pickup['_pickup_location_phone']['address_2']
$pickup['_pickup_location_address']['postcode']
$pickup['_pickup_location_address']['city']
$pickup['_pickup_location_address']['state'] $pickup['_pickup_location_address']['country']
$pickup['_pickup_location_phone']
$pickup['_pickup_date']
$pickup['_pickup_minimum_hours']
For Order items shipping details refer to: "Get orders shipping method details in WooCommerce 3"
To target order shipping lines details from the WC_Order object you can use the following code:
// Loop though order items shipping
foreach( $order->get_shipping_methods() as $item_id => $item ){
$shipping_item_name = $item->get_name();
$shipping_item_type = $item->get_type();
$shipping_method_title = $item->get_method_title();
$shipping_method_id = $item->get_method_id();
$shipping_method_instance_id = $item->get_instance_id();
$shipping_method_total = $item->get_total();
$shipping_method_total_tax = $item->get_total_tax();
$shipping_method_taxes = $item->get_taxes();
// Get custom meta-data
$formatted_meta_data = $item->get_formatted_meta_data( ' ', true );
// Displaying the row custom meta data Objects (just for testing)
echo '<pre>'; print_r($formatted_meta_data); echo '</pre>';
}
Regarding the custom shipping metadata:
You can access it using the WC_Data method get_meta() from the custom meta "key" located in any custom meta data Objects, like:
$value = $item->get_meta('the_custom_key'); // 'the_custom_key' need to be replaced by the meta "key".
Note: In most Woocommerce email templates and email notification related hooks, you can use the WC_Order object as it's globally included. If not you can get it from the Order ID like:
$order = wc_get_order( $order_id );
Orders related threads:
Get orders shipping method details in WooCommerce 3
Get Order items and WC_Order_Item_Product in Woocommerce 3
How to get WooCommerce order details
Addition - For Local Pickup Plus plugin
It seems that you are using Local Pickup Plus plugin which adds specific custom meta data in the shipping lines.
// Loop though order items shipping
foreach( $order->get_shipping_methods() as $item_id => $item ){
$location_id = $item->get_meta('_pickup_location_id');
$location_name = $item->get_meta('_pickup_location_name');
$location_address = $item->get_meta('_pickup_location_address'); // Array
$location_address_1 = $location_address['address_1'];
$location_address_2 = $location_address['address_2'];
$location_postcode = $location_address['postcode'];
$location_city = $location_address['city'];
$location_state = $location_address['state'];
$location_country = $location_address['country'];
$location_phone = $item->get_meta('_pickup_location_phone');
$pickup_date = $item->get_meta('_pickup_date');
$pickup_min_hours = $item->get_meta('_pickup_minimum_hours');
}

Change payment gateway option based on cart total in Woocommerce checkout

For my woocommerce shop, i have this filter in functions.php. It force 3DS with "woocommerce Stripe Gateway" plugin.
add_filter('wc_stripe_require_3ds','__return_true');
It work great.
I would like this filter to be active only for order over 50€.
I tried with this but it doesn't work, the order is validated without 3DS.
$minsecure = 50;
$order = new WC_Order( $order_id );
$total = $order->get_total();
if($total>$minsecure) {
add_filter('wc_stripe_require_3ds','__return_true');
}
I also try to get the cart amount instead of the order amount, but I don't know which one to get for the filter to be active between the time of the "order" click and the confirmation page.
Any help is appreciated.
I would go for this to check if the price of the order is over 50
if( WC()->cart->subtotal > 50 )
So the snippet would look like
add_action('woocommerce_cart_updated', 'wc_stripe_require_3ds', 90);
function wc_stripe_require_3ds ( $cart ){
if( WC()->cart->subtotal > 50 ) {
add_filter('wc_stripe_require_3ds','__return_true');
}
}
Have a try.

Applying programmatically a coupon to an Order in WooCommerce3

I'm developing a plugin that creates an order directly (no cart) and applies a coupon. In version 3.0 of the woo API the function add_coupon() has been deprecated in favour of a WC_Order_Item_Coupon object you add to the order.
Create the coupon
$coupon = new WC_Order_Item_Coupon();
$coupon->set_props(array('code' => $coupon, 'discount' => $discount_total,
'discount_tax' => 0));
$coupon->save();
This is successful. I can validate by calling $coupon->get_discount().
I then add the coupon to the order and recalculate totals:
$order->add_item($item);
$order->calculate_totals($discount_total);
$order->save();
Logging into wp-admin I can see the order with coupon code visible. However, the coupon has had no effect on line items or total.
Have a misunderstood how api v3.0 intends us to handle coupons?
How about using WC_Abstract_Order::apply_coupon?
/**
* Apply a coupon to the order and recalculate totals.
*
* #since 3.2.0
* #param string|WC_Coupon $raw_coupon Coupon code or object.
* #return true|WP_Error True if applied, error if not.
*/
public function apply_coupon( $raw_coupon )
Here is my code.
$user = wp_get_current_user();
$order = new WC_Order();
$order->set_status('completed');
$order->set_customer_id($user->ID);
$order->add_product($product , 1); // This is an existing SIMPLE product
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
$order->set_customer_user_agent( wc_get_user_agent() );
$order->set_address([
'first_name' => $user->first_name,
'email' => $user->user_email,
], 'billing' );
// $order->calculate_totals(); // You don't need this
$order->apply_coupon($coupon_code);
$order->save();
OK, so I played about a little longer and it looks like in V3 things are a little more manual.
Adding a WC_Order_Item_Coupon item to a woo order does simply that. It adds the coupon object to the order object. No calculations are made and the product line items remain unchanged. You have to iterate over the product items manually and apply the coupon yourself by calculating the line item totals and subtotals. calculate_totals() then does as expected.
// Create the coupon
global $woocommerce;
$coupon = new WC_Coupon($coupon_code);
// Get the coupon discount amount (My coupon is a fixed value off)
$discount_total = $coupon->get_amount();
// Loop through products and apply the coupon discount
foreach($order->get_items() as $order_item){
$product_id = $order_item->get_product_id();
if($this->coupon_applies_to_product($coupon, $product_id)){
$total = $order_item->get_total();
$order_item->set_subtotal($total);
$order_item->set_total($total - $discount_total);
$order_item->save();
}
}
$order->save();
I wrote a helper function to make sure the coupon applies to the product in question coupon_applies_to_product(). Strictly not needed given I'm creating the order entirely in code.. but I use it it other places so added it.
// Add the coupon to the order
$item = new WC_Order_Item_Coupon();
$item->set_props(array('code' => $coupon_code, 'discount' => $discount_total, 'discount_tax' => 0));
$order->add_item($item);
$order->save();
You now get the nicely formatted order in wp-admin with line items showing the specific discount + the coupon code etc.

update woocommerce cart after changing shipping method

I am currently working on an online shop with WooCommerce. I faced the problem that I want to grant a discount to customers who chose a specific shipping method. The discount is 0.50 for every single product. I basically solved this problem with the following code in my "functions.php":
add_action('woocommerce_before_calculate_totals', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$cart = $woocommerce->cart->get_cart();
//Calculating Quantity
foreach ($cart as $cart_val => $cid) {
$qty += $cid['quantity'];
}
if ($woocommerce->cart->shipping_label == "specific shipping method") {
$woo_fee = $qty * (-0.5);
$woo_name = "discount for specific shipping method";
}
$woocommerce->cart->add_fee(__($woo_name, 'woocommerce'), $woo_fee, true);
}
The code technically works, the only problem I have now is that if a customer changes the shipping method i.e. from the "specific shipping method" to a "normal one" (without any discount) or the other way round, it always displays and calculates the discount value from the previously chosen shipping method. In other words it is always one step back and therefore displays the customer the wrong total amount.
Does anyone has an idea to solve this problem?
This solutions is for Woocommerce 2.1.X!
I am not sure if this might help. I was facing a similar problem, where I needed to retrieve the chosen shipping method. In the file \wp-content\plugins\woocommerce\includes\wc-cart-functions.php I found a method called wc_cart_totals_shipping_html().
Within this method there is a check of the current selected shipping method that contains the following code:
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
I used this code in my own functions.php to check for the currently selected shipping method and it works. Example:
add_filter( 'woocommerce_billing_fields', 'wc_change_required_fields');
function wc_change_required_fields($address_fields) {
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
if ($chosen_method == 'local_delivery') {
$address_fields['billing_address_1']['required'] = true;
// place your changes that depend on the shipping method here...
}
}
Hope that helps!
This is very old, but I ran into this issue myself and had to work out the solution.
Woocommerce stores pre-calculated cart totals in the database, rather than calculating them on the fly. But the shipping method choice is stored as a session variable. So shipping changes are not reflected immediately at checkout without a visit or refresh of a cart page.
With the original posted code, the shipping changes were not reflected because they aren't recalculated and stored. To do this, the function needs to be tricked into thinking it's a cart page first, and then recalculating the totals to be stored.
GLOBAL $woocommerce;
if ( ! defined('WOOCOMMERCE_CART') ) {
define( 'WOOCOMMERCE_CART', true );
}
And then at the end of the function, after all the desired changes have been made refresh and store.
WC()->cart->calculate_totals();
See also CODEX for WC_AJAX::update_shipping_method()
http://docs.woothemes.com/wc-apidocs/source-class-WC_AJAX.html#148-174
Mark's answer worked for me, however I had to delete all transient values prior to running the code. Otherwise, it would simply restore the saved values.
public function clear_shipping_transients() {
global $wpdb;
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_cp_quote_%') OR `option_name` LIKE ('_transient_timeout_cp_quote_%') OR `option_name` LIKE ('_transient_wc_ship_%')" );
}

Categories