How to set a maximum stock level for total WooCommerce variations sold, disregarding individual variation stock levels - variables

I'm wondering if we are able to hook into WooCommerce to set a maximum amount of stock that can be purchased for a variable product. Disregarding the individual variation stock levels once this maximum amount is reached.
For example, I have a variable product selling workshop groups. There are 4 variations, each with a stock level set at 100. This is because no group can have more than 100 people in. However, only 250 tickets are available for sale (not 400 that we might expect because of the 4x100 quantity).
So this works as far as the max 100 places per workshop group. We just need to somehow be able to limit the total stock level of all 4 variations to 250.
I had hoped enabling the parent product "Manage stock" option and setting this to 250 would work. But obviously, variations must override this. If we can hook into that and turn that back on even when variation stock management is in use that might be a nice way of solving this.
Thanks for any help.

I came up with a solution to my problem by doing the following:
Add 2 custom fields to the WooCommerce product page, which will store the max quantity of the total variations we can sell and also the max quantity of an individual variation. The code for this is:
// Modify WooCommerce Product Settings
add_action('woocommerce_product_options_inventory_product_data', 'wc_add_custom_field' );
function wc_add_custom_field() {
$fields = array('Total quantity' => 'total_quantity','Variation quantity' => 'variation_quantity');
$field_description = array('total_quantity' ='description','variation_quantity' ='description');
$field_placeholder = array('total_quantity' =>'e.g. 300','variation_quantity' =>'e.g. 100');
foreach ($fields as $key => $value) {
woocommerce_wp_text_input( array(
'id' => $value,
'label' => $key,
'description' => $field_description[$value],
'desc_tip' => 'true',
'placeholder' => $field_placeholder[$value]
) );
}
}
// Save Fields
add_action( 'save_post_product', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields_save( $post_id ){
update_post_meta( $post_id, 'total_quantity', $_POST['total_quantity'] );
update_post_meta( $post_id, 'variation_quantity', $_POST['variation_quantity'] );
}
Add cart/basket validation rules to stop customers being able to purchase products that exceed the value of the custom "total_quantity" field added above:
add_action( "woocommerce_add_to_cart_validation","sc_woocommerce_add_to_cart_validation", 1, 5 );
function sc_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id, $variations ) {
// Iterate through each variation and get the total stock remaining
$product_variable = new WC_Product_Variable($product_id);
$product_variations = $product_variable->get_available_variations();
settype($variation_stock_availability, "integer");
foreach ($product_variations as $variation) {
$variation_stock_availability = +(int)$variation['max_qty'];
}
$count_variations = count($product_variations);
$total_quantity = get_post_meta( $product_id, 'total_quantity', true );
$variation_quantity = get_post_meta( $product_id, 'variation_quantity', true );
// formula to test if any stock remaining based on sold variations
$formula = $count_variations * $variation_quantity;
$formula1 = (int)$formula + (int)$quantity;
$formula1 = $formula1 - $variation_stock_availability;
// Iterating through each cart item and use the current running quantity in the cart in the forumula
foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
// count(selected category) quantity
$running_qty += (int) $cart_item['quantity'];
$formula2 = (int)$formula + (int)$running_qty;
$formula2 = $formula2 - $variation_stock_availability;
// More than allowed products in the cart is not allowed
if ($formula2 >= $total_places) {
wc_add_notice( sprintf( __( "Unfortunately there is no availability based on your selection", "donaheys" )), 'error' );
$passed = false;
return $passed;
}
}
// More than allowed products in the cart is not allowed
if ($formula1 >= $total_places) {
// Add the error
wc_add_notice( sprintf( __( "Unfortunately there is no availability based on your selection", "donaheys" )), 'error' );
$passed = false;
return $passed;
} else {
$passed = true;
return $passed;
}
$running_qty = 0;
}
The result of the above code ensures we are can set a maximum amount of stock that can be purchased for a variable product, disregarding the individual variation stock levels once this maximum amount is reached.

Related

getProduct()->getTag() return null, when it should return tags associated to the Product

In my project, we have products that has tag called serviceItem. Those item with that tag when ordered should be separated by the quantity into individuals order.
It issue is that getTags() returns null, and getTagIds gets "Call to a member function getTagIds() on null" when it gets to the next loop.
Is there a reason for why getTags() returns null?
private function transformOrderLines(OrderEntity $order): array
{
/**
* TODO: If we need to send advanced prices,
* the price value of the the lines array should be changed to caldulate the advanced price,
* with the built in quantity calculator
*/
$lines = [];
foreach ($order->getLineItems() as $orderLine) {
$hasDsmServiceItemTag = $orderLine->getProduct()->getTags();
$lines[] = [
'name' => $orderLine->getLabel(),
'sku' => substr($orderLine->getProduct()->getProductNumber(), 0, 19),
'price' => (string) ($orderLine->getProduct()->getPrice()->first()->getNet()
* $order->getCurrencyFactor()), //gets original price, calculates factor
'quantity' => (string) $orderLine->getQuantity()
];
}
$shipping = $this->transformShipping($order);
if ($shipping) {
$lines = array_merge($lines, $shipping);
}
return $lines;
}`
I also tried $orderLine->getProduct()->getTags()->getName() it also return "Call to a member function getTags() on null"
The problem is wherever the $order is fetched from the DB the orderLineItem.product.tag association is not included in the criteria.
For performance reasons shopware does not lazily load all association when you access them on entities, but you have to exactly define which associations should be included when you fetch the entities from the database.
For the full explanation take a look at the docs.

How we display discount percentage badge and sale badge separately on WooCommerce product?

Want to display discount percentage badge on right hand side of product image and sale badge on left hand side of product image in products slider. So, Please Suggest some hooks for this functionality!
Tried to add the following hook but it will replace the existing sale badge with the discount percentage badge on shop page and also this hook is not working for the product slider on homepage.
add_filter('woocommerce_sale_flash', 'add_percentage_to_sale_bubble'); function add_percentage_to_sale_bubble( $html ) { global $product; $percentage = round( ( ( $product->regular_price - $product->sale_price ) / $product->regular_price ) * 100 ); $output =' <span class="onsale">'.$percentage.'%</span>'; return $output; }
you can use the following hook
add_filter( 'woocommerce_get_price_html', 'change_displayed_sale_price_html', 10, 2 );
function change_displayed_sale_price_html( $price, $product ) {
// Only on sale products on frontend and excluding min/max price on variable products
if( $product->is_on_sale() && ! is_admin() && $product->is_type('simple') ){
// Get product prices
$regular_price = (float) $product->get_regular_price(); // Regular price
// Active price (the "Sale price" when on-sale)
$sale_price = (float) $product->get_price();
// "Saving Percentage" calculation and formatting
$precision = 0; // Max number of decimals
$saving_percentage = round( 100 - ( $sale_price / $regular_price * 100 ), $precision ) . '%';
// Append to the formated html price
$price .= sprintf( __('<p class="saved-sale">%s</p>', 'woocommerce' ), $saving_percentage );
}
return $price;
}

VueJS dependent properties

There is a form with 3 fields,
Total Item (QTY)
UNIT Price (Single Item)
Total Price (All Items)
The scenario is like this, user will enter Total Item along with one other value, either unit price or total price,
if user enter unit price, I have to calculate total price by multiplying total item into unit price,
if user enter total price, I have to divide it by total item and get the unit price.
I am trying to achieve it with including unit price and total price in watch without success, here is my code,
watch:{
unit_rate: function(newVal){
if(newVal != ''){
this.total_rate = parseInt(this.total_item) * parseFloat(newVal);
}else{
this.total_rate = '';
}
},
total_rate: function(newVal){
if(newVal != ''){
this.unit_rate = parseFloat(newVal) / parseInt(this.total_item);
}else{
this.unit_rate = '';
}
}
},
I am not sure whether I can modify the property like this while both of them are already in the observe. I did not get the expected output as well, something wrong,,

I want to disable cod when 100 cod orders is completed in prestashop

I want to disable the cod delivery feature in the order page of prestashop 1.6.3 when 100 /50 orders ( this will be a parameter) is completed per day.
How to disable this programmatically by finding out whether 100 cod is been completed .
I will modify the hookPayment() in cashondelivery module to do this :
public function hookPayment($params)
{
if (!$this->active)
return ;
global $smarty;
// Check if cart has product download
if ($this->hasProductDownload($params['cart']))
return false;
//Check whether the cod done exceeds the daily limit if yes dont display the cod option
$cod_limit = Configuration::get('PS_KITS_COD_DAILY_LIMIT');// number of cod
$sql = "select count(*) AS cod_count from ps_orders where module='cashondelivery' and date(date_add) = CURDATE() and ( current_state= 3 or current_state=4)";
if ($row = Db::getInstance()->getRow($sql)){
$cod_count = $row['cod_count'];
}
if ($cod_count >= $cod_limit){
return ;
}
$smarty->assign(array(
'this_path' => $this->_path, //keep for retro compat
'this_path_cod' => $this->_path,
'this_path_ssl' => Tools::getShopDomainSsl(true, true).__PS_BASE_URI__.'modules/'.$this->name.'/'
));
return $this->display(__FILE__, 'payment.tpl');
}

Exclude products out of stock from list of new products - Prestashop

I am using a module that displays new added products in home page. I need to customize the module so that this list doesnt contain products sold. In other words, if a product is out of stock before the number of days it is condidered new is over, then do not show this product in list.
I can do it in view part by using {if $product.quantity < 0}{/if} but my goal is to perform it in controller. Here is my code:
function hookHome($params)
{
global $smarty, $cookie;
$nb = intval(Configuration::get('HOME_NEW_PRODUCTS_NBR'));
$rand = intval(Configuration::get('HOME_NEW_PRODUCTS_RANDOM'));
if ($rand == 1) {
$products = Product::getNewProducts(intval($cookie->id_lang), 0, $nb);
if ( $products )
{
shuffle($products);
array_slice($products, ($nb ? $nb : 10));
}
}
else
{
$products = Product::getNewProducts(intval($cookie->id_lang), NULL - 0, (intval($nb ? $nb : 4)), false, NULL, NULL);
}
$smarty->assign(array(
....
'products' => $products,
....
);
return $this->display(__FILE__, 'homenewproducts.tpl');
}
How can I override the class Product so that the method getNewProducts take into account excluding products out of stock?
Or at least, how can I remove from $products the products with quantity =0 using PHP?
Your help is appreciated.
Well, the solution I am using now is:
In product.php, I changed the sql queries in getNewProducts method inside of NewProductsController so that it takes into account if product is available in stock
I added AND 'quantity'!=0 in line 2062 and $sql->where('p.'quantity' != 0'); in line 2086 . Prestashop 1.6.0.6.
Of course, better override the classe Product.php than modifying it.
I hope it can help.