1
votes

In WooCommerce I am using variables products which variations are based on date availability.

In backend, I have added successfully 2 custom fields with custom prices (adult/children) on each variation of the variable products settings (the 2 custom fields appear at the bottom):

Screen shot of Back-end variations: The 2 custom fields at the bottom

It is necessary that the 2 prices show on the same variation because they are linked to the same date attribute and quantity.

Problem
I can define the prices in backend, show them with a quantity selector on frontend, but I can't add them to cart at the same "click" on add to cart button. When user clicks add to cart button, the appropriate number of adults is added to cart with the right quantity, but I don't know how to add children as well...

In fact I would like to also add to the cart another item corresponding to the same variation, but with the children prices and quantity, and an attribute like "Price : children".

I am thinking of using the add_to_cart function, like:

add_to_cart( $product_id , $quantity , $variation_id, $variation , array(__('Price:','ah')=>__('Children','ah)) );

With the appropriate $product_id, $variation_id and $variation, with $quantity = the number of children input, but I don't really know where and when to hook to include that properly.

What I've tried (and does not work) :

  • I've tried hooking into add_to_cart itself, but only succeeded to create a recursive call that ends with an error.
  • I've also tried to hook into woocommerce_before_calculate_totals, but it seems that it's the right place to alter price, but that it is "too late" to add a new product, and set quantity for it.

Display of adult and children quantity and prices
Product page is modified to show the 2 prices, with each a quantity selector, and also a jQuery script that calculates total dynamically :

View of the front-end, with prices and quantity selector, and automatic calculation of total View of the front-end, with prices and quantity selector, and automatic calculation of total

Here is my code:

function ah_activity_product_price_display() {
    global $product;
    $stock = $product->get_total_stock();
    $pricepolicy = get_post_meta( $product->id, '_ah_pricing', true );
    if( is_activity()) {
        $price_list = array();
        if ($pricepolicy == 'multiple') { 
            $variations=$product->get_available_variations();
            foreach ($variations as $variation) {
                $variation_id = $variation['variation_id'] ;
                $start = $variation['attributes']['attribute_date'];
                $price_list[$start]['adult_price'] = get_post_meta( $variation_id, '_adult_price', true );
                $price_list[$start]['children_price'] = get_post_meta( $variation_id, '_children_price', true );
            }
            //var_dump($price_list,json_encode($price_list));
            ?>
            <div class="multiple">
                <p class="clear">
                    <div class="quantity">
                        <input class="minus" type="button" value="-">
                        <input type="number" step="1" min="1" max="<?php echo esc_attr( 0 < $stock ? $stock : '' ); ?>" name="nb_adulte" value="1" title="<?php echo esc_attr_x( 'Adult number', 'Product quantity input tooltip', 'ah' ); ?>" class="input-text qty text" size="4" pattern="" inputmode="" />
                        <input class="plus" type="button" value="+">
                    </div> <?php _e('Adult x ','ah'); ?> <span class="prix-adulte"><?php esc_html($adult_price); ?></span> €
                </p>
                <p class="clear">
                    <div class="quantity">
                        <input class="minus" type="button" value="-">
                        <input type="number" step="1" min="0" max="<?php echo esc_attr( 0 < $stock ? $stock : '' ); ?>" name="nb_enfant" value="0" title="<?php echo esc_attr_x( 'Children number', 'Product quantity input tooltip', 'ah' ); ?>" class="input-text qty text" size="4" pattern="" inputmode="" />
                        <input class="plus" type="button" value="+">
                    </div> <?php _e('Children (under 18) x ','ah'); ?> <span class="prix-enfant"><?php echo esc_html($children_price); ?></span> €
                </p>
            </div>
            <?php 
        }
        echo '<input type="hidden" id="pricelist" name="pricelist" value=\''.json_encode($price_list).'\'>';

    } else {
        woocommerce_get_price_html();
    }

}   
2
Thanks. Indeed it is not that easy to give enough explanations about what I want to do, and at the same time being minimal. I am afraid of missing details and that people think that it is just a basic question of someone who don't know using woocomerce or so... Anyway, I try to rewrite my question with only the necessary.Aurélien Sanrey
Thanks a lot @LoicTheAztec for formatting and clearing up the question. In the meantime I've almost achieved what I want to do, using an Ajax callback (see answer below), thanks to the following topic : stackoverflow.com/questions/27270880/… I can add the 2 products with the right quantity, but I can't set the price directly in the ajax add to cart. I posted the code and solution here under, but for my new question : is it better to open a new topic, or to follow this one ?Aurélien Sanrey
I have commented your answer. The way is to make a new topic (new question) for a new question… Last thing, the comments in your code should be in english on that forum…LoicTheAztec

2 Answers

2
votes

I've almost succeeded in achieving what I want. Here is the code added in PHP :

/**
 * Définition et paramètres des scripts pour ajout au panier
 */
add_action( 'wp_enqueue_scripts', 'ah_load_script', 20 );
function ah_load_script(){
    wp_enqueue_script( 'ah_ajax_add_to_cart', get_stylesheet_directory_uri() . '/js/test.js' );
    $i18n = array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'checkout_url' => get_permalink( wc_get_page_id( 'checkout' ) ) );
    wp_localize_script( 'ah_ajax_add_to_cart', 'ah_add_to_cart_params', $i18n );
}
/**
 * Fonction d'ajout au panier Ajax
 */
add_action('wp_ajax_myajax', 'ah_add_to_cart_callback');
add_action('wp_ajax_nopriv_myajax', 'ah_add_to_cart_callback');

    /**
     * AJAX add to cart.
     */
function ah_add_to_cart_callback() {        
    ob_start();
    //$product_id        = 264;
    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
    $quantity          = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] );
    $variation_id      = isset( $_POST['variation_id'] ) ? absint( $_POST['variation_id'] ) : '';
    $variations         = ! empty( $_POST['variation'] ) ? (array) $_POST['variation'] : '';

    $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations, $cart_item_data );
    $product_status    = get_post_status( $product_id );
    if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) && 'publish' === $product_status ) {
        do_action( 'woocommerce_ajax_added_to_cart', $product_id );
        wc_add_to_cart_message( $product_id );
    } else {
        // If there was an error adding to the cart, redirect to the product page to show any errors
        $data = array(
            'error'       => true,
            'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
        );
        wp_send_json( $data );
    }
    die();
}

And this is the corresponding js (excuse me if the code is ugly, but I'm not a real js coder, just a self-learning...) :

jQuery(document).ready(function($){

  $(".test-button").click(function(e){ 
    e.preventDefault();  // Prevent the click from going to the link
    $variation_form = $( this ).closest( '.variations_form' );
    var product_id = $variation_form.find( 'input[name=product_id]' ).val();
    var adult_qty = $variation_form.find( 'input[name=nb_adulte]' ).val();
    var children_qty = $variation_form.find( 'input[name=nb_enfant]' ).val();
    var adult_price = $variation_form.find( '.prix-adulte' ).text();
    var children_price = $variation_form.find( '.prix-enfant' ).text();

    var var_id = $variation_form.find( 'input[name=variation_id]' ).val();
    var att_date = $variation_form.find( 'select[name=attribute_date]' ).val();
    var att_adult = "adulte";
    var att_children = "enfant";

    $.ajax({
        url: ah_add_to_cart_params.ajax_url,
        method: 'post',
        data: {
            'action': 'myajax',
            'product_id': product_id,
            'quantity': adult_qty,
            'price' : adult_price,
            'variation_id': var_id,
            'variation': { 'attribute_date': att_date, 'attribute_tarif': att_adult }
        }
    }).done( function (response) {
      if( response.error != 'undefined' && response.error ){
        //some kind of error processing or just redirect to link
        // might be a good idea to link to the single product page in case JS is disabled
        return true;
      } else {
        $.ajax({
            url: ah_add_to_cart_params.ajax_url,
            method: 'post',
            data: {
                'action': 'myajax',
                'product_id': product_id,
                'quantity': children_qty,
                'price' : children_price,
                'variation_id': var_id,
                'variation': { 'attribute_date': att_date, 'attribute_tarif': att_children }
            }
        }).done( function (response) {
          if( response.error != 'undefined' && response.error ){
            //some kind of error processing or just redirect to link
            // might be a good idea to link to the single product page in case JS is disabled
            return true;
          } else {
            window.location.href = ah_add_to_cart_params.checkout_url;
          }
        });
      }
    });
  });
});

This adds 2 products, with attribute "pricing"=> "adult" or "children", and the corresponding quantity, but the price is the default price of the variation (not my custom price), because I don't know how to include it in the WC()->cart->add_to_cart call (see how my cart looks with the above code for 5 adult, 2 children).

0
votes

Okay, all done ! In complement of my previous answer, here is the bit of code that I added to set the price of corresponding variation with the adult/children price accordingly, in case it can help someone else :

/**
 * Override price in cart in case of adult/children prices
 */
function ah_add_custom_price( $cart_object ) {
    $custom_price = 10; // This will be your custom price  
    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        //var_dump($cart_item);
        $my_item = $cart_item['variation'];
        $adult_price = get_post_meta($cart_item['variation_id'], "_adult_price" , true );
        $children_price = get_post_meta($cart_item['variation_id'], "_children_price" , true );
        if ( array_key_exists('attribute_tarif', $my_item) ) {
            if (( $my_item['attribute_tarif'] == "adulte" ) && ( isset($adult_price) ) ) {
                $cart_item['data']->set_price($adult_price);
            } elseif (( $my_item['attribute_tarif'] == "enfant" ) && ( isset($children_price) ) ) {
                $cart_item['data']->set_price($children_price);   
            }
        } 

    }
}
add_action( 'woocommerce_before_calculate_totals', 'ah_add_custom_price' );

And here is the result : Product page with adult/children prices

Cart with custom attribute and custom price