How to update the shipping cost for a specific zone name and method id?
foreach (WC_Shipping_Zones::get_zones() as $shipping_zone) {
if($shipping_zone['zone_name'] == 'Hrvatska') {
foreach($shipping_zone['shipping_methods'] as $shipping_method) {
if($shipping_method->id == 'flat_rate') {
$new_cost = 5;
// how to update cost?
}
}
}
}
I did it with
$new_cost = 5;
$shipping_method->instance_settings['cost'] = $new_cost ;
update_option( $shipping_method->get_instance_option_key(), apply_filters( 'woocommerce_shipping_' . $shipping_method->id . '_instance_settings_values', $shipping_method->instance_settings, $shipping_method ) );
Related
My Woocommerce store with 5 store managers, I use the Local Pickup plugin, it automatically generates custom fields.
How do I correctly assign orders to them and make them invisible to each other's orders?
I have found a similar function, but can't work for custom fields.
function before_checkout_create_order($order, $data) {
$idp = get_post_meta( $order_id, 'pickup_name', true );
$store_manager_id = '';
$belgium_region = ['3'];
$czech_region = ['2'];
$uk_region = ['1'];
if (in_array($idp, $belgium_region)) {
// Manually assigning the _store_manager_id using the user id, yours will differ
$store_manager_id = 7;
} else if (in_array($idp, $czech_region)) {
$store_manager_id = 3;
} else if (in_array($idp, $uk_region)) {
$store_manager_id = 2;
} else {
$store_manager_id = 1;
}
$order->update_meta_data('_store_manager_id', $store_manager_id);
}
add_action('woocommerce_checkout_create_order', 'before_checkout_create_order', 20, 2);
function custom_admin_shop_manager_orders($query) {
global $pagenow;
$qv = &$query->query_vars;
$currentUserRoles = wp_get_current_user()->roles;
$user_id = get_current_user_id();
if (in_array('shop_manager', $currentUserRoles)) {
if ( $pagenow == 'edit.php' &&
isset($qv['post_type']) && $qv['post_type'] == 'shop_order' ) {
// I use the meta key from step 1 as a second parameter here
$query->set('meta_key', '_store_manager_id');
// The value we want to find is the $user_id defined above
$query->set('meta_value', $user_id);
}
}
return $query;
}
add_filter('pre_get_posts', 'custom_admin_shop_manager_orders');
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;
}
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();
}
}
}
}
I have different product categories on my store. I have a "nettoyage" category that i'd like to allow clients to order ONLY if the number of "nettoyage" items in the cart is > 3 .
Right now, i've set up the default quantity to 3 and don't allow the user to go below on product pages. But what i'd like is to allow them to add 1 of each for example and allow them to order as long as the number of "nettoyage" products in the cart is > 3. How can i refactor this code to do this ?
Right now, i have the following code in my functions.php :
add_filter('woocommerce_quantity_input_args', 'bloomer_woocommerce_quantity_changes', 10, 2);
function bloomer_woocommerce_quantity_changes($args, $product)
{
if (!is_cart()) {
if (is_singular('product') && (has_term('nettoyage', 'product_cat'))) {
$args['input_value'] = 3; // Start from this value (default = 1)
$args['max_value'] = 10; // Max quantity (default = -1)
$args['min_value'] = 3; // Min quantity (default = 0)
$args['step'] = 1; // Increment/decrement by this value (default = 1)
}
}
return $args;
}
add_filter('woocommerce_quantity_input_args', 'min_qty_filter_callback', 20, 2);
function min_qty_filter_callback($args, $product)
{
$categories = array('Noten'); // The targeted product category(ies)
$min_qty = 3; // The minimum product quantity
$product_id = $product->is_type('simple') ? $product->get_parent_id() : $product->get_id();
if (has_term($categories, 'product_cat', $product_id)) {
$args['min_value'] = $min_qty;
}
return $args;
}
// On shop and archives pages
add_filter('woocommerce_loop_add_to_cart_args', 'min_qty_loop_add_to_cart_args', 10, 2);
function min_qty_loop_add_to_cart_args($args, $product)
{
$categories = array('nettoyage'); // The targeted product category
$min_qty = 3; // The minimum product quantity
$product_id = $product->get_id();
if (has_term($categories, 'product_cat', $product_id)) {
$args['quantity'] = $min_qty;
}
return $args;
}
add_action('woocommerce_check_cart_items', 'wc_min_item_required_qty');
function wc_min_item_required_qty()
{
$categories = array('nettoyage'); // The targeted product category
$min_item_qty = 3; // Minimum Qty required (for each item)
$display_error = false; // Initializing
// Loop through cart items
foreach (WC()->cart->get_cart() as $cart_item_key) {
$item_quantity = $cart_item['quantity']; // Cart item quantity
$product_id = $cart_item['product_id']; // The product ID
// For cart items remaining to "Noten" producct category
if (has_term($categories, 'product_cat', $product_id) && $item_quantity < $min_item_qty) {
wc_clear_notices(); // Clear all other notices
// Add an error notice (and avoid checkout).
wc_add_notice(sprintf("Le service livraison nettoyage n'est valable qu'à partir de %s paires!", $min_item_qty, $item_quantity), 'error');
break; // Stop the loop
}
}
}
Finaly found the solution. For those interested, here is the custom function :
add_action('woocommerce_check_cart_items', 'custom_set_min_total');
function custom_set_min_total()
{
if (is_cart() || is_checkout()) {
global $woocommerce, $product;
$i = 0;
foreach ($woocommerce->cart->cart_contents as $product) :
$minimum_cart_product_total = 3;
if (has_term('nettoyage', 'product_cat', $product['product_id'])) :
$total_quantity += $product['quantity'];
endif;
endforeach;
foreach ($woocommerce->cart->cart_contents as $product) :
if (has_term('nettoyage', 'product_cat', $product['product_id'])) :
if ($total_quantity < $minimum_cart_product_total && $i == 0) {
wc_add_notice(
sprintf(
'<strong>A Minimum of %s products is required from the nettoyage category before checking out.</strong>'
. '<br />Current number of items in the cart: %s.',
$minimum_cart_product_total,
$total_quantity
),
'error'
);
}
$i++;
endif;
endforeach;
}
}