PageRenderTime 73ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 1ms

/classes/Product.php

https://bitbucket.org/yhjohn/ayanapure.com
PHP | 5036 lines | 3563 code | 609 blank | 864 comment | 473 complexity | 84d708637d2f5db0d20dceb03059be96 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @version Release: $Revision: 7506 $
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. * International Registered Trademark & Property of PrestaShop SA
  26. */
  27. /**
  28. * @deprecated 1.5.0.1
  29. */
  30. define('_CUSTOMIZE_FILE_', 0);
  31. /**
  32. * @deprecated 1.5.0.1
  33. */
  34. define('_CUSTOMIZE_TEXTFIELD_', 1);
  35. class ProductCore extends ObjectModel
  36. {
  37. /** @var string Tax name */
  38. public $tax_name;
  39. /** @var string Tax rate */
  40. public $tax_rate;
  41. /** @var integer Manufacturer id */
  42. public $id_manufacturer;
  43. /** @var integer Supplier id */
  44. public $id_supplier;
  45. /** @var integer default Category id */
  46. public $id_category_default;
  47. /** @var integer default Shop id */
  48. public $id_shop_default;
  49. /** @var string Manufacturer name */
  50. public $manufacturer_name;
  51. /** @var string Supplier name */
  52. public $supplier_name;
  53. /** @var string Name */
  54. public $name;
  55. /** @var string Long description */
  56. public $description;
  57. /** @var string Short description */
  58. public $description_short;
  59. /** @var integer Quantity available */
  60. public $quantity = 0;
  61. /** @var integer Minimal quantity for add to cart */
  62. public $minimal_quantity = 1;
  63. /** @var string available_now */
  64. public $available_now;
  65. /** @var string available_later */
  66. public $available_later;
  67. /** @var float Price in euros */
  68. public $price = 0;
  69. /** @var float Additional shipping cost */
  70. public $additional_shipping_cost = 0;
  71. /** @var float Wholesale Price in euros */
  72. public $wholesale_price = 0;
  73. /** @var boolean on_sale */
  74. public $on_sale = false;
  75. /** @var boolean online_only */
  76. public $online_only = false;
  77. /** @var string unity */
  78. public $unity = null;
  79. /** @var float price for product's unity */
  80. public $unit_price;
  81. /** @var float price for product's unity ratio */
  82. public $unit_price_ratio = 0;
  83. /** @var float Ecotax */
  84. public $ecotax = 0;
  85. /** @var string Reference */
  86. public $reference;
  87. /** @var string Supplier Reference */
  88. public $supplier_reference;
  89. /** @var string Location */
  90. public $location;
  91. /** @var string Width in default width unit */
  92. public $width = 0;
  93. /** @var string Height in default height unit */
  94. public $height = 0;
  95. /** @var string Depth in default depth unit */
  96. public $depth = 0;
  97. /** @var string Weight in default weight unit */
  98. public $weight = 0;
  99. /** @var string Ean-13 barcode */
  100. public $ean13;
  101. /** @var string Upc barcode */
  102. public $upc;
  103. /** @var string Friendly URL */
  104. public $link_rewrite;
  105. /** @var string Meta tag description */
  106. public $meta_description;
  107. /** @var string Meta tag keywords */
  108. public $meta_keywords;
  109. /** @var string Meta tag title */
  110. public $meta_title;
  111. /** @var boolean Product statuts */
  112. public $quantity_discount = 0;
  113. /** @var boolean Product customization */
  114. public $customizable;
  115. /** @var boolean Product is new */
  116. public $new = null;
  117. /** @var integer Number of uploadable files (concerning customizable products) */
  118. public $uploadable_files;
  119. /** @var int Number of text fields */
  120. public $text_fields;
  121. /** @var boolean Product statuts */
  122. public $active = true;
  123. /** @var boolean Product available for order */
  124. public $available_for_order = true;
  125. /** @var string Object available order date */
  126. public $available_date = '0000-00-00';
  127. /** @var enum Product condition (new, used, refurbished) */
  128. public $condition;
  129. /** @var boolean Show price of Product */
  130. public $show_price = true;
  131. /** @var boolean is the product indexed in the search index? */
  132. public $indexed = 0;
  133. /** @var string ENUM('both', 'catalog', 'search', 'none') front office visibility */
  134. public $visibility;
  135. /** @var string Object creation date */
  136. public $date_add;
  137. /** @var string Object last modification date */
  138. public $date_upd;
  139. /*** @var array Tags */
  140. public $tags;
  141. public $id_tax_rules_group = 1;
  142. /**
  143. * We keep this variable for retrocompatibility for themes
  144. * @deprecated 1.5.0
  145. */
  146. public $id_color_default = 0;
  147. /**
  148. * @since 1.5.0
  149. * @var boolean Tells if the product uses the advanced stock management
  150. */
  151. public $advanced_stock_management = 0;
  152. public $out_of_stock;
  153. public $depends_on_stock;
  154. public $isFullyLoaded = false;
  155. public $cache_is_pack;
  156. public $cache_has_attachments;
  157. public $is_virtual;
  158. public $cache_default_attribute;
  159. /**
  160. * @var string If product is populated, this property contain the rewrite link of the default category
  161. */
  162. public $category;
  163. public static $_taxCalculationMethod = PS_TAX_EXC;
  164. protected static $_prices = array();
  165. protected static $_pricesLevel2 = array();
  166. protected static $_incat = array();
  167. protected static $_cart_quantity = array();
  168. protected static $_tax_rules_group = array();
  169. protected static $_cacheFeatures = array();
  170. protected static $_frontFeaturesCache = array();
  171. protected static $producPropertiesCache = array();
  172. /** @var array cache stock data in getStock() method */
  173. protected static $cacheStock = array();
  174. public static $definition = array(
  175. 'table' => 'product',
  176. 'primary' => 'id_product',
  177. 'multilang' => true,
  178. 'multilang_shop' => true,
  179. 'fields' => array(
  180. // Classic fields
  181. 'id_shop_default' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
  182. 'id_manufacturer' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
  183. 'id_supplier' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
  184. 'reference' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 32),
  185. 'supplier_reference' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 32),
  186. 'location' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 64),
  187. 'width' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'),
  188. 'height' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'),
  189. 'depth' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'),
  190. 'weight' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'),
  191. 'quantity_discount' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
  192. 'ean13' => array('type' => self::TYPE_STRING, 'validate' => 'isEan13', 'size' => 13),
  193. 'upc' => array('type' => self::TYPE_STRING, 'validate' => 'isUpc', 'size' => 12),
  194. 'cache_is_pack' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
  195. 'cache_has_attachments' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
  196. 'is_virtual' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
  197. /* Shop fields */
  198. 'id_category_default' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'),
  199. 'id_tax_rules_group' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'),
  200. 'on_sale' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  201. 'online_only' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  202. 'ecotax' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'),
  203. 'minimal_quantity' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'),
  204. 'price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'required' => true),
  205. 'wholesale_price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'),
  206. 'unity' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isString'),
  207. 'unit_price_ratio' => array('type' => self::TYPE_FLOAT, 'shop' => true),
  208. 'additional_shipping_cost' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'),
  209. 'customizable' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'),
  210. 'text_fields' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'),
  211. 'uploadable_files' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'),
  212. 'active' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  213. 'available_for_order' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  214. 'available_date' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'),
  215. 'condition' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isGenericName', 'values' => array('new', 'used', 'refurbished'), 'default' => 'new'),
  216. 'show_price' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  217. 'indexed' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  218. 'visibility' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isProductVisibility', 'values' => array('both', 'catalog', 'search', 'none'), 'default' => 'both'),
  219. 'cache_default_attribute' => array('type' => self::TYPE_INT, 'shop' => true),
  220. 'advanced_stock_management' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
  221. 'date_add' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'),
  222. 'date_upd' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'),
  223. /* Lang fields */
  224. 'meta_description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
  225. 'meta_keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
  226. 'meta_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128),
  227. 'link_rewrite' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 128),
  228. 'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 128),
  229. 'description' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isString'),
  230. 'description_short' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isString'),
  231. 'available_now' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
  232. 'available_later' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'IsGenericName', 'size' => 255),
  233. ),
  234. 'associations' => array(
  235. 'manufacturer' => array('type' => self::HAS_ONE),
  236. 'supplier' => array('type' => self::HAS_ONE),
  237. 'default_category' => array('type' => self::HAS_ONE, 'field' => 'id_category_default', 'object' => 'Category'),
  238. 'tax_rules_group' => array('type' => self::HAS_ONE),
  239. 'categories' => array('type' => self::HAS_MANY, 'field' => 'id_category', 'object' => 'Category', 'association' => 'category_product'),
  240. 'stock_availables' => array('type' => self::HAS_MANY, 'field' => 'id_stock_available', 'object' => 'StockAvailable', 'association' => 'stock_availables'),
  241. ),
  242. );
  243. protected $webserviceParameters = array(
  244. 'objectMethods' => array(
  245. 'add' => 'addWs',
  246. 'update' => 'updateWs'
  247. ),
  248. 'objectNodeNames' => 'products',
  249. 'fields' => array(
  250. 'id_manufacturer' => array(
  251. 'xlink_resource' => 'manufacturers'
  252. ),
  253. 'id_supplier' => array(
  254. 'xlink_resource' => 'suppliers'
  255. ),
  256. 'id_category_default' => array(
  257. 'xlink_resource' => 'categories'
  258. ),
  259. 'new' => array(),
  260. 'cache_default_attribute' => array(),
  261. 'id_default_image' => array(
  262. 'getter' => 'getCoverWs',
  263. 'setter' => 'setCoverWs',
  264. 'xlink_resource' => array(
  265. 'resourceName' => 'images',
  266. 'subResourceName' => 'products'
  267. )
  268. ),
  269. 'id_default_combination' => array(
  270. 'getter' => 'getWsDefaultCombination',
  271. 'setter' => 'setWsDefaultCombination',
  272. 'xlink_resource' => array(
  273. 'resourceName' => 'combinations'
  274. )
  275. ),
  276. 'id_tax_rules_group' => array(
  277. 'xlink_resource' => array(
  278. 'resourceName' => 'tax_rules_group'
  279. )
  280. ),
  281. 'position_in_category' => array(
  282. 'getter' => 'getWsPositionInCategory',
  283. 'setter' => false
  284. ),
  285. 'manufacturer_name' => array(
  286. 'getter' => 'getWsManufacturerName',
  287. 'setter' => false
  288. ),
  289. 'quantity' => array(
  290. 'getter' => false,
  291. 'setter' => false
  292. ),
  293. ),
  294. 'associations' => array(
  295. 'categories' => array(
  296. 'resource' => 'category',
  297. 'fields' => array(
  298. 'id' => array('required' => true),
  299. )
  300. ),
  301. 'images' => array(
  302. 'resource' => 'image',
  303. 'fields' => array('id' => array())
  304. ),
  305. 'combinations' => array(
  306. 'resource' => 'combinations',
  307. 'fields' => array(
  308. 'id' => array('required' => true),
  309. )
  310. ),
  311. 'product_option_values' => array(
  312. 'resource' => 'product_options_values',
  313. 'fields' => array(
  314. 'id' => array('required' => true),
  315. )
  316. ),
  317. 'product_features' => array(
  318. 'resource' => 'product_feature',
  319. 'fields' => array(
  320. 'id' => array('required' => true),
  321. 'id_feature_value' => array(
  322. 'required' => true,
  323. 'xlink_resource' => 'product_feature_values'
  324. ),
  325. )
  326. ),
  327. 'tags' => array('resource' => 'tag',
  328. 'fields' => array(
  329. 'id' => array('required' => true),
  330. )),
  331. 'stock_availables' => array('resource' => 'stock_available',
  332. 'fields' => array(
  333. 'id' => array('required' => true),
  334. 'id_product_attribute' => array('required' => true),
  335. ),
  336. 'setter' => false
  337. ),
  338. ),
  339. );
  340. const CUSTOMIZE_FILE = 0;
  341. const CUSTOMIZE_TEXTFIELD = 1;
  342. /**
  343. * Note: prefix is "PTYPE" because TYPE_ is used in ObjectModel (definition)
  344. */
  345. const PTYPE_SIMPLE = 0;
  346. const PTYPE_PACK = 1;
  347. const PTYPE_VIRTUAL = 2;
  348. public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null)
  349. {
  350. parent::__construct($id_product, $id_lang, $id_shop);
  351. if (!$context)
  352. $context = Context::getContext();
  353. if ($full && $this->id)
  354. {
  355. $this->isFullyLoaded = $full;
  356. $this->tax_name = 'deprecated'; // The applicable tax may be BOTH the product one AND the state one (moreover this variable is some deadcode)
  357. $this->manufacturer_name = Manufacturer::getNameById((int)$this->id_manufacturer);
  358. $this->supplier_name = Supplier::getNameById((int)$this->id_supplier);
  359. $address = null;
  360. if (is_object($context->cart) && $context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')} != null)
  361. $address = $context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
  362. $this->tax_rate = $this->getTaxesRate(new Address($address));
  363. $this->new = $this->isNew();
  364. $this->price = Product::getPriceStatic((int)$this->id, false, null, 6, null, false, true, 1, false, null, null, null, $this->specificPrice);
  365. $this->unit_price = ($this->unit_price_ratio != 0 ? $this->price / $this->unit_price_ratio : 0);
  366. if ($this->id)
  367. $this->tags = Tag::getProductTags((int)$this->id);
  368. $this->loadStockData();
  369. }
  370. if ($this->id_category_default)
  371. $this->category = Category::getLinkRewrite((int)$this->id_category_default, (int)$id_lang);
  372. }
  373. /**
  374. * @see ObjectModel::getFieldsShop()
  375. * @return array
  376. */
  377. public function getFieldsShop()
  378. {
  379. $fields = parent::getFieldsShop();
  380. if (is_null($this->update_fields) || (!empty($this->update_fields['price']) && !empty($this->update_fields['unit_price'])))
  381. $fields['unit_price_ratio'] = (float)$this->unit_price > 0 ? $this->price / $this->unit_price : 0;
  382. return $fields;
  383. }
  384. public function add($autodate = true, $null_values = false)
  385. {
  386. if (!parent::add($autodate, $null_values))
  387. return false;
  388. if ($this->getType() == Product::PTYPE_VIRTUAL)
  389. StockAvailable::setProductOutOfStock((int)$this->id, 1);
  390. else
  391. StockAvailable::setProductOutOfStock((int)$this->id, 2);
  392. $this->setGroupReduction();
  393. Hook::exec('actionProductSave', array('id_product' => $this->id));
  394. return true;
  395. }
  396. public function update($null_values = false)
  397. {
  398. $return = parent::update($null_values);
  399. $this->setGroupReduction();
  400. Hook::exec('actionProductSave', array('id_product' => $this->id));
  401. return $return;
  402. }
  403. public static function initPricesComputation($id_customer = null)
  404. {
  405. if ($id_customer)
  406. {
  407. $customer = new Customer((int)$id_customer);
  408. if (!Validate::isLoadedObject($customer))
  409. die(Tools::displayError());
  410. self::$_taxCalculationMethod = Group::getPriceDisplayMethod((int)$customer->id_default_group);
  411. }
  412. else
  413. self::$_taxCalculationMethod = Group::getPriceDisplayMethod(Group::getCurrent()->id);
  414. }
  415. public static function getTaxCalculationMethod($id_customer = null)
  416. {
  417. if ($id_customer)
  418. Product::initPricesComputation((int)$id_customer);
  419. return (int)self::$_taxCalculationMethod;
  420. }
  421. /**
  422. * Move a product inside its category
  423. * @param boolean $way Up (1) or Down (0)
  424. * @param integer $position
  425. * return boolean Update result
  426. */
  427. public function updatePosition($way, $position)
  428. {
  429. if (!$res = Db::getInstance()->executeS('
  430. SELECT cp.`id_product`, cp.`position`, cp.`id_category`
  431. FROM `'._DB_PREFIX_.'category_product` cp
  432. WHERE cp.`id_category` = '.(int)Tools::getValue('id_category', 1).'
  433. ORDER BY cp.`position` ASC'
  434. ))
  435. return false;
  436. foreach ($res as $product)
  437. if ((int)$product['id_product'] == (int)$this->id)
  438. $moved_product = $product;
  439. if (!isset($moved_product) || !isset($position))
  440. return false;
  441. // < and > statements rather than BETWEEN operator
  442. // since BETWEEN is treated differently according to databases
  443. return (Db::getInstance()->execute('
  444. UPDATE `'._DB_PREFIX_.'category_product`
  445. SET `position`= `position` '.($way ? '- 1' : '+ 1').'
  446. WHERE `position`
  447. '.($way
  448. ? '> '.(int)$moved_product['position'].' AND `position` <= '.(int)$position
  449. : '< '.(int)$moved_product['position'].' AND `position` >= '.(int)$position).'
  450. AND `id_category`='.(int)$moved_product['id_category'])
  451. && Db::getInstance()->execute('
  452. UPDATE `'._DB_PREFIX_.'category_product`
  453. SET `position` = '.(int)$position.'
  454. WHERE `id_product` = '.(int)$moved_product['id_product'].'
  455. AND `id_category`='.(int)$moved_product['id_category']));
  456. }
  457. /*
  458. * Reorder product position in category $id_category.
  459. * Call it after deleting a product from a category.
  460. *
  461. * @param int $id_category
  462. */
  463. public static function cleanPositions($id_category)
  464. {
  465. $return = true;
  466. $result = Db::getInstance()->executeS('
  467. SELECT `id_product`
  468. FROM `'._DB_PREFIX_.'category_product`
  469. WHERE `id_category` = '.(int)$id_category.'
  470. ORDER BY `position`
  471. ');
  472. $total = count($result);
  473. for ($i = 0; $i < $total; $i++)
  474. $return &= Db::getInstance()->update('category_product', array(
  475. 'position' => $i,
  476. ), '`id_category` = '.(int)$id_category.' AND `id_product` = '.(int)$result[$i]['id_product']);
  477. return $return;
  478. }
  479. /**
  480. * Get the default attribute for a product
  481. *
  482. * @return int Attributes list
  483. */
  484. public static function getDefaultAttribute($id_product, $minimum_quantity = 0)
  485. {
  486. if (!Combination::isFeatureActive())
  487. return 0;
  488. $sql = 'SELECT pa.id_product_attribute
  489. FROM '._DB_PREFIX_.'product_attribute pa
  490. '.Shop::addSqlAssociation('product_attribute', 'pa').'
  491. '.($minimum_quantity > 0 ? Product::sqlStock('pa', 'pa') : '').
  492. ' WHERE product_attribute_shop.default_on = 1 '
  493. .($minimum_quantity > 0 ? ' AND IFNULL(stock.quantity, 0) >= '.(int)$minimum_quantity : '').
  494. ' AND pa.id_product = '.(int)$id_product;
  495. $result = Db::getInstance()->getValue($sql);
  496. if (!$result)
  497. {
  498. $sql = 'SELECT pa.id_product_attribute
  499. FROM '._DB_PREFIX_.'product_attribute pa
  500. '.Shop::addSqlAssociation('product_attribute', 'pa').'
  501. '.($minimum_quantity > 0 ? Product::sqlStock('pa', 'pa') : '').
  502. ' WHERE pa.id_product = '.(int)$id_product
  503. .($minimum_quantity > 0 ? ' AND IFNULL(stock.quantity, 0) >= '.(int)$minimum_quantity : '');
  504. $result = Db::getInstance()->getValue($sql);
  505. }
  506. if (!$result)
  507. {
  508. $sql = 'SELECT pa.id_product_attribute
  509. FROM '._DB_PREFIX_.'product_attribute pa
  510. '.Shop::addSqlAssociation('product_attribute', 'pa').'
  511. WHERE product_attribute_shop.`default_on` = 1
  512. AND pa.id_product = '.(int)$id_product;
  513. $result = Db::getInstance()->getValue($sql);
  514. }
  515. if (!$result)
  516. {
  517. $sql = 'SELECT pa.id_product_attribute
  518. FROM '._DB_PREFIX_.'product_attribute pa
  519. '.Shop::addSqlAssociation('product_attribute', 'pa').'
  520. WHERE pa.id_product = '.(int)$id_product;
  521. $result = Db::getInstance()->getValue($sql);
  522. }
  523. return $result;
  524. }
  525. public static function updateDefaultAttribute($id_product)
  526. {
  527. Db::getInstance()->update('product_shop', array(
  528. 'cache_default_attribute' => (int)Product::getDefaultAttribute($id_product),
  529. ), 'id_product = '.(int)$id_product);
  530. }
  531. public static function updateIsVirtual($id_product)
  532. {
  533. Db::getInstance()->update('product', array(
  534. 'is_virtual' => (int)$id_product,
  535. ), 'id_product = '.(int)$id_product);
  536. }
  537. /**
  538. * @see ObjectModel::validateFieldsLang()
  539. */
  540. public function validateFieldsLang($die = true, $error_return = false)
  541. {
  542. $limit = (int)Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT');
  543. if ($limit <= 0)
  544. $limit = 800;
  545. $this->def['fields']['description_short']['size'] = $limit;
  546. return parent::validateFieldsLang($die, $error_return);
  547. }
  548. public function delete()
  549. {
  550. /*
  551. * @since 1.5.0
  552. * It is NOT possible to delete a product if there are currently:
  553. * - physical stock for this product
  554. * - supply order(s) for this product
  555. */
  556. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && $this->advanced_stock_management)
  557. {
  558. $stock_manager = StockManagerFactory::getManager();
  559. $physical_quantity = $stock_manager->getProductPhysicalQuantities($this->id, 0);
  560. $real_quantity = $stock_manager->getProductRealQuantities($this->id, 0);
  561. if ($physical_quantity > 0)
  562. return false;
  563. if ($real_quantity > $physical_quantity)
  564. return false;
  565. }
  566. $result = parent::delete();
  567. // Removes the product from StockAvailable, for the current shop
  568. StockAvailable::removeProductFromStockAvailable($this->id);
  569. $result &= ($this->deleteProductAttributes() && $this->deleteImages() && $this->deleteSceneProducts());
  570. // If there are still entries in product_shop, don't remove completly the product
  571. if ($this->hasMultishopEntries())
  572. return true;
  573. Product::cleanPositions($this->id);
  574. Hook::exec('actionProductDelete', array('product' => $this));
  575. if (!$result ||
  576. !GroupReduction::deleteProductReduction($this->id) ||
  577. !$this->deleteCategories(true) ||
  578. !$this->deleteProductFeatures() ||
  579. !$this->deleteTags() ||
  580. !$this->deleteCartProducts() ||
  581. !$this->deleteAttributesImpacts() ||
  582. !$this->deleteAttachments() ||
  583. !$this->deleteCustomization() ||
  584. !SpecificPrice::deleteByProductId((int)$this->id) ||
  585. !$this->deletePack() ||
  586. !$this->deleteProductSale() ||
  587. !$this->deleteSearchIndexes() ||
  588. !$this->deleteAccessories() ||
  589. !$this->deleteFromAccessories() ||
  590. !$this->deleteFromSupplier() ||
  591. !$this->deleteDownload() ||
  592. !$this->deleteFromCartRules())
  593. return false;
  594. return true;
  595. }
  596. public function deleteSelection($products)
  597. {
  598. $return = 1;
  599. if (is_array($products) && ($count = count($products)))
  600. {
  601. // Deleting products can be quite long on a cheap server. Let's say 1.5 seconds by product (I've seen it!).
  602. if (intval(ini_get('max_execution_time')) < round($count * 1.5))
  603. ini_set('max_execution_time', round($count * 1.5));
  604. foreach ($products as $id_product)
  605. {
  606. $product = new Product((int)$id_product);
  607. $return &= $product->delete();
  608. }
  609. }
  610. return $return;
  611. }
  612. public function deleteFromCartRules()
  613. {
  614. CartRule::cleanProductRuleIntegrity('products', $this->id);
  615. return true;
  616. }
  617. public function deleteFromSupplier()
  618. {
  619. return Db::getInstance()->delete('product_supplier', 'id_product = '.(int)$this->id);
  620. }
  621. /**
  622. * addToCategories add this product to the category/ies if not exists.
  623. *
  624. * @param mixed $categories id_category or array of id_category
  625. * @return boolean true if succeed
  626. */
  627. public function addToCategories($categories = array())
  628. {
  629. if (empty($categories))
  630. return false;
  631. if (!is_array($categories))
  632. $categories = array($categories);
  633. if (!count($categories))
  634. return false;
  635. $categories = array_map('intval', $categories);
  636. $current_categories = $this->getCategories();
  637. $current_categories = array_map('intval', $current_categories);
  638. // for new categ, put product at last position
  639. $res_categ_new_pos = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  640. SELECT id_category, MAX(position)+1 newPos
  641. FROM `'._DB_PREFIX_.'category_product`
  642. WHERE `id_category` IN('.implode(',', $categories).')
  643. GROUP BY id_category');
  644. foreach ($res_categ_new_pos as $array)
  645. $new_categories[(int)$array['id_category']] = (int)$array['newPos'];
  646. $new_categ_pos = array();
  647. foreach ($categories as $id_category)
  648. $new_categ_pos[$id_category] = isset($new_categories[$id_category]) ? $new_categories[$id_category] : 0;
  649. $product_cats = array();
  650. foreach ($categories as $new_id_categ)
  651. if (!in_array($new_id_categ, $current_categories))
  652. $product_cats[] = array(
  653. 'id_category' => (int)$new_id_categ,
  654. 'id_product' => (int)$this->id,
  655. 'position' => (int)$new_categ_pos[$new_id_categ],
  656. );
  657. Db::getInstance()->insert('category_product', $product_cats);
  658. return true;
  659. }
  660. /**
  661. * Update categories to index product into
  662. *
  663. * @param string $productCategories Categories list to index product into
  664. * @param boolean $keeping_current_pos (deprecated, no more used)
  665. * @return array Update/insertion result
  666. */
  667. public function updateCategories($categories, $keeping_current_pos = false)
  668. {
  669. if (empty($categories))
  670. return false;
  671. $result = Db::getInstance()->executeS('
  672. SELECT c.`id_category`
  673. FROM `'._DB_PREFIX_.'category_product` cp
  674. LEFT JOIN `'._DB_PREFIX_.'category` c ON (c.`id_category` = cp.`id_category`)
  675. '.Shop::addSqlAssociation('category', 'c', true).'
  676. WHERE cp.`id_category` NOT IN ('.implode(',', array_map('intval', $categories)).')
  677. AND cp.id_product = '.$this->id
  678. );
  679. foreach ($result as $categ_to_delete)
  680. $this->deleteCategory($categ_to_delete['id_category']);
  681. // if none are found, it's an error
  682. if (!is_array($result))
  683. return false;
  684. if (!$this->addToCategories($categories))
  685. return false;
  686. SpecificPriceRule::applyAllRules(array((int)$this->id));
  687. return true;
  688. }
  689. /**
  690. * deleteCategory delete this product from the category $id_category
  691. *
  692. * @param mixed $id_category
  693. * @param mixed $clean_positions
  694. * @return boolean
  695. */
  696. public function deleteCategory($id_category, $clean_positions = true)
  697. {
  698. $result = Db::getInstance()->executeS(
  699. 'SELECT `id_category`
  700. FROM `'._DB_PREFIX_.'category_product`
  701. WHERE `id_product` = '.(int)$this->id.'
  702. AND id_category = '.(int)$id_category.''
  703. );
  704. $return = Db::getInstance()->delete('category_product', 'id_product = '.(int)$this->id.' AND id_category = '.(int)$id_category);
  705. if ($clean_positions === true)
  706. foreach ($result as $row)
  707. $this->cleanPositions((int)$row['id_category']);
  708. SpecificPriceRule::applyAllRules(array((int)$this->id));
  709. return $return;
  710. }
  711. /**
  712. * Delete all association to category where product is indexed
  713. *
  714. * @param boolean $clean_positions clean category positions after deletion
  715. * @return array Deletion result
  716. */
  717. public function deleteCategories($clean_positions = false)
  718. {
  719. $return = Db::getInstance()->delete('category_product', 'id_product = '.(int)$this->id);
  720. if ($clean_positions === true)
  721. {
  722. $result = Db::getInstance()->executeS(
  723. 'SELECT `id_category`
  724. FROM `'._DB_PREFIX_.'category_product`
  725. WHERE `id_product` = '.(int)$this->id
  726. );
  727. foreach ($result as $row)
  728. $this->cleanPositions((int)$row['id_category']);
  729. }
  730. return $return;
  731. }
  732. /**
  733. * Delete products tags entries
  734. *
  735. * @return array Deletion result
  736. */
  737. public function deleteTags()
  738. {
  739. return Db::getInstance()->delete('product_tag', 'id_product = '.(int)$this->id)
  740. && Db::getInstance()->delete('tag', 'id_tag NOT IN (SELECT id_tag FROM '._DB_PREFIX_.'product_tag)');
  741. }
  742. /**
  743. * Delete product from cart
  744. *
  745. * @return array Deletion result
  746. */
  747. public function deleteCartProducts()
  748. {
  749. return Db::getInstance()->delete('cart_product', 'id_product = '.(int)$this->id);
  750. }
  751. /**
  752. * Delete product images from database
  753. *
  754. * @return bool success
  755. */
  756. public function deleteImages()
  757. {
  758. $result = Db::getInstance()->executeS('
  759. SELECT `id_image`
  760. FROM `'._DB_PREFIX_.'image`
  761. WHERE `id_product` = '.(int)$this->id
  762. );
  763. $status = true;
  764. if ($result)
  765. foreach ($result as $row)
  766. {
  767. $image = new Image($row['id_image']);
  768. $status &= $image->delete();
  769. }
  770. return $status;
  771. }
  772. /**
  773. * @deprecated 1.5.0 Use Combination::getPrice()
  774. */
  775. public static function getProductAttributePrice($id_product_attribute)
  776. {
  777. return Combination::getPrice($id_product_attribute);
  778. }
  779. /**
  780. * Get all available products
  781. *
  782. * @param integer $id_lang Language id
  783. * @param integer $start Start number
  784. * @param integer $limit Number of products to return
  785. * @param string $order_by Field for ordering
  786. * @param string $order_way Way for ordering (ASC or DESC)
  787. * @return array Products details
  788. */
  789. public static function getProducts($id_lang, $start, $limit, $order_by, $order_way, $id_category = false,
  790. $only_active = false, Context $context = null)
  791. {
  792. if (!$context)
  793. $context = Context::getContext();
  794. $front = true;
  795. if (!in_array($context->controller->controller_type, array('front', 'modulefront')))
  796. $front = false;
  797. if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way))
  798. die (Tools::displayError());
  799. if ($order_by == 'id_product' || $order_by == 'price' || $order_by == 'date_add' || $order_by == 'date_upd')
  800. $order_by_prefix = 'p';
  801. else if ($order_by == 'name')
  802. $order_by_prefix = 'pl';
  803. else if ($order_by == 'position')
  804. $order_by_prefix = 'c';
  805. if (strpos($order_by, '.') > 0)
  806. {
  807. $order_by = explode('.', $order_by);
  808. $order_by_prefix = $order_by[0];
  809. $order_by = $order_by[1];
  810. }
  811. $sql = 'SELECT p.*, product_shop.*, pl.* , t.`rate` AS tax_rate, m.`name` AS manufacturer_name, s.`name` AS supplier_name
  812. FROM `'._DB_PREFIX_.'product` p
  813. '.Shop::addSqlAssociation('product', 'p').'
  814. LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.Shop::addSqlRestrictionOnLang('pl').')
  815. LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
  816. AND tr.`id_country` = '.(int)Context::getContext()->country->id.'
  817. AND tr.`id_state` = 0)
  818. LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)
  819. LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
  820. LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)'.
  821. ($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '').'
  822. WHERE pl.`id_lang` = '.(int)$id_lang.
  823. ($id_category ? ' AND c.`id_category` = '.(int)$id_category : '').
  824. ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').
  825. ($only_active ? ' AND product_shop.`active` = 1' : '').'
  826. ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').'`'.pSQL($order_by).'` '.pSQL($order_way).
  827. ($limit > 0 ? ' LIMIT '.(int)$start.','.(int)$limit : '');
  828. $rq = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
  829. if ($order_by == 'price')
  830. Tools::orderbyPrice($rq, $order_way);
  831. return ($rq);
  832. }
  833. public static function getSimpleProducts($id_lang, Context $context = null)
  834. {
  835. if (!$context)
  836. $context = Context::getContext();
  837. $front = true;
  838. if (!in_array($context->controller->controller_type, array('front', 'modulefront')))
  839. $front = false;
  840. $sql = 'SELECT p.`id_product`, pl.`name`
  841. FROM `'._DB_PREFIX_.'product` p
  842. '.Shop::addSqlAssociation('product', 'p').'
  843. LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.Shop::addSqlRestrictionOnLang('pl').')
  844. WHERE pl.`id_lang` = '.(int)$id_lang.'
  845. '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').'
  846. ORDER BY pl.`name`';
  847. return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
  848. }
  849. public function isNew()
  850. {
  851. $result = Db::getInstance()->executeS('
  852. SELECT p.id_product
  853. FROM `'._DB_PREFIX_.'product` p
  854. '.Shop::addSqlAssociation('product', 'p').'
  855. WHERE p.id_product = '.(int)$this->id.'
  856. AND DATEDIFF(
  857. product_shop.`date_add`,
  858. DATE_SUB(
  859. NOW(),
  860. INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY
  861. )
  862. ) > 0
  863. ');
  864. return count($result) > 0;
  865. }
  866. public function productAttributeExists($attributes_list, $current_product_attribute = false, Context $context = null, $all_shops = false, $return_id = false)
  867. {
  868. if (!Combination::isFeatureActive())
  869. return false;
  870. if ($context === null)
  871. $context = Context::getContext();
  872. $result = Db::getInstance()->executeS(
  873. 'SELECT pac.`id_attribute`, pac.`id_product_attribute`
  874. FROM `'._DB_PREFIX_.'product_attribute` pa
  875. JOIN `'._DB_PREFIX_.'product_attribute_shop` pas ON (pas.id_product_attribute = pa.id_product_attribute)
  876. LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_product_attribute` = pa.`id_product_attribute`)
  877. WHERE 1 '.(!$all_shops ? ' AND pas.id_shop ='.(int)$context->shop->id : '').' AND pa.`id_product` = '.(int)$this->id.
  878. ($all_shops ? ' GROUP BY pac.id_attribute, pac.id_product_attribute ' : '')
  879. );
  880. /* If something's wrong */
  881. if (!$result || empty($result))
  882. return false;
  883. /* Product attributes simulation */
  884. $product_attributes = array();
  885. foreach ($result as $product_attribute)
  886. $product_attributes[$product_attribute['id_product_attribute']][] = $product_attribute['id_attribute'];
  887. /* Checking product's attribute existence */
  888. foreach ($product_attributes as $key => $product_attribute)
  889. if (count($product_attribute) == count($attributes_list))
  890. {
  891. $diff = false;
  892. for ($i = 0; $diff == false && isset($product_attribute[$i]); $i++)
  893. if (!in_array($product_attribute[$i], $attributes_list) || $key == $current_product_attribute)
  894. $diff = true;
  895. if (!$diff)
  896. {
  897. if ($return_id)
  898. return $key;
  899. return true;
  900. }
  901. }
  902. return false;
  903. }
  904. /**
  905. * addProductAttribute is deprecated
  906. *
  907. * The quantity params now set StockAvailable for the current shop with the specified quantity
  908. * The supplier_reference params now set the supplier reference of the default supplier of the product if possible
  909. *
  910. * @see StockManager if you want to manage real stock
  911. * @see StockAvailable if you want to manage available quantities for sale on your shop(s)
  912. * @see ProductSupplier for manage supplier reference(s)
  913. *
  914. * @deprecated since 1.5.0
  915. */
  916. public function addProductAttribute($price, $weight, $unit_impact, $ecotax, $quantity, $id_images, $reference,
  917. $id_supplier = null, $ean13, $default, $location = null, $upc = null, $minimal_quantity = 1)
  918. {
  919. Tools::displayAsDeprecated();
  920. $id_product_attribute = $this->addAttribute(
  921. $price, $weight, $unit_impact, $ecotax, $id_images,
  922. $reference, $ean13, $default, $location, $upc, $minimal_quantity
  923. );
  924. if (!$id_product_attribute)
  925. return false;
  926. StockAvailable::setQuantity($this->id, $id_product_attribute, $quantity);
  927. //Try to set the default supplier reference
  928. $this->addSupplierReference($id_supplier, $id_product_attribute);
  929. return $id_product_attribute;
  930. }
  931. public function generateMultipleCombinations($combinations, $attributes)
  932. {
  933. $attributes_list = array();
  934. $res = true;
  935. $default_on = 1;
  936. foreach ($combinations as $key => $combination)
  937. {
  938. $id_combination = (int)$this->productAttributeExists($attributes[$key], false, null, true, true);
  939. $obj = new Combination($id_combination);
  940. if ($id_combination)
  941. {
  942. $obj->minimal_quantity = 1;
  943. $obj->available_date = '0000-00-00';
  944. }
  945. foreach ($combination as $field => $value)
  946. $obj->$field = $value;
  947. $obj->default_on = $default_on;
  948. $default_on = 0;
  949. $obj->save();
  950. if (!$id_combination)
  951. {
  952. $attribute_list = array();
  953. foreach ($attributes[$key] as $id_attribute)
  954. $attribute_list[] = array(
  955. 'id_product_attribute' => (int)$obj->id,
  956. 'id_attribute' => (int)$id_attribute
  957. );
  958. $res &= Db::getInstance()->insert('product_attribute_combination', $attribute_list);
  959. }
  960. }
  961. return $res;
  962. }
  963. /**
  964. * @param integer $quantity DEPRECATED
  965. * @param string $supplier_reference DEPRECATED
  966. */
  967. public function addCombinationEntity($wholesale_price, $price, $weight, $unit_impact, $ecotax, $quantity,
  968. $id_images, $reference, $id_supplier, $ean13, $default, $location = null, $upc = null, $minimal_quantity = 1, array $id_shop_list = array())
  969. {
  970. $id_product_attribute = $this->addAttribute(
  971. $price, $weight, $unit_impact, $ecotax, $id_images,
  972. $reference, $ean13, $default, $location, $upc, $minimal_quantity, $id_shop_list);
  973. $this->addSupplierReference($id_supplier, $id_product_attribute);
  974. $result = ObjectModel::updateMultishopTable('Combination', array(
  975. 'wholesale_price' => (float)$wholesale_price,
  976. ), 'a.id_product_attribute = '.(int)$id_product_attribute);
  977. if (!$id_product_attribute || !$result)
  978. return false;
  979. return $id_product_attribute;
  980. }
  981. public function addProductAttributeMultiple($attributes, $set_default = true)
  982. {
  983. Tools::displayAsDeprecated();
  984. $return = array();
  985. $default_value = 1;
  986. foreach ($attributes as &$attribute)
  987. {
  988. $obj = new Combination();
  989. foreach ($attribute as $key => $value)
  990. $obj->$key = $value;
  991. if ($set_default)
  992. {
  993. $obj->default_on = $default_value;
  994. $default_value = 0;
  995. // if we add a combination for this shop and this product does not use the combination feature in other shop,
  996. // we clone the default combination in every shop linked to this product
  997. if (!$this->hasAttributesInOtherShops())
  998. {
  999. $id_shop_list_array = Product::getShopsByProduct($this->id);
  1000. $id_shop_list = array();
  1001. foreach ($id_shop_list_array as $array_shop)
  1002. $id_shop_list[] = $array_shop['id_shop'];
  1003. $obj->id_shop_list = $id_shop_list;
  1004. }
  1005. }
  1006. $obj->add();
  1007. $return[] = $obj->id;
  1008. }
  1009. return $return;
  1010. }
  1011. /**
  1012. * Del all default attributes for product
  1013. */
  1014. public function deleteDefaultAttributes()
  1015. {
  1016. return ObjectModel::updateMultishopTable('Combination', array(
  1017. 'default_on' => 0,
  1018. ), 'id_product = '.(int)$this->id);
  1019. }
  1020. public function setDefaultAttribute($id_product_attribute)
  1021. {
  1022. $result = ObjectModel::updateMultishopTable('Combination', array(
  1023. 'default_on' => 1
  1024. ), '`id_product` = '.(int)$this->id.' AND a.`id_product_attribute` = '.(int)$id_product_attribute);
  1025. $result &= ObjectModel::updateMultishopTable('product', array(
  1026. 'cache_default_attribute' => (int)$id_product_attribute,
  1027. ), 'a.`id_product` = '.(int)$this->id);
  1028. return $result;
  1029. }
  1030. /**
  1031. * Update a product attribute
  1032. *
  1033. * @deprecated since 1.5
  1034. * @see updateAttribute() to use instead
  1035. * @see ProductSupplier for manage supplier reference(s)
  1036. *
  1037. */
  1038. public function updateProductAttribute($id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax,
  1039. $id_images, $reference, $id_supplier = null, $ean13, $default, $location = null, $upc = null, $minimal_quantity, $available_date)
  1040. {
  1041. Tools::displayAsDeprecated();
  1042. $return = $this->updateAttribute(
  1043. $id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax,
  1044. $id_images, $reference, $ean13, $default, $location = null, $upc = null, $minimal_quantity, $available_date
  1045. );
  1046. $this->addSupplierReference($id_supplier, $id_product_attribute);
  1047. return $return;
  1048. }
  1049. /**
  1050. * Sets Supplier Reference
  1051. *
  1052. * @param int $id_supplier
  1053. * @param int $id_product_attribute
  1054. * @param string $supplier_reference
  1055. * @param float $price
  1056. * @param int $id_currency
  1057. */
  1058. public function addSupplierReference($id_supplier, $id_product_attribute, $supplier_reference = null, $price = null, $id_currency = null)
  1059. {
  1060. //in some case we need to add price without supplier reference
  1061. if ($supplier_reference === null)
  1062. $supplier_reference = '';
  1063. //Try to set the default supplier reference
  1064. if ($id_supplier > 0)
  1065. {
  1066. $id_product_supplier = (int)ProductSupplier::getIdByProductAndSupplier($this->id, $id_product_attribute, $id_supplier);
  1067. if (!$id_product_supplier)
  1068. {
  1069. //create new record
  1070. $product_supplier_entity = new ProductSupplier();
  1071. $product_supplier_entity->id_product = (int)$this->id;
  1072. $product_supplier_entity->id_product_attribute = (int)$id_product_attribute;
  1073. $product_supplier_entity->id_supplier = (int)$id_supplier;
  1074. $product_supplier_entity->product_supplier_reference = pSQL($supplier_reference);
  1075. $product_supplier_entity->product_supplier_price_te = (int)$price;
  1076. $product_supplier_entity->id_currency = (int)$id_currency;
  1077. $product_supplier_entity->save();
  1078. }
  1079. else
  1080. {
  1081. $product_supplier = new ProductSupplier((int)$id_product_supplier);
  1082. $product_supplier->product_supplier_reference = pSQL($supplier_reference);
  1083. $product_supplier->update();
  1084. }
  1085. }
  1086. }
  1087. /**
  1088. * Update a product attribute
  1089. *
  1090. * @param integer $id_product_attribute Product attribute id
  1091. * @param float $wholesale_price Wholesale price
  1092. * @param float $price Additional price
  1093. * @param float $weight Additional weight
  1094. * @param float $unit
  1095. * @param float $ecotax Additional ecotax
  1096. * @param integer $id_image Image id
  1097. * @param string $reference Reference
  1098. * @param string $ean13 Ean-13 barcode
  1099. * @param int $default Default On
  1100. * @param string $upc Upc barcode
  1101. * @param string $minimal_quantity Minimal quantity
  1102. * @return array Update result
  1103. */
  1104. public function updateAttribute($id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax,
  1105. $id_images, $reference, $ean13, $default, $location = null, $upc = null, $minimal_quantity = null, $available_date = null, $update_all_fields = true, array $id_shop_list = array())
  1106. {
  1107. $combination = new Combination($id_product_attribute);
  1108. if (!$update_all_fields)
  1109. $combination->setFieldsToUpdate(array(
  1110. 'price' => !is_null($price),
  1111. 'wholesale_price' => !is_null($wholesale_price),
  1112. 'ecotax' => !is_null($ecotax),
  1113. 'weight' => !is_null($weight),
  1114. 'unit_price_impact' => !is_null($unit),
  1115. 'default_on' => !is_null($default),
  1116. 'minimal_quantity' => !is_null($minimal_quantity),
  1117. 'available_date' => !is_null($available_date),
  1118. ));
  1119. $price = str_replace(',', '.', $price);
  1120. $weight = str_replace(',', '.', $weight);
  1121. $combination->price = (float)$price;
  1122. $combination->wholesale_price = (float)$wholesale_price;
  1123. $combination->ecotax = (float)$ecotax;
  1124. $combination->weight = (float)$weight;
  1125. $combination->unit_price_impact = (float)$unit;
  1126. $combination->reference = pSQL($reference);
  1127. $combination->location = pSQL($location);
  1128. $combination->ean13 = pSQL($ean13);
  1129. $combination->upc = pSQL($upc);
  1130. $combination->default_on = (int)$default;
  1131. $combination->minimal_quantity = (int)$minimal_quantity;
  1132. $combination->available_date = $available_date ? pSQL($available_date) : '0000-00-00';
  1133. if (count($id_shop_list))
  1134. $combination->id_shop_list = $id_shop_list;
  1135. $combination->save();
  1136. if (!empty($id_images))
  1137. $combination->setImages($id_images);
  1138. Product::updateDefaultAttribute($this->id);
  1139. Hook::exec('actionProductAttributeUpdate', array('id_product_attribute' => $id_product_attribute));
  1140. return true;
  1141. }
  1142. /**
  1143. * Add a product attribute
  1144. * @since 1.5.0.1
  1145. *
  1146. * @param float $price Additional price
  1147. * @param float $weight Additional weight
  1148. * @param float $ecotax Additional ecotax
  1149. * @param integer $id_images Image ids
  1150. * @param string $reference Reference
  1151. * @param string $location Location
  1152. * @param string $ean13 Ean-13 barcode
  1153. * @param boolean $default Is default attribute for product
  1154. * @param integer $minimal_quantity Minimal quantity to add to cart
  1155. * @return mixed $id_product_attribute or false
  1156. */
  1157. public function addAttribute($price, $weight, $unit_impact, $ecotax, $id_images, $reference, $ean13,
  1158. $default, $location = null, $upc = null, $minimal_quantity = 1, array $id_shop_list = array())
  1159. {
  1160. if (!$this->id)
  1161. return;
  1162. $price = str_replace(',', '.', $price);
  1163. $weight = str_replace(',', '.', $weight);
  1164. $combination = new Combination();
  1165. $combination->id_product = (int)$this->id;
  1166. $combination->price = (float)$price;
  1167. $combination->ecotax = (float)$ecotax;
  1168. $combination->quantity = 0;
  1169. $combination->weight = (float)$weight;
  1170. $combination->unit_price_impact = (float)$unit_impact;
  1171. $combination->reference = pSQL($reference);
  1172. $combination->location = pSQL($location);
  1173. $combination->ean13 = pSQL($ean13);
  1174. $combination->upc = pSQL($upc);
  1175. $combination->default_on = (int)$default;
  1176. $combination->minimal_quantity = (int)$minimal_quantity;
  1177. // if we add a combination for this shop and this product does not use the combination feature in other shop,
  1178. // we clone the default combination in every shop linked to this product
  1179. if ($default && !$this->hasAttributesInOtherShops())
  1180. {
  1181. $id_shop_list_array = Product::getShopsByProduct($this->id);
  1182. foreach ($id_shop_list_array as $array_shop)
  1183. $id_shop_list[] = $array_shop['id_shop'];
  1184. }
  1185. if (count($id_shop_list))
  1186. $combination->id_shop_list = $id_shop_list;
  1187. $combination->add();
  1188. if (!$combination->id)
  1189. return false;
  1190. Product::updateDefaultAttribute($this->id);
  1191. if (!empty($id_images))
  1192. $combination->setImages($id_images);
  1193. return (int)$combination->id;
  1194. }
  1195. /**
  1196. * @deprecated since 1.5.0
  1197. */
  1198. public function updateQuantityProductWithAttributeQuantity()
  1199. {
  1200. Tools::displayAsDeprecated();
  1201. return Db::getInstance()->execute('
  1202. UPDATE `'._DB_PREFIX_.'product`
  1203. SET `quantity` = IFNULL(
  1204. (
  1205. SELECT SUM(`quantity`)
  1206. FROM `'._DB_PREFIX_.'product_attribute`
  1207. WHERE `id_product` = '.(int)$this->id.'
  1208. ), \'0\')
  1209. WHERE `id_product` = '.(int)$this->id);
  1210. }
  1211. /**
  1212. * Delete product attributes
  1213. *
  1214. * @return array Deletion result
  1215. */
  1216. public function deleteProductAttributes()
  1217. {
  1218. Hook::exec('actionProductAttributeDelete', array('id_product_attribute' => 0, 'id_product' => $this->id, 'deleteAllAttributes' => true));
  1219. $result = true;
  1220. $combinations = new Collection('Combination');
  1221. $combinations->where('id_product', '=', $this->id);
  1222. foreach ($combinations as $combination)
  1223. $result &= $combination->delete();
  1224. SpecificPriceRule::applyAllRules(array((int)$this->id));
  1225. return $result;
  1226. }
  1227. /**
  1228. * Delete product attributes impacts
  1229. *
  1230. * @return Deletion result
  1231. */
  1232. public function deleteAttributesImpacts()
  1233. {
  1234. return Db::getInstance()->execute(
  1235. 'DELETE FROM `'._DB_PREFIX_.'attribute_impact`
  1236. WHERE `id_product` = '.(int)$this->id
  1237. );
  1238. }
  1239. /**
  1240. * Delete product features
  1241. *
  1242. * @return array Deletion result
  1243. */
  1244. public function deleteProductFeatures()
  1245. {
  1246. SpecificPriceRule::applyAllRules(array((int)$this->id));
  1247. return $this->deleteFeatures();
  1248. }
  1249. /**
  1250. * Delete product attachments
  1251. *
  1252. * @return array Deletion result
  1253. */
  1254. public function deleteAttachments()
  1255. {
  1256. return Db::getInstance()->execute('
  1257. DELETE FROM `'._DB_PREFIX_.'product_attachment`
  1258. WHERE `id_product` = '.(int)$this->id
  1259. );
  1260. }
  1261. /**
  1262. * Delete product customizations
  1263. *
  1264. * @return array Deletion result
  1265. */
  1266. public function deleteCustomization()
  1267. {
  1268. return (
  1269. Db::getInstance()->execute(
  1270. 'DELETE FROM `'._DB_PREFIX_.'customization_field`
  1271. WHERE `id_product` = '.(int)$this->id
  1272. )
  1273. &&
  1274. Db::getInstance()->execute(
  1275. 'DELETE FROM `'._DB_PREFIX_.'customization_field_lang`
  1276. WHERE `id_customization_field` NOT IN (SELECT id_customization_field
  1277. FROM `'._DB_PREFIX_.'customization_field`)'
  1278. )
  1279. );
  1280. }
  1281. /**
  1282. * Delete product pack details
  1283. *
  1284. * @return array Deletion result
  1285. */
  1286. public function deletePack()
  1287. {
  1288. return Db::getInstance()->execute(
  1289. 'DELETE FROM `'._DB_PREFIX_.'pack`
  1290. WHERE `id_product_pack` = '.(int)$this->id.'
  1291. OR `id_product_item` = '.(int)$this->id
  1292. );
  1293. }
  1294. /**
  1295. * Delete product sales
  1296. *
  1297. * @return array Deletion result
  1298. */
  1299. public function deleteProductSale()
  1300. {
  1301. return Db::getInstance()->execute(
  1302. 'DELETE FROM `'._DB_PREFIX_.'product_sale`
  1303. WHERE `id_product` = '.(int)$this->id
  1304. );
  1305. }
  1306. /**
  1307. * Delete product in its scenes
  1308. *
  1309. * @return array Deletion result
  1310. */
  1311. public function deleteSceneProducts()
  1312. {
  1313. return Db::getInstance()->execute(
  1314. 'DELETE FROM `'._DB_PREFIX_.'scene_products`
  1315. WHERE `id_product` = '.(int)$this->id
  1316. );
  1317. }
  1318. /**
  1319. * Delete product indexed words
  1320. *
  1321. * @return array Deletion result
  1322. */
  1323. public function deleteSearchIndexes()
  1324. {
  1325. return (
  1326. Db::getInstance()->execute(
  1327. 'DELETE FROM `'._DB_PREFIX_.'search_index`
  1328. WHERE `id_product` = '.(int)$this->id
  1329. )
  1330. &&
  1331. Db::getInstance()->execute(
  1332. 'DELETE FROM `'._DB_PREF

Large files files are truncated, but you can click here to view the full file