I'm trying to display a custom notice, depending on wheater the order contains products which are in back-order or are all in stock.
I have modified code from this answer.
It works, but only in the cart. I want it to show everywhere: in the cart, in checkout, admin, emails and so on.
this is what I have:
// Conditional function: Checking cart items stock
function is_mixed_stock_items(){
$enough_stock = $out_of_stock = false; // initializing
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Get an instance of the WC_Product Object
$product = $cart_item['data'];
// Get stock quantity
$stock_qty = $product->get_stock_quantity();
// get cart item quantity
$item_qty = $cart_item['quantity'];
if( ( $stock_qty - $item_qty ) >= 0 ){
$enough_stock = true; // enough stock
} else {
$out_of_stock = true; // not enough stock
}
}
// return true if stock is mixed and false if not
return $enough_stock && $out_of_stock ? true : false;
}
// Display a custom notice
add_action( 'woocommerce_before_cart_totals', 'display_delivery_notification' );
function display_delivery_notification() {
if( is_mixed_stock_items() ){
echo'<div class="cart_totals">Forventet levering: 2-10 hverdage</div>';
}
else {
echo'<div class="cart_totals">Levering: 1-2 hverdage</div>';
}
}
Related
In Woocommerce, i want to hide the credit card payment option if a specific product variation is in the cart. Please help.
Thanks.
This is what i have working now. I assigned a separate shipping class to each variation i want to disable a specific payment method at checkout. But it would be much easier if i coud target specific attribute values, so i don't have to assign a shipping class.
<?php
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
$shipping_class_target = 106; // the shipping class ID assigned to specific variations
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if ( $in_cart ) {
unset($available_gateways['cod']); // unset 'cod'
}
else {
unset($available_gateways['bacs']); // unset 'bacs'
}
return $available_gateways;
}
If you are looking to check the variations for each item in the cart, you have to lookup the attributes $product->get_attributes() and then loop through those and get the array key and value for each.
In this example, I used
Size (pa_size) and Small
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
// See if there is an attribute called 'pa_size' in the cart
// Replace with whatever attribute you want
if (array_key_exists('pa_size', (array) $values['data']->get_attributes() ) ) {
foreach ($values['data']->get_attributes() as $attribute => $variation);
// Replace 'small' with your value.
if ($variation == 'small') $in_cart = true; //edited
}
}
if ( $in_cart ) {
unset($available_gateways['cod']); // unset 'cod'
}
else {
unset($available_gateways['bacs']); // unset 'bacs'
}
return $available_gateways;
}
I need to customize cash on delivery (COD) in WooCommerce based on user role and product category.
Requirements:
By default COD is hidden
COD is visible when a product in cart, belongs to a certain category (category 1)
Users who have the role "x" must always see COD, even if they don't have category 1 products in the cart.
This is my code attempt to meet the above requirements:
function disable_cod($available_gateways)
{
if (is_admin()) return $available_gateways;
$role = false;
//check whether the avaiable payment gateways have Cash on delivery and user is not logged in or he is a user with role customer
if (isset($available_gateways['cod']) && (current_user_can('customer') || !is_user_logged_in()))
{
$role = true;
}
foreach (WC()
->cart
->get_cart() as $cart_item_key => $cart_item)
{
$prod_simple = true;
// Get the WC_Product object
$product = wc_get_product($cart_item['product_id']);
// Get the product types in cart (example)
if ($product->is_product_category('X')) $prod_simple = false;
}
if ($prod_simple = $role = true) unset($available_gateways['cod']); // unset 'cod'
return $available_gateways;
}
add_filter('woocommerce_available_payment_gateways', 'disable_cod', 99, 1);
I definitely got something wrong in the syntax, but I hope the logical concept is correct? Any advice?
Your code contains some minor mistakes:
is_product_category() is a conditional tag which returns true when viewing a product category archive. Use has_term() instead.
Loop through the cart is only necessary when the user role is not fulfilled.
So you get:
function filter_woocommerce_available_payment_gateways( $payment_gateways ) {
// Not on admin
if ( is_admin() ) return $payment_gateways;
// Initialize: flag - default true
$flag = true;
// Has certain user role
if ( current_user_can( 'certain-user-role' ) ) {
// False
$flag = false;
} else {
// Specific categories: the term name/term_id/slug. Several could be added, separated by a comma
$categories = array( 63, 15, 'categorie-1' );
// Isset
if ( WC()->cart ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Has term (product category)
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
// False, break loop
$flag = false;
break;
}
}
}
}
// True
if ( $flag ) {
// Cod
if ( isset( $payment_gateways['cod'] ) ) {
// Remove
unset( $payment_gateways['cod'] );
}
}
return $payment_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'filter_woocommerce_available_payment_gateways', 10, 1 );
How can I change shipping cost in Woocommerce by Ajax request?
I tried this:
add_action('wp_ajax_set_shipping_price', 'set_shipping_price');
add_action('wp_ajax_nopriv_set_shipping_price', 'set_shipping_price');
function set_shipping_price(){
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $package_key => $package){
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
$WC_Shipping = new WC_Shipping();
$WC_Shipping->calculate_shipping_for_package( $package, $package_key = 0);
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
}
wp_die();
}
and:
add_filter( 'woocommerce_package_rates', 'custom_shipping_costs', 20, 2 );
function custom_shipping_costs( $rates, $package ) {
if (isset($_POST['cost'])){
$new_cost = $_POST['cost'];
}
$new_cost = 0;
$tax_rate = 0.2;
foreach( $rates as $rate_key => $rate ){
if( $rate->method_id != 'free_shipping'){
$rates[$rate_key]->cost = $new_cost;
$taxes = array();
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 )
$taxes[$key] = $new_cost * $tax_rate;
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
The hook woocommerce_package_rates works at page load but do nothing by ajax. Help please.
Here is the explanation on how to get post data from an Ajax call.
Additionally, you can use the WooCommerce update_checkout event to update the checkout (and thus recalculate shipping costs) after a field in the checkout has changed or been clicked (instead of creating a custom Ajax call). Here you will find a complete list.
So, assuming you want to recalculate shipping costs when the field with id custom_shipping_price (for example) changes, you can use this script:
// updates the checkout (and shipping charge calculation) when the value of a field changes
add_action( 'wp_footer', 'update_checkout' );
function update_checkout() {
// only in the checkout
if ( ! is_checkout() ) {
return;
}
?>
<script type="text/javascript">
jQuery( document ).ready(function( $ ) {
// when the value of the field with id "custom_shipping_price" changes it updates the checkout
jQuery('#custom_shipping_price').change(function(){
jQuery('body').trigger('update_checkout');
});
});
</script>
<?php
}
Now you will need to use the woocommerce_package_rates hook to calculate the new shipping cost.
Make sure you initialize the value of the $new_cost variable in
case the custom field is not set or is empty.
Here the function:
// update the shipping cost based on a custom field in the checkout
add_filter( 'woocommerce_package_rates', 'update_shipping_cost_based_on_custom_field', 10, 2 );
function update_shipping_cost_based_on_custom_field( $rates, $package ) {
if ( ! $_POST || is_admin() || ! is_ajax() ) {
return;
}
// gets the post serialized data sent with the Ajax call
if ( isset( $_POST['post_data'] ) ) {
parse_str( $_POST['post_data'], $post_data );
} else {
$post_data = $_POST;
}
// set the cost of shipping (if the custom field should be empty)
$new_cost = 0;
// if the field is set it gets the value
if ( isset( $post_data['custom_shipping_price'] ) && ! empty( $post_data['custom_shipping_price'] ) ) {
// forces conversion of value into number
$new_cost = (float) str_replace( ',', '.', $post_data['custom_shipping_price'] );
}
// set the percentage of taxes (ex. 22%)
$tax_rate = 0.22;
foreach( $rates as $rate_key => $rate ) {
if ( 'free_shipping' !== $rate->method_id ) {
// set rate cost
$rates[$rate_key]->cost = $new_cost;
// set taxes rate cost (if enabled)
$taxes = array();
foreach ( $rates[$rate_key]->taxes as $key => $tax ) {
if ( $rates[$rate_key]->taxes[$key] > 0 ) {
$taxes[$key] = $new_cost * $tax_rate;
}
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
Both codes have been tested and work. Add them to your active theme's functions.php.
Good afternoon!
Please help me implement this feature in woocommerce.
I have three specific products (with specific IDs 2, 3, 4).
I need to make sure that when adding two of them together to the cart, the prices for both of them change automatically. For example, for a product with id #2, the price was set to $ 100, and for a product with id #3, the price became$200.
Also, when they are all added together (three of them), the price changed for all three of them: product # 2 = 100; product # 3 = 200; product # 4 = 250.
I haven't found anything like this anywhere yet, so I would appreciate any hint.
I managed to solve the problem on my own.
I made sure that the products I need "watch" each other in the basket. And if one of them "notices" the other, the price is updated for both of them.
In the same way, the price is updated if the three of them simultaneously "see" each other in the basket.
It may not be the best code, but it works and it works well.
The original code was taken from this resource and modified by me for my tasks.
(https://www.webroomtech.com/change-product-price-when-other-product-is-in-cart-woocommerce/)
function simple_func_product_in_cart($product_id){
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
if ( $in_cart ) {
return true;
}
return false;
}
add_action( 'woocommerce_before_calculate_totals', 'simple_change_price_of_product' );
function simple_change_price_of_product( $cart_object ) {
$target_product_id = 7101; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7105)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(160); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7101; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7107)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(160); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7105; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7101)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(170); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7105; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7107)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(170); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7107; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7101)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(180); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7107; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7105)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(180); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
}
On Woocommerce, I have an error message on my function but i don't unserstand why it happens.
Uncaught Error: Call to a member function get_cart() on null in ...
The function checks id a category product is in the cart.
The error is displayed in the order detail in backend
It's used in functions.php , executed on checkout page,
called by these hooks:
woocommerce_before_order_notes
woocommerce_checkout_process
woocommerce_checkout_update_order_meta
woocommerce_admin_order_data_after_billing_address
woocommerce_email_order_meta_keys
https://i.stack.imgur.com/v7Atp.png
function is_in_the_cart() {
$found=false;
// Find if product is in the cart price <=40
foreach( WC()->cart->get_cart() as $cart_item_key => $values )
{
$cart_product = $values['data'];
$price = accessProtected($values['data'], 'changes')['price'];
if ($price >=40 && $cart_product->id=='969') {$found=true;}
}
if ( $found ) {
return true;
} else {
return false;
}
}
add_action('woocommerce_checkout_process', 'wps_select_checkout_field_process');
function wps_select_checkout_field_process() {
if (is_in_the_cart())
{
global $woocommerce;
// Check if set, if its not set add an error.
if ($_POST['dvd'] == "blank")
wc_add_notice( '<strong>Merci de séléctionner un DVD</strong>', 'error' );
}
}
//* Update the order meta with field value
add_action('woocommerce_checkout_update_order_meta', 'wps_select_checkout_field_update_order_meta');
function wps_select_checkout_field_update_order_meta( $order_id ) {
if (is_in_the_cart())
{
if ($_POST['dvd']) update_post_meta( $order_id, 'dvd', esc_attr($_POST['dvd']));
}
}
add_action( 'woocommerce_admin_order_data_after_billing_address', 'wps_select_checkout_field_display_admin_order_meta', 10, 1 );
function wps_select_checkout_field_display_admin_order_meta($order){
if (is_in_the_cart())
{
echo '<p><strong>DVD: </strong> ' . get_post_meta( $order->id, 'dvd', true ) . '</p>';
}
}
//* Add selection field value to emails
add_filter('woocommerce_email_order_meta_keys', 'wps_select_order_meta_keys');
function wps_select_order_meta_keys( $keys ) {
if (is_in_the_cart())
{
$keys['Dvd:'] = 'dvd';
return $keys;
}
}
I think you want to check to make sure the cart isn't empty before your function.
Also... You can access price by using get_price() from the product object which you are retrieving with $values['data']
function is_in_the_cart(){
// Make sure it's only on front end
if (is_admin()) return false;
$found = false;
// If cart is empty - bail and return false
if (empty (WC()->cart->get_cart())) {
return false;
} else {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = $values[ 'data' ];
// Find if product is in the cart price <=40
$price = floatval($cart_product->get_price());
if ( $price >= 40 && $cart_product->id == '969' ) {
$found = true;
}
}
if ( $found ) {
return true;
} else {
return false;
}
}
}
UPDATED - added is_admin to prevent from running on backend.