<?php

/**
  * Products class, Product.php
  * Products management
  * @category classes
  *
  * @author PrestaShop <support@prestashop.com>
  * @copyright PrestaShop
  * @license http://www.opensource.org/licenses/osl-3.0.php Open-source licence 3.0
  * @version 1.0
  *
  */

class		Product extends ObjectModel
{
	/** @var integer Tax id */
	public		$id_tax;

	/** @var string Tax name */
	public		$tax_name;

	/** @var string Tax rate */
	public		$tax_rate;

	/** @var integer Manufacturer id */
	public		$id_manufacturer;

	/** @var integer Supplier id */
	public		$id_supplier;

	/** @var integer default Category id */
	public 		$id_category_default;

	/** @var integer default Attribute id if color picker is enabled */
	public 		$id_color_default;

	/** @var string Manufacturer name */
	public		$manufacturer_name;

	/** @var string Supplier name */
	public		$supplier_name;

	/** @var string Name */
	public 		$name;

	/** @var string Long description */
	public 		$description;

	/** @var string Short description */
	public 		$description_short;

	/** @var integer Quantity available */
	public 		$quantity = 0;

	/** @var string availability */
	public 		$availability;

	/** @var float Price in euros */
	public 		$price = 0;

	/** @var float Wholesale Price in euros */
	public 		$wholesale_price = 0;

	/** @var float Reduction price in euros */
	public 		$reduction_price = 0;

	/** @var float Reduction percentile */
	public 		$reduction_percent = 0;

	/** @var string Reduction beginning */
	public		$reduction_from = '1942-01-01';

	/** @var string Reduction end */
	public		$reduction_to = '1942-01-01';

	/** @var boolean on_sale */
	public 		$on_sale = false;

	/** @var float Ecotax */
	public		$ecotax = 0;

	/** @var string Reference */
	public 		$reference;
	
	/** @var string Supplier Reference */
	public 		$supplier_reference;	

	/** @var string Weight in default weight unit */
	public 		$weight = 0;

	/** @var string Ean-13 barcode */
	public 		$ean13;

	/** @var string Friendly URL */
	public 		$link_rewrite;

	/** @var string Meta tag description */
	public 		$meta_description;

	/** @var string Meta tag keywords */
	public 		$meta_keywords;

	/** @var string Meta tag title */
	public 		$meta_title;

	/*** @var integer Out of stock behavior */
	public		$out_of_stock = 2;

	/** @var boolean Product statuts */
	public		$quantity_discount = 0;

	/** @var boolean Product statuts */
	public		$active = 1;

	/** @var string Object creation date */
	public 		$date_add;

	/** @var string Object last modification date */
	public 		$date_upd;

	/*** @var array Tags */
	public		$tags;

	/** @var array tables */
	protected $tables = array ('product', 'product_lang');

	protected $fieldsRequired = array('id_tax', 'quantity', 'price');
	protected $fieldsSize = array('reference' => 32, 'supplier_reference' => 32, 'ean13' => 13);
	protected $fieldsValidate = array(
		'id_tax' => 'isUnsignedId',
		'id_manufacturer' => 'isUnsignedId',
		'id_supplier' => 'isUnsignedId',
		'id_category_default' => 'isUnsignedId',
		'id_color_default' => 'isUnsignedId',
		'quantity' => 'isUnsignedInt',
		'price' => 'isPrice',
		'wholesale_price' => 'isPrice',
		'reduction_price' => 'isPrice',
		'reduction_percent' => 'isFloat',
		'reduction_from' => 'isDate',
		'reduction_to' => 'isDate',
		'on_sale' => 'isBool',
		'ecotax' => 'isPrice',
		'reference' => 'isReference',
    	'supplier_reference' => 'isReference',		
		'weight' => 'isFloat',
		'out_of_stock' => 'isUnsignedInt',
		'quantity_discount' => 'isBool',
		'active' => 'isBool',
		'ean13' => 'isEan13'
	);
	protected $fieldsRequiredLang = array('link_rewrite', 'name');
	/* Description short is limited to 400 chars, but without html, so it can't be generic */
	protected $fieldsSizeLang = array('meta_description' => 255, 'meta_keywords' => 255,
		'meta_title' => 128, 'link_rewrite' => 128, 'name' => 128, 'availability' => 255);
	protected $fieldsValidateLang = array(
		'meta_description' => 'isGenericName', 'meta_keywords' => 'isGenericName',
		'meta_title' => 'isGenericName', 'link_rewrite' => 'isLinkRewrite', 'name' => 'isCatalogName',
		'description' => 'isCleanHtml', 'description_short' => 'isCleanHtml', 'availability' => 'isGenericName');

	protected 	$table = 'product';
	protected 	$identifier = 'id_product';

	public	function __construct($id_product = NULL, $full = false, $id_lang = NULL)
	{
		parent::__construct($id_product, $id_lang);
		if ($full AND $this->id)
		{
			$this->manufacturer_name = Manufacturer::getNameById(intval($this->id_manufacturer));
			$this->supplier_name = Supplier::getNameById(intval($this->id_supplier));
			$tax = new Tax(intval($this->id_tax), intval($id_lang));
			$this->tax_name = $tax->name;
			$this->tax_rate = floatval($tax->rate);
		}
		$this->tags = Tag::getProductTags($this->id);
	}

	public function getFields()
	{
		parent::validateFields();

		$fields['id_tax'] = intval($this->id_tax);
		$fields['id_manufacturer'] = intval($this->id_manufacturer);
		$fields['id_supplier'] = intval($this->id_supplier);
		$fields['id_category_default'] = intval($this->id_category_default);
		$fields['id_color_default'] = intval($this->id_color_default);
		$fields['quantity'] = intval($this->quantity);
		$fields['price'] = floatval($this->price);
		$fields['wholesale_price'] = floatval($this->wholesale_price);
		$fields['reduction_price'] = floatval($this->reduction_price);
		$fields['reduction_percent'] = floatval($this->reduction_percent);
		$fields['reduction_from'] = pSQL($this->reduction_from);
		$fields['reduction_to'] = pSQL($this->reduction_to);
		$fields['on_sale'] = intval($this->on_sale);
		$fields['ecotax'] = floatval($this->ecotax);
		$fields['reference'] = pSQL($this->reference);
		$fields['supplier_reference'] = pSQL($this->supplier_reference);		
		$fields['weight'] = floatval($this->weight);
		$fields['out_of_stock'] = pSQL($this->out_of_stock);
		$fields['quantity_discount'] = intval($this->quantity_discount);
		$fields['active'] = intval($this->active);
		$fields['ean13'] = pSQL($this->ean13);
		$fields['date_add'] = pSQL($this->date_add);
		$fields['date_upd'] = pSQL($this->date_upd);

		return $fields;
	}

	/**
	* Check then return multilingual fields for database interaction
	*
	* @return array Multilingual fields
	*/
	public function getTranslationsFieldsChild()
	{
		self::validateFieldsLang();

		$fieldsArray = array('meta_description', 'meta_keywords', 'meta_title', 'link_rewrite', 'name', 'availability');
		$fields = array();
		$languages = Language::getLanguages();
		$defaultLanguage = Configuration::get('PS_LANG_DEFAULT');
		foreach ($languages as $language)
		{
			$fields[$language['id_lang']]['id_lang'] = $language['id_lang'];
			$fields[$language['id_lang']][$this->identifier] = intval($this->id);
			$fields[$language['id_lang']]['description'] = (isset($this->description[$language['id_lang']])) ? pSQL($this->description[$language['id_lang']], true) : '';
			$fields[$language['id_lang']]['description_short'] = (isset($this->description_short[$language['id_lang']])) ? pSQL($this->description_short[$language['id_lang']], true) : '';
			foreach ($fieldsArray as $field)
			{
				if (!Validate::isTableOrIdentifier($field))
					die(Tools::displayError());

				/* Check fields validity */
				if (isset($this->{$field}[$language['id_lang']]) AND !empty($this->{$field}[$language['id_lang']]))
					$fields[$language['id_lang']][$field] = pSQL($this->{$field}[$language['id_lang']]);
				elseif (in_array($field, $this->fieldsRequiredLang))
					$fields[$language['id_lang']][$field] = pSQL($this->{$field}[$defaultLanguage]);
				else
					$fields[$language['id_lang']][$field] = '';
			}
		}
		return $fields;
	}

	/**
	 * Move a product inside its category
	 * @param boolean $way Up (1)  or Down (0)
	 * return boolean Update result
	 */
	public function updatePosition($way)
	{
		if (!$res = Db::getInstance()->ExecuteS('SELECT cp.`id_product`, cp.`position`, cp.`id_category` FROM `'._DB_PREFIX_.'category_product` cp
												 WHERE cp.`id_category` = '.pSQL(Tools::getValue('id_category'))
												  .' ORDER BY cp.`position` '.(intval($way) ? 'ASC' : 'DESC')))
			return false;
		foreach ($res AS $key => $values)
			if (intval($values[$this->identifier]) == intval($this->id))
			{
				$k = $key ;
				break ;
			}
		if (!isset($k))
			return false;
		$from = $res[$k];
		$to = $res[$k + 1];
		return (Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'category_product`
			SET `position`='.intval($to['position']).'
			WHERE `'.pSQL($this->identifier).'` = '.intval($from[$this->identifier]).' AND `id_category`='.intval($from['id_category']))
			AND
			Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'category_product`
			SET `position`='.intval($from['position']).'
			WHERE `'.pSQL($this->identifier).'` = '.intval($to[$this->identifier]).' AND `id_category`='.intval($to['id_category']))
			);
	}

	/**
	* Get the default attribute for a product
	*
	* @return array Attributes list
	*/
	static public function getDefaultAttribute($id_product)
	{

		$sql = 'SELECT `id_product_attribute`
		FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `default_on` = 1 AND `quantity` > 0 AND `id_product` = '.intval($id_product);
		$result = Db::getInstance()->getRow($sql);
		if (!$result)
			$result = Db::getInstance()->getRow('
			SELECT `id_product_attribute`
			FROM `'._DB_PREFIX_.'product_attribute`
			WHERE `quantity` > 0  AND `id_product` = '.intval($id_product));
		return $result['id_product_attribute'];
	}

	public function validateFieldsLang($die = true)
	{
		foreach ($this->description_short as $k => $value)
			if (Tools::strlen(strip_tags($value)) > 400)
			{
				if ($die) die (Tools::displayError().' ('.get_class($this).'->description: length > 400 for language '.$k.')');
				return false;
			}
		return parent::validateFieldsLang($die);
	}

	public function delete()
	{
		Hook::deleteProduct($this);
		parent::delete();
		$this->deleteCategories();
		$this->deleteImages();
		$this->deleteProductAttributes();
		$this->deleteProductFeatures();
		$this->deleteTags();
		$this->deleteCartProducts();
        $this->deleteAttributesImpacts();
		if ($id = ProductDownload::getIdFromIdProduct($this->id))
			if ($productDownload = new ProductDownload($id))
				$productDownload->delete(true);
		return true;
	}

	public function deleteSelection($products)
	{
		$return = true;
		foreach ($products AS $id_product)
		{
			$product = new Product(intval($id_product));
			$return &= $product->delete();
		}
		return $return;
	}


	public static function getByReference($reference)
	{
		if (!Validate::isReference($reference))
			die(Tools::displayError());

		$result = Db::getInstance()->getRow('
		SELECT `id_product`
		FROM `'._DB_PREFIX_.'product` p
		WHERE p.`reference` = \''.pSQL($reference).'\'');
		if (!isset($result['id_product']))
			return false;

		return new self($result['id_product']);
	}

	/**
	* Update categories to index product into
	*
	* @param string $productCategories Categories list to index product into
	* @return array Update/insertion result
	*/
	public function updateCategories($categories, $keepingCurrentPos = false)
	{
		$positions = array();
		$result = Db::getInstance()->ExecuteS('SELECT IFNULL(MAX(`position`), 0) + 1 AS max, `id_category`
				FROM `'._DB_PREFIX_.'category_product`
				WHERE `id_category` IN('.implode(',', $categories).')
				GROUP BY `id_category`
			');
		if (!is_array($result))
			return (false);
		foreach ($result AS $position)
			$positions[$position['id_category']] = $position;
		/* Product Update, so saving current positions */
		if ($keepingCurrentPos)
		{
			if (!is_array($oldPositions = Db::getInstance()->ExecuteS('SELECT `id_category`, `id_product`, `position` AS max FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.intval($this->id))))
				return false;
			foreach ($oldPositions AS $position)
				$positions[$position['id_category']] = $position;
		}
		$this->deleteCategories();
        $productCats = array();
		foreach ($categories AS &$categorie)
			$categorie = intval($categorie);
		foreach ($categories AS $k => $productCategory)
			$productCats[] = '('.$productCategory.','.$this->id.','.(isset($positions[$productCategory]) ? $positions[$productCategory]['max'] : 0).')';

		$result = Db::getInstance()->Execute('
		INSERT INTO `'._DB_PREFIX_.'category_product` (`id_category`, `id_product`, `position`)
		VALUES '.implode(',', $productCats));

		return ($result);
	}

	/**
	* Delete categories where product is indexed
	*
	* @return array Deletion result
	*/
	public function deleteCategories()
	{
		return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.intval($this->id));
	}

	/**
	* Delete products tags entries
	*
	* @return array Deletion result
	*/
	public function deleteTags()
	{
		return (Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'product_tag` WHERE `id_product` = '.intval($this->id))
		AND Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'tag` WHERE `id_tag` NOT IN (SELECT `id_tag` FROM `'._DB_PREFIX_.'product_tag`)'));
	}

	/**
	* Delete product from cart
	*
	* @return array Deletion result
	*/
	public function deleteCartProducts()
	{
		return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'cart_product` WHERE `id_product` = '.intval($this->id));
	}

	/**
	* Delete product images from database
	*
	* @return array Deletion result
	*/
	public function deleteImages()
	{
		$result = Db::getInstance()->ExecuteS('
		SELECT `id_image`
		FROM `'._DB_PREFIX_.'image`
		WHERE `id_product` = '.intval($this->id));
		foreach($result as $row)
		{
			deleteImage(intval($this->id), $row['id_image']);
			Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'image_lang` WHERE `id_image` = '.intval($row['id_image']));
		}
		Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'image` WHERE `id_product` = '.intval($this->id));
	}

	static public function getProductAttribute($id_product_attribute)
	{
		$rq = Db::getInstance()->getRow('
		SELECT `price`
		FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `id_product_attribute` = '.intval($id_product_attribute));
		return $rq['price'];
	}

	/**
	* Get all available products
	*
	* @param integer $id_lang Language id
	* @param integer $start Start number
	* @param integer $limit Number of products to return
	* @param string $orderBy Field for ordering
	* @param string $orderWay Way for ordering (ASC or DESC)
	* @return array Products details
	*/
	static public function getProducts($id_lang, $start, $limit, $orderBy, $orderWay, $id_category = false)
	{
		if (!Validate::isOrderBy($orderBy) OR !Validate::isOrderWay($orderWay))
			die (Tools::displayError());
		if ($orderBy == 'id_product' OR	$orderBy == 'price' OR	$orderBy == 'date_add')
			$orderByPrefix = 'p';
		elseif ($orderBy == 'name')
			$orderByPrefix = 'pl';
		elseif ($orderBy == 'position')
			$orderByPrefix = 'c';

		$rq = Db::getInstance()->ExecuteS('
		SELECT p.*, pl.* , t.`rate` AS tax_rate, m.`name` AS manufacturer_name, s.`name` AS supplier_name
		FROM `'._DB_PREFIX_.'product` p
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product`)
		LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = p.`id_tax`)
		LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
		LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)'.
		($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '').'
		WHERE pl.`id_lang` = '.intval($id_lang).
		($id_category ? ' AND c.`id_category` = '.$id_category : '').'
		ORDER BY '.(isset($orderByPrefix) ? $orderByPrefix.'.' : '').'`'.pSQL($orderBy).'` '.pSQL($orderWay).
		($limit > 0 ? ' LIMIT '.intval($start).','.intval($limit) : '')
		);
		if($orderBy == 'price')
		{
			Tools::orderbyPrice($rq,$orderWay);
		}

		return ($rq);
	}


	static public function getSimpleProducts($id_lang)
	{
		return Db::getInstance()->ExecuteS('
		SELECT p.`id_product`, pl.`name`
		FROM `'._DB_PREFIX_.'product` p
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product`)
		WHERE pl.`id_lang` = '.intval($id_lang).'
		ORDER BY pl.`name`');
	}

	/**
	  * Return the products in the same category than the default category of the instancied product
	  *
	  * @param integer $id_lang Language ID
	  * @return array Products
	  */
	public function getDefaultCategoryProducts($idLang = NULL, $limit = NULL)
	{
		//get idLang
		$idLang = is_null($idLang) ? _USER_ID_LANG_ : intval($idLang);

		$result = Db::getInstance()->ExecuteS('
		SELECT p.`id_product`, pl.`description_short`, pl.`link_rewrite`, pl.`name`, i.`id_image`
		FROM `'._DB_PREFIX_.'category_product` cp
		LEFT JOIN `'._DB_PREFIX_.'product` p ON (p.id_product = cp.id_product)
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = p.`id_product`)
		LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)
		WHERE cp.id_category = ' . $this->id_category_default . '
		AND id_lang = ' . $idLang . '
		AND p.`active` = 1
		AND i.`cover` = 1
		'. (is_null($limit) ? '' : ' LIMIT 0 , ' . $limit));
		return $result;
	}

	public function productAttributeExists($attributesList, $currentProductAttribute = false)
	{
		$result = Db::getInstance()->ExecuteS('SELECT pac.`id_attribute`, pac.`id_product_attribute`
		FROM `'._DB_PREFIX_.'product_attribute` pa
		LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_product_attribute` = pa.`id_product_attribute`)
		WHERE pa.`id_product` = '.intval($this->id));
		/* If something's wrong */
		if (!$result OR empty($result))
			return false;
		/* Product attributes simulation */
		$productAttributes = array();
		foreach ($result AS $productAttribute)
			$productAttributes[$productAttribute['id_product_attribute']][] = $productAttribute['id_attribute'];
		/* Checking product's attribute existence */
		foreach ($productAttributes AS $key => $productAttribute)
			if (sizeof($productAttribute) == sizeof($attributesList))
			{
				$diff = false;
				for ($i = 0; $diff == false AND isset($productAttribute[$i]); $i++)
					if (!in_array($productAttribute[$i], $attributesList) OR $key == $currentProductAttribute)
						$diff = true;
				if (!$diff)
					return true;
			}
		return false;
	}

	/**
	* Add a product attribute
	*
	* @param float $price Additional price
	* @param float $weight Additional weight
	* @param float $ecotax Additional ecotax
	* @param integer $quantity Quantity available
	* @param integer $id_image Image id
	* @param string $reference Reference
	* @param string $supplier_reference Supplier Reference 	
	* @param string $ean13 Ean-13 barcode
	* @param boolean $default Is default attribute for product
	* @return array Insertion result
	*/
	public function addProductAttribute($price, $weight, $ecotax, $quantity, $id_image, $reference, $supplier_reference, $ean13, $default)
	{
		$price = str_replace(',', '.', $price);
		$weight = str_replace(',', '.', $weight);
		Db::getInstance()->AutoExecute(_DB_PREFIX_.'product_attribute',
		array('id_product' => intval($this->id), 'price' => floatval($price), 'ecotax' => floatval($ecotax), 'quantity' => intval($quantity),
		'id_image' => intval($id_image), 'weight' => ($weight ? floatval($weight) : 0), 'reference' => pSQL($reference), 'supplier_reference' => pSQL($supplier_reference), 'ean13' => pSQL($ean13), 'default_on' => intval($default)),
		'INSERT');
		return Db::getInstance()->Insert_ID();
	}

	public function addProductAttributeMultiple($attributes, $setDefault = true)
	{
		$values = array();
		$keys = array();
		$fields = array();
		$default_value = 1;
		foreach ($attributes AS &$attribute)
			foreach ($attribute AS $key => $value)
				if ($value != "")
					$fields[$key] = $key;

		foreach ($attributes AS &$attribute)
		{
			$k = array();
			$v = array();
			foreach ($attribute AS $key => $value)
			{
				if (in_array($key, $fields))
				{
					$k[] = '`'.$key.'`';
					$v[] = '\''.$value.'\'';
				}
			}
			if ($setDefault)
			{
				$k[] = '`default_on`';
				$v[] = '\''.$default_value.'\'';
				$default_value = 0;
			}
			$values[] = '(' . implode(', ', $v).')';
			$keys[] = implode(', ', $k);
		}
		Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'product_attribute` ('. $keys[0].') VALUES '.implode(', ', $values));

		return (array_map(create_function('$elem', 'return $elem[\'id_product_attribute\'];'),
			Db::getInstance()->ExecuteS('
			SELECT `id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` WHERE `id_product_attribute` >= '.intval(Db::getInstance()->Insert_ID())
			)));
	}

	/**
	* Del all default attributes for product
	*/
	public function deleteDefaultAttributes()
	{
		return Db::getInstance()->Execute('
		UPDATE `'._DB_PREFIX_.'product_attribute`
		SET `default_on` = 0
		WHERE `id_product` = '.intval($this->id));
	}

	/**
	* Update a product attribute
	*
	* @param integer $id_product_attribute Product attribute id
	* @param float $price Additional price
	* @param float $weight Additional weight
	* @param float $ecotax Additional ecotax
	* @param integer $quantity Quantity available
	* @param integer $id_image Image id
	* @param string $reference Reference
	* @param string $ean13 Ean-13 barcode
	* @return array Update result
	*/
	public function updateProductAttribute($id_product_attribute, $price, $weight, $ecotax, $quantity, $id_image, $reference, $supplier_reference, $ean13, $default)
	{
		Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'product_attribute_combination`
		WHERE `id_product_attribute` = '.intval($id_product_attribute));

		$price = str_replace(',', '.', $price);
		$weight = str_replace(',', '.', $weight);
		return Db::getInstance()->AutoExecute(_DB_PREFIX_.'product_attribute',
		array('price' => floatval($price), 'ecotax' => floatval($ecotax), 'quantity' => intval($quantity),
		'id_image' => intval($id_image), 'weight' => ($weight ? floatval($weight) : 0), 'reference' => pSQL($reference),  'supplier_reference' => pSQL($supplier_reference), 'ean13' => pSQL($ean13), 'default_on' => intval($default)),
		'UPDATE', '`id_product_attribute` = '.intval($id_product_attribute));
	}

	/**
	* Delete product attributes
	*
	* @return array Deletion result
	*/
	public function deleteProductAttributes()
	{
		$result = Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'product_attribute_combination`
		WHERE `id_product_attribute` IN (SELECT `id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` WHERE `id_product` = '.intval($this->id).')');

		$result2 = Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `id_product` = '.intval($this->id));

		return ($result & $result2);
	}

	/**
	* Delete product attributes impacts
	*
	* @return Deletion result
	*/
    public function deleteAttributesImpacts()
    {
        return Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'attribute_impact`
		WHERE `id_product` = '.intval($this->id));
    }

	/**
	* Delete product features
	*
	* @return array Deletion result
	*/
	public function deleteProductFeatures()
	{
		return $this->deleteFeatures();
	}

	/**
	* Add a product attributes combinaison
	*
	* @param integer $id_product_attribute Product attribute id
	* @param array $attributes Attributes to forge combinaison
	* @return array Insertion result
	*/
	public function addAttributeCombinaison($id_product_attribute, $attributes)
	{
		if (!is_array($attributes))
			die(Tools::displayError());
		if (!sizeof($attributes))
			return false;
		$attributesList = '';
		foreach($attributes AS $id_attribute)
			$attributesList .= '('.intval($id_product_attribute).','.intval($id_attribute).'),';
		$attributesList = rtrim($attributesList, ',');

		if (!Validate::isValuesList($attributesList))
			die(Tools::displayError());

		$result = Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'product_attribute_combination` (`id_product_attribute`, `id_attribute`) VALUES '.$attributesList);
		return $result;
	}

	public function addAttributeCombinationMultiple($id_attributes, $combinations)
	{
		$attributesList = '';
		foreach ($id_attributes AS $nb => $id_product_attribute)
			if (isset($combinations[$nb]))
				foreach ($combinations[$nb] AS $id_attribute)
					$attributesList .= '('.intval($id_product_attribute).','.intval($id_attribute).'),';
		$attributesList = rtrim($attributesList, ',');
		return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'product_attribute_combination` (`id_product_attribute`, `id_attribute`) VALUES '.$attributesList);
	}

	/**
	* Delete a product attributes combinaison
	*
	* @param integer $id_product_attribute Product attribute id
	* @return array Deletion result
	*/
	public function deleteAttributeCombinaison($id_product_attribute)
	{
		if (!$id_product_attribute OR !is_numeric($id_product_attribute))
			return false;

		$result = Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `id_product_attribute` = '.intval($id_product_attribute).'
		AND `id_product` = '.intval($this->id));
		$result2 = Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'product_attribute_combination`
		WHERE `id_product_attribute` = '.intval($id_product_attribute));
		return ($result & $result2);
	}

	/**
	* Delete features
	*
	*/
	public function deleteFeatures()
	{
		// List products features
		$result1 = Db::getInstance()->ExecuteS('
		SELECT p.*, f.*
		FROM `'._DB_PREFIX_.'feature_product` as p
		LEFT JOIN `'._DB_PREFIX_.'feature_value` as f ON (f.`id_feature_value` = p.`id_feature_value`)
		WHERE `id_product` = '.intval($this->id));
		foreach ($result1 as $tab)
			// Delete product custom features
			if ($tab['custom']) {
				$result2 = Db::getInstance()->Execute('
				DELETE FROM `'._DB_PREFIX_.'feature_value`
				WHERE `id_feature_value` = '.$tab['id_feature_value']);
				$result3 = Db::getInstance()->Execute('
				DELETE FROM `'._DB_PREFIX_.'feature_value_lang`
				WHERE `id_feature_value` = '.$tab['id_feature_value']);
			}
		// Delete product features
		$result4 = Db::getInstance()->Execute('
		DELETE FROM `'._DB_PREFIX_.'feature_product`
		WHERE `id_product` = '.intval($this->id));
		return ($result4);
	}

	/**
	* Get all available product attributes combinaisons
	*
	* @param integer $id_lang Language id
	* @return array Product attributes combinaisons
	*/
	public function getAttributeCombinaisons($id_lang)
	{
		return Db::getInstance()->ExecuteS('
		SELECT pa.*, ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, al.`name` AS attribute_name, a.`id_attribute`
		FROM `'._DB_PREFIX_.'product_attribute` pa
		LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
		LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute`
		LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group`
		LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.intval($id_lang).')
		WHERE pa.`id_product` = '.intval($this->id).'
		ORDER BY pa.`id_product_attribute`');
	}

	/**
	* Check if product has attributes combinaisons
	*
	* @return integer Attributes combinaisons number
	*/
	public function hasAttributes()
	{
		$result = Db::getInstance()->getRow('
		SELECT COUNT(`id_product_attribute`) AS nb
		FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `id_product` = '.intval($this->id));
		return $result['nb'];
	}

	/**
	* Get new products
	*
	* @param integer $id_lang Language id
	* @param integer $pageNumber Start from (optional)
	* @param integer $nbProducts Number of products to return (optional)
	* @return array New products
	*/
	static public function getNewProducts($id_lang, $pageNumber=0, $nbProducts=10, $count=false, $orderBy=NULL, $orderWay=NULL)
	{
		global $link, $cookie;

		if ($pageNumber < 0) $pageNumber = 0;
		if ($nbProducts < 1) $nbProducts = 10;
		if (empty($orderBy)) $orderBy = 'date_add';
		if (empty($orderWay)) $orderWay = 'DESC';
		if ($orderBy == 'id_product' OR $orderBy == 'price' OR $orderBy == 'date_add')
			$orderByPrefix = 'p';
		elseif ($orderBy == 'name')
            $orderByPrefix = 'pl';
		if (!Validate::isOrderBy($orderBy) OR !Validate::isOrderWay($orderWay))
			die (Tools::displayError());

		if ($count)
		{
			$result = Db::getInstance()->getRow('
			SELECT COUNT(`id_product`) AS nb
			FROM `'._DB_PREFIX_.'product`
			WHERE `active` = 1');
			return intval($result['nb']);
		}

		$result = Db::getInstance()->ExecuteS('
			SELECT p.*, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, p.`ean13`,
				i.`id_image`, il.`legend`, t.`rate`
			FROM `'._DB_PREFIX_.'product` p
			LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
			LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
			LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
			LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = p.`id_tax`
			WHERE p.`active` = 1
			ORDER BY '.(isset($orderByPrefix) ? $orderByPrefix.'.' : '').'`'.pSQL($orderBy).'` '.pSQL($orderWay).'
			LIMIT '.intval($pageNumber * $nbProducts).', '.intval($nbProducts));
			if($orderBy == 'price')
			{
				Tools::orderbyPrice($result,$orderWay);
			}
		if (!$result)
			return false;

		return Product::getProductsProperties($id_lang, $result);
	}

	/**
	* Get a random special
	*
	* @param integer $id_lang Language id
	* @return array Special
	*/
	static public function getRandomSpecial($id_lang, $beginning = false, $ending = false)
	{
		global	$link, $cookie;

		$currentDate = date('Y-m-d');
		$row = Db::getInstance()->getRow('
		SELECT p.*, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, p.`ean13`,
			i.`id_image`, il.`legend`, t.`rate`
		FROM `'._DB_PREFIX_.'product` p
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
		LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = p.`id_tax`
		WHERE (`reduction_price` > 0 OR `reduction_percent` > 0)
		'.((!$beginning AND !$ending) ?
			'AND (`reduction_from` = `reduction_to` OR (`reduction_from` <= \''.$currentDate.'\' AND `reduction_to` >= \''.$currentDate.'\'))'
		:
			($beginning ? 'AND `reduction_from` <= \''.$beginning.'\'' : '').($ending ? 'AND `reduction_to` >= \''.$ending.'\'' : '')).'
		AND p.`active` = 1
		ORDER BY RAND()');

		if ($row)
			return Product::getProductProperties($id_lang, $row);

		return $row;
	}

	/**
	* Get prices drop
	*
	* @param integer $id_lang Language id
	* @param integer $pageNumber Start from (optional)
	* @param integer $nbProducts Number of products to return (optional)
	* @param boolean $count Only in order to get total number (optional)
	* @return array Prices drop
	*/
	static public function getPricesDrop($id_lang, $pageNumber=0, $nbProducts=10, $count=false, $orderBy=NULL, $orderWay=NULL, $beginning = false, $ending = false)
	{
		global $link, $cookie;
		if (!Validate::isBool($count))
			die(Tools::displayError());

		if ($pageNumber < 0) $pageNumber = 0;
		if ($nbProducts < 1) $nbProducts = 10;
		if (empty($orderBy)) $orderBy = 'myprice';
		if (empty($orderWay)) $orderWay = 'DESC';
		if ($orderBy == 'id_product' OR $orderBy == 'price' OR $orderBy == 'date_add')
			$orderByPrefix = 'p';
		elseif ($orderBy == 'name')
            $orderByPrefix = 'pl';
		if (!Validate::isOrderBy($orderBy) OR !Validate::isOrderWay($orderWay))
			die (Tools::displayError());

		if ($count)
		{
			$result = Db::getInstance()->getRow('
			SELECT COUNT(`id_product`) AS nb
			FROM `'._DB_PREFIX_.'product`
			WHERE `reduction_price` > 0
			OR `reduction_percent` > 0
			AND `active` = 1');
			return intval($result['nb']);
		}
		$currentDate = date('Y-m-d');
		$result = Db::getInstance()->ExecuteS('
		SELECT p.*, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, p.`ean13`, i.`id_image`, il.`legend`, t.`rate`, (p.`reduction_price` + (p.`reduction_percent` * p.`price`)) AS myprice
		FROM `'._DB_PREFIX_.'product` p
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
		LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = p.`id_tax`
		WHERE (`reduction_price` > 0 OR `reduction_percent` > 0)
		'.((!$beginning AND !$ending) ?
			'AND (`reduction_from` = `reduction_to` OR (`reduction_from` <= \''.$currentDate.'\' AND `reduction_to` >= \''.$currentDate.'\'))'
		:
			($beginning ? 'AND `reduction_from` <= \''.$beginning.'\'' : '').($ending ? 'AND `reduction_to` >= \''.$ending.'\'' : '')).'
		AND p.`active` = 1
		ORDER BY '.(isset($orderByPrefix) ? $orderByPrefix.'.' : '').'`'.pSQL($orderBy).'`'.' '.pSQL($orderWay).'
		LIMIT '.intval($pageNumber * $nbProducts).', '.intval($nbProducts));
		if($orderBy == 'price')
		{
			Tools::orderbyPrice($result,$orderWay);
		}
		if (!$result)
			return false;
		return Product::getProductsProperties($id_lang, $result);
	}

	/**
	* Get categories where product is indexed
	*
	* @param integer $id_product Product id
	* @return array Categories where product is indexed
	*/
	static public function getIndexedCategories($id_product)
	{
		return Db::getInstance()->ExecuteS('
		SELECT `id_category`
		FROM `'._DB_PREFIX_.'category_product`
		WHERE `id_product` = '.intval($id_product));
	}

	/**
	* Get product images and legends
	*
	* @param integer $id_lang Language id for multilingual legends
	* @return array Product images and legends
	*/
	public function	getImages($id_lang)
	{
		return Db::getInstance()->ExecuteS('
		SELECT i.`cover`, i.`id_image`, il.`legend`
		FROM `'._DB_PREFIX_.'image` i
		LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
		WHERE i.`id_product` = '.intval($this->id).'
		ORDER BY `position`');
	}

	/**
	* Get product cover image
	*
	* @return array Product cover image
	*/
	public static function getCover($id_product)
	{
		return Db::getInstance()->getRow('
		SELECT `id_image`
		FROM `'._DB_PREFIX_.'image`
		WHERE `id_product` = '.intval($id_product).'
		AND `cover` = 1');
	}

	/**
	* Get reduction value for a given product
	*
	* @param array $result SQL result with reduction informations
	* @param boolean $wt With taxes or not (optional)
	* @return float Reduction value in euros
	*/
	public static function getReductionValue($result, $wt = true)
	{
		if (!is_array($result) OR !Validate::isBool($wt))
			die(Tools::displayError());


		// tax value
		$tax = floatval(Tax::getApplicableTax(intval($result['id_tax']), floatval($result['rate'])));

		// prices values
		$price = floatval($result['price']);
		$attribute_price = isset($result['attribute_price']) ? floatval($result['attribute_price']) : 0;
		$price_ht = $price + ($attribute_price / (1 + ($tax / 100)));
		if ($wt)
			$price = $price_ht * (1 + ($tax / 100));
		else
			$price = $price_ht;

		// reduction values
		$currentDate = date('Y-m-d');
		if ($result['reduction_from'] != $result['reduction_to'] AND ($currentDate > $result['reduction_to'] OR $currentDate < $result['reduction_from']))
			return 0;
		$reduction_price = floatval($result['reduction_price']);
		if (!$wt)
			$reduction_price /= (1 + ($tax / 100));
		$reduction_percent = floatval($result['reduction_percent']);
		$reductionValue = $price * $reduction_percent / 100;
		// make the reduction
		if ($reduction_price AND $reduction_price > 0)
		{
			if ($reduction_price >= $price)
				$ret = $price;
			else
				$ret = $reduction_price;
		}
		elseif ($reduction_percent AND $reduction_percent > 0)
		{
			if ($reduction_percent >= 100)
				$ret = $price;
			else
				$ret = $reductionValue ;
		}
		return isset($ret) ? number_format($ret, ($wt ? 2 : 4), '.', '') : 0;
	}

	/**
	* Get product price
	*
	* @param integer $id_product Product id
	* @param boolean $tax With taxes or not (optional)
	* @param integer $id_product_attribute Product attribute id (optional)
	* @param integer $decimals Number of decimals (optional)
	* @param integer $divisor Util when paying many time without fees (optional)
	* @return float Product price
	*/
	public static function getPriceStatic($id_product, $usetax = true, $id_product_attribute = NULL, $decimals = 6, $divisor = NULL, $only_reduc = false, $usereduc = true, $quantity = 1)
	{
		if (!Validate::isBool($usetax) OR !Validate::isUnsignedId($id_product))
			die(Tools::displayError());

		if ($usetax) $decimals = 2;
		$result = Db::getInstance()->GetRow('
		SELECT p.`price`, p.`reduction_price`, p.`reduction_percent`, p.`reduction_from`, p.`reduction_to`, p.`id_tax`, t.`rate`'.($id_product_attribute ? ', pa.`price` AS attribute_price' : '').'
		FROM `'._DB_PREFIX_.'product` p
		'.($id_product_attribute ? 'LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON pa.`id_product_attribute` = '.intval($id_product_attribute) : '').'
		LEFT JOIN `'._DB_PREFIX_.'tax` AS t ON t.`id_tax` = p.`id_tax`
		WHERE p.`id_product` = '.intval($id_product));
		$price = $result['price'];

		// Exclude tax
		if (Tax::excludeTaxeOption())
			$usetax = false;
		$tax = floatval(Tax::getApplicableTax(intval($result['id_tax']), floatval($result['rate'])));
		if ($usetax)
			$price *= (1 + ($tax / 100));
		if (isset($result['attribute_price']))
			$price += $usetax ? $result['attribute_price'] : ($result['attribute_price'] / (1 + ($tax / 100)));
		$reduc = self::getReductionValue($result, $usetax);

		if ($only_reduc)
			return $reduc;

		// Quantity discount
		if ($quantity > 1 AND ($qtyD = QuantityDiscount::getDiscountFromQuantity($id_product, $quantity)))
			$reduc += QuantityDiscount::getValue($price, $qtyD->id_discount_type, $qtyD->value);
		if ($usereduc)
			$price -= $reduc;
		return ($divisor AND $divisor != 'NULL') ? number_format($price/$divisor, $decimals, '.', '') : number_format($price, $decimals, '.', '');
	}

	/**
	* Get product price
	* Same as static function getPriceStatic, no need to specify product id
	*
	* @param boolean $tax With taxes or not (optional)
	* @param integer $id_product_attribute Product attribute id (optional)
	* @param integer $decimals Number of decimals (optional)
	* @param integer $divisor Util when paying many time without frais (optional)
	* @return float Product price in euros
	*/
	public function getPrice($tax = true, $id_product_attribute = NULL, $decimals = 6, $divisor = NULL, $only_reduc = false, $usereduc = true, $quantity = 1)
	{
			return self::getPriceStatic(intval($this->id), $tax, $id_product_attribute, $decimals, $divisor, $only_reduc, $usereduc, $quantity);
	}

	public function getPriceWithoutReduct()
	{
		$res = Db::getInstance()->getRow('
			SELECT p.`price`, t.`rate`, t.`id_tax`
			FROM `'._DB_PREFIX_.$this->table.'` p
			LEFT JOIN `'._DB_PREFIX_.'tax`t ON (p.`id_tax` = t.`id_tax`)
			WHERE p.`id_product` = '.intval($this->id));
		if (!$res)
			return false;
		$tax = floatval(Tax::getApplicableTax(intval($res['id_tax']), floatval($res['rate'])));
		if (!Tax::excludeTaxeOption())
			return ($res['price'] * (1 + $tax / 100));
		return ($res['price']);
	}

	/**
	* Get product price for display
	* Also display currency sign and reduction
	*
	* @param array $params Product price, reduction...
	* @param object $smarty Smarty object
	* @return string Product price fully formated in customer currency
	*/
	static function productPrice($params, &$smarty)
	{
		$ret = '';
		if (isset($params['p']['reduction']) AND $params['p']['reduction'])
			$ret .= '<span class="discounted">'.Tools::displayPrice($params['p']['price_without_reduction'], $smarty->ps_currency).'</span><br />';
		$ret .= Tools::displayPrice($params['p']['price'], $smarty->ps_currency);
		return $ret;
	}

	static function productPriceWithoutDisplay($params, &$smarty)
	{
		return Tools::convertPrice($params['p'], $params['c']);
	}

	/**
	* Display price with right format and currency
	*
	* @param array $params Params
	* @object $smarty Smarty object
	* @return string Price with right format and currency
	*/
	static function convertPrice($params, &$smarty)
	{
		return Tools::displayPrice($params['price'], $smarty->ps_currency);
	}

	static function convertPriceWithCurrency($params, &$smarty)
	{
		if (!isset($params['convert']))
			$params['convert'] = true;
		return Tools::displayPrice($params['price'], $params['currency'], false, $params['convert']);
	}

	static function displayWtPrice($params, &$smarty)
	{
		return Tools::displayPrice($params['p'], $smarty->ps_currency);
	}

	static function displayWtPriceWithCurrency($params, &$smarty)
	{
		return Tools::displayPrice($params['price'], $params['currency'], false, $params['convert']);
	}

	/**
	* Get available product quantities
	*
	* @param integer $id_product Product id
	* @param integer $id_product_attribute Product attribute id (optional)
	* @return integer Available quantities
	*/
	public static function getQuantity($id_product, $id_product_attribute = NULL)
	{
		$result = Db::getInstance()->GetRow('
		SELECT p.`quantity` AS p_qty, pa.`quantity` AS at_qty, SUM(pa.`quantity`) AS total
		FROM `'._DB_PREFIX_.'product` p
		LEFT JOIN `'._DB_PREFIX_.'product_attribute` AS pa ON pa.`id_product` = p.`id_product`
		WHERE p.`id_product` = '.intval($id_product).'
		'.(isset($id_product_attribute) ? 'AND `id_product_attribute` = '.intval($id_product_attribute) : '').'
		GROUP BY p.`id_product`');

		return ($result['total'] > 0 ? $result['total'] : $result['p_qty']);
	}

	/**
	* Update available product quantities
	*
	* @param array $product Array with ordered product (quantity, id_product_attribute if applicable)
	* @return mixed Query result
	*/
	public static function updateQuantity($product)
	{
		if (!is_array($product))
			die (Tools::displayError());

		$result = Db::getInstance()->getRow('
		SELECT `quantity`
		FROM `'._DB_PREFIX_.($product['id_product_attribute'] ? 'product_attribute' : 'product').'`
		WHERE `id_product` = '.intval($product['id_product']).($product['id_product_attribute'] ?
		' AND `id_product_attribute` = '.intval($product['id_product_attribute']) : ''));

		if (!Configuration::get('PS_STOCK_MANAGEMENT'))
			return true;
		if (self::isAvailableWhenOutOfStock($product['out_of_stock']) AND intval($result['quantity']) == 0)
			return -1;

		if ($result['quantity'] < $product['quantity'])
		{
			Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.($product['id_product_attribute'] ? ' product_attribute' : 'product').'`
			SET `quantity` = 0
			WHERE id_product = '.intval($product['id_product']).($product['id_product_attribute'] ?
			' AND id_product_attribute = '.intval($product['id_product_attribute']) : ''));
			return false;
		}

		return Db::getInstance()->Execute('
		UPDATE `'._DB_PREFIX_.'product'.($product['id_product_attribute'] ? '_attribute' : '').'`
		SET `quantity` = `quantity`-'.intval($product['quantity']).'
		WHERE `id_product` = '.intval($product['id_product']).
		($product['id_product_attribute'] ? ' AND `id_product_attribute` = '.intval($product['id_product_attribute']) : ''));
	}

	public static function reinjectQuantities($product_id, $quantity)
	{
		$orderDetail = new OrderDetail(intval($product_id));
		$sql = '
		UPDATE `'._DB_PREFIX_.'product'.($orderDetail->product_attribute_id ? '_attribute' : '').'`
		SET `quantity` = `quantity`+'.intval($quantity).'
		WHERE `id_product` = '.intval($orderDetail->product_id).
		($orderDetail->product_attribute_id ? ' AND `id_product_attribute` = '.intval($orderDetail->product_attribute_id) : '');
		return Db::getInstance()->Execute($sql);
	}

	public static function isAvailableWhenOutOfStock($oos)
	{
		return (intval($oos) == 2 ? intval(Configuration::get('PS_ORDER_OUT_OF_STOCK')) : intval($oos));
	}

	/**
	* Check product availability
	*
	* @param integer $qty Quantity desired
	* @return boolean True if product is available with this quantity
	*/
	public function checkQty($qty)
	{
		if ($this->isAvailableWhenOutOfStock($this->out_of_stock))
			return true;

		$result = Db::getInstance()->getRow('
		SELECT `quantity`
		FROM `'._DB_PREFIX_.'product`
		WHERE `id_product` = '.intval($this->id));

		return ($result AND $qty <= $result['quantity']);
	}

	/**
	* Check if there is not a default attribute and create it not
	*/
	public function checkDefaultAttributes()
	{
		$row = Db::getInstance()->getRow('
		SELECT id_product
		FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `default_on` = 1 AND `id_product` = '.intval($this->id));
		if ($row)
			return true;

		$mini = Db::getInstance()->getRow('
		SELECT MIN(pa.id_product_attribute) as `id_attr`
		FROM `'._DB_PREFIX_.'product_attribute` pa
		WHERE `id_product` = '.intval($this->id));
		if (!$mini)
			return false;

		if (!Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'product_attribute`
			SET `default_on` = 1
			WHERE `id_product_attribute` = '.intval($mini['id_attr'])))
			return false;
		return true;
	}

	/**
	* Get all available attribute groups
	*
	* @param integer $id_lang Language id
	* @return array Attribute groups
	*/
	public function getAttributesGroups($id_lang)
	{
		return Db::getInstance()->ExecuteS('
		SELECT ag.`id_attribute_group`, agl.`name` AS group_name, agl.`public_name` AS public_group_name, a.`id_attribute`, al.`name` AS attribute_name,
		a.`color` AS attribute_color, pa.`id_product_attribute`, pa.`quantity`, pa.`price`, pa.`ecotax`, pa.`weight`, pa.`id_image`, pa.`default_on`, pa.`reference`
		FROM `'._DB_PREFIX_.'product_attribute` pa
		LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
		LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute`
		LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group`
		LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON a.`id_attribute` = al.`id_attribute`
		LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON ag.`id_attribute_group` = agl.`id_attribute_group`
		WHERE pa.`id_product` = '.intval($this->id).'
		AND al.`id_lang` = '.intval($id_lang).'
		AND agl.`id_lang` = '.intval($id_lang).'
		ORDER BY pa.`id_product_attribute`');
	}

	/**
	* Delete product accessories
	*
	* @return mixed Deletion result
	*/
	public function deleteAccessories()
	{
		return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'accessory` WHERE `id_product_1` = '.intval($this->id));
	}

	/**
	* Get product accessories (only names)
	*
	* @param integer $id_lang Language id
	* @param integer $id_product Product id
	* @return array Product accessories
	*/
	public static function getAccessoriesLight($id_lang, $id_product)
	{
		return Db::getInstance()->ExecuteS('
		SELECT p.`id_product`, pl.`name`
		FROM `'._DB_PREFIX_.'accessory`
		LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product`= `id_product_2`
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
		WHERE `id_product_1` = '.intval($id_product));
	}

	/**
	* Get product accessories
	*
	* @param integer $id_lang Language id
	* @return array Product accessories
	*/
	public function getAccessories($id_lang, $active = true)
	{
		global	$link, $cookie;

		$result = Db::getInstance()->ExecuteS('
		SELECT p.*, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, p.`ean13`,
		i.`id_image`, il.`legend`, t.`rate`, m.`name` as manufacturer_name
		FROM `'._DB_PREFIX_.'accessory`
		LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = `id_product_2`
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
		LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (p.`id_manufacturer`= m.`id_manufacturer`)
		LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = p.`id_tax`
		WHERE `id_product_1` = '.intval($this->id).'
		'.($active ? 'AND p.`active` = 1' : ''));

		if (!$result)
			return false;

		return $this->getProductsProperties($id_lang, $result);
	}

	public function getAccessoryById($accessoryId)
	{
		return Db::getInstance()->getRow('SELECT `id_product`, `name` FROM `'._DB_PREFIX_.'product_lang` WHERE `id_product` = '.intval($accessoryId));
	}

	/**
	* Link accessories with product
	*
	* @param array $accessories_id Accessories ids
	*/
	public function changeAccessories($accessories_id)
	{
		foreach ($accessories_id as $id_product_2)
			Db::getInstance()->AutoExecute(_DB_PREFIX_.'accessory', array('id_product_1' => intval($this->id), 'id_product_2' => intval($id_product_2)), 'INSERT');
	}

	/**
	* Add new feature to product
	*/
	public function addFeaturesCustomToDB($id_value, $lang, $cust)
	{
		$row = array('id_feature_value' => intval($id_value), 'id_lang' => intval($lang), 'value' => pSQL($cust));
		$result = Db::getInstance()->autoExecute(_DB_PREFIX_.'feature_value_lang', $row, 'INSERT');
	}

	public function addFeaturesToDB($id_feature, $id_value, $cust = 0)
	{
		if ($cust)
		{
			$row = array('id_feature' => intval($id_feature), 'custom' => 1);
			$result = Db::getInstance()->autoExecute(_DB_PREFIX_.'feature_value', $row, 'INSERT');
			$id_value = Db::getInstance()->Insert_ID();
		}
		$row = array('id_feature' => intval($id_feature), 'id_product' => intval($this->id), 'id_feature_value' => intval($id_value));
		$result = Db::getInstance()->autoExecute(_DB_PREFIX_.'feature_product', $row, 'INSERT');
		if ($id_value)
			return ($id_value);
	}

	/**
	* Select all features for the object
	*
	* @return array Array with feature product's data
	*/
	public function getFeatures()
	{
		return self::getFeaturesStatic(intval($this->id));
	}

	static public function getFeaturesStatic($id_product)
	{
		return Db::getInstance()->ExecuteS('
		SELECT id_feature, id_product, id_feature_value
		FROM `'._DB_PREFIX_.'feature_product`
		WHERE `id_product` = '.$id_product);
	}

	/**
	* Admin panel product search
	*
	* @param integer $id_lang Language id
	* @param string $query Search query
	* @return array Matching products
	*/
	static public function searchByName($id_lang, $query)
	{
		if (!Validate::isCatalogName($query))
			die(Tools::displayError());

		$result = Db::getInstance()->ExecuteS('
		SELECT p.`id_product`, pl.`name`, pl.`link_rewrite`, p.`weight`, p.`active`, p.`ecotax`, i.`id_image`, p.`reference`,
		il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name
		FROM `'._DB_PREFIX_.'category_product` cp
		LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`
		LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'tax` t ON t.`id_tax` = p.`id_tax`
		LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl ON (t.`id_tax` = tl.`id_tax` AND tl.`id_lang` = '.intval($id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON m.`id_manufacturer` = p.`id_manufacturer`
		LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`) AND i.`cover` = 1
		LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.intval($id_lang).')
		WHERE pl.`name` LIKE \'%'.pSQL($query).'%\' OR p.`reference` LIKE \'%'.pSQL($query).'%\' OR p.`supplier_reference` LIKE \'%'.pSQL($query).'%\'
		GROUP BY `id_product`
		ORDER BY pl.`name` ASC');

		if (!$result)
			return false;

		$resultsArray = array();
		foreach ($result AS $k => $row)
		{
			$row['price'] = Product::getPriceStatic($row['id_product'], true, NULL, 2);
			$row['quantity'] = Product::getQuantity($row['id_product']);
			$resultsArray[] = $row;
		}
		return $resultsArray;
	}

	/**
	* Duplicate attributes when duplicating a product
	*
	* @param integer $id_product_old Old product id
	* @param integer $id_product_old New product id
	*/
	static public function duplicateAttributes($id_product_old, $id_product_new)
	{
		$return = true;

		$result = Db::getInstance()->ExecuteS('
		SELECT *
		FROM `'._DB_PREFIX_.'product_attribute`
		WHERE `id_product` = '.intval($id_product_old));
		foreach ($result as $row)
		{
			$result2 = Db::getInstance()->ExecuteS('
			SELECT *
			FROM `'._DB_PREFIX_.'product_attribute_combination`
			WHERE `id_product_attribute` = '.intval($row['id_product_attribute']));

			$row['id_product'] = $id_product_new;
			unset($row['id_product_attribute']);
			unset($row['id_image']);
			$return &= Db::getInstance()->AutoExecute(_DB_PREFIX_.'product_attribute', $row, 'INSERT');

			$id_product_attribute = Db::getInstance()->Insert_ID();
			foreach ($result2 AS $row2)
			{
				$row2['id_product_attribute'] = $id_product_attribute;
				$return &= Db::getInstance()->AutoExecute(_DB_PREFIX_.'product_attribute_combination', $row2, 'INSERT');
			}
		}
		return $return;
	}

	/**
	* Duplicate features when duplicating a product
	*
	* @param integer $id_product_old Old product id
	* @param integer $id_product_old New product id
	*/
	static public function duplicateFeatures($id_product_old, $id_product_new)
	{
		$return = true;

		$result = Db::getInstance()->ExecuteS('
		SELECT *
		FROM `'._DB_PREFIX_.'feature_product`
		WHERE `id_product` = '.intval($id_product_old));
		foreach ($result as $row)
		{
			$result2 = Db::getInstance()->getRow('
			SELECT *
			FROM `'._DB_PREFIX_.'feature_value`
			WHERE `id_feature_value` = '.intval($row['id_feature_value']));
			// Custom feature value, need to duplicate it
			if ($result2['custom'])
			{
				$old_id_feature_value = $result2['id_feature_value'];
				unset($result2['id_feature_value']);
				$return &= Db::getInstance()->AutoExecute(_DB_PREFIX_.'feature_value', $result2, 'INSERT');
				$max_fv = Db::getInstance()->getRow('
					SELECT MAX(`id_feature_value`) AS nb
					FROM `'._DB_PREFIX_.'feature_value`');
				$new_id_feature_value = $max_fv['nb'];
				$languages = Language::getLanguages();
				foreach ($languages as $language)
				{
					$result3 = Db::getInstance()->getRow('
					SELECT *
					FROM `'._DB_PREFIX_.'feature_value_lang`
					WHERE `id_feature_value` = '.intval($old_id_feature_value).'
					AND `id_lang` = '.intval($language['id_lang']));
					$result3['id_feature_value'] = $new_id_feature_value;
					$return &= Db::getInstance()->AutoExecute(_DB_PREFIX_.'feature_value_lang', $result3, 'INSERT');
				}
				$row['id_feature_value'] = $new_id_feature_value;
			}
			$row['id_product'] = $id_product_new;
			$return &= Db::getInstance()->AutoExecute(_DB_PREFIX_.'feature_product', $row, 'INSERT');
		}
		return $return;
	}

	/**
	* Get the link of the product page of this product
	*
	*/
	public function getLink()
	{
		global $link;
		return $link->getProductLink($this->id, $this->link_rewrite);
	}

	public function getTags($id_lang)
	{
		if (!($this->tags AND key_exists($id_lang, $this->tags)))
			return '';
		$result = '';
		foreach ($this->tags[$id_lang] AS $tagName)
			$result .= $tagName.', ';
		return rtrim($result, ', ');
	}

	static public function defineProductImage($row)
	{
		global $cookie;
		if (!$row['id_image'])
		{
			$row['id_image'] = Language::getIsoById($cookie->id_lang).'-default';
			$row['legend'] = 'no picture';
		}
		else
			$row['id_image'] = $row['id_product'].'-'.$row['id_image'];
		return $row['id_image'];
	}

	static public function getProductProperties($id_lang, $row)
	{
		if (!$row['id_product'])
			return false;
		$link = new Link();
		$row['link'] = $link->getProductLink($row['id_product'], $row['link_rewrite'], $row['ean13']);
		$row['id_product_attribute'] = Product::getDefaultAttribute($row['id_product']);
		$row['attribute_price'] = Product::getProductAttribute($row['id_product_attribute']);
		$row['reduction'] = Product::getReductionValue($row, true);
		$row['price'] = Product::getPriceStatic($row['id_product'], true, ((isset($row['id_product_attribute']) AND !empty($row['id_product_attribute'])) ? intval($row['id_product_attribute']) : NULL), 2);
		$row['price_without_reduction'] = Product::getPriceStatic($row['id_product'], true, ((isset($row['id_product_attribute']) AND !empty($row['id_product_attribute'])) ? intval($row['id_product_attribute']) : NULL), 2, NULL, false, false);
		$row['quantity'] = Product::getQuantity($row['id_product']);
		$row['id_image'] = Product::defineProductImage($row);
		$row['features'] = Product::getFrontFeaturesStatic(intval($id_lang), $row['id_product']);
		$row['allow_oosp'] = Product::isAvailableWhenOutOfStock($row['out_of_stock']);
		return $row;
	}

	static public function getProductsProperties($id_lang, $query_result)
	{
		$resultsArray = array();
		foreach ($query_result AS $row)
			if ($row2 = Product::getProductProperties($id_lang, $row))
				$resultsArray[] = $row2;
		return $resultsArray;
	}

	/*
	* Select all features for a given language
	*
	* @param $id_lang Language id
	* @return array Array with feature's data
	*/
	static public function getFrontFeaturesStatic($id_lang, $id_product)
	{
		return Db::getInstance()->ExecuteS('
		SELECT name, value, pf.id_feature
		FROM '._DB_PREFIX_.'feature_product pf
		LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = pf.id_feature AND fl.id_lang = '.intval($id_lang).')
		LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = pf.id_feature_value AND fvl.id_lang = '.intval($id_lang).')
		WHERE pf.id_product = '.intval($id_product));
	}

	public function getFrontFeatures($id_lang)
	{
		return self::getFrontFeaturesStatic($id_lang, $this->id);
	}
}

?>
