1

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

thepi
  • 23
  • 6
  • 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 May 31 '19 at 08:33
  • 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 May 31 '19 at 08:43
  • 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 May 31 '19 at 08:48
  • @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](https://stackoverflow.com/help/minimal-reproducible-example)"*. – LoicTheAztec May 31 '19 at 09:26
  • @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 May 31 '19 at 10:01
  • @thepi Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise… StackOverFlow is mainly code based. – LoicTheAztec May 31 '19 at 10:37

2 Answers2

1

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 );
Faham Shaikh
  • 983
  • 2
  • 6
  • 15
1

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.

thepi
  • 23
  • 6