0
votes

I am using WooCommerce with WPML plugin. I want to implement a feature on checkout when a customer under certain conditions can have an upgrade of his product but keeping the old product price.

The products are variable with many variable attributes. So, more specifically, what I want is if a customer has selected a specific product variation with x price on checkout (under a certain condition) I could change his cart item with another product's variation but keep the x price.

What I tried first is to change only the name of the product using woocommerce_order_item_name hook but the change doesn't follow on the order. This is important because some order data are then sent to an API.

Afterwards I used "Changing WooCommerce cart item names" answer code, which worked perfectly for my purpose until I installed WPML. For some reason the WC_Cart method set_name() doesn't work with WPML. I opened a support thread but they still can't find a solution.

Can anyone suggest any other solution?

Update

I have tried an approach where I remove the product item on cart and then I add the one I need. After I use set_price() to change the price of the newly added item. The removal/addition seems to be working but the price is not changed on one language and it is not applied on both languages after placing order. This is the code I use:

function berrytaxiplon_change_product_name( $cart ) {

if ( is_admin() && ! defined( 'DOING_AJAX' ) )
    return;

if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
    return;

    // Loop through cart items
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {

        // Get an instance of the WC_Product object
        $product = $cart_item['data'];

        // Get the product name (Added Woocommerce 3+ compatibility)
        $product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;

        if ( ICL_LANGUAGE_CODE == 'en') {
            if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {

                $new_product = wc_get_product( 82 );
                $atrributes = $product->get_attributes('view');
                foreach ($atrributes as $atrribute_key => $atrribute_value) {
                    $new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
                }
                $new_variation_id = find_matching_product_variation_id(82, $new_attributes);
                $cart->remove_cart_item( $cart_item_key );
                $cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );

                foreach ( WC()->cart->get_cart() as $new_item ) {

                    $new_item['data']->set_price( $cart_item['s-fare'] );
                }
            }
        } else {
            if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {

                $new_product = wc_get_product( 303 );

                $atrributes = $product->get_attributes('view');
                foreach ($atrributes as $atrribute_key => $atrribute_value) {
                    $new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
                }
                $new_variation_id = find_matching_product_variation_id(303, $new_attributes);
                $cart->remove_cart_item( $cart_item_key );
                $cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
                foreach ( WC()->cart->get_cart() as $new_item ) {
                    $new_item['data']->set_price( $cart_item['s-fare']);
                }

            }
        }

    }

}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );

Any idea why the set_price() method is not applied?

Update 2

WPMl uses 'woocommerce_before_calculate_totals' and overrides the action added on functions.php

WPML support provided a solution using 3 filters:

https://wpml.org/forums/topic/cant-use-set_name-method-for-the-product-object-on-checkout/#post-3977153

2
Can you elaborate the "certain condition" part. Is this some action that user performs or determined on page load or something of that sort?Faham Shaikh
The user makes a choice before checkout (cart page is skipped and only one product can be bought) that is passed as an item meta. If this meta exists and a specific product is selected I have to make the upgrade.thepi
In that case, when the choice is made, you can empty the cart and add a new product with custom price before redirecting to checkout. I will paste example code in the answer in few minutes.Faham Shaikh
@thepi Note that "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Reproducible Example".LoicTheAztec
@LoicTheAztec I am sorry if I used the wrong question category. I have created (but not published) this question days earlier, before opening a support thread on WPML. The question is more about asking for suggestion on how to approach the issue.thepi

2 Answers

0
votes

So this is a code that I am using in one of my projects to add a product variation to cart based off of some filters and the selected product:

$product = new WC_Product($product_id); //The main product whose variation has to be added
$product_name = $product->get_name(); //Name of the main product
$quantity = sanitize_text_field($cData['quantity']); //You can set this to 1
$variation_id = sanitize_text_field($cData['variation_id']); //I had the variation ID from filters
$variation = array(
    'pa_duration' => sanitize_text_field($cData['duration']) //The variation slug was also available for me.
);
$cart_item_data = array('custom_price' => sanitize_text_field($custom_price));
$cart = WC()->cart->add_to_cart( (int)$product_id, (int)$quantity, (int)$variation_id, $variation, $cart_item_data ); //This will add products to cart but with the actual price of the variation being added and meta data holding the custom price.
WC()->cart->calculate_totals();
WC()->cart->set_session();
WC()->cart->maybe_set_cart_cookies();

Then you need to do a check on before cart totals are calculated and set the price to custom price like this:

function woocommerce_custom_price_to_cart_item( $cart_object ) {  
    if( !WC()->session->__isset( "reload_checkout" )) {
        foreach ( $cart_object->cart_contents as $key => $value ) {
            if( isset( $value["custom_price"] ) ) {
                $value['data']->set_price($value["custom_price"]);
            }
        }  
    }  
}
add_action( 'woocommerce_before_calculate_totals', 'woocommerce_custom_price_to_cart_item', 99 );
0
votes

The code provided from Faham is very helpful but the page-template that leads to checkout is already over-complicated so I focused to use his logic on the 'woocommerce_before_calculate_totals' hook I am trying all along. So instead of trying to change the name I remove the item and add the new one. Then calling a new loop I set the price to be of the item that was removed.

function berrytaxiplon_change_product_name( $cart ) {

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // Loop through cart items
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {

        // Get an instance of the WC_Product object
        $product = $cart_item['data'];

        // Get the product name (Added Woocommerce 3+ compatibility)
        $product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;

        if ( ICL_LANGUAGE_CODE == 'en') {
            if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {
            // SET THE NEW NAME
            $new_product = wc_get_product( 82 );

            $atrributes = $product->get_attributes('view');
            foreach ($atrributes as $atrribute_key => $atrribute_value) {
                $new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
            }
            $new_variation_id = find_matching_product_variation_id(82, $new_attributes);
            $cart->remove_cart_item( $cart_item_key );
            $cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );

            foreach ( WC()->cart->get_cart() as $new_item ) {
                $new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
            }

            }
        } else {
            if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {
                // SET THE NEW NAME
                $new_product = wc_get_product( 303 );

                $atrributes = $product->get_attributes('view');
                foreach ($atrributes as $atrribute_key => $atrribute_value) {
                    $new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
                }
                $new_variation_id = find_matching_product_variation_id(303, $new_attributes);
                $cart->remove_cart_item( $cart_item_key );
                $cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
                foreach ( WC()->cart->get_cart() as $new_item ) {
                    $new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
                }

               }
        }

    }
}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );  

I use the function below to match the attributes taken from the question WooCommerce: Get Product Variation ID from Matching Attributes

function find_matching_product_variation_id($product_id, $attributes)
{
    return (new \WC_Product_Data_Store_CPT())->find_matching_product_variation(
        new \WC_Product($product_id),
        $attributes
    );
}

I am a little skeptical using add_to_cart() and a second foreach() inside the $cart_item. But I tested and it seems to work without errors.

Update

Actually there is an issue with this code (or with WPML again). It seems that set_price() is not applied on the secondary language. Yet if I reload the checkout page an send the data again the new price is applied.