I Made a custom form option on site to update price and send that data through ajax and try to get in filter woocommerce_before_calculate_totals Session.
Here is code
add_action( 'woocommerce_before_calculate_totals', 'calculate_total_price', 99 );
function calculate_total_price( $cart_object )
{
global $woocommerce;
session_start();
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data'].'<br/>.';
$_SESSION['productlist'][] =
[
'price' => $tac_dd_discounted_price,
'productid' => $target_product_id
];
$arrys = array_merge( $_SESSION[ "productlist" ]);
$_SESSION[ "productlist" ] = array_unique($arrys);
// This unique array created in seesion fro multi product which show correct data.
$price_blank="1";
foreach ( $cart_object->get_cart() as $cart_item ) {
$id= $cart_item['data']->get_id();
//$target_product_id=$arrys['productlist']['productid'];
//$tac_dd_discounted_price=$arrys['productlist']['price'];
if ( $id == $target_product_id ) {
$cart_item['data']->set_price($tac_dd_discounted_price);
}
else
{
$cart_item['data']->set_price($my_price['productlist']['price']);
}
}
}
But issue is for one product in cart price show correct but when try to add two product the Seession variable append same value in both product
First, instead of using PHP $_SESSION you should better use WooCommerce dedicated WC_Session class:
// Set the data (the value can be also an indexed array)
WC()->session->set( 'custom_key', 'value' );
// Get the data
WC()->session->get( 'custom_key' );
Now in your code function, you are just getting from PHP Session one product and one price in:
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data']; // ==> Removed .'<br/>.'
Instead you should need to get an array of product IDs and prices when there is many products in cart.
Also, you don't need global $woocommerce;…
As you don't show all other related JS/Ajax/PHP code and as your question is not detailed, is not possible to help you more than that.
I did it anyone who used custom session for price need to update the cart and checkout page this is the hook which will help you a lot.
add_filter( 'woocommerce_add_cart_item' , 'set_woo_prices');
add_filter( 'woocommerce_get_cart_item_from_session', 'set_session_prices', 20 , 3 );
function set_woo_prices( $woo_data ) {
session_start();
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data'];
if ( ! isset($tac_dd_discounted_price ) || empty ($tac_dd_discounted_price ) ) { return $woo_data; }
$woo_data['data']->set_price( $tac_dd_discounted_price );
$woo_data['my_price'] = $tac_dd_discounted_price;
return $woo_data;
}
function set_session_prices ( $woo_data , $values , $key ) {
if ( ! isset( $woo_data['my_price'] ) || empty ( $woo_data['my_price'] ) ) { return $woo_data; }
$woo_data['data']->set_price( $woo_data['my_price'] );
return $woo_data;
}
Related
I need to deactivate a checkout date picker generated by a plugin when a product on cart is virtual.
Here's the hook they gave for that:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
Based on that information, this is my code attempt:
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $fields ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters(‘woocommerce_delivery_disabled_dates’, $disableDates);
}
return $fields;
}
However this does not give the desired result, any advice how to hide the checkout date picker when the cart contains a virtual product?
The main issues is that $disabledDates is undefined - I would however change $fields to $disableDates as it makes a bit more sense. See below:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $disableDates ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
}
return $disableDates;
}
The $disableDates variable is the input argument for your callback of the hook which you named $fields ( I think )
Ps. this is just a guess based on the code you posted. There is quite a bit that is not clear to me, but $disableDates in your original code clearly should have a value.
No need to use the woocommerce_checkout_fields hook,
the logic can be applied in the appropriate hook.
So you get:
function filter_woocommerce_delivery_disabled_dates( $disableDates ) {
// WC Cart NOT null
if ( ! is_null( WC()->cart ) ) {
// Loop through cart items
foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
// Check if there are virtual products
if ( $cart_item['data']->is_virtual() ) {
$disableDates = true;
break;
}
}
}
return $disableDates;
}
add_filter( 'woocommerce_delivery_disabled_dates', 'filter_woocommerce_delivery_disabled_dates', 10, 1 );
I'm adding a custom fees to WC with WC()->cart->add_fee() method.
My problem is that I'd like to add metadata to that fee item too. Preferably same time I'm adding the actual fee.
Apparently the WC_Order_Item_Fee Object is generated in the order creation only, so there seems to be no way to add FeeItem-specific metadata to custom fees.
Of course I could save this meta to session, but because add_fee doesn't return any identifier I have no idea which custom fee is actually which.
Any ideas how to solve this issue?
This is the code I use to add Fees:
add_filter('woocommerce_cart_calculate_fees', function (){
foreach( FeeChecker::getFees() as $fee )
{
$cart->add_fee("Added fee: ". $fee, 10 , true, $tax_class);
}
}
Note: In your code the $cart argument is missing from the hooked function and it's an action hook, but not a filter hook.
The WC_Cart method add_fee() doesn't allow to add custom meta data, so you will need to add it before on add_to_cart event or in WC_Session.
You can add custom meta data to WC_Order_Item_Fee when order is submitted using the following code example (using WC_Session to set and get the custom meta data in here):
// Add a custom cart fee
add_action( 'woocommerce_cart_calculate_fees', 'adding_cart_fees', 10, 1 );
function adding_cart_fees( $cart ){
$cart->add_fee(__("Added fee"), 10, true, '');
}
// Set Fee custom meta data in WC_Session
add_action( 'woocommerce_calculate_totals', 'calculate_totals_for_fees_meta_data', 10, 1 );
function calculate_totals_for_fees_meta_data( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$fees_meta = WC()->session->get('fees_meta');
$update = false;
// Loop through applied fees
foreach( $cart->get_fees() as $fee_key => $fee ) {
// Set the fee in the fee custom meta data array
if( ! isset($fees_meta[$fee_key]) ){
$fees_meta[$fee_key] = 'some value';
$update = true;
}
}
// If any fee meta data doesn't exist yet, we update the WC_Session custom meta data array
if ( $update ) {
WC()->session->set('fees_meta', $fees_meta);
}
}
// Save fee custom meta data to WC_Order_Item_Fee.
add_action( 'woocommerce_checkout_create_order_fee_item', 'save_custom_met_data_to_fee_order_items', 10, 4 );
function save_custom_met_data_to_fee_order_items( $item, $fee_key, $fee, $order ) {
// Get fee meta data from WC_Session
$fees_meta = WC()->session->get('fees_meta');
// If fee custom meta data exist, save it to fee order item
if ( isset($fees_meta[$fee_key]) ) {
$item->update_meta_data( 'custom_key', $fees_meta[$fee_key] );
}
}
// Remove Fee meta data from WC_Session.
add_action( 'woocommerce_checkout_create_order', 'remove_fee_custom_met_data_from_wc_session', 10, 2 );
function remove_fee_custom_met_data_from_wc_session( $order, $data ) {
$fees_meta = WC()->session->__unset('fees_meta');
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
The custom meta data is saved to WC_Order_Item_Fee and you can get it using something like:
// Get an instance of the WC_Order Object (if needed)
$order = wc_get_order( $order_id );
// loop through fee order items
foreach ( $order->get_items('fee') as $fee_key => $item ) {
// Get the fee custom meta data
$fee_custom_meta = $item->get_meta('custom_key');
if ( $fee_custom_meta ) {
// Display the custom meta data value
echo '<p>' . $fee_custom_meta . '</p>';
}
}
A somewhat hacky way is to use the ID field of the fee item to link to data stored somewhere else.
$args = array(
'id' => $data_id,
'name' => $name,
'amount' => (float) $amount,
'taxable' => false,
'tax_class' => '',
);
$cart->fees_api()->add_fee( $args );
Later you can use the hook woocommerce_checkout_create_order_fee_item to fetch the data and populate the order item fee. In my case:
add_action( 'woocommerce_checkout_create_order_fee_item', array( $this, 'create_order_fee_item' ), 20, 4 );
public function create_order_fee_item( $item, $fee_key, $fee, $order ) {
$item->add_meta_data( 'product_id', $fee_key, true );
}
It seems I overlooked WC_Cart->fees_api
In there I have method that actually returns the created Fee so I know the exact Fee in woocommerce_checkout_create_order_fee_item action
Edit: This code was originally mostly done by LoicTheAztec. I edited it to fit my particular use case and posted it as solution. Unfortunatelly he deleted his post which could have been beneficial to others.
// Add cod fee
add_action('woocommerce_cart_calculate_fees', function ( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
//Make sure it's the right payment method
if( WC()->session->chosen_payment_method == "cod"){
$tax_class = '';
$amount = 5;
$session_data = [];
foreach( $cart->get_shipping_packages() as $package)
{
$fee = $cart->fees_api()->add_fee(
array(
'name' => "Additional cost: ".$package['custom_data'],
'amount' => (float) $amount,
'taxable' => true,
'tax_class' => $tax_class,
)
);
$session_data [ $fee->id ] = $package['custom_data'];
}
WC()->session->set('COD_fee_meta', $session_data);
}
}, 10, 2);
// Save fee custom meta data to WC_Order_Item_Fee.
add_action( 'woocommerce_checkout_create_order_fee_item', function ( $item, $fee_key, $fee, $order ) {
// Get fee meta data from WC_Session
$fees_meta = WC()->session->get('COD_fee_meta');
// If fee custom meta data exist, save it to fee order item
if ( isset($fees_meta[$fee_key]) ) {
$item->update_meta_data( '_custom_data', $fees_meta[$fee_key] );
}
}, 10, 4 );
// Remove Fee meta data from WC_Session.
add_action( 'woocommerce_checkout_create_order', function ( $order, $data ) {
WC()->session->__unset('COD_fee_meta');
}, 10, 2 );
I'm trying to change the WooCommerce price dynamically on WooCommerce, already tried various solution posted here, but no one worked for my case.
Here's an example, I have an ajax function that calls the receive_order_ajax_request function, add the products into the cart, and after I call the update_vehicle_price to update the product's price. But all products remains with the same price. Can someone give me a help?
class WhollOrder {
public function __construct() {
add_action( 'wp_ajax_nopriv_receive_order_ajax_request', array( $this, 'receive_order_ajax_request' ) );
add_action( 'wp_ajax_receive_order_ajax_request', array( $this, 'receive_order_ajax_request' ) );
}
public function receive_order_ajax_request() {
global $woocommerce;
$booking_details = $_POST['booking_details'];
if ( isset( $booking_details ) && ! empty( $booking_details ) ) {
foreach ( $booking_details['vehicles'] as $vehicle ) {
$woocommerce->cart->add_to_cart( $vehicle['product_id'], (int) $vehicle['product_qty'] );
}
$this->update_vehicle_price( $booking_details['vehicles'] );
} else {
wp_send_json_error(
array(
'message' => 'Preencha todos os campos'
)
);
}
wp_die();
}
private function update_vehicle_price( $vehicles ) {
global $woocommerce;
foreach ( WC()->cart->get_cart() as $key => $cart_item ) {
foreach ( $vehicles as $vehicle ) {
if ( $vehicle['product_id'] == $cart_item['product_id'] ) {
WC()->cart->get_cart()[$key]['data']->set_price( 1000.00 );
}
}
}
}
}
new WhollOrder();
This is not a duplicate because the way I coded is not possible to use the others question's answers.
Yea dude, I've been here. The price is reloaded after you go to the checkout page.
You need to set it using this hook : woocommerce_checkout_create_order_line_item.
Change cart item prices in WooCommerce version 3.0
This function scrapes my shopping cart for a particular product, and if that product exists, removes shipping services. E.G. if product id:40 is in the cart, disable shipping service values in array($shipping_services_to_hide).
I would like to display a message to customers that notifies them of the shipping restriction, in the form of an alert or preferably injecting code into the cart in a specific place (based on class -no div id available-. Also,using :before or :after with css content: would be acceptable)(no idea if this part can be done)
What would be a good way to do that?
add_filter( 'woocommerce_package_rates','hide_shipping_method_if_particular_product_available_in_cart' , 10, 2 );
function hide_shipping_method_if_particular_product_available_in_cart( $available_shipping_methods ) {
global $woocommerce;
// products_array should be filled with all the products ids
// for which shipping method (stamps) to be restricted.
$products_array = array(
40
);
// You can find the shipping service codes by doing inspect element using
// developer tools of chrome. Code for each shipping service can be obtained by
// checking 'value' of shipping option.
$shipping_services_to_hide = array(
'wf_shipping_ups:12',
'wf_shipping_ups:02',
'wf_shipping_ups:59',
'wf_shipping_ups:01',
'wf_shipping_ups:13',
'wf_shipping_ups:14',
'wf_shipping_ups:11',
'wf_shipping_ups:07',
'wf_shipping_ups:54',
'wf_shipping_ups:08',
'wf_shipping_ups:65'
);
// Get all products from the cart.
$products = $woocommerce->cart->get_cart();
// Crawl through each items in the cart.
foreach ( $products as $key => $item ) {
// If any product id from the array is present in the cart,
// unset all shipping method services part of shipping_services_to_hide array.
if( in_array( $item['product_id'], $products_array ) ) {
foreach ( $shipping_services_to_hide as &$value ) {
unset( $available_shipping_methods[ $value ] );
}
break;
}
}
// return updated available_shipping_methods;
return $available_shipping_methods;
}
You can trigger a notice with wc_add_notice()
if( in_array( $item['product_id'], $products_array ) ) {
foreach ( $shipping_services_to_hide as &$value ) {
unset( $available_shipping_methods[ $value ] );
// add your notice here
wc_add_notice( 'No shipping error.', 'your-plugin' ), 'notice' );
}
break;
}
I am writing the function which should check up whether there is in the cart an item, which has a particular category.
My idea was:
add_filter( 'woocommerce_package_rates', 'remove_flat_rate_from_used_products', 10, 2 );
function remove_flat_rate_from_used_products($rates, $package) {
if( is_woocommerce() && ( is_checkout() || is_cart() ) ) {
if( check_whether_item_has_the_category() ) {
unset( $rates['flat_rate'] );
}
}
return $rates;
}
I guessed, the get_cart() function returns the contents of the cart, and I would be able to get information about items categories out there. I need to know the structure of the array get_cart() returns, so I wrote:
function check_whether_item_has_the_category() {
global $woocommerce;
var_dump(WC()->cart->get_cart());
}
And got
Warning: Invalid argument supplied for foreach() in ...wp-content\plugins\woocommerce\includes\class-wc-shipping.php on line 295
Then I tried find the category name in results of the get_cart() function:
function check_whether_item_has_the_category() {
global $woocommerce;
if( in_array('used', WC()->cart->get_cart()) ) {
echo 'do something';
}
}
And got the same error.
The use of $woocommerce instead of WC() give nothing, as well as the remove of global $woocommerce
What am I doing wrong and how do I get items categories (or check them for existing of the particular one)?
The variable $package contains also the cart contents ($package['contents']), that is an array with all the products in cart.
So you can loop through that and see if the single product has the category wanted. To get the categories you can use wp_get_post_terms():
function remove_flat_rate_from_used_products($rates, $package) {
// for each product in cart...
foreach ($package['contents'] as $product) {
// get product categories
$product_cats = wp_get_post_terms( $product['product_id'], 'product_cat', array('fields' => 'names') );
// if it has category_name unset flat rate
if( in_array('category_name', $product_cats) ) {
unset( $rates['flat_rate'] );
break;
}
}
return $rates;
}