PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/Product.php

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

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