简体   繁体   中英

Limit maximum quantity of free products in Woocommerce cart

My client has asked for the ability to offer free samples of their variable products on their Woocommerce online store, with a maximum of 1 of each unique product sample and 5 of any of the samples on their order.

I've achieved part of this by adding an extra “Free Sample” variation to each product and then using a free Min/Max quantity plugin to limit the amount of each individual free sample to 1 per order, please see the following screenshots:

https://ibb.co/f0Wd7XC https://ibb.co/8DrsZZj

So far I haven't established a way to limit the maximum number of any combination of the “Free Sample” variations to 5 though. The only way I can see is by limiting the total number of free products (ie price = £0) per order to 5, or alternatively by assigning a specific shipping class to each variation (ie”Free Samples”) and then somehow limiting the amount of products assigned with this shipping class in each order to 5. Is this possible?

Cheers,

M.

Woocommerce have validation hook which you can use.

First one is when we add to our cart

function add_to_cart_free_samples($valid, $product_id, $quantity) {
    $max_allowed = 5;
    $current_cart_count = WC()->cart->get_cart_contents_count();
    foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
        // Here change attribute group if needed - currently assigned to default size attribute
        $variation = $cart_item['variation']['attribute_pa_size'];
    }
    if( ( $current_cart_count > $max_allowed || $current_cart_count + $quantity > $max_allowed ) && $variation === 'free-sample' && $valid ){
        wc_add_notice( sprintf( __( 'Whoa hold up. You can only have %d items in your cart', 'your-textdomain' ), $max_allowed ), 'error' );
        $valid = false;
    }

    return $valid;
}

add_filter( 'woocommerce_add_to_cart_validation', 'add_to_cart_free_samples', 10, 3 );

Second one is when we update the cart on cart page for example.

function update_add_to_cart_free_samples( $passed, $cart_item_key, $values, $updated_quantity ) {

    $cart_items_count = WC()->cart->get_cart_contents_count();
    $original_quantity = $values['quantity'];
    $max_allowed = 5;
    $total_count = $cart_items_count - $original_quantity + $updated_quantity;
    foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
        // Here change attribute group if needed - currently assigned to default size attribute
        $variation = $cart_item['variation']['attribute_pa_size'];
    }
    if( $cart_items_count > $max_allowed && $variation === 'free-sample' ){
        $passed = false;
        wc_add_notice( sprintf( __( 'Whoa hold up. You can only have %d items in your cart', 'your-textdomain' ), $max_allowed ), 'error' );
    }
    return $passed;
}
add_filter( 'woocommerce_update_cart_validation', 'update_add_to_cart_free_samples', 10, 4 );

In $cart_item you can debug and see all info for the current product in the cart. From there you can add condition depending on shipping or attribue or price or w/e you want.

This is what you are looking for (NO PLUGIN NEEDED):

In the theme's function.php add the following:

/*----------------------------------------------------------
 * Adds custom field to the inventory tab in the product data meta box
 * ---------------------------------------------------------- */
function action_woocommerce_product_options_stock_status() {        
    woocommerce_wp_text_input(
        array(
            'id'                => '_max_qty',
            'placeholder'       => __( 'Set the bundle maximum to at least 2 items or leave blank', 'woocommerce' ),
            'label'             => __( 'Maximum Bundle', 'woocommerce' ),
            'desc_tip'          => true,
            'description'       => __( 'Limits the maximum amount of product bundles your customer can order', 'woocommerce' ),
            'type'              => 'number',
            'custom_attributes' => array(
                'step' => 'any',
            ),
        )
    );
}
add_action( 'woocommerce_product_options_stock_status', 'action_woocommerce_product_options_stock_status', 10, 0 );

/* ----------------------------------------------------------
 * Save custom field to the inventory tab in the product data meta box
 * ---------------------------------------------------------- */
function action_woocommerce_admin_process_product_object( $product ) {
    // Isset
    if ( isset( $_POST['_max_qty'] ) ) {        
        // Update
        $product->update_meta_data( '_max_qty', sanitize_text_field( $_POST['_max_qty'] ) );
    }
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );

function get_cart_quantity_variable_product( $child_ids ) { 
    // Get cart items quantities
    $cart_item_quantities = WC()->cart->get_cart_item_quantities();
    
    // Counter
    $qty = 0;
    
    // Loop through the childIDs
    foreach ( $child_ids as $child_id ) {
        // Checks if the given key or index exists in the array
        if ( array_key_exists( $child_id, $cart_item_quantities ) ) {
            // Addition
            $qty += $cart_item_quantities[$child_id];
        }
    }
    
    return $qty;
}

function action_woocommerce_check_cart_items() {    
    // Will increment
    $i = 0;
    
    // Will hold information about products that have not met the minimum order quantity
    $bad_products = array();
    
    // Will hold information about which product ID has already been checked so that this does not happen twice (especially applies to products with variants)
    $already_checked = array();
    
    // Loop through cart items
    foreach( WC()->cart->get_cart() as $cart_item ) {
        // Get IDs
        $product_id = $cart_item['product_id'];
        $variation_id = $cart_item['variation_id'];
        
        // NOT in array, already checked? Continue
        if ( ! in_array( $product_id, $already_checked ) ) {            
            // Push to array
            $already_checked[] = $product_id;
            
            // Get the parent variable product for product variation items
            $product = $variation_id > 0 ? wc_get_product( $product_id ) : $cart_item['data'];
            
            // Get meta
            $max_qty = $product->get_meta( '_max_qty', true );
            
            // NOT empty & minimum quantity is lesser than or equal to 5 (1 never needs to be checked)
            if ( ! empty( $max_qty ) && $max_qty <= 5 ) {               
                // Get current quantity in cart
                $cart_qty = $cart_item['quantity'];
                
                // Product type = variable & cart quantity is less than the maximum quantity
                if ( $product->get_type() == 'variable' && ( $cart_qty < $max_qty ) ) {                 
                    // Get childIDs in an array
                    $child_ids = $product->get_children();

                    // Call function, get total quantity in cart for a variable product
                    $cart_qty = get_cart_quantity_variable_product( $child_ids );               
                }
                
                // Cart quantity is less than the maximum quantity
                if ( $cart_qty > $max_qty ) {
                    // The product ID
                    $bad_products[$i]['product_id'] = $product_id;
                    
                    // The variation ID (optional)
                    //$bad_products[$i]['variation_id'] = $variation_id;                    
                    
                    // The Product quantity already in the cart for this product
                    $bad_products[$i]['in_cart'] = $cart_qty;
                    
                    // Get the maximum required for this product
                    $bad_products[$i]['max_req'] = $max_qty;
                    
                    // Increment $i
                    $i++;
                }
            }
        }
    }
    
    // Time to build our error message to inform the customer, about the maximum quantity per order.
    if ( is_array( $bad_products) && count( $bad_products ) < 5 ) { 
        // Clear all other notices          
        wc_clear_notices();
        
        foreach( $bad_products as $bad_product ) {
            // Displaying an error notice
            wc_add_notice( sprintf(
                __( '%s has a maximum quantity of %d. You currently have %d in cart', 'woocommerce' ),
                get_the_title( $bad_product['product_id'] ),
                $bad_product['max_req'],
                $bad_product['in_cart'],
            ), 'error' );
        }
        
        // Optional: remove proceed to checkout button
        remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
    }
}   
add_action( 'woocommerce_check_cart_items' , 'action_woocommerce_check_cart_items', 10, 0 );

Credit goes to 7uc1f3r for the solution on: Force minimum order quantity for specific products including variations on WooCommerce cart page

I only added and changed some code to fit it for the maximum bundle. This works for free samples stuff and you can also use it for any product the shop owner wants to limit to a set amount of bundle, as this isn't hardcoding the limit to a certain product.

Here is an example of this working:

功能示例

功能示例 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM