Is there a way to stop products from going straight to completed status? - woocommerce

We use Woocommerce Bookings which set the bookable products as virtual products. This means when a customer checks out and pays, the order goes straight to completed status.
Is there any way to make all orders that contain these virtual bookable products go to processing first?
add_action( 'woocommerce_order_item_needs_processing', 'prefix_item_need_processing', 10, 3 );
function prefix_item_need_processing( $needs_processing, $product, $order_id ) {
// Check if the product is virtual and downloadable
$is_virtual_downloadable_item = /**$product->is_downloadable() && **/ $product->is_virtual();
// If the product is virtual and downloadable, decide if it needs processing, meaning the order will first be set to Processing
if ( $is_virtual_downloadable_item ) {
$needs_procesing = true;
}
return $needs_processing;
}

Related

Hide express paid shipping options when stock of the product is 0 or less than 0 (backorder cases) in Wocommerce checkout

I set 2 methods of delivery:
Express shipping 48 hours at a cost which es a % of the total cart $ amount
Normal Free shipping in 29 days
The functionality that I want is the following:
If the stock of the product is = 0 or less than 0 (backorder is allowed), the Express shipping option should be hidden. Just must show the Normal free shipping option.
If the stock of the product is >0, both options should be shown.
If I selected more than one product, and one or more of the products does not have stock, the Express shipping option also should be hidden.
From another answer related to this question, I tried the script showed in the answer.
The problem is the following: in the last part of the script there is a mention to a variable in an external plugin.
How I adapt the code to a more generic woocommerce without that plugin?
Script:
/* !Hide Shipping Options Woocommerce */
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_quantity' , 10, 1 );
function check_cart_for_oos() {
// load the contents of the cart into an array.
global $woocommerce;
$found = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$_product_quantity = $_product->get_stock_quantity();
$_cart_quantity = $values['quantity'];
if (($_product_quantity <= 0) || ($_cart_quantity > $_product_quantity)) {
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_quantity( $available_methods ) {
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['table_rate_shipping_next-day'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}
// return the available methods without the one you unset.
return $available_methods;
}
From: Remove next day shipping method for out of stock items in Woocommerce
In that answer, it's already mentioned to use your own shipping method identifier. So you just need to replace the shipping method identifier with yours.
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['USE_YOUR_OWN_IDENTIFIER_HERE'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}

woocommerce payfast split payment to merchant id based on product purchased?

based off this link Payfast split payments which appears to work. How does one run a multi vendor woocommerce store and then split the payment to the associated merchant based on the product purchased?
Typically, this information is stored at the order item level. So, you need to loop through the items, get the vendor id and do your splitting payment action.
E.g. like this on the admin order page:
global $order;
$items = $order->get_items();
foreach ( $items as $item_key => $item ) {
$vendor_id = wc_get_order_item_meta( $item_key, '_vendor_id', true );
// Now get the merchant id with $vendor_id (=user id) and do split payment.
// You can also create an array with $merchant_id => $item->get_subtotal()
// and do the split payment after the loop.
}
The meta key "_vendor_id" depends on your plugin - I used WCMP (Multivendor Marketplace Solution for WooCommerce – WC Marketplace). You may need to look it up in your database how the field is called.

In WooCommerce how can I make it so users can only buy 1 product per order and make it so on a specific product they can only buy it once per user?

I'm building a ticketing website for events. I'm using WooCommerce and The Events Calendar plugin.
I've already made it so that users must be logged in to buy a ticket (product).
Then I found this code snippet that would limit it so that only 1 product can be purchased per order
add_filter( 'woocommerce_add_to_cart_validation', 'wc_limit_one_per_order', 10, 2 );
function wc_limit_one_per_order( $passed_validation, $product_id ) {
if ( 31 !== $product_id ) {
return $passed_validation;
}
if ( WC()->cart->get_cart_contents_count() >= 1 ) {
wc_add_notice( __( 'This product cannot be purchased with other products. Please, empty your cart first and then add it again.', 'woocommerce' ), 'error' );
return false;
}
return $passed_validation;
}
This would work, but in the first if statement you can see that the product ID has to be specified. I know I can also change this to an array like if ( ! in_array( $product_id, array( 31, 32, 33 ) ) ) { but the problem is that I would need to keep updating the IDs for every new event. Is there a way to do this so it applies to all products all the time? If not with code then maybe with settings or a plugin?
I also need to prevent customers (users) from returning to the site later and buying another ticket. So, I need to limit specific products so that only 1 of that SKU can be purchased per user account forever, meaning they can't just return to the site and start the buying process again. Is there a way to do this?
Many thanks in advance.
Is there a way to do this so it applies to all products all the time?
Sure. Just force the Cart to empty before adding a new item:
add_filter( 'woocommerce_add_to_cart_validation', 'bbloomer_only_one_in_cart', 9999, 2 );
function bbloomer_only_one_in_cart( $passed, $added_product_id ) {
wc_empty_cart();
return $passed;
}
Source and screenshot: https://www.businessbloomer.com/woocommerce-allow-1-product-cart/
I need to limit specific products so that only 1 of that SKU can be
purchased per user account forever, meaning they can't just return to
the site and start the buying process again
As #7uc1f3r said, please share the code you tried with and then we'll take a look

How to copy an order item meta from the parent item to the child order item in a Woocommerce Subscription?

I'm running a function hooked to woocommerce_order_status_completed that uses an API to get a phone number and pin from an external source and save that in a meta key on some order items. Some of these order items are subscriptions.
With the current code, when a Woocommerce subscription renewal order runs automatically, it fires the API and gets a new set of call-in data, but I want to stop it from doing that.
I need to check if a completed order is a subscription renewal and if so, skip the API call and instead get the renewed item's parent meta data and insert it into the that child items meta.
The top portion of the code I have tried here is not working. The API call portion of the code within else{} is working so I have truncated it.
add_action ( 'woocommerce_order_status_completed', 'add_item_meta_or_run_api', 10 , 1);
function add_item_meta_or_run_api( $order_id ) {
$order = wc_get_order( $order_id );
// if (wcs_order_contains_subscription( $order, 'renewal' )){ //check if the order contains a renewal subscription
if (wcs_order_contains_renewal( $order_id)){ //Updated: a better way to do this.
foreach ($order->get_items() as $item_id => $item_obj) { //loop through each rewnewal item
$parent_id = $item_obj->get_parent_id(); // Get the parent order ID for the subscriptions.
$parentSubscriptions = wcs_get_subscriptions_for_order( $parent_id );//get parent order subscriptions
foreach ( $parentSubscriptions->get_items() as $parent_item_id => $subscription_item_obj) { //loop through parent order items and get the meta.
$ParentCallinData = $subscription_item_obj->get_meta('call_in_data');
// Store parenent item call in data in renewal order item meta
wc_update_order_item_meta($item_id,'call_in_data', $ParentCallinData, true);
}
}
}
else {//if there is not a subscription renewal in the order then we run the API
foreach ($order->get_items() as $item_id => $item_obj) {
//Code here has been removed that builds and runs the API call to dynamically get the call-in data and store it in $APIresponse
wc_update_order_item_meta($item_id,'call_in_data', $APIresponse, true); //the APIresponse is added to an order item meta key. I need to insert this meta in each child subscription.
}
}
}

Woocommerce "No Shipping methods" message: Customise message based on the Zipcode customer enters

Our store has been setup to process orders only within Sydney city. We manage this in Woocommerce by setting the allowed postcodes for a Flat Rate Delivery.
We are now extending deliveries to other other areas but only via telephone (no online orders for these new postcodes). Woocommerce displays a standard No Shipping message but we would to customise such that
Customer enters a postcode within the city, allow online orders. No
change to current behaviour.
Customer enters a postcode for which
telephone orders are allowed, show a customised message asking the
customer to make the call.
All other postcodes, disallow orders.
Any technical direction will be greatly appreciated.
Thanks.
Below code should help you. Change the value of array variable $zip_array in both the functions as a comma separated list of zip codes, which you want to show a custom message. Also, change the string value of $custom_msg to your custom message. More details, please refer this article.
// For Cart Page.
add_filter( 'woocommerce_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
// For Checkout page
add_filter( 'woocommerce_cart_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
function wf_customize_default_message( $default_msg ) {
$zip_array = array(
'30031',
);
if ( in_array( WC()->customer->get_shipping_postcode() , $zip_array) ) {
$custom_msg = "Call us for quotation - 1-800-XXX-XXXX";
if( empty( $custom_msg ) ) {
return $default_msg;
}
return $custom_msg;
}
return $default_msg;
}
add_filter('woocommerce_package_rates', 'wf_remove_shipping_options_for_particular_zip_codes', 8, 2);
function wf_remove_shipping_options_for_particular_zip_codes($rates, $package)
{
global $woocommerce;
$zip_array = array(
'30031',
);
if ( in_array( $woocommerce->customer->get_shipping_postcode() , $zip_array) ) {
$rates = array();
}
return $rates;
}

Resources