Back to PhocacartCartCalculation class

Method calculateBasicProducts

public
calculateBasicProducts
(mixed &$fullItems, mixed &$fullItemsGroup, mixed &$total, mixed &$stock, mixed &$minqty, mixed &$minmultipleqty, mixed $items)

Method calculateBasicProducts - Source code

// ==============
// BASIC PRODUCT
// ==============
public function calculateBasicProducts(&$fullItems, &$fullItemsGroup, &$total, &$stock, &$minqty, &$minmultipleqty, $items)
{
    $app = Factory::getApplication();
    $paramsC = PhocacartUtils::getComponentParameters();
    $tax_calculation = $paramsC->get('tax_calculation', 0);
    // Moved to product parameters
    //$min_ quantity_calculation	= $paramsC->get( 'min_ quantity_calculation', 0 );
    //$stock_ calculation	= $paramsC->get( 'stock_ calculation', 0 );
    $price = new PhocacartPrice();
    $total['netto'] = 0;
    $total['brutto'] = 0;
    $total['brutto_currency'] = 0;
    $total['tax'] = array();
    $total['weight'] = 0;
    $total['volume'] = 0;
    $total['dnetto'] = 0;
    $total['quantity'] = 0;
    $total['length'] = 0;
    $total['width'] = 0;
    $total['height'] = 0;
    $total['points_needed'] = 0;
    $total['points_received'] = 0;
    // Free shipping or payment
    $total['free_shipping'] = 0;
    $total['free_payment'] = 0;
    // Discount fixed amount
    $total['discountcartfixedamount'] = array();
    $total['couponcartfixedamount'] = array();
    $total['discountcarttxtsuffix'] = '';
    $total['couponcarttxtsuffix'] = '';
    $total['rewardproductusedtotal'] = '';
    $total['rewardproducttxtsuffix'] = '';
    $total['countallproducts'] = 0;
    $total['countphysicalproducts'] = 0;
    $total['countdigitalproducts'] = 0;
    $total['countpriceondemandproducts'] = 0;
    // OPTIONS (VARIANTS) QUANTITY
    // The same option can be in different items
    $optionsQuantity = array();
    // Rounding
    $total['rounding'] = 0;
    $total['rounding_currency'] = 0;
    $total['wdnetto'] = 0;
    //Subtotal after all discounts
    $total['rounding_currency'] = 0;
    foreach ($items as $k => $v) {
        $item = explode(':', $k);
        $itemId = $item[0];
        // Define
        $fullItems[$k]['id'] = (int) $itemId;
        $fullItems[$k]['idkey'] = (string) $k;
        $fullItems[$k]['netto'] = 0;
        $fullItems[$k]['brutto'] = 0;
        $fullItems[$k]['tax'] = 0;
        $fullItems[$k]['final'] = 0;
        // netto or brutto * quantity
        $fullItems[$k]['nettodiscount'] = 0;
        $fullItems[$k]['bruttodiscount'] = 0;
        $fullItems[$k]['taxdiscount'] = 0;
        $fullItems[$k]['finaldiscount'] = 0;
        $fullItems[$k]['taxid'] = 0;
        $fullItems[$k]['taxkey'] = '';
        // Tax Id: Country Tax Id: Region Tax Id
        $fullItems[$k]['taxtitle'] = '';
        $fullItems[$k]['weight'] = '';
        $fullItems[$k]['volume'] = '';
        $fullItems[$k]['quantity'] = $fQ = (int) $v['quantity'];
        // Quantity of product - one product
        $fullItems[$k]['catid'] = 0;
        $fullItems[$k]['alias'] = '';
        $fullItems[$k]['sku'] = '';
        $fullItems[$k]['image'] = '';
        $fullItems[$k]['title'] = '';
        $fullItems[$k]['stock'] = 0;
        // database value set in product settings
        $fullItems[$k]['stockvalid'] = 1;
        // variable to inform if stock validity is ok
        $fullItems[$k]['stockcalculation'] = 0;
        $fullItems[$k]['minqty'] = 0;
        // database value set in product settings
        $fullItems[$k]['minmultipleqty'] = 0;
        $fullItems[$k]['minqtyvalid'] = 1;
        // variable to inform if minimum order is ok
        $fullItems[$k]['minmultipleqtyvalid'] = 1;
        // DISCOUNTS (Product, Cart, Voucher) / Fixed amount / Percentage
        $fullItems[$k]['discountproduct'] = 0;
        $fullItems[$k]['discountproducttitle'] = '';
        $fullItems[$k]['discountcart'] = 0;
        $fullItems[$k]['discountcarttitle'] = '';
        $fullItems[$k]['discountcartfixedid'] = 0;
        $fullItems[$k]['discountcartid'] = 0;
        $fullItems[$k]['couponcart'] = 0;
        $fullItems[$k]['couponcarttitle'] = '';
        $fullItems[$k]['couponcartfixedid'] = 0;
        $fullItems[$k]['couponcartid'] = 0;
        $fullItems[$k]['rewardproduct'] = 0;
        $fullItems[$k]['rewardproducttitle'] = Text::_('COM_PHOCACART_REWARD_POINTS');
        $fullItems[$k]['rewardproductpoints'] = 0;
        $fullItems[$k]['rewardproducttxtsuffix'] = '';
        $fullItems[$k]['points_needed'] = 0;
        $fullItems[$k]['points_received'] = 0;
        $fullItems[$k]['type'] = 0;
        // GROUP QUANTITY
        // Get quantity of a group. Group is sum of all product variations
        // - explained in PhocacartDiscountProduct::getProductDiscount
        $fullItemsGroup[$itemId]['id'] = (int) $itemId;
        $fullItemsGroup[$itemId]['title'] = '';
        if (isset($fullItemsGroup[$itemId]['quantity'])) {
            $fullItemsGroup[$itemId]['quantity'] += (int) $v['quantity'];
        } else {
            $fullItemsGroup[$itemId]['quantity'] = (int) $v['quantity'];
        }
        $total['quantity'] += (int) $v['quantity'];
        // ATTRIBUTES
        $attribs = array();
        if (!empty($item[1])) {
            $attribs = unserialize(base64_decode($item[1]));
        }
        // ITEM D - product info from database
        $itemD = PhocacartProduct::getProduct((int) $itemId, (int) $v['catid'], $this->type);
        // Correct the tax rate - no tax calculation, no tax rate for each product
        if (!empty($itemD) && $tax_calculation == 0) {
            $itemD->taxrate = 0;
        }
        if (isset($itemD->id) && (int) $itemD->id > 0) {
            $fullItems[$k]['title'] = $itemD->title;
            $fullItems[$k]['catid'] = $itemD->catid;
            $fullItems[$k]['alias'] = $itemD->alias;
            $fullItems[$k]['sku'] = $itemD->sku;
            $fullItems[$k]['image'] = $itemD->image;
            $fullItems[$k]['type'] = $itemD->type;
            $fullItems[$k]['default_price'] = $itemD->price;
            $fullItems[$k]['price'] = $price->getPriceItem($itemD->price, $itemD->group_price, 0);
            $fullItems[$k]['taxid'] = $itemD->taxid;
            $fullItems[$k]['taxrate'] = $itemD->taxrate;
            $fullItems[$k]['taxtitle'] = Text::_($itemD->taxtitle);
            $fullItems[$k]['taxcountryid'] = $itemD->taxcountryid;
            $fullItems[$k]['taxregionid'] = $itemD->taxregionid;
            $taxKey = PhocacartTax::getTaxKey($itemD->taxid, $itemD->taxcountryid, $itemD->taxregionid);
            $fullItems[$k]['taxkey'] = $taxKey;
            $fullItems[$k]['taxcalctype'] = $itemD->taxcalculationtype;
            $fullItems[$k]['weight'] = $itemD->weight;
            $fullItems[$k]['volume'] = $itemD->volume;
            $fullItems[$k]['stock'] = $itemD->stock;
            $fullItems[$k]['stockadvanced'] = 0;
            $fullItems[$k]['stockcalculation'] = $itemD->stock_calculation;
            $fullItems[$k]['minqty'] = $itemD->min_quantity;
            $fullItems[$k]['minmultipleqty'] = $itemD->min_multiple_quantity;
            $fullItems[$k]['minqtycalculation'] = $itemD->min_quantity_calculation;
            $fullItems[$k]['default_points_received'] = $itemD->points_received;
            $pointsN = PhocacartReward::getPoints($itemD->points_needed, 'needed');
            $pointsR = PhocacartReward::getPoints($itemD->points_received, 'received', $itemD->group_points_received);
            $fullItems[$k]['points_needed'] = $pointsN;
            $fullItems[$k]['points_received'] = $pointsR;
            // Group
            $fullItemsGroup[$itemId]['minqty'] = $itemD->min_quantity;
            $fullItemsGroup[$itemId]['minmultipleqty'] = $itemD->min_multiple_quantity;
            $fullItemsGroup[$itemId]['title'] = $itemD->title;
            $fullItemsGroup[$itemId]['minqtyvalid'] = 1;
            $fullItemsGroup[$itemId]['minmultipleqtyvalid'] = 1;
            $priceI = $price->getPriceItems($itemD->price, $itemD->taxid, $itemD->taxrate, $itemD->taxcalculationtype, $itemD->taxtitle, 0, '', 0, 1, $itemD->group_price);
            // Get price from advanced stock managment TO DO group price
            if ($fullItems[$k]['stockcalculation'] == 3) {
                $aA = PhocacartAttribute::sanitizeAttributeArray($attribs);
                $price->getPriceItemsChangedByAttributes($priceI, $aA, $price, $itemD, 1);
            }
            $fullItems[$k]['netto'] = $priceI['netto'];
            $fullItems[$k]['brutto'] = $priceI['brutto'];
            $fullItems[$k]['tax'] = $priceI['tax'];
            // Advanced Stock Calculation
            if ($fullItems[$k]['stockcalculation'] == 2 || $fullItems[$k]['stockcalculation'] == 3) {
                $fullItems[$k]['stockadvanced'] = PhocacartAttribute::getCombinationsStockByKey($k);
            }
            // Total
            $total['netto'] += $fullItems[$k]['netto'] * $fQ;
            $total['brutto'] += $fullItems[$k]['brutto'] * $fQ;
            $total['weight'] += $fullItems[$k]['weight'] * $fQ;
            $total['volume'] += $fullItems[$k]['volume'] * $fQ;
            $total['length'] = $itemD->length > $total['length'] ? $itemD->length : $total['length'];
            $total['width'] = $itemD->width > $total['width'] ? $itemD->width : $total['width'];
            $total['height'] = $itemD->height > $total['height'] ? $itemD->height : $total['height'];
            $total['points_needed'] += $fullItems[$k]['points_needed'] * $fQ;
            $total['points_received'] += $fullItems[$k]['points_received'] * $fQ;
            // TAX
            if (!isset($total['tax'][$taxKey]['tax'])) {
                $total['tax'][$taxKey]['tax'] = 0;
                // Define
            }
            if (!isset($total['tax'][$taxKey]['netto'])) {
                $total['tax'][$taxKey]['netto'] = 0;
                // Define (set netto for each tax)
            }
            if (!isset($total['tax'][$taxKey]['brutto'])) {
                $total['tax'][$taxKey]['brutto'] = 0;
                // Define
            }
            $total['tax'][$taxKey]['tax'] += $fullItems[$k]['tax'] * $fQ;
            $total['tax'][$taxKey]['netto'] += $fullItems[$k]['netto'] * $fQ;
            $total['tax'][$taxKey]['brutto'] += $fullItems[$k]['brutto'] * $fQ;
            $taxSuffix = '';
            if ($itemD->taxcalculationtype == 1) {
                $taxSuffix = ' (' . $price->getTaxFormat($itemD->taxrate, $itemD->taxcalculationtype, 0) . ')';
            }
            $total['tax'][$taxKey]['title'] = Text::_($itemD->taxtitle) . $taxSuffix;
            $total['tax'][$taxKey]['title_lang'] = $itemD->taxtitle;
            $total['tax'][$taxKey]['title_lang_suffix2'] = '(' . $taxSuffix . ')';
            $total['tax'][$taxKey]['type'] = $itemD->taxcalculationtype;
            $total['tax'][$taxKey]['rate'] = $itemD->taxrate;
            // PRODUCTTYPE Digital product
            $total['countallproducts']++;
            if ($itemD->type == 0) {
                $total['countphysicalproducts']++;
            } else {
                if ($itemD->type == 1) {
                    $total['countdigitalproducts']++;
                } else {
                    if ($itemD->type == 2) {
                        // physical and digital
                        // This rule can be changed but for now e.g. we test if the product is digital to ensure that the shipping will be skipped
                        // if the product is both - digital and physical, we cannot skip shipping so we do not count it as digital
                        // Uncomment if you need to opposite rule
                        //$total['countphysicalproducts']++;
                        //$total['countdigitalproducts']++;
                    } else {
                        if ($itemD->type == 3) {
                            $total['countpriceondemandproducts']++;
                        } else {
                            if ($itemD->type == 4) {
                                // Gift Vouchers are even digital products
                                $total['countdigitalproducts']++;
                            }
                        }
                    }
                }
            }
            // ==========
            // ATTRIBUTES
            // ==========
            //
            // Stock handling - one variant can be set in e.g. two products, so we need to count attributes stock:
            if (!empty($attribs)) {
                foreach ($attribs as $k2 => $v2) {
                    // Make array from all attributes even they are not multiple - to go through the foreach
                    if (!is_array($v2)) {
                        $v2 = array(0 => $v2);
                    }
                    if (!empty($v2)) {
                        // Be aware the k3 is not the key of attribute
                        // this is the k2
                        foreach ($v2 as $k3 => $v3) {
                            if ((int) $k2 > 0 && (int) $k3 > 0) {
                                $attrib = PhocacartAttribute::getAttributeValue((int) $k3, (int) $k2);
                                // Price is set as fixed with help of advanced stock management
                                if ($fullItems[$k]['stockcalculation'] != 3) {
                                    /*	if (!$attrib->aid) {
                                    
                                                                                    $app->enqueueMessage(Text::_('COM_PHOCACART_ERROR_PRODUCT_ATTRIBUTE_NOT_EXISTS_PLEASE_RECHECK_PRODUCTS_IN_YOUR_CART'), 'error');
                                                                                    break;
                                                                                }*/
                                    if (isset($attrib->title) && isset($attrib->amount) && isset($attrib->operator)) {
                                        // If there is fixed VAT - don't change it in attributes - it is just fix - set taxtrate to 0
                                        if ($itemD->taxcalculationtype == 2) {
                                            $priceA = $price->getPriceItems($attrib->amount, $itemD->taxid, 0, $itemD->taxcalculationtype, $itemD->taxtitle);
                                        } else {
                                            $priceA = $price->getPriceItems($attrib->amount, $itemD->taxid, $itemD->taxrate, $itemD->taxcalculationtype, $itemD->taxtitle);
                                        }
                                        //$fQ 	= (int)$fullItems[$k]['quantity'];
                                        // Price
                                        if ($attrib->operator == '-') {
                                            $nettoBefore = $fullItems[$k]['netto'];
                                            $fullItems[$k]['netto'] -= $priceA['netto'];
                                            if ($fullItems[$k]['netto'] < 0) {
                                                $total['netto'] -= $nettoBefore * $fQ;
                                                $fullItems[$k]['netto'] = 0;
                                            } else {
                                                $total['netto'] -= $priceA['netto'] * $fQ;
                                            }
                                            $bruttoBefore = $fullItems[$k]['brutto'];
                                            $fullItems[$k]['brutto'] -= $priceA['brutto'];
                                            if ($fullItems[$k]['brutto'] < 0) {
                                                $total['brutto'] -= $bruttoBefore * $fQ;
                                                $fullItems[$k]['brutto'] = 0;
                                            } else {
                                                $total['brutto'] -= $priceA['brutto'] * $fQ;
                                            }
                                            $taxBefore = $fullItems[$k]['tax'];
                                            $fullItems[$k]['tax'] -= $priceA['tax'];
                                            if ($fullItems[$k]['tax'] < 0) {
                                                $total['tax'][$taxKey]['tax'] -= $taxBefore * $fQ;
                                                $total['tax'][$taxKey]['netto'] -= $nettoBefore * $fQ;
                                                $total['tax'][$taxKey]['brutto'] -= $bruttoBefore * $fQ;
                                                $fullItems[$k]['tax'] = 0;
                                            } else {
                                                $total['tax'][$taxKey]['tax'] -= $priceA['tax'] * $fQ;
                                                $total['tax'][$taxKey]['netto'] -= $priceA['netto'] * $fQ;
                                                $total['tax'][$taxKey]['brutto'] -= $priceA['brutto'] * $fQ;
                                            }
                                        } else {
                                            if ($attrib->operator == '+') {
                                                $fullItems[$k]['brutto'] += $priceA['brutto'];
                                                // * multiply in render checkout
                                                $fullItems[$k]['netto'] += $priceA['netto'];
                                                // * multiply in render checkout
                                                $fullItems[$k]['tax'] += $priceA['tax'];
                                                // * multiply in render checkout
                                                $total['netto'] += $priceA['netto'] * $fQ;
                                                $total['brutto'] += $priceA['brutto'] * $fQ;
                                                $total['tax'][$taxKey]['tax'] += $priceA['tax'] * $fQ;
                                                $total['tax'][$taxKey]['netto'] += $priceA['netto'] * $fQ;
                                                $total['tax'][$taxKey]['brutto'] += $priceA['brutto'] * $fQ;
                                            }
                                        }
                                    }
                                }
                                // Weight
                                if ($attrib->operator_weight == '-') {
                                    $fullItems[$k]['weight'] -= $attrib->weight;
                                    $fullItems[$k]['weight'] < 0 ? $fullItems[$k]['weight'] = 0 : ($total['weight'] -= $attrib->weight * $fullItems[$k]['quantity']);
                                } else {
                                    if ($attrib->operator_weight == '+') {
                                        $fullItems[$k]['weight'] += $attrib->weight;
                                        $total['weight'] += $attrib->weight * $fullItems[$k]['quantity'];
                                    }
                                }
                                // Volume
                                if ($attrib->operator_volume == '-') {
                                    $fullItems[$k]['volume'] -= $attrib->volume;
                                    $fullItems[$k]['volume'] < 0 ? $fullItems[$k]['volume'] = 0 : ($total['volume'] -= $attrib->volume * $fullItems[$k]['quantity']);
                                } else {
                                    if ($attrib->operator_volume == '+') {
                                        $fullItems[$k]['volume'] += $attrib->volume;
                                        $total['volume'] += $attrib->volume * $fullItems[$k]['quantity'];
                                    }
                                }
                                if (isset($optionsQuantity[$attrib->id])) {
                                    $optionsQuantity[$attrib->id] += (int) $fQ;
                                } else {
                                    $optionsQuantity[$attrib->id] = (int) $fQ;
                                }
                                // STOCK-1 ... we count each product variation separately
                                if ($fullItems[$k]['stockcalculation'] == 1 && (int) $optionsQuantity[$attrib->id] > (int) $attrib->stock) {
                                    $total['stockvalid'] = 0;
                                    $fullItems[$k]['stockvalid'] = 0;
                                    $stock['valid'] = 0;
                                }
                                // Attribute values
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['aid'] = $attrib->aid;
                                // Attribute Id
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['atitle'] = $attrib->atitle;
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['atype'] = $attrib->atype;
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['oid'] = $attrib->id;
                                // Option Id
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['otitle'] = $attrib->title;
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['oimage'] = $attrib->image;
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['ovalue'] = PhocacartAttribute::setAttributeValue($attrib->atype, $v3, false, true, $attrib->type);
                                $fullItems[$k]['attributes'][$attrib->aid][$k3]['otype'] = $attrib->type;
                                //$fullItems[$k]['attributes'][$attrib->aid][$k3]['odownloadfile']= $attrib->download_file;
                            }
                        }
                    }
                }
            }
            // ==============================
            // MINIUM ORDER AMOUNT
            // ==============================
            // THERE CAN BE THREE METHODS HOW TO COUNT MINIMUM ORDER AMOUNT
            // a) every product is unique (Product A - Option A, Product A - Option B are two different products)
            // b) there are product groups (Product A- Option A, Product A - Option B is still one product - product A)
            // c) advanced stock management - in this case it is the same like a)
            if ($fullItems[$k]['minqtycalculation'] == 1 || $fullItems[$k]['minqtycalculation'] == 2) {
                // a)
                // MINIMUM QUANTITY - FOR ITEM - PRODUCT VARIATION - each product variation
                if ((int) $fullItems[$k]['quantity'] < (int) $fullItems[$k]['minqty']) {
                    $minqty['valid'] = 0;
                    $fullItems[$k]['minqtyvalid'] = 0;
                }
                if ((int) $fullItems[$k]['minmultipleqty'] == 0) {
                    // Do not modulo by zero
                    // Set it back because we are in foreach
                    $minmultipleqty['valid'] = 1;
                    $fullItems[$k]['minmultipleqtyvalid'] = 1;
                } else {
                    if ((int) $fullItems[$k]['quantity'] % (int) $fullItems[$k]['minmultipleqty'] != 0) {
                        $minmultipleqty['valid'] = 0;
                        $fullItems[$k]['minmultipleqtyvalid'] = 0;
                    }
                }
            } else {
                // b)
                // MINIMUM QUANTITY - FOR GROUP (Group is the same product but with different options values) - MAIN PRODUCT
                if (empty($fullItemsGroup[$itemId]['minqty'])) {
                    $minqty['valid'] = 1;
                    $fullItemsGroup[$itemId]['minqtyvalid'] = 1;
                } else {
                    if ((int) $fullItemsGroup[$itemId]['quantity'] < (int) $fullItemsGroup[$itemId]['minqty']) {
                        $minqty['valid'] = 0;
                        $fullItemsGroup[$itemId]['minqtyvalid'] = 0;
                    } else {
                        // Set it back because we are in foreach
                        $minqty['valid'] = 1;
                        $fullItemsGroup[$itemId]['minqtyvalid'] = 1;
                    }
                }
                // MINIMUM MULTIPLE QUANTITY
                if (empty($fullItemsGroup[$itemId]['minmultipleqty'])) {
                    $minmultipleqty['valid'] = 1;
                    $fullItemsGroup[$itemId]['minmultipleqtyvalid'] = 1;
                } else {
                    if ($fullItemsGroup[$itemId]['minmultipleqty'] == 0) {
                        // Do not modulo by zero
                        // Set it back because we are in foreach
                        $minmultipleqty['valid'] = 1;
                        $fullItemsGroup[$itemId]['minmultipleqtyvalid'] = 1;
                    } else {
                        if ((int) $fullItemsGroup[$itemId]['quantity'] % (int) $fullItemsGroup[$itemId]['minmultipleqty'] != 0) {
                            $minmultipleqty['valid'] = 0;
                            $fullItemsGroup[$itemId]['minmultipleqtyvalid'] = 0;
                        } else {
                            // Set it back because we are in foreach
                            $minmultipleqty['valid'] = 1;
                            $fullItemsGroup[$itemId]['minmultipleqtyvalid'] = 1;
                        }
                    }
                }
            }
            // ==============================
            // STOCK VALID
            // ==============================
            // The difference between STOCK-1, STOCK-0
            // b) STOCK-0 - There is only one main product even it is divided into more product variations
            //         - so we count only main product - group
            // a) STOCK-1 - Each product variation is one product but this means that product without any variation
            //         - is in one one of the product variation:
            // Product 1 Option A - one product
            // Product 1 Option B - one product
            // Product 1 (no options) - one product - as sum there are 3 products
            // c) STOCK-2 - advanced stock management
            // Product 1 Option A - one product
            // Product 1 Option B - one product
            // Product 1 (no options) - one product - as sum there are 3 products
            // Product 1 Option A + Option B - one product
            // STOCK-2 ... we count main product as own product variation - only in case it does not have any attributes
            //         ... but combination of attributes can create different products
            if (($fullItems[$k]['stockcalculation'] == 2 || $fullItems[$k]['stockcalculation'] == 3) && (int) $fullItems[$k]['quantity'] > (int) $fullItems[$k]['stockadvanced']) {
                $stock['valid'] = 0;
                // Global - some of the product is out of stock
                $fullItems[$k]['stockvalid'] = 0;
                // Current product is out of stock
            }
            // STOCK-1 ... we count main product as own product variation - only in case it does not have any attributes
            //             variations of product are checked in ohter place (cca line 271)
            // THIS IS DIVEDED RULE - ONE HERE, SECOND ABOVE IN ATTRIBUTES FOREACH
            if ($fullItems[$k]['stockcalculation'] == 1 && empty($fullItems[$k]['attributes']) && (int) $fullItems[$k]['quantity'] > (int) $fullItems[$k]['stock']) {
                $stock['valid'] = 0;
                // Global - some of the product is out of stock
                $fullItems[$k]['stockvalid'] = 0;
                // Current product is out of stock
            }
            // STOCK-0 ... we count main product as group: Product 1 Option A ... 5 + Product 1 Option B ... 5 = 10
            if ($fullItems[$k]['stockcalculation'] == 0 && (int) $fullItemsGroup[$itemId]['quantity'] > (int) $fullItems[$k]['stock']) {
                $stock['valid'] = 0;
                // Global - some of the product is out of stock
                $fullItems[$k]['stockvalid'] = 0;
                // Current product is out of stock
            }
            $fullItems[$k]['final'] = $fullItems[$k]['netto'] && !$this->posbruttocalculation ? $fullItems[$k]['netto'] * $fQ : $fullItems[$k]['brutto'] * $fQ;
        }
        if ($this->correctsubtotal) {
            $this->correctSubTotal($fullItems[$k], $total);
        }
    }
}