Search code examples
phpwordpresswoocommerceproductcart

Force minimum order quantity for specific products including variations on WooCommerce cart page


i am trying to achive the following in WooCommerce. I want to set minimum order quantity for specific product.

The problem is that this product for example is a variable product, and i want to set a minimum quantity of 12 pcs but this minimum i want to be on entire product not on every variation.

For example:

  • if a customer add to cart 2 pcs of this product choosing a variation of XL Black, there should be a notice that the minimum quantity is 12 pcs.
  • When this customer add 10 pcs of a variation of the same product of L Red then he has fulfil the minimum order requirement and he should be able to place his order.

The code I have so far works for simple products, but it counts variations as different products.

How can I adjust this so that variants quantities in the cart are counted as 1 product?

// Set minimum quantity per product before checking out
add_action( 'woocommerce_check_cart_items', 'spyr_set_min_qty_per_product' );
function spyr_set_min_qty_per_product() {
    // Only run in the Cart or Checkout pages
    if( is_cart() || is_checkout() ) {  
        global $woocommerce;

        // Product Id and Min. Quantities per Product
        $product_min_qty = array( 
            array( 'id' => 9059, 'min' => 12 ),
        );

        // Will increment
        $i = 0;
        // Will hold information about products that have not
        // met the minimum order quantity
        $bad_products = array();

        // Loop through the products in the Cart
        foreach( $woocommerce->cart->cart_contents as $product_in_cart ) {
            // Loop through our minimum order quantities per product
            foreach( $product_min_qty as $product_to_test ) {
                // If we can match the product ID to the ID set on the minimum required array
                if( $product_to_test['id'] == $product_in_cart['product_id'] ) {
                    // If the quantity required is less than than the quantity in the cart now
                    if( $product_in_cart['quantity'] < $product_to_test['min'] ) {
                        // Get the product ID
                        $bad_products[$i]['id'] = $product_in_cart['product_id'];
                        // Get the Product quantity already in the cart for this product
                        $bad_products[$i]['in_cart'] = $product_in_cart['quantity'];
                        // Get the minimum required for this product
                        $bad_products[$i]['min_req'] = $product_to_test['min'];
                    }
                }
            }
            // Increment $i
            $i++;
        }

        // Time to build our error message to inform the customer
        // About the minimum quantity per order.
        if( is_array( $bad_products) && count( $bad_products ) > 0 ) {
            // Lets begin building our message
            $message = '<strong>A minimum quantity per product has not been met.</strong><br />';
            foreach( $bad_products as $bad_product ) {
                // Append to the current message
                $message .= get_the_title( $bad_product['id'] ) .' requires a minimum quantity of '
                         . $bad_product['min_req'] 
                         .'. You currently have: '. $bad_product['in_cart'] .'.<br />';
            }
            wc_add_notice( $message, 'error' );
        }
    }
}

Solution

  • To keep it dynamic you can use the following code to add a custom field to the inventory tab in the product data meta box (simple & variable products).

    This way you don't have to hardcode the product IDs

    // Add 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'                => '_min_qty',
                'placeholder'       => __( 'My placeholder', 'woocommerce' ),
                'label'             => __( 'My label', 'woocommerce' ),
                'desc_tip'          => true,
                'description'       => __( 'My description', '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
    function action_woocommerce_admin_process_product_object( $product ) {
        // Isset
        if ( isset( $_POST['_min_qty'] ) ) {        
            // Update
            $product->update_meta_data( '_min_qty', sanitize_text_field( $_POST['_min_qty'] ) );
        }
    }
    add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );
    

    custom_field


    To set a minimum quantity, you can use the following code, which works for both simple products and products with variants

    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
                $min_qty = $product->get_meta( '_min_qty', true );
                
                // NOT empty & minimum quantity is greater than or equal to 2 (1 never needs to be checked)
                if ( ! empty( $min_qty ) && $min_qty >= 2 ) {               
                    // Get current quantity in cart
                    $cart_qty = $cart_item['quantity'];
                    
                    // Product type = variable & cart quantity is less than the minimum quantity
                    if ( $product->get_type() == 'variable' && ( $cart_qty < $min_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 minimum quantity
                    if ( $cart_qty < $min_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 minimum required for this product
                        $bad_products[$i]['min_req'] = $min_qty;
                        
                        // Increment $i
                        $i++;
                    }
                }
            }
        }
        
        // Time to build our error message to inform the customer, about the minimum quantity per order.
        if ( is_array( $bad_products) && count( $bad_products ) > 0 ) { 
            // Clear all other notices          
            wc_clear_notices();
            
            foreach( $bad_products as $bad_product ) {
                // Displaying an error notice
                wc_add_notice( sprintf(
                    __( '%s requires a minimum quantity of %d. You currently have %d in cart', 'woocommerce' ),
                    get_the_title( $bad_product['product_id'] ),
                    $bad_product['min_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 );
    

    error