In Woocommerce on the "edit order" page, is there any way to display all of the shipping methods and their costs for that order? A bit like the shipping calculator the customer sees on the cart page or checkout page, but on the edit order page instead?
Or alternatively, to add all of the items and customer shipping postcode etc from the order back to a new cart session so we can see the shipping caclulator in the cart page using this data?
extracting the products from the order using something like:
$order_items = $order_object->get_items( array('line_item', 'fee', 'shipping') );
if ( !is_wp_error( $order_items ) ) {
foreach( $order_items as $item_id => $order_item ) {
echo $order_item->get_quantity(); // or $order_item['quantity'];
echo $order_item->get_product_id(); // or $order_item['product_id'];
echo $order_item->get_variation_id(); // or $order_item['variation_id'];
echo $order_item->get_product(); // get the associated product
}
}
And then wp_create_order() to add those to a new cart?
Many thanks
I found this code, would this work with the modern version of woocommerce?
// Post variables
$order_id = isset($_POST['order_id'])?$_POST['order_id']:0;
$country = isset($_POST['country'])?$_POST['country']:0;
$state = isset($_POST['state'])?$_POST['state']:0;
$postcode = isset($_POST['postcode'])?$_POST['postcode']:0;
$city = isset($_POST['city'])?$_POST['city']:0;
// Order and order items
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
// Reset shipping first
WC()->shipping()->reset_shipping();
// Set correct temporary location
if ( $country != '' ) {
WC()->customer->set_billing_location( $country, $state, $postcode, $city );
WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
} else {
WC()->customer->set_billing_address_to_base();
WC()->customer->set_shipping_address_to_base();
}
// Remove all current items from cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
WC()->cart->empty_cart();
}
// Add all items to cart
foreach ($order_items as $order_item) {
WC()->cart->add_to_cart($order_item['product_id'], $order_item['qty']);
}
// Calculate shipping
$packages = WC()->cart->get_shipping_packages();
$shipping = WC()->shipping->calculate_shipping($packages);
$available_methods = WC()->shipping->get_packages();
Related
I have problem to restrict cart based on multiple product ID, to be exact, with the variables product. I found the code online and try it out. The solutions seem to be okay with simple product.
The only problem is, how to include variations ID in product array? For example, I have two product ID, 1669 and 1694. 1694 is variable products, where it have 4 variations ID; 1769,1770, 1771 and 1772 while 1669 is simple product. When I click 1669(simple product) and add to cart, I cannot add 1694(variables product). I want to make it enable for ID 1694.
However, when i add to cart the variable product first (1694), then the 1669 can be add to cart. below is the code :
function aelia_get_cart_contents() {
$cart_contents = array();
/**
* Load the cart object. This defaults to the persistant cart if null.
*/
$cart = WC()->session->get( 'cart', null );
if ( is_null( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart_' . get_current_blog_id(), true ) ) ) { // #codingStandardsIgnoreLine
$cart = $saved_cart['cart'];
}
elseif ( is_null( $cart ) ) {
$cart = array();
}
elseif ( is_array( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart_' . get_current_blog_id(), true ) ) ) { // #codingStandardsIgnoreLine
$cart = array_merge( $saved_cart['cart'], $cart );
}
if ( is_array( $cart ) ) {
foreach ( $cart as $key => $values ) {
$_product = wc_get_product( $values['variation_id'] ? $values['variation_id'] : $values['product_id'] );
if ( ! empty( $_product ) && $_product->exists() && $values['quantity'] > 0 ) {
if ( $_product->is_purchasable() ) {
// Put session data into array. Run through filter so other plugins can load their own session data
$session_data = array_merge( $values, array( 'data' => $_product ) );
$cart_contents[ $key ] = apply_filters( 'woocommerce_get_cart_item_from_session', $session_data, $values, $key );
}
}
}
}
return $cart_contents;
}
// Step 1 - Keep track of cart contents
add_action('wp_loaded', function() {
// If there is no session, then we don't have a cart and we should not take
// any action
if(!is_object(WC()->session)) {
return;
}
global $allowed_cart_items;
global $restricted_cart_items;
$restricted_cart_items = array( 1669,1694) ;
// 1669 is simple product, 1694 is variable product
// "Snoop" into the cart contents, without actually loading the whole cart
foreach(aelia_get_cart_contents() as $item) {
if(in_array($item['data']->get_id(), $restricted_cart_items)) {
$allowed_cart_items[] = $item['data']->get_id();
// If you need to allow MULTIPLE restricted items in the cart, comment
// the line below
break;
}
}
// Step 2 - Make disallowed products "not purchasable"
add_filter('woocommerce_is_purchasable', function($is_purchasable, $product) {
global $restricted_cart_items;
global $allowed_cart_items;
// If any of the restricted products is in the cart, any other must be made
// "not purchasable"
if(!empty($allowed_cart_items)) {
// To allow MULTIPLE products from the restricted ones, use the line below
$is_purchasable = in_array($product->id, $allowed_cart_items) || in_array($product->id, $restricted_cart_items);
// To allow a SINGLE products from the restricted ones, use the line below
// $is_purchasable = in_array($product->get_id(), $allowed_cart_items);
}
return $is_purchasable;
}, 10, 2);
}, 10);
hope you guys can help me. thank you
to make sure those product (simple and variable products) can be add to cart together.
in WooCommerce, I am trying to hide the postal code field on checkout whenever specific product is selected. I've tried a bunch of different things.
This is my last attempt:
/**
* Conditionally remove a checkout field based on products in the cart
*/
function wc_ninja_product_is_in_the_cart() {
// Add your special product IDs here
$ids = array( '9531', '9072', '9035' );;
// Products currently in the cart
$cart_ids = array();
// Find each product in the cart and add it to the $cart_ids array
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = $values['data'];
$cart_ids[] = $cart_product->id;
}
// If one of the special products are in the cart, return true.
if ( ! empty( array_intersect( $ids, $cart_ids ) ) ) {
return true;
} else {
return false;
}
}
function wc_ninja_remove_checkout_field( $fields ) {
if ( ! wc_ninja_product_is_in_the_cart() ) {
unset( $fields['billing']['billing_postcode'] );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'wc_ninja_remove_checkout_field' );
<!-- end snippet -->
If an admin goes to create an order but abandons it, the stock level is still reduced.
Steps to reproduce:
install WordPress
install WooCommerce
create simple product and tick "manage stock?" and set stock level to 10
view on front-end (see screenshot before.png)
as admin create new order but don't save it (new -> order -> add item -> exit page)
view on front-end (see screenshot after.png)
Notice stock level has been reduced even those order wasn't saved.
Is there anyway to avoid this?
Before.png:
After.png
I have worked on this issue and wrote a basic code for that.
Hooks used: "woocommerce_order_item_add_action_buttons" for Admin Order Add Item(s) and "woocommerce_process_shop_order_meta" for Admin Order Creation/Update.
First part: Stop reducing stocks of items that added while the order has not been created.
// define the woocommerce_order_item_add_action_buttons callback
function action_woocommerce_order_item_add_action_buttons( $order ) {
$orderID = $order->ID;
//check if this is the admin manual order creation
if(get_post_status($orderID) == "auto-draft" && get_post_type($orderID) == "shop_order")
{
foreach( $order->get_items() as $item_id => $item )
{
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$product_quantity = $item->get_quantity();
if($variation_id == 0)
{
$product = wc_get_product($product_id);
wc_update_product_stock($product, $product_quantity, 'increase');
}
else
{
$variation = wc_get_product($variation_id);
wc_update_product_stock($variation, $product_quantity, 'increase' );
}
// The text for the note
$note = __("Stock incremented due to the auto draft post type. Stock for each item will be decremented when this order created.");
// Add the note
$order->add_order_note( $note );
}
}
};
// add the action
add_action( 'woocommerce_order_item_add_action_buttons', 'action_woocommerce_order_item_add_action_buttons', 10, 1 );
Second Part: Reduce stocks of items that added if order is created.
add_action( 'woocommerce_process_shop_order_meta', 'woocommerce_process_shop_order', 10, 2 );
function woocommerce_process_shop_order ( $post_id, $post ) {
$order = wc_get_order( $post_id );
//check if this is order create action, not an update action
if(get_post_status($post_id) == "draft" && get_post_type($post_id) == "shop_order")
{
foreach( $order->get_items() as $item_id => $item )
{
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$product_quantity = $item->get_quantity();
if($variation_id == 0)
{
$product = wc_get_product($product_id);
wc_update_product_stock($product, $product_quantity, 'decrease');
}
else
{
$variation = wc_get_product($variation_id);
wc_update_product_stock($variation, $product_quantity, 'decrease' );
}
// The text for the note
$note = __("Stock decremented for all items in this order.");
// Add the note
$order->add_order_note( $note );
}
}
}
Tested and works fine. I hope this will help you. Have a good day.
In woocommerce I am trying to remove unwanted checkout shipping fields for a product category "house".
Here is my code:
function woo_custom_category_is_in_the_cart( $categories ) {
// Products currently in the cart
$cart_ids = array();
// Categories currently in the cart
$cart_categories = array('house');
// Find each product in the cart and add it to the $cart_ids array
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = $values['data'];
$cart_ids[] = $cart_product->id;
}
// Connect the products in the cart w/ their categories
foreach( $cart_ids as $id ) {
$products_categories = get_the_terms( $id, 'product_cat' );
// Loop through each product category and add it to our $cart_categories array
foreach ( $products_categories as $products_category ) {
$cart_categories[] = $products_category->slug;
}
}
// If one of the special categories are in the cart, return true.
if ( ! empty( array_intersect( $categories, $cart_categories ) ) ) {
return true;
} else {
return false;
}
}
/************************************************
* Remove unwanted checkout fields on condition *
************************************************/
function woo_custom_remove_checkout_field( $fields ) {
$categories = array( 'house' );
// If a special category is in the cart, hide the following billing fields
if ( woo_custom_category_is_in_the_cart( $categories ) ) {
// hide the billing fields
unset($fields['shipping']['shipping_first_name']);
unset($fields['shipping']['shipping_last_name']);
unset($fields['shipping']['shipping_company']);
unset($fields['shipping']['shipping_address_1']);
unset($fields['shipping']['shipping_address_2']);
unset($fields['shipping']['shipping_city']);
unset($fields['shipping']['shipping_postcode']);
unset($fields['shipping']['shipping_country']);
unset($fields['shipping']['shipping_state']);
unset($fields['shipping']['shipping_phone']);
// hide the additional information section
add_filter('woocommerce_enable_order_notes_field', '__return_false');
add_filter( 'woocommerce_ship_to_different_address_checked', '__return_false' );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'woo_custom_remove_checkout_field' );
But the code removes the fields on for other product categories too…
What I am doing wrong?
Any help is appreciated.
I have revisited and simplified your code:
add_filter( 'woocommerce_checkout_fields', 'conditionally_remove_checkout_fields', 25, 1 );
function conditionally_remove_checkout_fields( $fields ) {
// HERE the defined product Categories
$categories = array('house');
$found = false;
// CHECK CART ITEMS: search for items from our defined product category
foreach ( WC()->cart->get_cart() as $cart_item ){
if( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
$found = true;
break;
}
}
// If a special category is in the cart, remove some shipping fields
if ( $found ) {
// hide the billing fields
unset($fields['shipping']['shipping_first_name']);
unset($fields['shipping']['shipping_last_name']);
unset($fields['shipping']['shipping_company']);
unset($fields['shipping']['shipping_address_1']);
unset($fields['shipping']['shipping_address_2']);
unset($fields['shipping']['shipping_city']);
unset($fields['shipping']['shipping_postcode']);
unset($fields['shipping']['shipping_country']);
unset($fields['shipping']['shipping_state']);
unset($fields['shipping']['shipping_phone']);
// hide the additional information section
add_filter('woocommerce_enable_order_notes_field', '__return_false');
add_filter( 'woocommerce_ship_to_different_address_checked', '__return_false' );
}
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
We have a WooCommerce Multi-step checkout. One checkout step is for insurance, but this insurance is only for the rental category.
Looking to only show the insurance step if a rental product is in the cart. Found a great article on Checking if the WooCommerce Cart Contains a Product Category but the code is not giving the desired result when we place our add_action snippet to load the checkout step:
Here is our add_action call for the custom template (
add_action('woocommerce_multistep_checkout_after_shipping', 'add_my_insurance_step_with_new_field');
function add_my_insurance_step_with_new_field( $checkout ) {
wc_get_template( 'checkout/insurance.php', array( 'checkout' => $checkout ) );
}
And here is it placed within the code from SkyVerge:
// set our flag to be false until we find a product in that category
$cat_check = false;
// check each cart item for our category
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
// replace 'membership' with your category's slug
if ( has_term( 'rentals', 'product_cat', $product->id ) ) {
$cat_check = true;
// break because we only need one "true" to matter here
break;
}
}
// if a product in the cart is in our category, do something
if ( $cat_check ) {
// we have the category, do what we want
add_action('woocommerce_multistep_checkout_after_shipping', 'add_my_insurance_step_with_new_field');
function add_my_insurance_step_with_new_field( $checkout ) {
wc_get_template( 'checkout/insurance.php', array( 'checkout' => $checkout ) );
}
}
So very close! Not sure what could be going wrong.
Thanks for your help,
-L
This did the fix. Set $cat_check to false, run through each product in the cart, check if it has our category slug (rentals) in the cart, if true we break out and run the next if statement.
Which gets the template file and loads it before checkout order info (woocommerce_multistep_checkout_before_order_info).
function cclever_show_insurance_step_if_rentals() {
if ( function_exists( 'wc_checkout_add_ons' ) ) {
// set to false first
$cat_check = false;
// check each cart item for our category
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
// replace 'rentals' with your category's slug
if ( has_term( 'rentals', 'product_cat', $product->id ) ) {
$cat_check = true;
// we only need one "true" to leave
break;
}
}
// if a product in the cart is in our category, remove the add-ons
if ( $cat_check ) {
add_action('woocommerce_multistep_checkout_before_order_info', 'cclever_add_insurance_custom_step');
function cclever_add_insurance_custom_step( $checkout ) {
wc_get_template( 'checkout/insurance.php', array( 'checkout' => $checkout ) );
}
}
}
}
add_action( 'woocommerce_before_checkout_form', 'cclever_show_insurance_step_if_rentals' );
Hope that helps someone out. ** or if there is anything wrong with my code, please let me know. Thanks!