danieltakeshi
danieltakeshi

Reputation: 939

Reduce and check stock of Variable Product by Attribute Value

Using this code to reduce the stock from inventory by Attribute Value. It works to reduce the stock, but when you have like 100 g in stock and the customer buys 120 g. It doesn't show Out of Stock.

So the following code was created:

<?php

add_filter( 'woocommerce_order_item_quantity', 'filter_order_item_quantity', 10, 3 ); 
function filter_order_item_quantity( $quantity, $order, $item )  
{
    $product   = $item->get_product();
    $stock_quantity = $product->get_stock_quantity();
    $term_name = $product->get_attribute('pa_weight');

    // The 'pa_size' attribute value is "15 grams" And we keep only the numbers
    $quantity_grams = preg_replace('/[^0-9.]+/', '', $term_name);

    // Calculated new quantity
    if( is_numeric ( $quantity_grams ) && $quantity_grams != 0 )
        $quantity *= $quantity_grams;

    if($quantity > $stock_quantity) {
        return $quantity;
    } else {
        echo '<p>Out of Stock</p>';
    } 

}

However, it isn't working...

Is there a way to check the stock from $quantity?

    if($quantity > $stock_quantity) {
        return $quantity;
    } else {
        echo '<p>Out of Stock</p>';
    } 

EDIT:

Tried to reduce the stock with a hook to wc_reduce_stock_levels, without success

add_filter( 'wc_reduce_stock_levels', 'filter_reduce_stock_levels', 5, 1); 
function filter_reduce_stock_levels( $order_id ) { 
    if ( is_a( $order_id, 'WC_Order' ) ) { 
        $order = $order_id; 
        $order_id = $order->get_id(); 
    } else { 
        $order = wc_get_order( $order_id ); 
    } 
    if ( 'yes' === get_option( 'woocommerce_manage_stock' ) && $order && apply_filters( 'woocommerce_can_reduce_order_stock', true, $order ) && sizeof( $order->get_items() ) > 0 ) { 
        foreach ( $order->get_items() as $item ) { 
            if ( $item->is_type( 'line_item' ) && ( $product = $item->get_product() ) && $product->managing_stock() ) { 
                $term_name = $product->get_attribute('pa_weight');
                $qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item ); 
                $item_name = $product->get_formatted_name(); 
                $new_stock = wc_update_product_stock( $product, $qty, 'decrease' ); 
                $quantity_grams = preg_replace('/[^0-9.]+/', '', $term_name);
                $qty *= $quantity_grams;
                if ( ! is_wp_error( $new_stock ) ) { 
                    /** translators: 1: item name 2: old stock quantity 3: new stock quantity */ 
                    $order->add_order_note( sprintf( __( '%1$s stock reduced from %2$s to %3$s.', 'woocommerce' ), $item_name, $new_stock + $qty, $new_stock ) ); 

                    // Get the latest product data. 
                    $product = wc_get_product( $product->get_id() ); 

                    if ( '' !== get_option( 'woocommerce_notify_no_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { 
                        do_action( 'woocommerce_no_stock', $product ); 
                    } elseif ( '' !== get_option( 'woocommerce_notify_low_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { 
                        do_action( 'woocommerce_low_stock', $product ); 
                    } 

                    if ( $new_stock < 0 ) { 
                        do_action( 'woocommerce_product_on_backorder', array( 'product' => $product, 'order_id' => $order_id, 'quantity' => $qty ) ); 
                    } 
                } 
            } 
        } 

        // ensure stock is marked as "reduced" in case payment complete or other stock actions are called 
        $order->get_data_store()->set_stock_reduced( $order_id, true ); 

        do_action( 'woocommerce_reduce_order_stock', $order ); 
    } 
} 

Upvotes: 1

Views: 1103

Answers (2)

Outsource WordPress
Outsource WordPress

Reputation: 3799

Remove all your old codes and add these to your theme's 'functions.php'.

// reduce stock based on 'pa_weight' attribute
add_filter( 'woocommerce_order_item_quantity', 'filter_order_item_quantity', 10, 3 ); 
function filter_order_item_quantity( $quantity, $order, $item )  
{
    $product   = $item->get_product();
    $term_name = $product->get_attribute('pa_weight');

    // 'pa_weight' attribute value is "15 grams" - keep only the numbers
    $quantity_grams = preg_replace('/[^0-9.]+/', '', $term_name);

    // new quantity
    if( is_numeric ( $quantity_grams ) && $quantity_grams != 0 )
        $quantity *= $quantity_grams;

    return $quantity;
}

// check out of stock using 'pa_weight' attribute
add_filter( 'woocommerce_add_to_cart_validation', 'woocommerce_validate_attribute_weight' );
function woocommerce_validate_attribute_weight() 
{
    // get product id
    if (isset($_REQUEST["add-to-cart"])) {
        $productid = (int)$_REQUEST["add-to-cart"];
    } else {
        $productid = null;
    }

    // get quantity
    if (isset($_REQUEST["quantity"])) {
        $quantity = (int)$_REQUEST["quantity"];
    } else {
        $quantity = 1;
    }

    // get weight of selected attribute
    if (isset($_REQUEST["attribute_pa_weight"])) {
        $weight = preg_replace('/[^0-9.]+/', '', $_REQUEST["attribute_pa_weight"]);
    } else {
        $weight = null;
    }

    // comparing stock
    if($productid && $weight)
    {
        $product = wc_get_product($productid);
        $productstock = (int)$product->get_stock_quantity();

        if(($weight * $quantity) > $productstock)
        {
            wc_add_notice( sprintf( 'You cannot add that amount of "%1$s" to the cart because there is not enough stock (%2$s remaining).', $product->get_title(), $productstock ), 'error' );
            return;
        }
    }

    return true;
}

The first function almost same as your's and you cannot show out of stock message using the woocommerce_order_item_quantity since it will execute only after successful order confirmation.

You need to validate the new stock while adding your product to the cart which can be achieved by using the second function (utilizing woocommerce_add_to_cart_validation).

Upvotes: 1

Sanan Guliyev
Sanan Guliyev

Reputation: 761

I never used woo commerce but I see that you are trying to get number from e.g. 15 grams. I am not sure but most probably you should do same thing for $stock_quantity as well. The final code should be something like this. And I also noticed that you are checking that if requested quantity is grater than stock quantity then return it which is not logical. It should be vice versus. I mean requested quantity should be less or equal to stock quantity.

<?php

add_filter( 'woocommerce_order_item_quantity', 'filter_order_item_quantity', 10, 3 ); 
function filter_order_item_quantity( $quantity, $order, $item )  
{
    $product   = $item->get_product();
    $stock_quantity = $product->get_stock_quantity();
    $term_name = $product->get_attribute('pa_weight');

    // The 'pa_size' attribute value is "15 grams" And we keep only the numbers
    $quantity_grams = preg_replace('/[^0-9.]+/', '', $term_name);
    $stock_quantity = (int)preg_replace('/[^0-9.]+/', '', $stock_quantity);

    // Calculated new quantity
    if( is_numeric ( $quantity_grams ) && $quantity_grams != 0 )
        $quantity *= $quantity_grams;

    if($quantity <= $stock_quantity) {
        return $quantity;
    } else {
        return '<p>Out of Stock</p>';
    } 
}

Upvotes: 0

Related Questions