public
calculateBasicProducts
(mixed &$fullItems, mixed &$fullItemsGroup, mixed &$total, mixed &$stock, mixed &$minqty, mixed &$minmultipleqty, mixed $items)
// ==============
// 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);
}
}
}