AnonSec Shell
Server IP : 54.36.91.62  /  Your IP : 216.73.217.111
Web Server : Apache
System : Linux webm013.cluster127.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User : coopiak ( 151928)
PHP Version : 8.3.23
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/coopiak/amisdesseniors-fr/montpellier/components/com_djcatalog2/helpers/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/coopiak/amisdesseniors-fr/montpellier/components/com_djcatalog2/helpers/cart.php
<?php

/**
 * @package DJ-Catalog2
 * @copyright Copyright (C) DJ-Extensions.com, All rights reserved.
 * @license http://www.gnu.org/licenses GNU/GPL
 * @author url: http://dj-extensions.com
 * @author email contact@dj-extensions.com
 */

use Joomla\Registry\Registry;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Filesystem\Path;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Plugin\PluginHelper;
defined('_JEXEC') or die('Restricted access');

require_once(JPATH_ROOT . '/components/com_djcatalog2/helpers/price.php');
require_once(JPATH_ROOT . '/components/com_djcatalog2/helpers/quantity.php');
require_once(JPATH_ROOT . '/components/com_djcatalog2/helpers/coupon.php');

class Djcatalog2HelperCart
{
	static $baskets = array();

	protected $_errors = array();

	protected $quantitiesItems = array();

	public $items = array();
	public $quantities = array();
	public $prices = array();

	public $productTypes = array('tangible' => 0, 'virtual' => 0, 'hybrid' => 0, 'subscription' => 0, 'bundle' => 0);

	public $delivery = false;
	public $payment = false;

	public $coupon = false;
	public $coupon_value = 0.0;

	public $product_total = array();
	public $total = array();
	public $sub_totals = array();
	public $product_old_total = array();
	public $attributes = null;
	public $attribute_values = array();
	public $features = null;
	// product features & customisable product features
	public $feature_values = array();
	// Customisable product dimensions
	public $dimension_values = array();
	public $customisations = array();
	public $calculators = array();
	public $discount_value = 0.0;
	public $price_components = array();
	public $shipping_days = 1;
	public $related_accessories = array();
	public $forceRecurring = false;
	public $recurringDate = null;
	/**
	 * Weight is being stored in grams
	 * @var float
	 */
	public $total_weight = 0.0;

	public $gauges_map = [];

	/**
	 * @var stdClass
	 */
	public $currency;

	/**
	 * Dimensions are being stored in meters
	 * @var array
	 */
	public $total_dimensions = array('length' => 0.0, 'width' => 0.0, 'height' => 0.0);

	protected static $tmpFiles = array();

	/**
	 *
	 * Retrieves or creates DJCatalog2HelperCart object
	 * @param bool $from_storage
	 * @param array $cart_items
	 * @return DJCatalog2HelperCart
	 */

	public static function getInstance($from_storage = true, $cart_items = array())
	{
		$app = Factory::getApplication();
		$params = Djcatalog2Helper::getParams();

		if ($from_storage) {
			$stored_items = $app->getUserState('com_djcatalog2.cart.items', array());
			if (empty($cart_items) && !empty($stored_items)) {
				$cart_items = $stored_items;
			} else if ($params->get('cart_cookie_enable', 1)) {
				$cookie_val = $app->input->cookie->getString('djc2cart');
				if ($cookie_val != '') {
					try {
						$cart_items = json_decode((string)$cookie_val, true);
					} catch (Exception $e) {
						$cart_items = array();
					}
				}
			}
		}

		$db = Factory::getDBO();
		$user = Factory::getUser();

		if ($from_storage && $user->id) {
			$query = $db->getQuery(true)->select('*')->from('#__djc2_usercarts')->where('user_id = ' . $user->id);
			$db->setQuery($query);
			$usercart = $db->loadObject();

			if (empty($cart_items) && $usercart) {
				$cart_items = json_decode((string)$usercart->items, true);
			} else {
				if ($usercart) {
					$query = $db->getQuery(true)->delete('#__djc2_usercarts')->where('user_id=' . $user->id);
					$db->setQuery($query);
					$db->execute();
				}

				if ($cart_items) {
					$query = $db->getQuery(true);
					$query->insert('#__djc2_usercarts');
					$query->columns(array('user_id', 'items'));
					$query->values($user->id . ',' . $db->quote(json_encode($cart_items)));
					$db->setQuery($query);
					$db->execute();
				}
			}
		}

		$delivery_id = $app->getUserState('com_djcatalog2.cart.delivery', false);
		$payment_id = $app->getUserState('com_djcatalog2.cart.payment', false);
		$shipping_days = $app->getUserState('com_djcatalog2.cart.shipping_days', false);

		$custom_items = (array)$app->getUserState('com_djcatalog2.cart.custom_items', array());

		//$cart_items = array_merge($cart_items, array_keys($custom_items));
		if (is_array($custom_items) && count($custom_items) > 0) {
			foreach ($custom_items as $csid => $citem) {
				if (empty($citem->_quantity)) continue;
				$cart_items[$csid] = $citem->_quantity;
			}
		}

		$hash = md5(serialize($cart_items) . ':' . $delivery_id . ':' . $payment_id);

		if (isset(self::$baskets[$hash])) {
			return self::$baskets[$hash];
		}

		$basket = new Djcatalog2HelperCart();

		if (!empty($cart_items)) {

			$basket->items = array();
			$basket->quantities = array();

			BaseDatabaseModel::addIncludePath(Path::clean(JPATH_ROOT . '/components/com_djcatalog2/models'), 'DJCatalog2Model');
			$model = BaseDatabaseModel::getInstance('Items', 'DJCatalog2Model', array('ignore_request' => true));
			$itemModel = BaseDatabaseModel::getInstance('Item', 'DJCatalog2Model', array('ignore_request' => true));

			$state = $model->getState();

			$model->setState('list.start', 0);
			$model->setState('list.limit', 0);

			$juser = Factory::getUser();
			$user = Djcatalog2Helper::getUserProfile($app->getUserState('com_djcatalog2.checkout.user_id', null));
			if (isset($user->customer_group_id)) {
				$model->setState('filter.customergroup', $user->customer_group_id);
			}

			$model->setState('filter.catalogue', false);
			$model->setState('list.ordering', 'i.name');
			$model->setState('list.direction', 'asc');
			$model->setState('filter.parent', '*');
			$model->setState('filter.state', '3');
			$model->setState('list.fields_visibility', '*');

			$basketIds = array();
			$ids = array();
			foreach ($cart_items as $sid => $qty) {
				$sidParts = self::parseSid($sid);
				if (!$sidParts || empty($sidParts['id'])) {
					continue;
				}
				$sidParts['qty'] = $qty;
				$basketIds[$sid] = $sidParts;
				$ids[] = $sidParts['id'];
			}
			$model->setState('filter.item_ids', $ids);
			$list = $model->getItems();
			if (count($list) > 0) {
				foreach ($basketIds as $sid => $sidData) {
					if (isset($list[$sidData['id']])) {
						$item = clone $list[$sidData['id']];
						$item->_sid = $sid;
						$item->_combination_id = $sidData['combination_id'];

						$type = (!empty($item->product_type)) ? $item->product_type : 'tangible';

						if ($item->_combination_id > 0) {
							if ($combination = $itemModel->getCombination($item->_combination_id)) {
								$item->_combination = $combination;
								$item->stock = $combination->stock;

								if (($type == 'tangible' || $type == 'hybrid') && $item->onstock > 0) {
									if ($item->onstock == 3) {
										$item->onstock = 2;
									} else {
										$item->onstock = $combination->stock > 0 ? 1 : 0;
									}
								}

								if ($combination->sku != '') {
									$item->sku = $combination->sku;
								} else if ($item->sku != '') {
									$item->sku .= '-CMB-' . $combination->id;
								} else {
									$item->sku = 'ID-' . $item->id . '-CMB-' . $combination->id;
								}

								if ($combination->price > 0) {
									$item->final_price = $item->price = $combination->price;
								}
								if (isset($combination->final_price) && $combination->final_price > 0.0) {
									$item->final_price = $item->final_price = $combination->final_price;
								}

								if ($combination->length > 0) {
									$item->length = $combination->length;
								}
								if ($combination->width > 0) {
									$item->width = $combination->width;
								}
								if ($combination->height > 0) {
									$item->height = $combination->height;
								}
								if ($combination->weight > 0) {
									$item->weight = $combination->weight;
								}
							} else {
								continue;
							}
						}

						if ($item->price_tier_modifier != '0') {
							$item->_price_tiers = $itemModel->getTierPrices($item->id);
						} else {
							$item->_price_tiers = array();
						}

						$unit = DJCatalog2HelperQuantity::getUnit($item->unit_id);
						$item->_unit = $unit->unit;

						if ($juser->guest) {
							if ($params->get('price_restrict') == '1' || $item->price_restrict) {
								$item->final_price = $item->price = $item->special_price = 0.0;
							}
						}

						$basket->items[$sid] = $item;

						$qty = DJCatalog2HelperQuantity::validateQuantity($sidData['qty'], $unit);
						$basket->quantities[$sid] = $qty;

						//Save quantity value
						$basket->quantitiesItems[$sidData['id']] = $qty;

						$basket->productTypes[$type]++;

						if ($type == 'bundle') {
							$bundle_items = $basket->getBundleItems($item);
							foreach ($bundle_items as $bundle_item) {
								$bundle_type = $bundle_item['item']->product_type;
								$basket->productTypes[$bundle_type]++;
							}
						}
					}
				}
			}

			if ($from_storage) {
				$stored_attributes = $app->getUserState('com_djcatalog2.cart.attributes', array());
				$stored_features = $app->getUserState('com_djcatalog2.cart.features', array());
				$dimension_values = $app->getUserState('com_djcatalog2.cart.dimension_values', array());
				$stored_customisations = $app->getUserState('com_djcatalog2.cart.customisations', array());
				$stored_calculators = $app->getUserState('com_djcatalog2.cart.calculators', array());

				$basket->attribute_values = $stored_attributes;
				$basket->feature_values = $stored_features;
				$basket->dimension_values = $dimension_values;
				$basket->customisations = $stored_customisations;
				$basket->calculators = $stored_calculators;
			}

			$stored_prices = $app->getUserState('com_djcatalog2.cart.prices', array());
			if (!empty($stored_prices)) {
				$basket->prices = $stored_prices;
			}
		}

		if (!empty($custom_items)) {
			$temp = [];
			foreach ($custom_items as $ck => $cv) {
				if (isset($basket->quantities[$ck])) {
					$cv->_quantity = $basket->quantities[$ck];
				}
				if (isset($cv->_prices)) {
					unset($cv->_prices);
				}
				$temp[$ck] = $cv;
			}
			$basket->setCustomItems($temp);
		}

		$coupon_id = $app->getUserState('com_djcatalog2.cart.coupon', false);

		if ($shipping_days !== false) {
			$basket->setShippingDays($shipping_days);
		}
		if ($delivery_id !== false) {
			$basket->setDelivery($delivery_id);
		}
		if ($payment_id !== false) {
			$basket->setPayment($payment_id);
		}
		if ($coupon_id !== false) {
			$coupon = Djcatalog2HelperCoupon::getCouponById($coupon_id);
			$basket->setCoupon($coupon, false);
		}

		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2CartGetInstance', array(&$basket));

		$basket->recalculate();

		self::$baskets[$hash] = $basket;

		return self::$baskets[$hash];
	}

	public function getTotal()
	{
		return $this->total;
	}

	public function getProductTotal()
	{
		return $this->product_total;
	}

	public function getProductOldTotal()
	{
		return $this->product_old_total;
	}

	public function getSubTotals()
	{
		return $this->total;
	}

	public function getItems()
	{
		return $this->items;
	}

	public function getTotalWeight()
	{
		return $this->total_weight;
	}

	public function getTotalDimensions()
	{
		return $this->total_dimensions;
	}

	public function getGaugesMap()
	{
		return $this->gauges_map;
	}

	public function getCurrencyCode()
	{
		if ($this->currency) {
			return $this->currency->currency;
		}

		$params = ComponentHelper::getParams('com_djcatalog2');
		return strtoupper($params->get('cart_currency', ''));
	}

	public function getCurrencyID()
	{
		if ($this->currency) {
			return $this->currency->id;
		}
		return 0;
	}

	public function removeItem($sid, $lazy = false)
	{

		foreach ($this->items as $k => $v) {
			if ($v->_sid == $sid) {
				unset($this->items[$k]);
			}
		}

		if (isset($this->quantities[$sid])) {
			unset($this->quantities[$sid]);
		}

		if (isset($this->prices[$sid])) {
			unset($this->prices[$sid]);
		}

		if (isset($this->attribute_values[$sid])) {
			unset($this->attribute_values[$sid]);
		}

		if (isset($this->feature_values[$sid])) {
			unset($this->feature_values[$sid]);
		}

		if (isset($this->customisations[$sid])) {
			unset($this->customisations[$sid]);
		}

		$app = Factory::getApplication();
		$custom_items = (array)$app->getUserState('com_djcatalog2.cart.custom_items', array());
		if (isset($custom_items[$sid])) {
			unset($custom_items[$sid]);
			$app->setUserState('com_djcatalog2.cart.custom_items', $custom_items);
		}

		if (!$lazy) {
			$this->recalculate();
		}

		return true;
	}

	public function addItem($item, $combination_id = 0, $features = [], $customisations = [], $dimensions = [], $configurable = [], $quantity = 1, $lazy = false, $calculator = [])
	{
		$params = Djcatalog2Helper::getParams();

		if (is_scalar($item) && (int)$item > 0) {

			BaseDatabaseModel::addIncludePath(Path::clean(JPATH_ROOT . '/components/com_djcatalog2/models'), 'DJCatalog2Model');

			$itemModel = BaseDatabaseModel::getInstance('Item', 'DJCatalog2Model', array('ignore_request' => true));

			$item = $itemModel->getItem($item);

			if (!empty($item)) {
				$type = (!empty($item->product_type)) ? $item->product_type : 'tangible';

				if ($combination_id > 0) {
					if ($combination = $itemModel->getCombination($combination_id)) {
						$item->_combination = $combination;
						$item->stock = $combination->stock;

						if (($type == 'tangible' || $type == 'hybrid') && $item->onstock > 0) {
							if ($item->onstock == 3) {
								$item->onstock = 2;
							} else {
								$item->onstock = $combination->stock > 0 ? 1 : 0;
							}
						}

						if ($combination->sku != '') {
							$item->sku = $combination->sku;
						} else if ($item->sku != '') {
							$item->sku .= '-CMB-' . $combination->id;
						} else {
							$item->sku = 'ID-' . $item->id . '-CMB-' . $combination->id;
						}

						if ($combination->price > 0) {
							$item->final_price = $item->price = $combination->price;
						}
						if (isset($combination->final_price) && $combination->final_price > 0.0) {
							$item->final_price = $item->final_price = $combination->final_price;
						}

						if ($combination->length > 0) {
							$item->length = $combination->length;
						}
						if ($combination->width > 0) {
							$item->width = $combination->width;
						}
						if ($combination->height > 0) {
							$item->height = $combination->height;
						}
						if ($combination->weight > 0) {
							$item->weight = $combination->weight;
						}
					}
				}

				if ($item->price_tier_modifier != '0') {
					$item->_price_tiers = $itemModel->getTierPrices($item->id);
				} else {
					$item->_price_tiers = array();
				}

				$unit = DJCatalog2HelperQuantity::getUnit($item->unit_id);
				$item->_unit = $unit->unit;

				if (Factory::getUser()->guest) {
					if ($params->get('price_restrict') == '1' || $item->price_restrict) {
						$item->final_price = $item->price = $item->special_price = 0.0;
					}
				}


			}
		}

		if (!is_object($item) || $item->available != 1) {
			$this->recalculate();
			return false;
		}

		// if a product has combinations and a combination has not been specified,
		// then product cannot be added to cart
		$db = Factory::getDbo();
		$query = $db->getQuery(true);
		$query->select('id')->from('#__djc2_items_combinations')->where('item_id=' . (int)$item->id);
		$db->setQuery($query);
		$possibleCombinations = $db->loadColumn();

		if (count($possibleCombinations) && !$combination_id) {
			$this->recalculate();
			return false;
		} else if ($combination_id && !in_array($combination_id, $possibleCombinations)) {
			$this->recalculate();
			return false;
		}

		//$item_id = $item->id;


		$sid = static::getSid($item->id, $combination_id, $features, $customisations, $dimensions, $configurable, null, $calculator);
		foreach ($this->items as $k => $v) {
			if (isset($v->_sid) && $v->_sid == $sid) {
				unset($this->items[$k]);
			}
		}

		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2CartAddItem', array(&$item, $sid));

		$this->items[$sid] = clone $item;
		$this->items[$sid]->_sid = $sid;
		$this->items[$sid]->_combination_id = $combination_id;



		$this->calculators[$sid] = $calculator;

		if (isset($this->quantities[$sid])) {
			$quantity += (($unit->is_int) ? (int)$this->quantities[$sid] : floatval($this->quantities[$sid]) + 0);
		}

		$quantity = DJCatalog2HelperQuantity::validateQuantity($quantity, $unit);

		if ($this->validateStock($item, $quantity) == false) {
			$this->setError(JText::_('COM_DJCATALOG2_ADD_CART_ERROR_QUANTITY'));
			return false;
		}

		$this->quantities[$sid] = $quantity;


		if (!$lazy) {
			$this->recalculate();
		}

		return true;
	}

	public function getItem($item_id, $combination_id = 0, $features = [], $customisations = [], $dimensions = [], $configurable = [])
	{
		$sid = static::getSid($item_id, $combination_id, $features, $customisations, $dimensions, $configurable);
		return $this->getItemBySid($sid);
	}

	public function getItemBySid($sid)
	{
		if (!$sid || !isset($this->items[$sid])) {
			return false;
		}

		return $this->items[$sid];
	}

	public static function getSid($item_id, $combination_id = 0, $features = array(), $customisations = array(), $dimensions = array(), $configurable = array(), $suffix = null, $calculator = [])
	{


		$parts = array($item_id, $combination_id);

		$hash = $item_id . '|' . $combination_id;

		if (!empty($features) && is_array($features)) {
			$hash .= '|' . serialize($features);
		} else {
			$hash .= '|-';
		}

		if (!empty($customisations) && is_array($customisations)) {
			$hash .= '|' . serialize($customisations);
		} else {
			$hash .= '|-';
		}

		if (!empty($dimensions) && is_array($dimensions)) {
			$hash .= '|' . serialize($dimensions);
		} else {
			$hash .= '|-';
		}

		if (!empty($configurable) && is_array($configurable)) {
			$hash .= '|' . serialize($configurable);
		} else {
			$hash .= '|-';
		}

		if (!empty($calculator) && is_array($calculator)) {
			$hash .= '|' . serialize($calculator);
		} else {
			$hash .= '|-';
		}


		if (!is_null($suffix)) {
			$hash .= $suffix;
		}


		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2CartGetSID', array(&$parts));

		$parts[] = md5($hash);

		return implode('.', $parts);
		//return $item_id.'.'.$combination_id;
	}

	public static function parseSid($sid)
	{
		$parts = explode('.', $sid, 3);
		if (count($parts) < 2) {
			return false;
		}

		$hash = '';
		if (count($parts) > 2) {
			if (isset($parts[2])) {
				$hash = $parts[2];
			}
		}

		$retVal = array(
			'id' => $parts[0],
			'combination_id' => $parts[1],
			'hash' => $hash,
		);

		return $retVal;
	}

	public function getCombinationAttributes($item, $format = 'array')
	{

		if (empty($item->_combination) || empty($item->_combination->fields)) {
			return $format == 'array' ? array() : '';
		}

		$data = Joomla\Utilities\ArrayHelper::fromObject($item->_combination);
		$fields = array_values($data['fields']);

		if ($format == 'string' || $format == 'string_values') {
			$pairs = array();
			foreach ($fields as $field) {
				$pairs[] = $format == 'string_values' ? $field['field_value'] : $field['field_name'] . ': ' . $field['field_value'];
			}
			return implode(', ', $pairs);
		}

		return ($format == 'array') ? $fields : json_encode($fields);
	}

	public function getItemAttributes($item, $translate = false, $format = 'array')
	{


		if (!$item || !isset($this->items[$item->_sid])) {
			return false;
		}

		if (!isset($this->attribute_values[$item->_sid]) && !isset($this->calculators[$item->_sid])) {
			return $format == 'array' ? array() : '';
		}

		$attributes = $this->attribute_values[$item->_sid];
		if ($translate) {
			$fields = $this->getAttributes();
			foreach ($fields as $key => $field) {
				$value = '';

				if (isset($attributes[$field->id])) {
					$value = $attributes[$field->id];
				}

				if (!empty($value)) {
					switch ($field->type) {
						case 'text':
						case 'textarea':
						case 'calendar':
						{
							$attributes[$field->id] = $value;
							break;
						}
						case 'select':
						case 'radio':
						case 'checkbox':
						{
							$selected = (is_array($value)) ? $value : array($value);
							$values = array();
							foreach ($field->optionlist as $option) {
								if (in_array($option->id, $selected)) {
									$values[] = $option->value;
								}
							}

							$attributes[$field->id] = implode(', ', $values);
							break;
						}
					}
				}
			}
			$calculator = CalculatorHelper::getInstance($item->calculator_id);
			$calculatorData = $this->calculators[$item->_sid];


			foreach ($calculator->fields as $calculatorField) {
				if(isset($calculatorData[$calculatorField->name])) {
					switch ($calculatorField->type) {
						case 'checkbox':

							foreach ($calculatorField->options as $option) {
								if($option->price == $calculatorData[$calculatorField->name]) {
									$attributes[$calculatorField->name] = $option->text;
								}
							}
							break;
						default:
							$attributes[$calculatorField->name] = $calculatorData[$calculatorField->name];
							break;
					}
				}
			}




			if ($format == 'json') {

				$output = array();
				foreach ($attributes as $key => $attribute) {
					if (isset($fields[$key])) {

					}else {
						$output[$key] = $attribute;
					}
				}
				$attributes = json_encode($output);

			} else if ($format == 'list') {
				$output = array();
				foreach ($attributes as $key => $attribute) {
					if (isset($fields[$key])) {
						$output[] = array('field_name' => $fields[$key]->name, 'field_value' => $attribute);
					}
				}
				$attributes = $output;
			}
		}




		return $attributes;
	}

	public function addCustomisations($customisations, $item)
	{
		if (count($customisations) < 1 /*|| empty($item)*/) {
			return false;
		}

		$app = Factory::getApplication();

		BaseDatabaseModel::addIncludePath(JPATH_BASE . '/components/com_djcatalog2/models', 'DJCatalog2Model');
		$itemModel = BaseDatabaseModel::getInstance('Item', 'Djcatalog2Model', array('ignore_request' => true));

		$individualCustoms = (!empty($item)) ? $itemModel->getCustomisations($item->id) : array();
		$commonCustoms = $itemModel->getCustomisations(0);

		$availCustoms = array_merge($individualCustoms, $commonCustoms);

		if (count($availCustoms) < 1) {
			return false;
		}

		$cartCustoms = array();
		$itemCustoms = array();

		$formData = array(
			'customisation' => array()
		);

		foreach ($availCustoms as $availCustom) {
			foreach ($customisations as $custom) {
				if ($custom['id'] == $availCustom->_cid) {

					$formData['customisation'][] = $availCustom->_cid;

					$customisation = array(
						'id' => $availCustom->customisation_id,
						'name' => $availCustom->name,
						'type' => $availCustom->type,
						'price' => $availCustom->price,
						'tax_rule_id' => $availCustom->tax_rule_id,
						'price_modifier' => $availCustom->price_modifier,
						'required' => $availCustom->required,
						'min_quantity' => $availCustom->min_quantity,
						'max_quantity' => $availCustom->max_quantity,
						'item_id' => (!empty($item) && $availCustom->type != 'c') ? $item->id : 0
					);

					$customisation = Joomla\Utilities\ArrayHelper::toObject($customisation);
					$customisation->data = array();

					foreach ($availCustom->input_params as $ik => $inputParam) {
						$input = Joomla\Utilities\ArrayHelper::toObject($inputParam);

						if (isset($custom['data'][$ik])) {
							$value = $custom['data'][$ik];
							if ($inputParam['type'] == 'checkbox' || $inputParam['type'] == 'radio') {
								$optionParamVal = array();
								foreach ($inputParam['options'] as $optionParamKey => $optionParam) {
									$selected = false;
									if (is_array($value)) {
										if (in_array($optionParamKey, $value)) {
											$optionParamVal[] = $optionParam['option_label'];
											$selected = true;
										}
									} else {
										if ($value == $optionParamKey) {
											$optionParamVal[] = $optionParam['option_label'];
											$selected = true;
										}
									}
									if ($selected && floatval(trim((string)$optionParam['option_price'])) > 0) {
										$customisation->price += floatval(trim((string)$optionParam['option_price']));
									}
								}
								$value = implode(', ', $optionParamVal);
							}

							$customisation->data[$ik] = array(
								'id' => $ik,
								'name' => $input->label,
								'type' => $input->type,
								'value' => $value
							);

							$formData['customValues-' . $availCustom->_cid . '[' . $ik . ']'] = $custom['data'][$ik];
						}
					}

					if ($availCustom->type == 'c') {
						$cartCustoms[] = $customisation;
					} else {
						$itemCustoms[] = $customisation;
					}
				}
			}
		}

		$this->customisations[$item->_sid] = (count($itemCustoms) > 0) ? $itemCustoms : null;
		$this->customisations[0] = (count($cartCustoms) > 0) ? $cartCustoms : null;

		$app->setUserState('com_djcatalog2.recent_customisation', $formData);

		$this->saveToStorage();

		return true;
	}

	public function getCustomisations($sid)
	{
		if (isset($this->customisations[$sid])) {
			return $this->customisations[$sid];
		}
		return array();
	}

	public function getCalculatorData($sid)
	{
		if (isset($this->calculators[$sid])) {
			return $this->calculators[$sid];
		}
		return array();
	}

	public function getCustomisationData($customisation, $format = 'json')
	{
		if (empty($customisation) || empty($customisation->data)) {
			return '';
		}


		$data = array();
		foreach ($customisation->data as $k => $custom) {

			$cData = new stdClass();
			$cData->name = $custom['name'];
			$cData->type = $custom['type'];
			$cData->value = trim((string)$custom['value']);

			if ($cData->value == '') {
				$data[] = $cData;
				continue;
			}

			if ($cData->type == 'file') {
				$fData = json_decode((string)$cData->value);
				if (empty($fData) || !is_array($fData)) {
					continue;
				}

				foreach ($fData as $file) {
					if (!isset(static::$tmpFiles[$file->id])) {
						$upFile = self::prepareCustomisationFile($file);

						static::$tmpFiles[$file->id] = $upFile;
					}
					$cData->value = static::$tmpFiles[$file->id];
				}
			}

			$data[] = $cData;
		}

		return ($format == 'json') ? json_encode($data) : '';
	}

	protected static function prepareCustomisationFile($file)
	{
		$app = Factory::getApplication();
		$uploaded = $app->getUserState('com_djcatalog2.customisation_files', array());

		if (is_array($uploaded) && array_key_exists($file->id, $uploaded)) {
			if (JFile::exists(Path::clean($uploaded[$file->id]->fullpath))) {
				return $uploaded[$file->id];
			}
		}

		$source = Path::clean(JPATH_ROOT . '/media/djcatalog2/tmp/' . $file->fullname);
		//$destination = Path::clean( DJCATATTFOLDER.'/customisation' );
		$destination = Path::clean(JPATH_ROOT . '/media/djcatalog2/files/customisation');

		if (!JFolder::exists($destination)) {
			JFolder::create($destination, 0755);
		}

		$ext = JFile::getExt($file->fullname);
		$newName = $file->caption . '.' . $file->id . '.' . $ext;
		$newName = \Joomla\String\StringHelper::strtolower(DJCatalog2FileHelper::createFileName($newName, $destination));
		$newPath = $destination . '/' . $newName;

		if (JFile::copy($source, $newPath)) {
			$file->file_id = md5($file->id . ':' . $file->caption . ':' . $file->fullname);
			$file->fullname = $newName;
			$file->url = 'media/djcatalog2/files/customisation/' . $newName;
			$file->size = filesize($newPath);
			$file->path = 'media/djcatalog2/files/customisation';
			$file->fullpath = $file->path . '/' . $file->fullname;

			$uploaded[$file->id] = $file;
			$app->setUserState('com_djcatalog2.customisation_files', $uploaded);

			return $file;
		}

		if (isset($uploaded[$file->id])) {
			unset($uploaded[$file->id]);
		}

		$app->setUserState('com_djcatalog2.customisation_files', $uploaded);

		return false;
	}

	public function updateQuantity($sid, $quantity, $attributes = null, $append = false)
	{
		if ($this->parseSid($sid) == false) {
			$sid = static::getSid($sid);
		}

		$sidParts = static::parseSid($sid);
		$hadErrors = false;

		if (!isset($this->quantities[$sid])) {
			//if (!$this->addItem($sid, 0, $quantity)) {
			if (!$this->addItem($sidParts[0], 0, [], [], $quantity)) {
				$hadErrors = true;
			}
		} else {
			$item = $this->getItemBySid($sid);
			$newQty = ($append) ? $this->quantities[$sid] + $quantity : $quantity;

			if ($this->validateStock($item, $newQty) == false) {
				if ($this->validateStock($item, $quantity) == false) {
					$this->setError(JText::sprintf('COM_DJCATALOG2_UPDATE_CART_ERROR_QUANTITY', $item->name, floatval($quantity), floatval($item->stock)));
					$hadErrors = true;
				} else {
					$this->quantities[$sid] = $quantity;
				}
			} else {
				$this->quantities[$sid] = $newQty;
			}
		}

		if (is_array($attributes) && count($attributes) > 0) {
			$attributes = $this->validateAttributes($attributes);
			$this->attribute_values[$sid] = $attributes;
		}

		if ($hadErrors) {
			return false;
		}

		$this->recalculate();

		return true;
	}

	public function validateAttributes($attributes)
	{
		$fields = $this->getAttributes();
		foreach ($attributes as $key => $attribute) {
			if (!isset($fields[$key])) {
				unset($attributes[$key]);
				continue;
			}

			if (is_array($attribute)) {
				if (isset($fields[$key]->optionlist) && count($fields[$key]->optionlist)) {
					foreach ($attribute as $opt_key => $option) {
						$exist = false;
						foreach ($fields[$key]->optionlist as $field_option) {
							if ($field_option->id == $option) {
								$exist = true;
								break;
							}
						}
						if (!$exist) {
							unset($attribute[$opt_key]);
						}
					}
				} else {
					unset($attributes[$key]);
				}

				if (count($attribute) < 1) {
					unset($attributes[$key]);
				}
			} else {
				if (isset($fields[$key]->optionlist) && count($fields[$key]->optionlist)) {
					$exist = false;
					foreach ($fields[$key]->optionlist as $field_option) {
						if ($field_option->id == $attribute) {
							$exist = true;
							break;
						}
					}
					if (!$exist) {
						unset($attributes[$key]);
					}
				}
			}
		}

		return $attributes;
	}

	public function addFeatures($sid, $item, $features)
	{
		$attributes = $this->getFeatures();

		foreach ($features as $id => $feature) {
			if (!isset($attributes[$id])) continue;
			$attribute = $attributes[$id];
			if (!is_array($attribute->optionlist) || empty($attribute->optionlist)) continue;

			$key = '_ef_' . $attribute->alias;
			if (!isset($item->$key)) continue;
			if (!is_array($item->$key) || empty($item->$key)) continue;

			foreach ($attribute->optionlist as $option) {
				if ($feature != $option->id) continue;

				if (isset($item->$key[$option->id])) {

					if (!isset($this->feature_values[$sid])) {
						$this->feature_values[$sid] = array();
					}
					if (!isset($this->feature_values[$sid][$id])) {
						$this->feature_values[$sid][$id] = array();
					}

					$this->feature_values[$sid][$id][] = $option->id;
				}
			}
		}


		$this->recalculate();
	}

	public function addDimensions($sid, $item, $dimensions)
	{
		if (empty($item->config_dimensions) /*|| trim((string)$item->config_dimensions) == ''*/) {
			return true;
		}

		if (is_array($dimensions) == false || empty($dimensions)) {
			return false;
		}

		$itemDims = is_string($item->config_dimensions) ? json_decode((string)$item->config_dimensions, true) : (array)$item->config_dimensions;

		foreach ($itemDims as $dim) {
			if (!isset($dimensions[$dim])) {
				return false;
			}
			if (trim((string)$dimensions[$dim]) === '') {
				return false;
			}
			if ($item->config_dimensions_unit != '') {
				$dimensions[$dim] .= ' [' . $item->config_dimensions_unit . ']';
			}
		}

		$this->dimension_values[$sid] = $dimensions;

		$this->recalculate();

		return true;
	}

	public function addConfigurable($sid, $item, $features)
	{
		if (empty($item->config_conditions) /*|| trim((string)$item->config_conditions) == ''*/) {
			return true;
		}

		if (is_array($features) == false || empty($features)) {
			return false;
		}

		$itemCond = is_string($item->config_conditions) ? json_decode((string)$item->config_conditions, true) : (array)$item->config_conditions;
		$choicePath = [];

		foreach ($itemCond as $field_id => $cond) {
			if (!isset($features[$field_id])) {
				return false;
			}
			if (trim((string)$features[$field_id]) === '') {
				return false;
			}

			foreach ($cond['options'] as $oidx => $option) {
				if ($option['id'] != $features[$field_id]) continue;

				$valid = false;
				if (empty($option['dependancies'])) {
					$valid = true;
				} else {
					$valid_deps = 0;
					foreach ($option['dependancies'] as $dep) {
						if (!isset($choicePath[$dep['id']])) {
							return false;
						}

						$val = $choicePath[$dep['id']];

						switch ($dep['operator']) {
							case 'eq' :
							{
								if (!is_array($dep['match_ids']) || empty($dep['match_ids'])) {
									$valid_deps++;
								} else if (in_array($val['id'], $dep['match_ids'])) {
									$valid_deps++;
								}
								break;
							}
							case 'not' :
							{
								if (!is_array($dep['match_ids']) || empty($dep['match_ids'])) {
									$valid_deps++;
								} else if (!in_array($val['id'], $dep['match_ids'])) {
									$valid_deps++;
								}
								break;
							}
							case 'lt' :
							{
								if (strlen($dep['match']) > 0) {
									if (floatval($val['value']) < floatval($dep['match'])) {
										$valid_deps++;
									}
								}
								break;
							}
							case 'lte' :
							{
								if (strlen($dep['match']) > 0) {
									if (floatval($val['value']) <= floatval($dep['match'])) {
										$valid_deps++;
									}
								}
								break;
							}
							case 'gt' :
							{
								if (strlen($dep['match']) > 0) {
									if (floatval($val['value']) > floatval($dep['match'])) {
										$valid_deps++;
									}
								}
								break;
							}
							case 'gte' :
							{
								if (strlen($dep['match']) > 0) {
									if (floatval($val['value']) >= floatval($dep['match'])) {
										$valid_deps++;
									}
								}
								break;
							}
							case 'contains' :
							{
								if (strlen($dep['match']) > 0) {
									if (strstr($val['value'], $dep['match']) !== false) {
										$valid_deps++;
									}
								}
								break;
							}
						}
					}

					$valid = (bool)(count($option['dependancies']) == $valid_deps);
				}

				if ($valid) {
					$choicePath[$field_id] = $option;
				}
			}
		}

		$addOnPrice = $item->final_price;
		foreach ($choicePath as $field_id => $option) {
			if ($option['price'] > 0.0) {
				if ($option['price_mod'] == 'add') {
					$addOnPrice += $option['price'];
				} else if ($option['price_mod'] == 'multiply') {
					$addOnPrice *= $option['price'];
				}
			}
		}

		$item->price = $item->final_price = $this->items[$sid]->price = $this->items[$sid]->final_price = floatval($addOnPrice);
		$this->prices[$sid] = $item->price;

		$this->feature_values[$sid] = array();
		foreach ($choicePath as $id => $choice) {
			if (!isset($this->feature_values[$sid][$id])) {
				$this->feature_values[$sid][$id] = array();
			}

			$this->feature_values[$sid][$id][] = $choice['id'];
		}

		$this->recalculate();
		return true;
	}

	public function getItemFeatures($item, $translate = false, $format = 'array')
	{
		if (!$item || !isset($this->items[$item->_sid])) {
			return false;
		}

		if (!isset($this->feature_values[$item->_sid])) {
			return $format == 'array' ? array() : '';
		}

		$attributes = $this->feature_values[$item->_sid];

		if ($translate) {
			$fields = $this->getFeatures();
			foreach ($fields as $key => $field) {
				$value = '';

				if (isset($attributes[$field->id])) {
					$value = $attributes[$field->id];
				}

				if (!empty($value)) {
					$selected = (is_array($value)) ? $value : array($value);
					$values = array();
					foreach ($field->optionlist as $option) {
						if (in_array($option->id, $selected)) {
							$values[] = $option->value;
						}
					}

					$attributes[$field->id] = implode(', ', $values);
				}
			}

			if ($format == 'json') {
				$output = array();
				foreach ($attributes as $key => $attribute) {
					if (isset($fields[$key])) {
						$output[$fields[$key]->name] = $attribute;
					}
				}
				if (!empty($this->dimension_values[$item->_sid])) {
					foreach ($this->dimension_values[$item->_sid] as $dim => $value) {
						$output[$dim] = $value;
					}
				}
				$attributes = json_encode($output);
			} else if ($format == 'list') {
				$output = array();
				foreach ($attributes as $key => $attribute) {
					if (isset($fields[$key])) {
						$obj = new stdClass();
						$obj->field_name = $fields[$key]->name;
						$obj->field_value = $attribute;
						$output[] = $obj;
					}
				}
				if (!empty($this->dimension_values[$item->_sid])) {
					foreach ($this->dimension_values[$item->_sid] as $dim => $value) {
						$obj = new stdClass();
						$obj->field_name = JText::_('COM_DJCATALOG2_' . $dim);
						$obj->field_value = $value;
						$output[] = $obj;
					}
				}
				$attributes = $output;
			}
		}

		return $attributes;
	}

	public function setDelivery($delivery_id)
	{
		if ($delivery_id == 0) {
			$this->delivery = false;
			return false;
		}
		$db = Factory::getDbo();
		$db->setQuery('select * from #__djc2_delivery_methods where id =' . (int)$delivery_id . ' and published=1');
		$delivery = $db->loadObject();
		if (!$delivery) {
			$this->delivery = false;
			$this->recalculate();
			$this->saveToStorage();
			throw new Exception(JText::_('COM_DJCATALOG2_DELIVERY_METHOD_IS_INVALID'), 500);
		}

		$params = new Registry();
		$params->loadString($delivery->params, 'JSON');
		$delivery->params = $params;

		if ($this->shipping_days > 1) {
			$delivery->days = $this->shipping_days;
		}


		PluginHelper::importPlugin('djcatalog2delivery');
		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2SetDeliveryMethod', array('com_djcatalog2.cart.set_delivery', &$this, &$delivery));

		$this->delivery = $delivery;

		// No needed to recalculate at this point. Used to cause issues with the price rules
		//$this->recalculate();

		return $this->delivery;
	}

	public function setPayment($payment_id)
	{
		if ($payment_id == 0) {
			$this->payment = false;
			return false;
		}
		$db = Factory::getDbo();
		$db->setQuery('select * from #__djc2_payment_methods where id =' . (int)$payment_id . ' and published=1');
		$payment = $db->loadObject();
		if (!$payment) {
			$this->payment = false;
			$this->recalculate();
			$this->saveToStorage();
			throw new Exception(JText::_('COM_DJCATALOG2_PAYMENT_METHOD_IS_INVALID'), 500);
		}

		if ($this->delivery) {
			$db->setQuery('select delivery_id from #__djc2_deliveries_payments where payment_id=' . (int)$payment_id);
			$validDeliveries = $db->loadColumn();
			if (!empty($validDeliveries) && !in_array((int)$this->delivery->id, $validDeliveries)) {
				$this->payment = false;
				$this->recalculate();
				$this->saveToStorage();
				throw new Exception(JText::_('COM_DJCATALOG2_PAYMENT_METHOD_IS_INVALID'), 500);
			}
		}

		$params = new Registry();
		$params->loadString($payment->params, 'JSON');
		$payment->params = $params;

		PluginHelper::importPlugin('djcatalog2payment');
		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2SetPaymentMethod', array('com_djcatalog2.cart.set_payment', &$this, &$payment));

		$this->payment = $payment;

		// No needed to recalculate at this point. Used to cause issues with the price rules
		//$this->recalculate();

		return $this->payment;
	}

	public function setCoupon(&$coupon, $check = true)
	{

		if ($this->coupon) {
			$coupon->setError(JText::_('COM_DJCATALOG2_COUPON_ALREADY_APPLIED'));
			return false;
		}

		// check basic and assign product restrictions
		if ($check && (!$coupon->checkRestrictions() || !$coupon->checkProductRestriction())) {
			return false;
		}

		// restrictions are met, apply the coupon
		$this->coupon = $coupon;

		// recalculate the cart prices
		$this->recalculate();
		// save cart state to storage
		$this->saveToStorage();

		return true;
	}

	public function removeCoupon()
	{

		if (!$this->coupon) return false;

		$coupon = $this->coupon;
		$this->coupon = false;
		$this->subscription = null;

		// recalculate the cart prices
		$this->recalculate();

		// save cart state to storage
		$this->saveToStorage();

		return $coupon;
	}

	public function recalculate()
	{
		$params = Djcatalog2Helper::getParams();
		$user = Factory::getUser();
		$app = Factory::getApplication();



		$sub_totals = array();
		$product_sub_totals = array();
		$product_sub_old_totals = array();

		$total = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
		$product_total = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
		$product_old_total = array('net' => 0, 'tax' => 0, 'gross' => 0.0);

		$tax_already_incl = (bool)($params->get('price_including_tax', 1) == 1);

		if ($this->coupon) {
			$this->coupon->resetValue();
		}
		$this->price_components = array();

		// prepare data for tier discount rules
		// i - indvidual, a - all, c - same category, p - same producer
		$tierRules = array('a' => 0, 'i' => array(), 'c' => array(), 'p' => array());
		foreach ($this->items as $k => &$item) {


			if (empty($item->id) || empty($item->_sid)) {
				unset($this->items[$k]);
				continue;
			}
			$item->_quantity = (isset($this->quantities[$item->_sid])) ? $this->quantities[$item->_sid] : 1;
			$item->final_price = (isset($this->prices[$item->_sid])) ? $this->prices[$item->_sid] : $item->final_price;

			if (!$item->tax_rule_id) {
				$item->tax_rule_id = 0;
			}

			$tierRules['a'] += (int)$item->_quantity;
			if (!isset($tierRules['i'][$item->id])) {
				$tierRules['i'][$item->id] = 0;
			}
			$tierRules['i'][$item->id] += (int)$item->_quantity;

			if (!isset($tierRules['c'][$item->cat_id])) {
				$tierRules['c'][$item->cat_id] = 0;
			}
			$tierRules['c'][$item->cat_id] += (int)$item->_quantity;

			if (!isset($tierRules['p'][$item->producer_id])) {
				$tierRules['p'][$item->producer_id] = 0;
			}
			$tierRules['p'][$item->producer_id] += (int)$item->_quantity;

			$this->applyCalculator($item);

		}

		if (isset($item->force_recurring) && $item->force_recurring) {
			$this->forceRecurring = true;
			$this->recurringDate = date("Y-m-d", strtotime('now + ' . $item->expiration));
		}

		unset($item);

		// apply tier discounts
		$this->applyTierPrices($tierRules);

		// apply cart rules before calculating the sub total
		Djcatalog2HelperPrice::applyRulesBeforeInit($this, $this->items);

		$this->total_weight = 0.0;
		$this->gauges_map = [];

		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();

		foreach ($this->items as $k => &$item) {
			Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2CartFetchPrice', array(&$item, &$this));

			$finalPrice = $item->final_price;
			$basePrice = $item->price;

			if ($this->coupon) {
				$finalPrice = $this->coupon->getPrice($finalPrice, $item->id, $item->_quantity);
			}

			$item->_prices = (isset($item->_prices)) ? $item->_prices : Djcatalog2HelperPrice::getCartPrices($finalPrice, $basePrice, $item->tax_rule_id, false, $item->_quantity, $params);
		}
		unset($item);

		// apply cart rules before calculating the sub total
		Djcatalog2HelperPrice::applyRulesAfterInit($this, $this->items);

		foreach ($this->items as $k => &$item) {
			//if($this->coupon) {
			if (!isset($product_sub_old_totals[$item->tax_rule_id])) {
				$product_sub_old_totals[$item->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
			}

			if (!isset($item->_old_prices)) {
				$item->_old_prices = Djcatalog2HelperPrice::getCartPrices($item->final_price, $item->price, $item->tax_rule_id, false, $item->_quantity, $params);
			}

			$product_sub_old_totals[$item->tax_rule_id]['net'] += ($item->_old_prices['total']['net']);
			$product_sub_old_totals[$item->tax_rule_id]['gross'] += ($item->_old_prices['total']['gross']);
			$product_sub_old_totals[$item->tax_rule_id]['tax'] += ($item->_old_prices['total']['tax']);
			//}
			if ($this->coupon) {
				$diffPrice = array(
					'net' => $item->_prices['total']['net'] - $item->_old_prices['total']['net'],
					'tax' => $item->_prices['total']['tax'] - $item->_old_prices['total']['tax'],
					'gross' => $item->_prices['total']['gross'] - $item->_old_prices['total']['gross']
				);
				$this->addPriceComponent($this->coupon, $diffPrice, 'coupon');
			}

			if (!isset($sub_totals[$item->tax_rule_id])) {
				$sub_totals[$item->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
			}

			$sub_totals[$item->tax_rule_id]['net'] += ($item->_prices['total']['net']);
			$sub_totals[$item->tax_rule_id]['gross'] += ($item->_prices['total']['gross']);
			$sub_totals[$item->tax_rule_id]['tax'] += ($item->_prices['total']['tax']);

			$this->total_weight += DJCatalog2HelperQuantity::convertWeigthUnit(($item->weight * $item->_quantity), $item->weight_unit, 'G');

			$this->total_dimensions['length'] += DJCatalog2HelperQuantity::convertDimensionUnit(($item->length * $item->_quantity), $item->dimensions_unit, 'M');
			$this->total_dimensions['width'] += DJCatalog2HelperQuantity::convertDimensionUnit(($item->width * $item->_quantity), $item->dimensions_unit, 'M');
			$this->total_dimensions['height'] += DJCatalog2HelperQuantity::convertDimensionUnit(($item->height * $item->_quantity), $item->dimensions_unit, 'M');

			if (isset($item->gauge_id)) {
				if (!isset($this->gauges_map[$item->gauge_id])) {
					$this->gauges_map[$item->gauge_id] = [];
				}
				$this->gauges_map[$item->gauge_id][] = $item->id;
			}
		}
		unset($item);

		if (!empty($this->customisations)) {

			foreach ($this->customisations as $sid => &$customOptions) {
				if (!is_array($customOptions)) {
					continue;
				}
				foreach ($customOptions as &$customOption) {
					$customOption->_quantity = 1;

					if ($customOption->price_modifier == 'm') {
						if ($sid == 0) {
							$customOption->_quantity = 0;
							foreach ($this->quantities as $qty) {
								$customOption->_quantity += $qty;
							}
						} else {
							if (isset($this->quantities[$sid])) {
								$customOption->_quantity = $this->quantities[$sid];
							}
						}
					}

					if (!$customOption->tax_rule_id) {
						$customOption->tax_rule_id = 0;
					}

					$customOption->_prices = Djcatalog2HelperPrice::getCartPrices($customOption->price, $customOption->price, $customOption->tax_rule_id, false, $customOption->_quantity, $params);

					if (!isset($sub_totals[$customOption->tax_rule_id])) {
						$sub_totals[$customOption->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
					}

					$sub_totals[$customOption->tax_rule_id]['net'] += ($customOption->_prices['total']['net']);
					$sub_totals[$customOption->tax_rule_id]['gross'] += ($customOption->_prices['total']['gross']);
					$sub_totals[$customOption->tax_rule_id]['tax'] += ($customOption->_prices['total']['tax']);
				}
				unset($customOption);
			}
			unset($customOptions);
		}

		//if($this->coupon) {
		foreach ($product_sub_old_totals as $tax_rule_id => $sub_total) {
			if ($tax_already_incl) {
				//$sub_total['gross'] = Djcatalog2HelperPrice::applyCartPriceRules($this, $sub_total['gross'], true, 'total_items');
				$product_sub_old_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['gross'], 'T', $tax_rule_id);
				$product_sub_old_totals[$tax_rule_id]['net'] = $sub_total['gross'] - $sub_total['tax'];
			} else {
				//$sub_total['net'] = Djcatalog2HelperPrice::applyCartPriceRules($this, $sub_total['net'], true, 'total_items');
				$product_sub_old_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['net'], 'T', $tax_rule_id);
				$product_sub_old_totals[$tax_rule_id]['gross'] = $sub_total['net'] + $sub_total['tax'];
			}

			$product_old_total ['net'] += $product_sub_old_totals[$tax_rule_id]['net'];
			$product_old_total ['tax'] += $product_sub_old_totals[$tax_rule_id]['tax'];
			$product_old_total ['gross'] += $product_sub_old_totals[$tax_rule_id]['gross'];
		}
		//}

		foreach ($sub_totals as $tax_rule_id => $sub_total) {
			$sub_total = Djcatalog2HelperPrice::applyCartPriceRules($this, $sub_totals[$tax_rule_id], true, 'total_items');
			$sub_totals[$tax_rule_id] = $sub_total;

			if ($tax_already_incl) {
				$sub_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['gross'], 'T', $tax_rule_id);
				$sub_totals[$tax_rule_id]['net'] = $sub_totals[$tax_rule_id]['gross'] - $sub_totals[$tax_rule_id]['tax'];
			} else {
				$sub_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['net'], 'T', $tax_rule_id);
				$sub_totals[$tax_rule_id]['gross'] = $sub_totals[$tax_rule_id]['net'] + $sub_totals[$tax_rule_id]['tax'];
			}

			$product_total ['net'] += $sub_totals[$tax_rule_id]['net'];
			$product_total ['tax'] += $sub_totals[$tax_rule_id]['tax'];
			$product_total ['gross'] += $sub_totals[$tax_rule_id]['gross'];
		}

		$product_sub_totals = $sub_totals;


		if (!empty($this->delivery)) {
			$this->delivery->_quantity = 1;

			$fee = 0;
			if ($this->delivery->additional_fee > 0) {
				if ($tax_already_incl) {
					$fee = round(($this->delivery->additional_fee * $product_total ['gross']) / 100, 2);
				} else {
					$fee = round(($this->delivery->additional_fee * $product_total ['net']) / 100, 2);
				}
			}
			$deliveryPrice = $this->delivery->price + $fee;

			$deliveryPrice = ($this->delivery->free_amount > 0 && $this->delivery->free_amount <= $product_total ['gross']) ? 0.0 : $deliveryPrice;
			$deliveryPrice = $deliveryPrice * $this->shipping_days;
			$deliveryPrice = Djcatalog2HelperPrice::applyPriceRules($deliveryPrice, true, 'delivery', $this->delivery, 'delivery');

			$this->delivery->_prices = Djcatalog2HelperPrice::getCartPrices($deliveryPrice, $deliveryPrice, $this->delivery->tax_rule_id, false, $this->delivery->_quantity, $params);
			if ($this->delivery->params->get('distance_calculation') !== null) {

				$input = $app->getInput()->getArray();
				$keyNumber = null;
				$distanceFromInput = null;
				foreach ($input as $key => $value) {
					if(is_array($value))continue;
					// Extract the number after "jform_djcatalog2delivery_calculated_distance_"
					if (preg_match('/jform_djcatalog2delivery_calculated_distance_(\d+)/', $key, $matches)) {
						$keyNumber = $matches[1]; // The number from the key
					}

					// Extract the numeric distance value and replace comma with dot for decimal
					if($value !== null)
						if (preg_match('/([\d,]+)\s*km/', $value, $matches)) {
							$distanceFromInput = (float) str_replace(',', '.', $matches[1]); // Convert "32,6" to 32.6 as a float
						}
				}


				if ($app->getInput()->get('distance') !== null ) {


					$distance = $app->getInput()->getRaw('distance');
					$distance = str_replace(['km', ','], ['', '.'], $distance);
					$distance = (float)$distance;
					$distances = $this->delivery->params->get('distance_calculation');
					//Added free amount of delivery Google Maps Distance
					$freeAmount = $this->delivery->free_amount;

					foreach ($distances as $range) {
						$from = (float) $range->options->from;
						$to = (float) $range->options->to;

						// Check if $distance falls within the current range
						if ($distance >= $from && $distance <= $to) {
							$price = (float) $range->options->price;
							$tax_rule_id_of_distance = $range->options->tax_rule_id_for_price_in_destination;
							break; // Exit loop once a matching range is found
						}
					}
					if($price != null){
						if (!$tax_rule_id_of_distance) {
							$this->delivery->tax_rule_id = 0;
						}else {
							$this->delivery->tax_rule_id = $tax_rule_id_of_distance;
						}
						if (!isset($sub_totals[$this->delivery->tax_rule_id])) {
							$sub_totals[$this->delivery->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
						}
						// 04.02.2025 - Changed logics between gettingCartPrices
						// Cart prices for delivery not include taxes! So the meaning of method is opposite
						// Thats why the method changed
						// We are also calculating the freeAmount and checking if it exceeds it


						//RG18 9QA - simple postcode for GoogleMaps UK - to test in frontend
						if($this->delivery->free_amount >= $product_total['gross']){
							$this->delivery->_prices = Djcatalog2HelperPrice::getCartPrices($price, $price, $this->delivery->tax_rule_id, false, $this->delivery->_quantity, $params);

							$sub_totals[$this->delivery->tax_rule_id]['net'] += ($this->delivery->_prices['total']['net']);
							$sub_totals[$this->delivery->tax_rule_id]['gross'] += ($this->delivery->_prices['total']['gross']);
							$sub_totals[$this->delivery->tax_rule_id]['tax'] += ($this->delivery->_prices['total']['tax']);
						}else {

							$this->delivery->_prices = Djcatalog2HelperPrice::getCartPrices(0, 0, $this->delivery->tax_rule_id, false, $this->delivery->_quantity, $params);


						}

					}
				}else {

					$distance = $distanceFromInput;
					$distances = $this->delivery->params->get('distance_calculation');

					$freeAmount = $this->delivery->free_amount;

					foreach ($distances as $range) {
						$from = (float) $range->options->from;
						$to = (float) $range->options->to;

						// Check if $distance falls within the current range
						if ($distance >= $from && $distance <= $to) {
							$price = (float) $range->options->price;
							$tax_rule_id_of_distance = $range->options->tax_rule_id_for_price_in_destination;
							break; // Exit loop once a matching range is found
						}
					}
					if($price != null){
						if (!$tax_rule_id_of_distance) {
							$this->delivery->tax_rule_id = 0;
						}else {
							$this->delivery->tax_rule_id = $tax_rule_id_of_distance;
						}
						if (!isset($sub_totals[$this->delivery->tax_rule_id])) {
							$sub_totals[$this->delivery->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
						}
						if($this->delivery->free_amount >= $product_total['gross']){
							$this->delivery->_prices = Djcatalog2HelperPrice::getCartPrices($price, $price, $this->delivery->tax_rule_id, false, $this->delivery->_quantity, $params);
							$sub_totals[$this->delivery->tax_rule_id]['net'] += ($this->delivery->_prices['total']['net']);
							$sub_totals[$this->delivery->tax_rule_id]['gross'] += ($this->delivery->_prices['total']['gross']);
							$sub_totals[$this->delivery->tax_rule_id]['tax'] += ($this->delivery->_prices['total']['tax']);
						}else {
							$this->delivery->_prices = Djcatalog2HelperPrice::getCartPrices(0, 0, $this->delivery->tax_rule_id, false, $this->delivery->_quantity, $params);
						}
					}
				}
			}else {
				if (!$this->delivery->tax_rule_id) {
					$this->delivery->tax_rule_id = 0;
				}
				if (!isset($sub_totals[$this->delivery->tax_rule_id])) {
					$sub_totals[$this->delivery->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
				}

				$sub_totals[$this->delivery->tax_rule_id]['net'] += ($this->delivery->_prices['total']['net']);
				$sub_totals[$this->delivery->tax_rule_id]['gross'] += ($this->delivery->_prices['total']['gross']);
				$sub_totals[$this->delivery->tax_rule_id]['tax'] += ($this->delivery->_prices['total']['tax']);
			}

		}

		if (!empty($this->payment)) {
			$this->payment->_quantity = 1;

			$fee = 0;
			if ($this->payment->additional_fee > 0) {
				if ($tax_already_incl) {
					$fee = round(($this->payment->additional_fee * $product_total ['gross']) / 100, 2);
				} else {
					$fee = round(($this->payment->additional_fee * $product_total ['net']) / 100, 2);
				}
			}
			$paymentPrice = $this->payment->price + $fee;

			$paymentPrice = ($this->payment->free_amount > 0 && $this->payment->free_amount <= $product_total ['gross']) ? 0.0 : $paymentPrice;
			$paymentPrice = Djcatalog2HelperPrice::applyPriceRules($paymentPrice, true, 'payment', $this->payment, 'payment');

			$this->payment->_prices = Djcatalog2HelperPrice::getCartPrices($paymentPrice, $paymentPrice, $this->payment->tax_rule_id, false, $this->payment->_quantity, $params);
			if (!$this->payment->tax_rule_id) {
				$this->payment->tax_rule_id = 0;
			}
			if (!isset($sub_totals[$this->payment->tax_rule_id])) {
				$sub_totals[$this->payment->tax_rule_id] = array('net' => 0, 'tax' => 0, 'gross' => 0.0);
			}

			$sub_totals[$this->payment->tax_rule_id]['net'] += ($this->payment->_prices['total']['net']);
			$sub_totals[$this->payment->tax_rule_id]['gross'] += ($this->payment->_prices['total']['gross']);
			$sub_totals[$this->payment->tax_rule_id]['tax'] += ($this->payment->_prices['total']['tax']);
		}

		foreach ($sub_totals as $tax_rule_id => $sub_total) {
			$sub_total = Djcatalog2HelperPrice::applyCartPriceRules($this, $sub_totals[$tax_rule_id], true, 'grand_total');
			$sub_totals[$tax_rule_id] = $sub_total;

			if ($tax_already_incl) {

				//$sub_totals[$tax_rule_id]['gross'] = $sub_total['gross'] = Djcatalog2HelperPrice::applyCartPriceRules($this, $sub_totals[$tax_rule_id]['gross'], true, 'grand_total');

				$sub_totals[$tax_rule_id]['gross'] = $sub_total['gross'];
				$sub_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['gross'], 'T', $tax_rule_id);
				$sub_totals[$tax_rule_id]['net'] = $sub_totals[$tax_rule_id]['gross'] - $sub_totals[$tax_rule_id]['tax'];
			} else {
				$sub_totals[$tax_rule_id]['net'] = $sub_total['net'];
				$sub_totals[$tax_rule_id]['tax'] = Djcatalog2HelperPrice::calculate($sub_total['net'], 'T', $tax_rule_id);
				$sub_totals[$tax_rule_id]['gross'] = $sub_totals[$tax_rule_id]['net'] + $sub_totals[$tax_rule_id]['tax'];
			}

			$total ['net'] += $sub_totals[$tax_rule_id]['net'];
			$total ['tax'] += $sub_totals[$tax_rule_id]['tax'];
			$total ['gross'] += $sub_totals[$tax_rule_id]['gross'];
		}

		$this->product_old_total = $product_old_total;
		$this->product_total = $product_total;
		$this->sub_totals = $sub_totals;

		$this->total = $total;

		$user_currency_id = Factory::getApplication()->getUserState('com_djcatalog2.checkout.currency');
		$default_currency = Djcatalog2HelperPrice::getCurrencyDefault();
		$user_currency = Djcatalog2HelperPrice::getCurrencyById($user_currency_id);

		$this->currency = ((!empty($user_currency)) ? $user_currency : ((!empty($default_currency)) ? $default_currency : false));

		return true;
	}

	public function saveToStorage()
	{
		$app = Factory::getApplication();
		$params = Djcatalog2Helper::getParams();
		$db = Factory::getDbo();
		$user = Factory::getUser();

		$app->setUserState('com_djcatalog2.cart.prices', $this->prices);
		$app->setUserState('com_djcatalog2.cart.items', $this->quantities);
		$app->setUserState('com_djcatalog2.cart.attributes', $this->attribute_values);
		$app->setUserState('com_djcatalog2.cart.features', $this->feature_values);
		$app->setUserState('com_djcatalog2.cart.dimension_values', $this->dimension_values);

		if ($user->id) {
			$query = $db->getQuery(true)->select('id')->from('#__djc2_usercarts')->where('user_id=' . $user->id);
			$db->setQuery($query);
			$exists = $db->loadResult();

			if ($exists) {
				$query = $db->getQuery(true)->delete('#__djc2_usercarts')->where('user_id=' . $user->id);
				$db->setQuery($query);
				$db->execute();
			}

			if (count($this->quantities) > 0) {
				$query = $db->getQuery(true);
				$query->insert('#__djc2_usercarts');
				$query->columns(array('user_id', 'items'));
				$query->values($user->id . ',' . $db->quote(json_encode($this->quantities)));
				$db->setQuery($query);
				$db->execute();
			}
		}
		if (!empty($this->delivery)) {
			$app->setUserState('com_djcatalog2.cart.delivery', $this->delivery->id);
		} else {
			$app->setUserState('com_djcatalog2.cart.delivery', null);
		}

		if ($this->shipping_days > 1) {
			$app->setUserState('com_djcatalog2.cart.shipping_days', $this->shipping_days);
		}

		if (!empty($this->payment)) {
			$app->setUserState('com_djcatalog2.cart.payment', $this->payment->id);
		} else {
			$app->setUserState('com_djcatalog2.cart.payment', null);
		}

		$app->setUserState('com_djcatalog2.cart.coupon', @$this->coupon->id);

		$app->setUserState('com_djcatalog2.cart.customisations', $this->customisations);
		$app->setUserState('com_djcatalog2.cart.calculators', $this->calculators);

		if ($params->get('cart_cookie_enable', 1)) {
			$cookie_val = json_encode($this->quantities);
			$cookie_time = (int)$params->get('cart_cookie_time', 2419200);
			$app->input->cookie->set('djc2cart', $cookie_val, (time() + $cookie_time), $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
		} else if ($app->input->cookie->getString('djc2cart') != '') {
			$app->input->cookie->set('djc2cart', '', (time() - 3600), $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
		}

		return true;
	}

	public function clear()
	{
		$app = Factory::getApplication();

		$this->items = array();
		$this->quantities = array();
		$this->prices = array();
		$this->total = array();
		$this->sub_totals = array();
		$this->product_total = array();
		$this->delivery = false;
		$this->payment = false;
		$this->attribute_values = array();
		$this->feature_values = array();
		$this->customisations = array();

		$app->setUserState('com_djcatalog2.cart.prices', null);
		$app->setUserState('com_djcatalog2.cart.items', null);
		$app->setUserState('com_djcatalog2.cart.attributes', null);
		$app->setUserState('com_djcatalog2.cart.features', null);
		$app->setUserState('com_djcatalog2.cart.dimension_values', null);
		$app->setUserState('com_djcatalog2.cart.delivery', null);
		$app->setUserState('com_djcatalog2.cart.payment', null);
		$app->setUserState('com_djcatalog2.cart.coupon', null);
		$app->setUserState('com_djcatalog2.cart.customisations', null);
		$app->setUserState('com_djcatalog2.cart.calculators', null);
		$app->setUserState('com_djcatalog2.recent_customisation', null);
		$app->setUserState('com_djcatalog2.customisation_files', null);
		$app->setUserState('com_djcatalog2.cart.shipping_days', null);
		$app->setUserState('com_djcatalog2.cart.custom_items', null);

		$app->input->cookie->set('djc2cart', '', (time() - 3600), $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));

		$db = Factory::getDbo();
		$user = Factory::getUser();

		if ($user->id) {
			$query = $db->getQuery(true)->delete('#__djc2_usercarts')->where('user_id=' . $user->id);
			$db->setQuery($query);
			$db->execute();
		}
	}

	public function getAttributes()
	{
		if (is_null($this->attributes)) {
			$db = Factory::getDbo();

			$query = $db->getQuery(true);
			$query->select('f.*');
			$query->from('#__djc2_cart_extra_fields AS f');
			$query->where('f.published=1');
			$query->order('f.ordering ASC');

			$db->setQuery($query);
			$attributes = $db->loadObjectList('id');

			if (count($attributes) > 0) {
				$query = $db->getQuery(true);
				$query->select('o.*');
				$query->from('#__djc2_cart_extra_fields_options AS o');
				$query->order('o.id ASC');

				$db->setQuery($query);
				$options = $db->loadObjectList();

				foreach ($options as $k => $v) {
					if (isset($attributes[$v->field_id])) {
						if (!isset($attributes[$v->field_id]->optionlist)) {
							$attributes[$v->field_id]->optionlist = array();
						}
						$attributes[$v->field_id]->optionlist[] = $v;
					}
				}
			}

			$this->attributes = $attributes;
		}

		return $this->attributes;
	}

	public function getFeatures()
	{
		if (is_null($this->features)) {
			$db = Factory::getDbo();

			$query = $db->getQuery(true);
			$query->select('f.*');
			$query->from('#__djc2_items_extra_fields AS f');
			$query->where('f.published=1 AND f.cart_variant = 2');
			$query->order('f.ordering ASC');

			$db->setQuery($query);
			$attributes = $db->loadObjectList('id');

			if (count($attributes) > 0) {
				$query = $db->getQuery(true);
				$query->select('o.*');
				$query->from('#__djc2_items_extra_fields_options AS o');
				$query->order('o.id ASC');

				$db->setQuery($query);
				$options = $db->loadObjectList();

				foreach ($options as $k => $v) {
					if (isset($attributes[$v->field_id])) {
						if (!isset($attributes[$v->field_id]->optionlist)) {
							$attributes[$v->field_id]->optionlist = array();
						}
						$attributes[$v->field_id]->optionlist[] = $v;
					}
				}
			}

			$this->features = $attributes;
		}

		return $this->features;
	}

	protected function applyTierPrices($rules)
	{
		foreach ($this->items as $k => &$item) {
			if ($item->price_tier_modifier != '0' && count($item->_price_tiers)) {
				if (isset($item->_tier_applied)) {
					continue;
				}
				$item->_tier_applied = true;

				$quantity = $item->_quantity;

				switch ($item->price_tier_break) {
					case 'i':
					{
						if (isset($rules['i'][$item->id]) && $rules['i'][$item->id] > 0) {
							$quantity = $rules['i'][$item->id];
						}
						break;
					}
					case 'c':
					{
						if ($item->cat_id && isset($rules['c'][$item->cat_id]) && $rules['c'][$item->cat_id] > 0) {
							$quantity = $rules['c'][$item->cat_id];
						}
						break;
					}
					case 'p':
					{
						if ($item->producer_id && isset($rules['p'][$item->producer_id]) && $rules['p'][$item->producer_id] > 0) {
							$quantity = $rules['p'][$item->producer_id];
						}
						break;
					}
					case 'a':
					{
						if ($rules['a'] > 0) {
							$quantity = $rules['a'];
						}
						break;
					}
				}

				$discounts = Djcatalog2HelperPrice::getTierDiscounts($item->final_price, $item->_price_tiers, $item->price_tier_modifier, $quantity);
				$item->final_price = $discounts['price'];


			}
		}
		unset($item);
	}

	public function addPriceComponent($rule, $value, $type = 'rule')
	{
		if (!is_array($this->price_components)) {
			$this->price_components = array();
		}

		$id = $type . ':' . $rule->id;
		$name = '';

		if ($type == 'rule') {
			$name = $rule->name;
		} else if ($type == 'coupon') {
			$name = ($rule->description != '') ? $rule->description : $rule->code;
		}

		$operation = ($type == 'rule') ? $rule->operation : 'sub';


		if (!isset($this->price_components[$id])) {
			$component = array(
				'name' => $name,
				'value' => array('net' => 0.0000, 'tax' => 0.0000, 'gross' => 0.0000),
				'rule' => $rule,
				'type' => $type,
				'operation' => $operation,
				'calc_type' => $rule->type
			);
			$this->price_components[$id] = (object)$component;
		}

		foreach ($this->price_components[$id]->value as $k => $v) {
			$this->price_components[$id]->value[$k] += $value[$k];
		}
	}

	public function getPriceComponents($ruleId = null)
	{
		return is_null($ruleId) ? $this->price_components : (isset($this->price_components[$ruleId]) ? $this->price_components[$ruleId] : false);
	}

	public function getShippingDays($items)
	{
		$shipping_days = array();
		foreach ($items as $item) {
			$days = ($item->shipping_days > 0) ? $item->shipping_days : 1;
			$shipping_days[$days][] = $item;
		}

		ksort($shipping_days);
		return $shipping_days;
	}

	public function setShippingDays($shipping_days)
	{
		if ($shipping_days > 0)
			$this->shipping_days = $shipping_days;
	}

	// Return array with all items in cart ids
	private function getItemsIds()
	{
		$items = self::getItems();
		$ids = array();

		if (count($items)) {
			foreach ($items as $item)
				$ids[] = $item->id;
		}


		return $ids;
	}


	// Get related items by Items Model
	private function fetchRelatedAccessories($ids)
	{
		if (!count($ids)) return array();

		$db = Factory::getDbo();
		$query = $db->getQuery(true);

		$query->select($db->quoteName('related_item'))
			->from($db->quoteName('#__djc2_items_related_accessories'))
			->where($db->quoteName('item_id') . ' IN (' . implode(',', $ids) . ')');

		$params = Djcatalog2Helper::getParams();
		$accessories_limit = $params->get('cart_related_accessories_limit', 0);

		if (intval($accessories_limit))
			$query->setLimit($accessories_limit);

		$db->setQuery($query);

		$results = $db->loadColumn();

		if (empty($results)) return array();

		BaseDatabaseModel::addIncludePath(JPATH_BASE . '/components/com_djcatalog2/models', 'DJCatalog2Model');
		$model = BaseDatabaseModel::getInstance('Items', 'Djcatalog2Model', array('ignore_request' => true));

		$ids = array_unique($results);
		$model->setState('filter.item_ids', $ids);
		return $model->getItems();
	}


	public function getRelatedAccessoriess()
	{
		if (empty($this->related_accessories)) {
			$items_ids = $this->getItemsIds();
			$this->related_accessories = $this->fetchRelatedAccessories($items_ids);
		}

		return $this->related_accessories;
	}

	public function validateStock($item, $quantity)
	{
		$juser = Factory::getUser();
		$params = Djcatalog2Helper::getParams();
		$salesman = $juser->authorise('djcatalog2.salesman', 'com_djcatalog2') || $juser->authorise('core.admin', 'com_djcatalog2');

		if ($salesman || $params->get('cart_query_enabled', 1)) {
			return true;
		}

		if (!$item->onstock || $quantity == 0.0000) {
			return false;
		} else if ($item->product_type == 'tangible' || $item->product_type == 'hybrid') {
			if ($quantity > $item->stock && $item->onstock < 2) {
				return false;
			}
		} else if ($item->product_type == 'bundle') {
			$bundleStock = $this->getBundleStock($item);
			if ($quantity > $bundleStock && $item->onstock < 2) {
				return false;
			}
		}

		return true;
	}

	protected function setCustomItems($items)
	{
		foreach ($items as $sid => $item) {
			$this->items[$sid] = $item;
			$this->quantities[$sid] = $item->_quantity;
		}
	}

	public function addCustomItem($item, $quantity = 1, $hashSuffix = null)
	{
		$app = Factory::getApplication();

		$sid = static::getSid($item->id, 0, [], [], [], [], $hashSuffix);

		$dispatcher = Joomla\CMS\Factory::getApplication()->getDispatcher();
		Joomla\CMS\Factory::getApplication()->triggerEvent('onDJC2CartAddItem', array(&$item, $sid));

		$custom_items = (array)$app->getUserState('com_djcatalog2.cart.custom_items', array());

		$item->_sid = $sid;
		$item->_quantity = $quantity;
		$this->items[$sid] = $item;
		$this->quantities[$sid] = $quantity;

		$custom_items[$sid] = $item;

		$app->setUserState('com_djcatalog2.cart.custom_items', $custom_items);

		$this->recalculate();
		$this->saveToStorage();
	}

	public function removeCustomItem($item)
	{
	}

	public function getBundleItems($item)
	{
		$params = ComponentHelper::getParams('com_djcatalog2');

		if (!empty($item->bundle_items)) {

			if (is_array($item->bundle_items)) {
				if (isset($item->bundle_items->prices)) {
					unset($item->bundle_items->prices);
				}
			}

			if (!is_array($item->bundle_items)) {
				$item->bundle_items = json_decode((string)$item->bundle_items, true);
				foreach ($item->bundle_items as $k => $v) {
					if ((float)$v['quantity'] == 0.0 || trim((string)$v['sku'] == '')) {
						unset($item->bundle_items[$k]);
						continue;
					}
				}
				$item->bundle_items = array_values($item->bundle_items);
			}
		} else {
			return false;
		}

		// sum of prices of all items in the bundle
		$bundle_total = 0.0;

		foreach ($item->bundle_items as $k => $v) {
			$bundle_item = $this->getItemBySKU($v['sku']);
			if (!empty($bundle_item)) {
				if (trim((string)$v['price']) != '' && $v['price'] > 0.0) {
					$bundle_item->final_price = $v['price'];
				}
				$bundle_total += (float)$bundle_item->final_price * (float)$v['quantity'];

				//$prices = Djcatalog2HelperPrice::getCartPrices($bundle_item->final_price, $bundle_item->final_price, $bundle_item->tax_rule_id, false,  $v['quantity'], $params);
				//$bundle_item->prices = $prices;

				$item->bundle_items[$k]['item'] = $bundle_item;
			} else {
				unset($item->bundle_items[$k]);
			}
		}

		$item->bundle_items = array_values($item->bundle_items);

		// another pass for price calculation
		foreach ($item->bundle_items as $k => $v) {
			$old_price = $v['item']->final_price;

			// new price, according to bundle's final price (usually discounted)
			if (trim((string)$v['price']) == '' || $v['price'] == 0.0) {
				// price share of the calculated total price of the bundle
				$price_share = round($old_price / $bundle_total, 4);

				$item->bundle_items[$k]['item']->final_price = round($price_share * $item->final_price, 4);
			}

			$finalPrice = $item->bundle_items[$k]['item']->final_price;
			if ($this->coupon) {
				$finalPrice = $this->coupon->getPrice($finalPrice, $item->id, $v['quantity']);
			}

			//$prices = Djcatalog2HelperPrice::getCartPrices($item->bundle_items[$k]['item']->final_price, $old_price, $v['item']->tax_rule_id, false,  $v['quantity'], $params);
			$prices = Djcatalog2HelperPrice::getCartPrices($finalPrice, $old_price, $v['item']->tax_rule_id, false, $v['quantity'], $params);
			$item->bundle_items[$k]['item']->prices = $prices;
		}

		//Factory::getApplication()->enqueueMessage('<pre>' . print_r($item->bundle_items, true) .'</pre>');

		if (count($item->bundle_items) < 1) return false;

		return $item->bundle_items;
	}

	public function getBundleStock($item)
	{
		$bundle_items = $this->getBundleItems($item);
		if ($bundle_items == false) return 0;

		$bundle_stock = $item->stock;
		foreach ($item->bundle_items as $k => $v) {

			// ignore always on stock in specific bundle products
			if ($v['onstock'] == 3 || ($v['onstock'] == 2 && $v['combination_id'] == 0)) {
				continue;
			}

			$stock = round(($v['stock'] / $v['quantity']), 2);
			$bundle_stock = min($stock, $bundle_stock);
		}

		return $bundle_stock < 0 ? 0 : (float)$bundle_stock;
	}

	public function getItemBySKU($sku)
	{
		BaseDatabaseModel::addIncludePath(Path::clean(JPATH_ROOT . '/components/com_djcatalog2/models'), 'DJCatalog2Model');
		$itemModel = BaseDatabaseModel::getInstance('Item', 'DJCatalog2Model', array('ignore_request' => true));

		$db = Factory::getDbo();
		$query = $db->getQuery(true);
		$query->select('*');
		$query->from('#__djc2_items_combinations');
		$query->where('sku = ' . $db->quote($sku));
		$db->setQuery($query, 0, 1);
		$resultObj = $db->loadObject();

		$combination_id = (empty($resultObj)) ? 0 : $resultObj->id;

		$query = $db->getQuery(true);
		if ($combination_id > 0) {
			$item_id = $resultObj->item_id;
		} else {
			$query->select('id');
			$query->from('#__djc2_items');
			$query->where('sku = ' . $db->quote($sku));
			$db->setQuery($query, 0, 1);
			$item_id = $db->loadResult();
		}

		if (!$item_id) return false;

		$item = $itemModel->getItem($item_id);

		if (empty($item)) return false;

		$combination = null;
		if ($combination_id > 0) {
			if ($combination = $itemModel->getCombination($combination_id)) {
				$item->_combination = $combination;
			}
		}


		$unit = DJCatalog2HelperQuantity::getUnit($item->unit_id);
		$item->_unit = $unit->unit;

		$return = [
			'id' => $item->id,
			'combination_id' => (!empty($item->_combination)) ? $item->_combination->id : 0,
			'sku' => $sku,
			'final_price' => (!empty($item->_combination) && $item->_combination->price > 0.0) ? $item->_combination->price : $item->final_price,
			'name' => $item->name,
			'unit' => $item->_unit,
			'tax_rule_id' => $item->tax_rule_id,
			'stock' => (!empty($item->_combination)) ? $item->_combination->stock : $item->stock,
			'onstock' => $item->onstock,
			'product_type' => $item->product_type,
			'_combination' => $combination
		];

		$return = (object)($return);

		return $return;

	}

	public function getError($i = null, $toString = true)
	{
		// Find the error
		if ($i === null) {
			// Default, return the last message
			$error = end($this->_errors);
		} elseif (!array_key_exists($i, $this->_errors)) {
			// If $i has been specified but does not exist, return false
			return false;
		} else {
			$error = $this->_errors[$i];
		}

		// Check if only the string is requested
		if ($error instanceof Exception && $toString) {
			return $error->getMessage();
		}

		return $error;
	}

	public function getErrors()
	{
		return $this->_errors;
	}

	public function setError($error)
	{
		$this->_errors[] = $error;
	}

	private function applyCalculator(&$item)
	{
		if (isset($this->calculators[$item->_sid]) && !empty($this->calculators[$item->_sid])) {
			$calculatorData = $this->calculators[$item->_sid];
			$calculator = CalculatorHelper::getInstance($item->calculator_id);
			$calculatorData += [
				'price' => $item->final_price
			];

			$item->final_price = $calculator->applyPattern($item, $calculatorData);
			$item->price = $calculator->applyPattern($item, $calculatorData);
		}
	}

}

Anon7 - 2022
AnonSec Team