PageRenderTime 55ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 1ms

/administrator/components/com_virtuemart/classes/ps_product.php

https://bitbucket.org/dgough/annamaria-daneswood-25102012
PHP | 2743 lines | 1976 code | 250 blank | 517 comment | 367 complexity | 912628defcddc12db09e6304fee92fb8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. if( !defined( '_VALID_MOS' ) && !defined( '_JEXEC' ) ) die( 'Direct Access to '.basename(__FILE__).' is not allowed.' );
  3. /**
  4. *
  5. * @version $Id: ps_product.php 1475 2008-07-16 17:35:35Z soeren_nb $
  6. * @package VirtueMart
  7. * @subpackage classes
  8. * @copyright Copyright (C) 2004-2008 soeren - All rights reserved.
  9. * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
  10. * VirtueMart is free software. This version may have been modified pursuant
  11. * to the GNU General Public License, and as distributed it includes or
  12. * is derivative of works licensed under the GNU General Public License or
  13. * other free or open source software licenses.
  14. * See /administrator/components/com_virtuemart/COPYRIGHT.php for copyright notices and details.
  15. *
  16. * http://virtuemart.net
  17. */
  18. /**
  19. * The class is is used to manage product repository.
  20. * @package virtuemart
  21. * @author pablo, jep, gday, soeren
  22. *
  23. */
  24. class ps_product extends vmAbstractObject {
  25. var $_key = 'product_id';
  26. var $_table_name = '#__{vm}_product';
  27. /**
  28. * Validates product fields and uploaded image files.
  29. *
  30. * @param array $d The input vars
  31. * @return boolean True when validation successful, false when not
  32. */
  33. function validate(&$d) {
  34. global $vmLogger, $database, $perm, $VM_LANG;
  35. require_once(CLASSPATH . 'imageTools.class.php' );
  36. $valid = true;
  37. $db = new ps_DB;
  38. $q = "SELECT product_id,product_thumb_image,product_full_image FROM #__{vm}_product WHERE product_sku='";
  39. $q .= $d["product_sku"] . "'";
  40. $db->setQuery($q); $db->query();
  41. if ($db->next_record()&&($db->f("product_id") != $d["product_id"])) {
  42. $vmLogger->err( "A Product with the SKU ".$d['product_sku']." already exists." );
  43. $valid = false;
  44. }
  45. if( !empty( $d['product_discount_id'] )) {
  46. if( $d['product_discount_id'] == "override" ) {
  47. $d['is_percent'] = "0";
  48. // If discount are applied before tax then base the discount on the untaxed price
  49. if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
  50. $d['amount'] = (float)$d['product_price'] - (float)$d['discounted_price_override'];
  51. }
  52. // Otherwise, base the discount on the taxed price
  53. else {
  54. $d['amount'] = (float)$d['product_price_incl_tax'] - (float)$d['discounted_price_override'];
  55. }
  56. // Set the discount start date as today
  57. $d['start_date'] = date( 'Y-m-d' );
  58. require_once( CLASSPATH. 'ps_product_discount.php' );
  59. $ps_product_discount = new ps_product_discount;
  60. $ps_product_discount->add( $d );
  61. $d['product_discount_id'] = $database->insertid();
  62. vmRequest::setVar( 'product_discount_id', $d['product_discount_id'] );
  63. }
  64. }
  65. if (empty($d['manufacturer_id'])) {
  66. $d['manufacturer_id'] = "1";
  67. }
  68. if (empty( $d["product_sku"])) {
  69. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_SKU',false) );
  70. $valid = false;
  71. }
  72. if (!$d["product_name"]) {
  73. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_NAME',false) );
  74. $valid = false;
  75. }
  76. if (empty($d["product_available_date"])) {
  77. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_AVAILDATE',false) );
  78. $valid = false;
  79. }
  80. else {
  81. $day = (int) substr ( $d["product_available_date"], 8, 2);
  82. $month= (int) substr ( $d["product_available_date"], 5, 2);
  83. $year = (int) substr ( $d["product_available_date"], 0, 4);
  84. $d["product_available_date_timestamp"] = mktime(0,0,0,$month, $day, $year);
  85. }
  86. /** Validate Product Specific Fields **/
  87. if (!$d["product_parent_id"]) {
  88. if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
  89. $d['product_categories'] = explode('|', $d['category_ids'] );
  90. }
  91. if (sizeof(@$d["product_categories"]) < 1) {
  92. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_CATEGORY') );
  93. $valid = false;
  94. }
  95. }
  96. /** Image Upload Validation **/
  97. // do we have an image URL or an image File Upload?
  98. if (!empty( $d['product_thumb_image_url'] )) {
  99. // Image URL
  100. if (substr( $d['product_thumb_image_url'], 0, 4) != "http") {
  101. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMAGEURL_MUSTBEGIN',false) );
  102. $valid = false;
  103. }
  104. // if we have an uploaded image file, prepare this one for deleting.
  105. if( $db->f("product_thumb_image") && substr( $db->f("product_thumb_image"), 0, 4) != "http") {
  106. $_REQUEST["product_thumb_image_curr"] = $db->f("product_thumb_image");
  107. $d["product_thumb_image_action"] = "delete";
  108. if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
  109. return false;
  110. }
  111. }
  112. $d["product_thumb_image"] = $d['product_thumb_image_url'];
  113. }
  114. else {
  115. // File Upload
  116. if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
  117. $valid = false;
  118. }
  119. }
  120. if (!empty( $d['product_full_image_url'] )) {
  121. // Image URL
  122. if (substr( $d['product_full_image_url'], 0, 4) != "http") {
  123. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMAGEURL_MUSTBEGIN',false) );
  124. return false;
  125. }
  126. // if we have an uploaded image file, prepare this one for deleting.
  127. if( $db->f("product_full_image") && substr( $db->f("product_full_image"), 0, 4) != "http") {
  128. $_REQUEST["product_full_image_curr"] = $db->f("product_full_image");
  129. $d["product_full_image_action"] = "delete";
  130. if (!vmImageTools::validate_image($d,"product_full_image","product")) {
  131. return false;
  132. }
  133. }
  134. $d["product_full_image"] = $d['product_full_image_url'];
  135. }
  136. else {
  137. // File Upload
  138. if (!vmImageTools::validate_image($d,"product_full_image","product")) {
  139. $valid = false;
  140. }
  141. }
  142. // added for advanced attribute modification
  143. // strips the trailing semi-colon from an attribute
  144. if(isset($d["product_advanced_attribute"])) {
  145. if (';' == substr($d["product_advanced_attribute"], strlen($d["product_advanced_attribute"])-1,1) ) {
  146. $d["product_advanced_attribute"] =substr($d["product_advanced_attribute"], 0, strlen($d["product_advanced_attribute"])-1);
  147. }
  148. }
  149. // added for custom attribute modification
  150. // strips the trailing semi-colon from an attribute
  151. if(isset($d["product_custom_attribute"])) {
  152. if (';' == substr($d["product_custom_attribute"], strlen($d["product_custom_attribute"])-1,1) ) {
  153. $d["product_custom_attribute"] =substr($d["product_custom_attribute"], 0, strlen($d["product_custom_attribute"])-1);
  154. }
  155. }
  156. $d["clone_product"] = empty($d["clone_product"]) ? "N" : "Y";
  157. $d["product_publish"] = empty($d["product_publish"]) ? "N" : "Y";
  158. $d["product_special"] = empty($d["product_special"]) ? "N" : "Y";
  159. //parse quantity and child options
  160. $d['display_headers'] = vmGet($d,'display_headers', 'Y') =='Y' ? 'Y' : 'N';
  161. $d['product_list_child'] = vmGet($d,'product_list_child', 'Y') =='Y' ? 'Y' : 'N';
  162. $d['display_use_parent'] = vmGet($d,'display_use_parent', 'Y') =='Y' ? 'Y' : 'N';
  163. $d['product_list_type'] = vmGet($d,'product_list_type', 'Y') =='Y' ? 'Y' : 'N';
  164. $d['display_desc'] = vmGet($d,'display_desc', 'Y') =='Y' ? 'Y' : 'N';
  165. if (@$d['product_list'] =="Y") {
  166. if($d['list_style'] == "one")
  167. $d['product_list'] = "Y";
  168. else
  169. $d['product_list'] = "YM";
  170. }
  171. else {
  172. $d['product_list'] = "N";
  173. }
  174. $d['quantity_options'] = ps_product::set_quantity_options($d);
  175. $d['child_options'] = ps_product::set_child_options($d);
  176. $d['order_levels'] = vmRequest::getInt('min_order_level').",".vmRequest::getInt('max_order_level');
  177. return $valid;
  178. }
  179. /**
  180. * Validates that a product can be deleted
  181. *
  182. * @param array $d The input vars
  183. * @return boolean Validation sucessful?
  184. */
  185. function validate_delete( $product_id, &$d ) {
  186. global $vmLogger, $VM_LANG;
  187. require_once(CLASSPATH . 'imageTools.class.php' );
  188. /* Check that ps_vendor_id and product_id match
  189. if (!$this->check_vendor($d)) {
  190. $d["error"] = "ERROR: Cannot delete product. Wrong product or vendor." ;
  191. return false;
  192. }*/
  193. if (empty($product_id)) {
  194. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_SPECIFY_DELETE',false) );
  195. return false;
  196. }
  197. /* Get the image filenames from the database */
  198. $db = new ps_DB;
  199. $q = "SELECT product_thumb_image,product_full_image ";
  200. $q .= "FROM #__{vm}_product ";
  201. $q .= "WHERE product_id='$product_id'";
  202. $db->setQuery($q); $db->query();
  203. $db->next_record();
  204. /* Prepare product_thumb_image for Deleting */
  205. if( !stristr( $db->f("product_thumb_image"), "http") ) {
  206. $_REQUEST["product_thumb_image_curr"] = $db->f("product_thumb_image");
  207. $d["product_thumb_image_action"] = "delete";
  208. if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
  209. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMGDEL_FAILED',false) );
  210. return false;
  211. }
  212. }
  213. /* Prepare product_full_image for Deleting */
  214. if( !stristr( $db->f("product_full_image"), "http") ) {
  215. $_REQUEST["product_full_image_curr"] = $db->f("product_full_image");
  216. $d["product_full_image_action"] = "delete";
  217. if (!vmImageTools::validate_image($d,"product_full_image","product")) {
  218. return false;
  219. }
  220. }
  221. return true;
  222. }
  223. /**
  224. * Function to add a new product into the product table
  225. *
  226. * @param array $d The input vars
  227. * @return boolean True, when the product was added, false when not
  228. */
  229. function add( &$d ) {
  230. global $perm, $vmLogger, $VM_LANG;
  231. $database = new ps_DB();
  232. if (!$this->validate($d)) {
  233. return false;
  234. }
  235. if (!vmImageTools::process_images($d)) {
  236. return false;
  237. }
  238. $timestamp = time();
  239. $db = new ps_DB;
  240. $ps_vendor_id = $_SESSION["ps_vendor_id"];
  241. if( $perm->check( 'admin' )) {
  242. $vendor_id = $d['vendor_id'];
  243. }
  244. else {
  245. $vendor_id = $ps_vendor_id;
  246. }
  247. // Insert into DB
  248. $fields = array ( 'vendor_id' => $vendor_id,
  249. 'product_parent_id' => vmRequest::getInt('product_parent_id'),
  250. 'product_sku' => vmGet($d,'product_sku'),
  251. 'product_name' => vmGet($d,'product_name'),
  252. 'product_desc' => vmRequest::getVar('product_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
  253. 'product_s_desc' => vmRequest::getVar('product_s_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
  254. 'product_thumb_image' => vmGet($d,'product_thumb_image'),
  255. 'product_full_image' => vmGet($d,'product_full_image'),
  256. 'product_publish' => $d['product_publish'],
  257. 'product_weight' => vmRequest::getFloat('product_weight'),
  258. 'product_weight_uom' => vmGet($d,'product_weight_uom'),
  259. 'product_length' => vmRequest::getFloat('product_length'),
  260. 'product_width' => vmRequest::getFloat('product_width'),
  261. 'product_height' => vmRequest::getFloat('product_height'),
  262. 'product_lwh_uom' => vmGet($d,'product_lwh_uom'),
  263. 'product_unit' => vmGet($d,'product_unit'),
  264. 'product_packaging' => (($d["product_box"] << 16) | ($d["product_packaging"]&0xFFFF)),
  265. 'product_url' => vmGet($d,'product_url'),
  266. 'product_in_stock' => vmRequest::getInt('product_in_stock'),
  267. 'attribute' => ps_product_attribute::formatAttributeX(),
  268. 'custom_attribute' => vmGet($d,'product_custom_attribute'),
  269. 'product_available_date' => $d['product_available_date_timestamp'],
  270. 'product_availability' => vmGet($d,'product_availability'),
  271. 'product_special' => $d['product_special'],
  272. 'child_options' => $d['child_options'],
  273. 'quantity_options' => $d['quantity_options'],
  274. 'product_discount_id' => vmRequest::getInt('product_discount_id'),
  275. 'cdate' => $timestamp,
  276. 'mdate' => $timestamp,
  277. 'product_tax_id' => vmRequest::getInt('product_tax_id'),
  278. 'child_option_ids' => vmGet($d,'included_product_id'),
  279. 'product_order_levels' => $d['order_levels'] );
  280. $db->buildQuery('INSERT', '#__{vm}_product', $fields );
  281. if( $db->query() === false ) {
  282. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_ADDING_FAILED',false) );
  283. return false;
  284. }
  285. $d["product_id"] = $_REQUEST['product_id'] = $db->last_insert_id();
  286. // If is Item, add attributes from parent //
  287. if ($d["product_parent_id"]) {
  288. $q = "SELECT attribute_name FROM #__{vm}_product_attribute_sku ";
  289. $q .= "WHERE product_id='" . vmRequest::getInt('product_parent_id') . "' ";
  290. $q .= "ORDER BY attribute_list,attribute_name";
  291. $db->query($q);
  292. $db2 = new ps_DB;
  293. $i = 0;
  294. while($db->next_record()) {
  295. $i++;
  296. $q = "INSERT INTO #__{vm}_product_attribute (`product_id`,`attribute_name`,`attribute_value`) VALUES ";
  297. $q .= "('".$d["product_id"]."', '".$db->f("attribute_name", false)."', '".vmGet($d,'attribute_'.$i )."')";
  298. $db2->query( $q );
  299. }
  300. }
  301. else {
  302. // If is Product, Insert category ids
  303. if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
  304. $d['product_categories'] = explode('|', $d['category_ids'] );
  305. }
  306. foreach( $d["product_categories"] as $category_id ) {
  307. $db->query('SELECT MAX(`product_list`) as list_order FROM `#__{vm}_product_category_xref` WHERE `category_id`='.$category_id );
  308. $db->next_record();
  309. $q = "INSERT INTO #__{vm}_product_category_xref ";
  310. $q .= "(category_id,product_id,product_list) ";
  311. $q .= "VALUES ('$category_id','". $d["product_id"] . "', ".intval($db->f('max') +1 ) . ")";
  312. $db->setQuery($q); $db->query();
  313. }
  314. }
  315. $q = "INSERT INTO #__{vm}_product_mf_xref VALUES (";
  316. $q .= "'".$d['product_id']."', '".vmRequest::getInt('manufacturer_id')."')";
  317. $db->setQuery($q); $db->query();
  318. if( !empty($d["related_products"])) {
  319. /* Insert Pipe separated Related Product IDs */
  320. $related_products = vmGet( $d, "related_products" );
  321. $q = "INSERT INTO #__{vm}_product_relations ";
  322. $q .= "(product_id, related_products) ";
  323. $q .= "VALUES ('".$d["product_id"]."','".$db->getEscaped($related_products)."')";
  324. $db->setQuery($q); $db->query();
  325. }
  326. // ADD A PRICE, IF NOT EMPTY ADD 0
  327. if (!empty($d['product_price'])) {
  328. if(empty($d['product_currency'])) {
  329. $d['product_currency'] = $_SESSION['vendor_currency'];
  330. }
  331. $d["price_quantity_start"] = 0;
  332. $d["price_quantity_end"] = "";
  333. require_once ( CLASSPATH. 'ps_product_price.php');
  334. $my_price = new ps_product_price;
  335. $my_price->add($d);
  336. }
  337. if( !empty( $d['product_type_id'])) {
  338. require_once( CLASSPATH.'ps_product_product_type.php' );
  339. $ps_product_product_type = new ps_product_product_type();
  340. $ps_product_product_type->add( $d );
  341. // Product Type Parameters!
  342. $this->handleParameters( $d );
  343. }
  344. // CLONE PRODUCT additional code
  345. if( $d["clone_product"] == "Y" ) {
  346. // Clone Parent Product's Attributes
  347. $q = "INSERT INTO #__{vm}_product_attribute_sku
  348. SELECT '".$d["product_id"]."', attribute_name, attribute_list
  349. FROM #__{vm}_product_attribute_sku WHERE product_id='" . (int)$d["old_product_id"] . "' ";
  350. $db->query( $q );
  351. if( !empty( $d["child_items"] )) {
  352. $database->query( "SHOW COLUMNS FROM #__{vm}_product" );
  353. $rows = $database->record;
  354. while(list(,$Field) = each( $rows) ) {
  355. $product_fields[$Field->Field] = $Field->Field;
  356. }
  357. // Change the Field Names
  358. // leave empty for auto_increment
  359. $product_fields["product_id"] = "''";
  360. // Update Product Parent ID to the new one
  361. $product_fields["product_parent_id"] = "'".$d["product_id"]."'";
  362. // Rename the SKU
  363. $product_fields["product_sku"] = "CONCAT(product_sku,'_".$d["product_id"]."')";
  364. $rows = Array();
  365. $database->query( "SHOW COLUMNS FROM #__{vm}_product_price" );
  366. $rows = $database->record;
  367. while(list(,$Field) = each( $rows) ) {
  368. $price_fields[$Field->Field] = $Field->Field;
  369. }
  370. foreach( $d["child_items"] as $child_id ) {
  371. $q = "INSERT INTO #__{vm}_product ";
  372. $q .= "SELECT ".implode(",", $product_fields )." FROM #__{vm}_product WHERE product_id='$child_id'";
  373. $db->query( $q );
  374. $new_product_id = $db->last_insert_id();
  375. $q = "INSERT INTO #__{vm}_product_attribute
  376. SELECT NULL, '$new_product_id', attribute_name, attribute_value
  377. FROM #__{vm}_product_attribute WHERE product_id='$child_id'";
  378. $db->query( $q );
  379. $price_fields["product_price_id"] = "''";
  380. $price_fields["product_id"] = "'$new_product_id'";
  381. $q = "INSERT INTO #__{vm}_product_price ";
  382. $q .= "SELECT ".implode(",", $price_fields )." FROM #__{vm}_product_price WHERE product_id='$child_id'";
  383. $db->query( $q );
  384. }
  385. }
  386. // End Cloning
  387. }
  388. if( $d['clone_product'] == 'Y') {
  389. $vmLogger->info( $VM_LANG->_('VM_PRODUCT_CLONED',false) );
  390. }
  391. else {
  392. $vmLogger->info( $VM_LANG->_('VM_PRODUCT_ADDED',false) );
  393. }
  394. return true;
  395. }
  396. /**
  397. * Function to update product $d['product_id'] in the product table
  398. *
  399. * @param array $d The input vars
  400. * @return boolean True, when the product was updated, false when not
  401. */
  402. function update( &$d ) {
  403. global $vmLogger, $perm, $VM_LANG;
  404. require_once(CLASSPATH.'ps_product_attribute.php');
  405. if (!$this->validate($d)) {
  406. return false;
  407. }
  408. if (!vmImageTools::process_images($d)) {
  409. return false;
  410. }
  411. $timestamp = time();
  412. $db = new ps_DB;
  413. $ps_vendor_id = $_SESSION["ps_vendor_id"];
  414. if( $perm->check( 'admin' )) {
  415. $vendor_id = $d['vendor_id'];
  416. }
  417. else {
  418. $vendor_id = $ps_vendor_id;
  419. }
  420. $old_vendor_id = $this->get_field($d['product_id'], 'vendor_id');
  421. // Insert into DB
  422. $fields = array ( 'vendor_id' => $vendor_id,
  423. 'product_sku' => vmGet($d,'product_sku'),
  424. 'product_name' => vmGet($d,'product_name'),
  425. 'product_desc' => vmRequest::getVar('product_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
  426. 'product_s_desc' => vmRequest::getVar('product_s_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
  427. 'product_thumb_image' => vmGet($d,'product_thumb_image'),
  428. 'product_full_image' => vmGet($d,'product_full_image'),
  429. 'product_publish' => $d['product_publish'],
  430. 'product_weight' => vmRequest::getFloat('product_weight'),
  431. 'product_weight_uom' => vmGet($d,'product_weight_uom'),
  432. 'product_length' => vmRequest::getFloat('product_length'),
  433. 'product_width' => vmRequest::getFloat('product_width'),
  434. 'product_height' => vmRequest::getFloat('product_height'),
  435. 'product_lwh_uom' => vmGet($d,'product_lwh_uom'),
  436. 'product_unit' => vmGet($d,'product_unit'),
  437. 'product_packaging' => (($d["product_box"] << 16) | ($d["product_packaging"]&0xFFFF)),
  438. 'product_url' => vmGet($d,'product_url'),
  439. 'product_in_stock' => vmRequest::getInt('product_in_stock'),
  440. 'attribute' => ps_product_attribute::formatAttributeX(),
  441. 'custom_attribute' => vmGet($d,'product_custom_attribute'),
  442. 'product_available_date' => $d['product_available_date_timestamp'],
  443. 'product_availability' => vmGet($d,'product_availability'),
  444. 'product_special' => $d['product_special'],
  445. 'child_options' => $d['child_options'],
  446. 'quantity_options' => $d['quantity_options'],
  447. 'product_discount_id' => vmRequest::getInt('product_discount_id'),
  448. 'mdate' => $timestamp,
  449. 'product_tax_id' => vmRequest::getInt('product_tax_id'),
  450. 'child_option_ids' => vmGet($d,'included_product_id'),
  451. 'product_order_levels' => $d['order_levels'] );
  452. $db->buildQuery( 'UPDATE', '#__{vm}_product', $fields, 'WHERE product_id='. (int)$d["product_id"] . ' AND vendor_id=' . (int)$old_vendor_id );
  453. $db->query();
  454. /* notify the shoppers that the product is here */
  455. /* see zw_waiting_list */
  456. if ($d["product_in_stock"] > "0" && @$d['notify_users'] == '1' && $d['product_in_stock_old'] == '0') {
  457. require_once( CLASSPATH . 'zw_waiting_list.php');
  458. $zw_waiting_list = new zw_waiting_list;
  459. $zw_waiting_list->notify_list($d["product_id"]);
  460. }
  461. $q = "UPDATE #__{vm}_product_mf_xref SET ";
  462. $q .= 'manufacturer_id='.vmRequest::getInt('manufacturer_id').' ';
  463. $q .= 'WHERE product_id = '.$d['product_id'];
  464. $db->query($q);
  465. /* If is Item, update attributes */
  466. if( !empty($d["product_parent_id"])) {
  467. $q = "SELECT attribute_name FROM #__{vm}_product_attribute_sku ";
  468. $q .= 'WHERE product_id=' .(int)$d["product_parent_id"] . ' ';
  469. $q .= "ORDER BY attribute_list,attribute_name";
  470. $db->query($q);
  471. $db2 = new ps_DB;
  472. $i = 0;
  473. while($db->next_record()) {
  474. $i++;
  475. $q2 = "UPDATE #__{vm}_product_attribute SET ";
  476. $q2 .= "attribute_value='" .vmGet($d,'attribute_'.$i ) . "' ";
  477. $q2 .= "WHERE product_id = '" . $d["product_id"] . "' ";
  478. $q2 .= "AND attribute_name = '" . $db->f("attribute_name", false ) . "' ";
  479. $db2->setQuery($q2); $db2->query();
  480. }
  481. /* If it is a Product, update Category */
  482. }
  483. else {
  484. // Handle category selection: product_category_xref
  485. $q = "SELECT `category_id` FROM `#__{vm}_product_category_xref` ";
  486. $q .= "WHERE `product_id` = '" . $d["product_id"] . "' ";
  487. $db->setQuery($q);
  488. $db->query();
  489. $old_categories = array();
  490. while( $db->next_record()) {
  491. $old_categories[$db->f('category_id')] = $db->f('category_id');
  492. }
  493. // NOW Insert new categories
  494. $new_categories = array();
  495. if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
  496. $d['product_categories'] = explode('|', $d['category_ids'] );
  497. }
  498. foreach( $d["product_categories"] as $category_id ) {
  499. if( !in_array( $category_id, $old_categories ) ) {
  500. $db->query('SELECT MAX(`product_list`) as list_order FROM `#__{vm}_product_category_xref` WHERE `category_id`='.(int)$category_id );
  501. $db->next_record();
  502. $q = "INSERT INTO #__{vm}_product_category_xref ";
  503. $q .= "(category_id,product_id,product_list) ";
  504. $q .= "VALUES ('".(int)$category_id."','". $d["product_id"] . "', ".intval($db->f('max') +1 ) . ")";
  505. $db->setQuery($q); $db->query();
  506. $new_categories[$category_id] = $category_id;
  507. }
  508. else {
  509. unset( $old_categories[$category_id]);
  510. }
  511. }
  512. // The rest of the old categories can be deleted
  513. foreach( $old_categories as $category_id ) {
  514. $q = "DELETE FROM `#__{vm}_product_category_xref` ";
  515. $q .= "WHERE `product_id` = '" . $d["product_id"] . "' ";
  516. $q .= "AND `category_id` = '" . $category_id . "' ";
  517. $db->query($q);
  518. }
  519. }
  520. if( !empty($d["related_products"])) {
  521. /* Insert Pipe separated Related Product IDs */
  522. $related_products = vmGet( $d, "related_products" );
  523. $q = "REPLACE INTO #__{vm}_product_relations (product_id, related_products)";
  524. $q .= " VALUES( '".$d["product_id"]."', '$related_products') ";
  525. $db->query($q);
  526. }
  527. else{
  528. $q = "DELETE FROM #__{vm}_product_relations WHERE product_id='".$d["product_id"]."'";
  529. $db->query($q);
  530. }
  531. // UPDATE THE PRICE, IF EMPTY ADD 0
  532. if(empty($d['product_currency'])) {
  533. $d['product_currency'] = $_SESSION['vendor_currency'];
  534. }
  535. // look if we have a price for this product
  536. $q = "SELECT product_price_id, price_quantity_start, price_quantity_end FROM #__{vm}_product_price ";
  537. $q .= "WHERE shopper_group_id=" . vmRequest::getInt('shopper_group_id');
  538. $q .= ' AND product_id = ' . $d["product_id"];
  539. $db->query($q);
  540. if ($db->next_record()) {
  541. $d["product_price_id"] = $db->f("product_price_id");
  542. require_once ( CLASSPATH. 'ps_product_price.php');
  543. $my_price = new ps_product_price;
  544. if (@$d['product_price'] != '') {
  545. // update prices
  546. $d["price_quantity_start"] = $db->f("price_quantity_start");
  547. $d["price_quantity_end"] = $db->f("price_quantity_end");
  548. $my_price->update($d);
  549. }
  550. else {
  551. // delete the price
  552. $my_price->delete( $d );
  553. }
  554. }
  555. else {
  556. if ( $d['product_price'] != '' ) {
  557. // add the price
  558. $d["price_quantity_start"] = 0;
  559. $d["price_quantity_end"] = "";
  560. require_once ( CLASSPATH. 'ps_product_price.php');
  561. $my_price = new ps_product_price;
  562. $my_price->add($d);
  563. }
  564. }
  565. // Product Type Parameters!
  566. $this->handleParameters( $d );
  567. $vmLogger->info( $VM_LANG->_('VM_PRODUCT_UPDATED',false) );
  568. return true;
  569. }
  570. /**
  571. * Handles adding or updating parameter values for a product an its product types
  572. * @since VirtueMart 1.1.0
  573. * @param array $d
  574. */
  575. function handleParameters( &$d ) {
  576. global $db;
  577. $product_id= intval( $d["product_id"] );
  578. $q = "SELECT `product_type_id` FROM `#__{vm}_product_product_type_xref` WHERE ";
  579. $q .= "`product_id`=$product_id";
  580. $db->query($q);
  581. $dbpt = new ps_DB;
  582. $dbp = new ps_DB;
  583. // For every Product Type
  584. while ($db->next_record()) {
  585. $product_type_id = $db->f("product_type_id");
  586. $q = "SELECT * FROM #__{vm}_product_type_parameter WHERE ";
  587. $q .= "product_type_id='$product_type_id' ";
  588. $q .= "ORDER BY parameter_list_order";
  589. $dbpt->query($q);
  590. $q = "SELECT COUNT(`product_id`) as num_rows FROM `#__{vm}_product_type_$product_type_id` WHERE ";
  591. $q .= "product_id='$product_id'";
  592. $dbp->query($q); $dbp->next_record();
  593. if ( $dbp->f('num_rows') == 0 ) { // Add record if not exist (Items)
  594. $q = "INSERT INTO #__{vm}_product_type_$product_type_id (product_id) ";
  595. $q .= "VALUES ('$product_id')";
  596. $dbp->query($q);
  597. }
  598. // Update record
  599. $q = "UPDATE #__{vm}_product_type_$product_type_id SET ";
  600. $q .= "product_id='$product_id'";
  601. while ($dbpt->next_record()) {
  602. if ($dbpt->f("parameter_type")!="B") { // if it is not breaker
  603. $value=$d["product_type_".$product_type_id."_".$dbpt->f("parameter_name")];
  604. if ($dbpt->f("parameter_type")=="V" && is_array($value)) {
  605. $value = join(';',$value);
  606. }
  607. if ($value=="") {
  608. $value='NULL';
  609. }
  610. else {
  611. $value="'".$dbpt->getEscaped($value)."'";
  612. }
  613. $q .= ',`'.$dbpt->f('parameter_name', false).'`='.$value;
  614. }
  615. }
  616. $q .= ' WHERE product_id = '.$d['product_id'];
  617. $dbp->query($q);
  618. }
  619. }
  620. /**
  621. * Function to delete product(s) $d['product_id'] from the product table
  622. *
  623. * @param array $d The input vars
  624. * @return boolean True, when the product was deleted, false when not
  625. */
  626. function delete(&$d) {
  627. $product_id = $d["product_id"];
  628. if( is_array( $product_id)) {
  629. foreach( $product_id as $product) {
  630. if( !$this->delete_product( $product, $d ))
  631. return false;
  632. }
  633. return true;
  634. }
  635. else {
  636. return $this->delete_product( $product_id, $d );
  637. }
  638. }
  639. /**
  640. * Move a product from one category to another
  641. *
  642. * @param array $d
  643. * @return boolean True on sucess, false on failure
  644. */
  645. function move( &$d ) {
  646. global $db, $vmLogger, $VM_LANG;
  647. if( !is_array( $d['product_id'])) {
  648. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MOVE_NOTFOUND',false));
  649. return false;
  650. }
  651. if( empty( $d['category_id'])) {
  652. $vmLogger->err( $VM_LANG->_('VM_PRODUCT_MUSTSELECT_ONE_CAT',false));
  653. return false;
  654. }
  655. // Loop though each product
  656. foreach( $d['product_id'] as $product_id ) {
  657. // check if the product is already assigned to the category it should be moved to
  658. $db->query( 'SELECT product_id FROM `#__{vm}_product_category_xref` WHERE `product_id`='.intval($product_id).' AND `category_id`='.intval($d['category_id']));
  659. if( !$db->next_record()) {
  660. // If the product is not yet in this category, move it!
  661. $db->query( 'SELECT MAX(`product_list`) as max FROM `#__{vm}_product_category_xref` WHERE `category_id`='.intval($d['category_id']));
  662. $db->next_record();
  663. $db->query('INSERT INTO `#__{vm}_product_category_xref` VALUES ('.intval($d['category_id']).', '.intval($product_id).', '.intval( $db->f('max') + 1) .') ');
  664. }
  665. $db->query('DELETE FROM `#__{vm}_product_category_xref` WHERE `product_id`='.intval($product_id).' AND `category_id`='.intval($d['old_category_id']));
  666. }
  667. return true;
  668. }
  669. /**
  670. * The function that holds the code for deleting
  671. * one product from the database and all related tables
  672. * plus deleting files related to the product
  673. *
  674. * @param int $product_id
  675. * @param array $d The input vars
  676. * @return boolean True on success, false on error
  677. */
  678. function delete_product( $product_id, &$d ) {
  679. global $vmLogger, $VM_LANG;
  680. $db = new ps_DB;
  681. if (!$this->validate_delete($product_id, $d)) {
  682. return false;
  683. }
  684. /* If is Product */
  685. if ($this->is_product($product_id)) {
  686. /* Delete all items first */
  687. $q = "SELECT product_id FROM #__{vm}_product WHERE product_parent_id='$product_id'";
  688. $db->setQuery($q); $db->query();
  689. while($db->next_record()) {
  690. $d2["product_id"] = $db->f("product_id");
  691. if (!$this->delete($d2)) {
  692. return false;
  693. }
  694. }
  695. /* Delete attributes */
  696. $q = "DELETE FROM #__{vm}_product_attribute_sku WHERE product_id='$product_id' ";
  697. $db->setQuery($q); $db->query();
  698. /* Delete categories xref */
  699. $q = "DELETE FROM #__{vm}_product_category_xref WHERE product_id = '$product_id' ";
  700. $db->setQuery($q); $db->query();
  701. }
  702. /* If is Item */
  703. else {
  704. /* Delete attribute values */
  705. $q = "DELETE FROM #__{vm}_product_attribute WHERE product_id='$product_id'";
  706. $db->setQuery($q); $db->query();
  707. }
  708. /* For both Product and Item */
  709. /* Delete product - manufacturer xref */
  710. $q = "DELETE FROM #__{vm}_product_mf_xref WHERE product_id='$product_id'";
  711. $db->setQuery($q); $db->query();
  712. /* Delete Product - ProductType Relations */
  713. $q = "DELETE FROM `#__{vm}_product_product_type_xref` WHERE `product_id`=$product_id";
  714. $db->setQuery($q); $db->query();
  715. /* Delete product votes */
  716. $q = "DELETE FROM #__{vm}_product_votes WHERE product_id='$product_id'";
  717. $db->setQuery($q); $db->query();
  718. /* Delete product reviews */
  719. $q = "DELETE FROM #__{vm}_product_reviews WHERE product_id='$product_id'";
  720. $db->setQuery($q); $db->query();
  721. /* Delete Image files */
  722. if (!vmImageTools::process_images($d)) {
  723. return false;
  724. }
  725. /* Delete other Files and Images files */
  726. require_once( CLASSPATH.'ps_product_files.php' );
  727. $ps_product_files = new ps_product_files();
  728. $db->query( "SELECT file_id FROM #__{vm}_product_files WHERE file_product_id='$product_id'" );
  729. while($db->next_record()) {
  730. $d["file_id"] = $db->f("file_id");
  731. $ps_product_files->delete( $d );
  732. }
  733. /* Delete Product Relations */
  734. $q = "DELETE FROM #__{vm}_product_relations WHERE product_id = '$product_id'";
  735. $db->setQuery($q); $db->query();
  736. /* Delete Prices */
  737. $q = "DELETE FROM #__{vm}_product_price WHERE product_id = '$product_id'";
  738. $db->setQuery($q); $db->query();
  739. /* Delete entry FROM #__{vm}_product table */
  740. $q = "DELETE FROM #__{vm}_product WHERE product_id = '$product_id'";
  741. $db->setQuery($q); $db->query();
  742. /* If only deleting an item, go to the parent product page after
  743. ** the deletion. This had to be done here because the product id
  744. ** of the item to be deleted had to be passed as product_id */
  745. if (!empty($d["product_parent_id"])) {
  746. $d["product_id"] = $d["product_parent_id"];
  747. $d["product_parent_id"] = "";
  748. }
  749. $vmLogger->info( str_replace('{product_id}',$product_id,$VM_LANG->_('VM_PRODUCT_DELETED',false)) );
  750. return true;
  751. }
  752. /**
  753. * Function to check if the vendor_id of the product
  754. * $d['product_id'] matches the vendor_id associated with the
  755. * user that calls this function
  756. *
  757. * @param array $d
  758. * @return boolean True, when vendor_id matches, false when not
  759. */
  760. function check_vendor($d) {
  761. $ps_vendor_id = $_SESSION["ps_vendor_id"];
  762. $db = new ps_DB;
  763. $q = "SELECT vendor_id FROM #__{vm}_product ";
  764. $q .= "WHERE vendor_id = '$ps_vendor_id' ";
  765. $q .= "AND product_id = '" . $d["product_id"] . "' ";
  766. $db->query($q);
  767. if ($db->next_record()) {
  768. return true;
  769. } else {
  770. return false;
  771. }
  772. }
  773. /**
  774. * Function to create a ps_DB object holding the data of product $d['product_id']
  775. * from the table #__{vm}_product
  776. *
  777. * @param int $product_id
  778. * @return ps_DB DB object holding all data for product $product_id
  779. */
  780. function sql($product_id) {
  781. $db = new ps_DB;
  782. if( !empty( $product_id )) {
  783. $q = 'SELECT * FROM #__{vm}_product WHERE product_id=' . (int)$product_id;
  784. $db->setQuery($q); $db->query();
  785. }
  786. return $db;
  787. }
  788. /**
  789. * Function to create a db object holding the data of all child items of
  790. * product $product_id
  791. *
  792. * @param int $product_id
  793. * @return ps_DB object that holds all items of product $product_id
  794. */
  795. function items_sql($product_id) {
  796. $db = new ps_DB;
  797. if( !empty($product_id) ) {
  798. $q = "SELECT * FROM #__{vm}_product ";
  799. $q .= "WHERE product_parent_id=".(int)$product_id.' ';
  800. $q .= "ORDER BY product_name";
  801. $db->setQuery($q); $db->query();
  802. }
  803. return $db;
  804. }
  805. /**
  806. * Function to check whether a product is a parent product or not
  807. * If is is a child product, it has a non-empty value for "product_parent_id"
  808. *
  809. * @param int $product_id
  810. * @return boolean True when the product is a parent product, false when product is a child item
  811. */
  812. function is_product($product_id) {
  813. $product_parent_id = ps_product::get_field($product_id, 'product_parent_id');
  814. return $product_parent_id == 0;
  815. }
  816. /**
  817. * Function to check whether a product is published
  818. *
  819. *
  820. * @param int $product_id
  821. * @return boolean True when the product is a parent product, false when product is a child item
  822. */
  823. function is_published($product_id, $check_stock=false) {
  824. if( CHECK_STOCK != '1') $check_stock=false;
  825. return ps_product::get_field($product_id, 'product_publish') == 'Y';
  826. }
  827. /**
  828. * Checks if a product is a downloadable product
  829. *
  830. * @param int $product_id
  831. * @return boolean
  832. */
  833. function is_downloadable($product_id) {
  834. if( empty( $GLOBALS['product_info'][$product_id]['is_downloadable'] )) {
  835. $db_check = new ps_DB;
  836. $q_dl = "SELECT attribute_name,attribute_value
  837. FROM #__{vm}_product_attribute WHERE
  838. product_id=".(int)$product_id." AND attribute_name='download'";
  839. $db_check->query($q_dl);
  840. $db_check->next_record();
  841. if( $db_check->num_rows() > 0 ) {
  842. $GLOBALS['product_info'][$product_id]['is_downloadable'] = 'Y';
  843. } else {
  844. $GLOBALS['product_info'][$product_id]['is_downloadable'] = 'N';
  845. }
  846. }
  847. return $GLOBALS['product_info'][$product_id]['is_downloadable'] == 'Y';
  848. }
  849. /**
  850. * Function to create a DB object that holds all information
  851. * from the attribute tables about item $item_id AND/OR product $product_id
  852. *
  853. * @param int $item_id The product_id of the item
  854. * @param int $product_id The product_id of the parent product
  855. * @param string $attribute_name The name of the attribute to filter
  856. * @return ps_DB The db object...
  857. */
  858. function attribute_sql($item_id="",$product_id="",$attribute_name="") {
  859. $db = new ps_DB;
  860. if ($item_id and $product_id) {
  861. $q = "SELECT * FROM #__{vm}_product_attribute,#__{vm}_product_attribute_sku ";
  862. $q .= "WHERE #__{vm}_product_attribute.product_id = '$item_id' ";
  863. $q .= "AND #__{vm}_product_attribute_sku.product_id ='$product_id' ";
  864. if ($attribute_name) {
  865. $q .= "AND #__{vm}_product_attribute.attribute_name = $attribute_name ";
  866. }
  867. $q .= "AND #__{vm}_product_attribute.attribute_name = ";
  868. $q .= "#__{vm}_product_attribute_sku.attribute_name ";
  869. $q .= "ORDER BY attribute_list,#__{vm}_product_attribute.attribute_name";
  870. } elseif ($item_id) {
  871. $q = "SELECT * FROM #__{vm}_product_attribute ";
  872. $q .= "WHERE product_id=$item_id ";
  873. if ($attribute_name) {
  874. $q .= "AND attribute_name = '$attribute_name' ";
  875. }
  876. } elseif ($product_id) {
  877. $q = "SELECT * FROM #__{vm}_product_attribute_sku ";
  878. $q .= "WHERE product_id =".(int)$product_id.' ';
  879. if ($attribute_name) {
  880. $q .= "AND #__{vm}_product_attribute.attribute_name = $attribute_name ";
  881. }
  882. $q .= "ORDER BY attribute_list,attribute_name";
  883. } else {
  884. /* Error: no arguments were provided. */
  885. return 0;
  886. }
  887. $db->setQuery($q); $db->query();
  888. return $db;
  889. }
  890. /**
  891. * Function to return the product ids of all child items of product $pid
  892. *
  893. * @param int $pid The ID of the parent product
  894. * @return array $list
  895. */
  896. function get_child_product_ids($pid) {
  897. $db = new ps_DB;
  898. $q = "SELECT product_id FROM #__{vm}_product ";
  899. $q .= "WHERE product_parent_id='$pid' ";
  900. $db->setQuery($q); $db->query();
  901. $i = 0;
  902. $list = Array();
  903. while($db->next_record()) {
  904. $list[$i] = $db->f("product_id");
  905. $i++;
  906. }
  907. return $list;
  908. }
  909. /**
  910. * Function to quickly check whether a product has child products or not
  911. *
  912. * @param int $pid The id of the product to check
  913. * @return boolean True when the product has childs, false when not
  914. */
  915. function parent_has_children($pid) {
  916. $db = new ps_DB;
  917. if( empty($GLOBALS['product_info'][$pid]["parent_has_children"] )) {
  918. $q = "SELECT COUNT(product_id) as num_rows FROM #__{vm}_product WHERE product_parent_id='$pid' ";
  919. $db->query($q);
  920. $db->next_record();
  921. if( $db->f('num_rows') > 0 ) {
  922. $GLOBALS['product_info'][$pid]["parent_has_children"] = True;
  923. }
  924. else {
  925. $GLOBALS['product_info'][$pid]["parent_has_children"] = False;
  926. }
  927. }
  928. return $GLOBALS['product_info'][$pid]["parent_has_children"];
  929. }
  930. /**
  931. * Function to quickly check whether a product has attributes or not
  932. *
  933. * @param int $pid The id of the product to check
  934. * @return boolean True when the product has attributes, false when not
  935. */
  936. function product_has_attributes($pid, $checkSimpleAttributes=false ) {
  937. if( is_array($pid) || empty($pid)) {
  938. return false;
  939. }
  940. $pid = intval( $pid );
  941. $db = new ps_DB;
  942. if( empty($GLOBALS['product_info'][$pid]["product_has_attributes"] )) {
  943. $db->query( "SELECT `product_id` FROM `#__{vm}_product_attribute_sku` WHERE `product_id`=$pid");
  944. if ($db->next_record()) {
  945. $GLOBALS['product_info'][$pid]["product_has_attributes"] = True;
  946. }
  947. elseif( $checkSimpleAttributes ) {
  948. $db->query( "SELECT `attribute`,`custom_attribute` FROM `#__{vm}_product` WHERE `product_id`=$pid");
  949. $db->next_record();
  950. if( $db->f('attribute') || $db->f('custom_attribute')) {
  951. $GLOBALS['product_info'][$pid]["product_has_attributes"] = True;
  952. }
  953. else {
  954. $GLOBALS['product_info'][$pid]["product_has_attributes"] = False;
  955. }
  956. }
  957. else {
  958. $GLOBALS['product_info'][$pid]["product_has_attributes"] = False;
  959. }
  960. }
  961. return $GLOBALS['product_info'][$pid]["product_has_attributes"];
  962. }
  963. /**
  964. * Get the value of the field $field_name for product $product_id from the product table
  965. *
  966. * @param int $product_id
  967. * @param string $field_name
  968. * @return string The value of the field $field_name for that product
  969. */
  970. function get_field( $product_id, $field_name, $force = false ) {
  971. if( $product_id == 0 ) return '';
  972. $db = new ps_DB;
  973. if( !isset($GLOBALS['product_info'][$product_id][$field_name] ) || $force ) {
  974. $q = 'SELECT product_id, `#__{vm}_product`.* FROM `#__{vm}_product` WHERE `product_id`='.(int)$product_id;
  975. $db->query($q);
  976. if ($db->next_record()) {
  977. $values = get_object_vars( $db->getCurrentRow() );
  978. foreach( $values as $key => $value ) {
  979. $GLOBALS['product_info'][$product_id][$key] = $value;
  980. }
  981. if( !isset( $GLOBALS['product_info'][$product_id][$field_name] ) && !is_null($GLOBALS['product_info'][$product_id][$field_name])) {
  982. $GLOBALS['vmLogger']->debug( 'The Field '.$field_name. ' does not exist in the product table!');
  983. $GLOBALS['product_info'][$product_id][$field_name] = true;
  984. }
  985. }
  986. else {
  987. $GLOBALS['product_info'][$product_id][$field_name] = false;
  988. }
  989. }
  990. return $GLOBALS['product_info'][$product_id][$field_name];
  991. }
  992. /**
  993. * Sets a global value for a fieldname for a specific product
  994. * Is to be used by other scripts to populate a field value for a prodct
  995. * that was already fetched from the database - so it doesn't need to e fetched again
  996. * Can be also used to override a value
  997. *
  998. * @param int $product_id
  999. * @param string $field_name
  1000. * @param mixed $value
  1001. */
  1002. function set_field( $product_id, $field_name, $value ) {
  1003. $GLOBALS['product_info'][$product_id][$field_name] = $value;
  1004. }
  1005. /**
  1006. * This is a very time consuming function.
  1007. * It fetches the category flypage for a specific product id
  1008. *
  1009. * @param int $product_id
  1010. * @return string The flypage value for that product
  1011. */
  1012. function get_flypage($product_id) {
  1013. if( empty( $_SESSION['product_sess'][$product_id]['flypage'] )) {
  1014. $db = new ps_DB;
  1015. $productParentId = (int)$product_id;
  1016. do {
  1017. $q = "SELECT
  1018. `#__{vm}_product`.`product_parent_id` AS product_parent_id,
  1019. `#__{vm}_category`.`category_flypage`
  1020. FROM
  1021. `#__{vm}_product`
  1022. LEFT JOIN `#__{vm}_product_category_xref` ON `#__{vm}_product_category_xref`.`product_id` = `#__{vm}_product`.`product_id`
  1023. LEFT JOIN `#__{vm}_category` ON `#__{vm}_product_category_xref`.`category_id` = `#__{vm}_category`.`category_id`
  1024. WHERE `#__{vm}_product`.`product_id`='$productParentId'
  1025. ";
  1026. $productParentId = $db->f("product_parent_id");
  1027. $db->query($q);
  1028. $db->next_record();
  1029. }
  1030. while( $db->f("product_parent_id") && !$db->f("category_flypage"));
  1031. if ($db->f("category_flypage")) {
  1032. $_SESSION['product_sess'][$product_id]['flypage'] = $db->f("category_flypage");
  1033. } else {
  1034. $_SESSION['product_sess'][$product_id]['flypage'] = FLYPAGE;
  1035. }
  1036. }
  1037. return $_SESSION['product_sess'][$product_id]['flypage'];
  1038. }
  1039. /**
  1040. * Function to get the name of the vendor the product is associated with
  1041. *
  1042. * @param int $product_id
  1043. * @return string The name of the vendor
  1044. */
  1045. function get_vendorname($product_id) {
  1046. $db = new ps_DB;
  1047. $q = "SELECT #__{vm}_vendor.vendor_name FROM #__{vm}_product, #__{vm}_vendor ";
  1048. $q .= "WHERE #__{vm}_product.product_id='$product_id' ";
  1049. $q .= "AND #__{vm}_vendor.vendor_id=#__{vm}_product.vendor_id";
  1050. $db->query($q);
  1051. $db->next_record();
  1052. if ($db->f("vendor_name")) {
  1053. return $db->f("vendor_name");
  1054. }
  1055. else {
  1056. return "";
  1057. }
  1058. }
  1059. /**
  1060. * Function to get the name of a vendor by its id
  1061. * @author pablo
  1062. * @param int $vendor_id
  1063. * @return string The name of the vendor
  1064. */
  1065. function get_vend_idname($vendor_id) {
  1066. $db = new ps_DB;
  1067. $q = "SELECT vendor_name,vendor_id FROM #__{vm}_vendor ";
  1068. $q .= "WHERE vendor_id='$vendor_id'";
  1069. $db->query($q);
  1070. $db->next_record();
  1071. if ($db->f("vendor_name")) {
  1072. return $db->f("vendor_name");
  1073. }
  1074. else {
  1075. return "";
  1076. }
  1077. }
  1078. /**
  1079. * Function to get the vendor_id of a product
  1080. * @author pablo
  1081. * @param int $product_id
  1082. * @return int The vendor id
  1083. */
  1084. function get_vendor_id($product_id) {
  1085. $db = new ps_DB;
  1086. if( empty( $_SESSION['product_sess'][$product_id]['vendor_id'] )) {
  1087. $q = "SELECT vendor_id FROM #__{vm}_product ";
  1088. $q .= "WHERE product_id='$product_id' ";
  1089. $db->query($q);
  1090. $db->next_record();
  1091. if ($db->f("vendor_id")) {
  1092. $_SESSION['product_sess'][$product_id]['vendor_id'] = $db->f("vendor_id");
  1093. }
  1094. else {
  1095. $_SESSION['product_sess'][$product_id]['vendor_id'] = "";
  1096. }
  1097. }
  1098. return $_SESSION['product_sess'][$product_id]['vendor_id'];
  1099. }
  1100. /**
  1101. * Function to get the manufacturer id the product $product_id is assigned to
  1102. * @author soeren
  1103. * @param int $product_id
  1104. * @return int The manufacturer id
  1105. */
  1106. function get_manufacturer_id($product_id) {
  1107. $db = new ps_DB;
  1108. $q = "SELECT manufacturer_id FROM #__{vm}_product_mf_xref ";
  1109. $q .= "WHERE product_id='$product_id' ";
  1110. $db->query($q);
  1111. $db->next_record();
  1112. if ($db->f("manufacturer_id")) {
  1113. return $db->f("manufacturer_id");
  1114. }
  1115. else {
  1116. return false;
  1117. }
  1118. }
  1119. /**
  1120. * Functon to get the name of the manufacturer this product is assigned to
  1121. *
  1122. * @param int $product_id
  1123. * @return string the manufacturer name
  1124. */
  1125. function get_mf_name($product_id) {
  1126. $db = new ps_DB;
  1127. $q = "SELECT mf_name,#__{vm}_manufacturer.manufacturer_id FROM #__{vm}_product_mf_xref,#__{vm}_manufacturer ";
  1128. $q .= "WHERE product_id='$product_id' ";
  1129. $q .= "AND #__{vm}_manufacturer.manufacturer_id=#__{vm}_product_mf_xref.manufacturer_id";
  1130. $db->query($q);
  1131. $db->next_record();
  1132. if ($db->f("mf_name")) {
  1133. return $db->f("mf_name");
  1134. }
  1135. else {
  1136. return "";
  1137. }
  1138. }
  1139. /**
  1140. * This function retrieves the "neighbor" products of a product specified by $product_id
  1141. * Neighbors are the previous and next product in the current list
  1142. *
  1143. * @param int $product_id
  1144. * @return array
  1145. */
  1146. function get_neighbor_products( $product_id ) {
  1147. global $perm, $orderby, $my, $auth, $keyword, $DescOrderBy, $limit, $limitstart, $search_limiter, $search_op,
  1148. $category_id, $manufacturer_id, $vm_mainframe, $vmInputFilter, $product_type_id, $keyword1, $keyword2;
  1149. $limit = 2000;
  1150. $limitstart = 0;
  1151. if( !empty( $_SESSION['last_browse_parameters'])) {
  1152. foreach( $_SESSION['last_browse_parameters'] as $paramName => $paramValue ) {
  1153. $$paramName = $paramValue;
  1154. }
  1155. }
  1156. $db = new ps_DB();
  1157. $db_browse = new ps_DB();
  1158. include( PAGEPATH . 'shop_browse_queries.php' );
  1159. $db->query( $list );
  1160. $neighbors = array('previous'=>'',
  1161. 'next'=>'');
  1162. while( $db->next_record() ) {
  1163. if( $db->f( 'product_id' ) == $product_id ) {
  1164. $previous_row = $db->previousRow();
  1165. $next_row = $db->nextRow();
  1166. if( !empty( $previous_row->product_id )) {
  1167. $neighbors['previous']['product_id'] = $previous_row->product_id;
  1168. $neighbors['previous']['product_name'] = $previous_row->product_name;
  1169. }
  1170. if( !empty( $next_row->product_id )) {
  1171. $neighbors['next']['product_id'] = $next_row->product_id;
  1172. $neighbors['next']['product_name'] = $next_row->product_name;
  1173. }
  1174. }
  1175. }
  1176. return $neighbors;
  1177. }
  1178. /**
  1179. * Prints the img tag for the given product image
  1180. *
  1181. * @param string $image The name of the imahe OR the full URL to the image
  1182. * @param string $args Additional attributes for the img tag
  1183. * @param int $resize
  1184. * (1 = resize the image by using height and width attributes,
  1185. * 0 = do not resize the image)
  1186. * @param string $path_appendix The path to be appended to IMAGEURL / IMAGEPATH
  1187. */
  1188. function show_image($image, $args="", $resize=1, $path_appendix="product") {
  1189. echo $this->image_tag($image, $args, $resize, $path_appendix);
  1190. }
  1191. /**
  1192. * Returns the img tag for the given product image
  1193. *
  1194. * @param string $image The name of the imahe OR the full URL to the image
  1195. * @param string $args Additional attributes for the img tag
  1196. * @param int $resize
  1197. * (1 = resize the image by using height and width attributes,
  1198. * 0 = do not resize the image)
  1199. * @param string $path_appendix The path to be appended to IMAGEURL / IMAGEPATH
  1200. * @return The HTML code of the img tag
  1201. */
  1202. function image_tag($image, $args="", $resize=1, $path_appendix='product', $thumb_width=0, $thumb_height=0 ) {
  1203. global $mosConfig_live_site, $mosConfig_absolute_path;
  1204. require_once( CLASSPATH . 'imageTools.class.php');
  1205. $border="";
  1206. if( strpos( $args, "border=" )===false ) {
  1207. $border = 'border="0"';
  1208. }
  1209. $height = $width = '';
  1210. if ($image != "") {
  1211. // URL
  1212. if( substr( $image, 0, 4) == "http" ) {
  1213. $url = $image;
  1214. }
  1215. // local image file
  1216. else {
  1217. if(PSHOP_IMG_RESIZE_ENABLE == '1' || $resize==1) {
  1218. $url = $mosConfig_live_site."/components/com_virtuemart/show_image_in_imgtag.php?filename=".urlencode($image)."&amp;newxsize=".PSHOP_IMG_WIDTH."&amp;newysize=".PSHOP_IMG_HEIGHT."&amp;fileout=";
  1219. if( !strpos( $args, "height=" )) {
  1220. $arr = @getimagesize( vmImageTools::getresizedfilename( $image, $path_appendix, '', $thumb_width, $thumb_height ) );
  1221. $width = $arr[0]; $height = $arr[1];
  1222. }
  1223. }
  1224. else {
  1225. $url = IMAGEURL.$path_appendix.'/'.$image;
  1226. if( file_exists($image)) {
  1227. $url = str_replace( $mosConfig_absolute_path, $mosConfig_live_site, $image );
  1228. } elseif( file_exists($mosConfig_absolute_path.'/'.$image)) {
  1229. $url = $mosConfig_live_site.'/'.$image;
  1230. }
  1231. if( !strpos( $args, "height=" ) ) {
  1232. $arr = getimagesize( str_replace( IMAGEURL, IMAGEPATH, $url ) );
  1233. $width = $arr[0]; $height = $arr[1];
  1234. }
  1235. if( $resize ) {
  1236. if( $height < $width ) {
  1237. $width = round($width / ($height / PSHOP_IMG_HEIGHT));
  1238. $height = PSHOP_IMG_HEIGHT;
  1239. } else {
  1240. $height = round($height / ($width / PSHOP_IMG_WIDTH ));
  1241. $width = PSHOP_IMG_WIDTH;
  1242. }
  1243. }
  1244. }
  1245. $url = str_replace( basename( $url ), $GLOBALS['VM_LANG']->convert(basename($url)), $url );
  1246. }
  1247. }
  1248. else {
  1249. $url = VM_THEMEURL.'images/'.NO_IMAGE;
  1250. }
  1251. return vmCommonHTML::imageTag( $url, '', '', $height, $width, '', '', $args.' '.$border );
  1252. }
  1253. /**
  1254. * Get the tax rate...
  1255. * @author soeren
  1256. * @return int The tax rate found
  1257. */
  1258. function get_taxrate() {
  1259. global $page;
  1260. $ps_vendor_id = $_SESSION["ps_vendor_id"];
  1261. $auth = $_SESSION['auth'];
  1262. if( !defined('_PSHOP_ADMIN' ) || $page == 'product.product_list') {
  1263. $db = new ps_DB;
  1264. if ($auth["show_price_including_tax"] == 1) {
  1265. require_once( CLASSPATH . 'ps_checkout.php' );
  1266. if (! ps_checkout::tax_based_on_vendor_address ()) {
  1267. if( $auth["user_id"] > 0 ) {
  1268. $q = "SELECT state, country FROM #__{vm}_user_info WHERE user_id='". $auth["user_id"] . "'";
  1269. $db->query($q);
  1270. $db->next_record();
  1271. $state = $db->f("state");
  1272. $country = $db->f("country");
  1273. $q = "SELECT tax_rate FROM #__{vm}_tax_rate WHERE tax_country='$country' ";
  1274. if( !empty($state)) {
  1275. $q .= "AND tax_state='$state'";
  1276. }
  1277. $db->query($q);
  1278. if ($db->next_record()) {
  1279. $_SESSION['taxrate'][$ps_vendor_id] = $db->f("tax_rate");
  1280. }
  1281. else {
  1282. $_SESSION['taxrate'][$ps_vendor_id] = 0;
  1283. }
  1284. }
  1285. else {
  1286. $_SESSION['taxrate'][$ps_vendor_id] = 0;
  1287. }
  1288. }
  1289. else {
  1290. if( empty( $_SESSION['taxrate'][$ps_vendor_id] )) {
  1291. // let's get the store's tax rate
  1292. $q = "SELECT `tax_rate` FROM #__{vm}_vendor, #__{vm}_tax_rate ";
  1293. $q .= "WHERE tax_country=vendor_country AND #__{vm}_vendor.vendor_id=1 ";
  1294. // !! Important !! take the highest available tax rate for the store's country
  1295. $q .= "ORDER BY `tax_rate` DESC";
  1296. $db->query($q);
  1297. if ($db->next_record()) {
  1298. $_SESSION['taxrate'][$ps_vendor_id] = $db->f("tax_rate");
  1299. }
  1300. else {
  1301. $_SESSION['taxrate'][$ps_vendor_id] = 0;
  1302. }
  1303. }
  1304. return $_SESSION['taxrate'][$ps_vendor_id];
  1305. }
  1306. }
  1307. else {
  1308. $_SESSION['taxrate'][$ps_vendor_id] = 0;
  1309. }
  1310. return $_SESSION['taxrate'][$ps_vendor_id];
  1311. }
  1312. else {
  1313. return 0;
  1314. }
  1315. }
  1316. /**
  1317. * Function to get the tax rate of product $product_id
  1318. * If not found, it uses get_taxrate()
  1319. *
  1320. * @param int $product_id
  1321. * @param int $weight_subtotal (tax virtual/zero-weight items?)
  1322. * @return int The tax rate for the product
  1323. */
  1324. function get_product_taxrate( $product_id, $weight_subtotal=0 ) {
  1325. require_once( CLASSPATH . 'ps_checkout.php' );
  1326. if (($weight_subtotal != 0 or TAX_VIRTUAL=='1') && !ps_checkout::tax_based_on_vendor_address() ) {
  1327. $_SESSION['product_sess'][$product_id]['tax_rate'] = $this->get_taxrate();
  1328. return $_SESSION['product_sess'][$product_id]['tax_rate'];
  1329. }
  1330. elseif( ($weight_subtotal == 0 or TAX_VIRTUAL != '1' ) && !ps_checkout::tax_based_on_vendor_address() ) {
  1331. $_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
  1332. return $_SESSION['product_sess'][$product_id]['tax_rate'];
  1333. }
  1334. elseif( ps_checkout::tax_based_on_vendor_address () ) {
  1335. // if( empty( $_SESSION['product_sess'][$product_id]['tax_rate'] ) ) {
  1336. $db = new ps_DB;
  1337. // Product's tax rate id has priority!
  1338. $q = "SELECT product_weight, tax_rate FROM #__{vm}_product, #__{vm}_tax_rate ";
  1339. $q .= "WHERE product_tax_id=tax_rate_id AND product_id='$product_id'";
  1340. $db->query($q);
  1341. if ($db->next_record()) {
  1342. $rate = $db->f("tax_rate");
  1343. $product_weight = $db->f('product_weight');
  1344. if( $weight_subtotal == 0 && $product_weight > 0 ) {
  1345. $weight_subtotal = $product_weight;
  1346. }
  1347. }
  1348. else {
  1349. // if we didn't find a product tax rate id, let's get the store's tax rate
  1350. $rate = $this->get_taxrate();
  1351. }
  1352. if ($weight_subtotal != 0 or TAX_VIRTUAL=='1') {
  1353. $_SESSION['product_sess'][$product_id]['tax_rate'] = $rate;
  1354. return $rate;
  1355. }
  1356. else {
  1357. $_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
  1358. return 0;
  1359. }
  1360. // }
  1361. // else {
  1362. // return $_SESSION['product_sess'][$product_id]['tax_rate'];
  1363. // }
  1364. }
  1365. return 0;
  1366. }
  1367. /**
  1368. * Function to get the "pure" undiscounted and untaxed price
  1369. * of product $product_id. Used by the administration section.
  1370. *
  1371. * @param int $product_id
  1372. * @return array The product price information
  1373. */
  1374. function get_retail_price($product_id) {
  1375. $db = new ps_DB;
  1376. // Get the vendor id for this product.
  1377. $q = "SELECT vendor_id FROM #__{vm}_product WHERE product_id='$product_id'";
  1378. $db->setQuery($q); $db->query();
  1379. $db->next_record();
  1380. $vendor_id = $db->f("vendor_id");
  1381. // Get the default shopper group id for this product and user
  1382. $q = "SELECT shopper_group_id FROM #__{vm}_shopper_group WHERE `vendor_id`='$vendor_id' AND `default`='1'";
  1383. $db->setQuery($q); $db->query();
  1384. $db->next_record();
  1385. $default_shopper_group_id = $db->f("shopper_group_id");
  1386. $q = "SELECT product_price,product_currency,price_quantity_start,price_quantity_end
  1387. FROM #__{vm}_product_price
  1388. WHERE product_id='$product_id' AND
  1389. shopper_group_id='$default_shopper_group_id'";
  1390. $db->query($q);
  1391. if ($db->next_record()) {
  1392. $price_info["product_price"]= $db->f("product_price");
  1393. $price_info["product_currency"]=$db->f("product_currency");
  1394. $price_info["price_quantity_start"]=$db->f("price_quantity_start"); // added alatak
  1395. $price_info["price_quantity_end"]=$db->f("price_quantity_end");// added alatak
  1396. }
  1397. else {
  1398. $price_info["product_price"]= "";
  1399. $price_info["product_currency"] = $_SESSION['vendor_currency'];
  1400. $price_info["price_quantity_start"]=$db->f("price_quantity_start"); // added alatak
  1401. $price_info["price_quantity_end"]=$db->f("price_quantity_end");// added alatak
  1402. }
  1403. return $price_info;
  1404. }
  1405. /**
  1406. * Get the price of product $product_id for the shopper group associated
  1407. * with $auth['user_id'] - including shopper group discounts
  1408. *
  1409. * @param int $product_id
  1410. * @param boolean $check_multiple_prices Check if the product has more than one price for that shopper group?
  1411. * @return array The product price information
  1412. */
  1413. function get_price($product_id, $check_multiple_prices=false, $overrideShopperGroup='' ) {
  1414. if( empty( $product_id)) return array();
  1415. $auth = $_SESSION['auth'];
  1416. $cart = $_SESSION['cart'];
  1417. if( empty( $GLOBALS['product_info'][$product_id]['price'] )
  1418. || !empty($GLOBALS['product_info'][$product_id]['price']["product_has_multiple_prices"])
  1419. || $check_multiple_prices) {
  1420. $db = new ps_DB;
  1421. $vendor_id = $this->get_vendor_id($product_id);
  1422. if( $overrideShopperGroup === '') {
  1423. $shopper_group_id = $auth["shopper_group_id"];
  1424. $shopper_group_discount = $auth["shopper_group_discount"];
  1425. }
  1426. else {
  1427. $shopper_group_id = $overrideShopperGroup;
  1428. $shopper_group_discount = 0;
  1429. }
  1430. ps_shopper_group::makeDefaultShopperGroupInfo();
  1431. // Get the product_parent_id for this product/item
  1432. $product_parent_id = $this->get_field($product_id, "product_parent_id");
  1433. if( !$check_multiple_prices ) {
  1434. /* Added for Volume based prices */
  1435. // This is an important decision: we add up all product quantities with the same product_id,
  1436. // regardless to attributes. This gives "real" volume based discount, because our simple attributes
  1437. // depend on one and the same product_id
  1438. $quantity = 0;
  1439. $parent_id = "";
  1440. if ($product_parent_id) {
  1441. $parent = true;
  1442. }
  1443. else {
  1444. $parent = false;
  1445. }
  1446. for ($i=0;$i<$cart["idx"];$i++) {
  1447. if ($cart[$i]["product_id"] == $product_id) {
  1448. if ($parent) {
  1449. $parent_id = $cart[$i]["parent_id"];
  1450. }
  1451. else {
  1452. $quantity += $cart[$i]["quantity"];
  1453. }
  1454. }
  1455. }
  1456. if ($parent) {
  1457. for ($i=0;$i<$cart["idx"];$i++) {
  1458. if (@$cart[$i]['parent_id'] == $parent_id) {
  1459. $quantity += $cart[$i]["quantity"];
  1460. }
  1461. }
  1462. }
  1463. $volume_quantity_sql = " ORDER BY price_quantity_start";
  1464. if( $quantity > 0 ) {
  1465. $volume_quantity_sql = " AND (('$quantity' >= price_quantity_start AND '$quantity' <= price_quantity_end)
  1466. OR (price_quantity_end='0') OR ('$quantity' > price_quantity_end)) ORDER BY price_quantity_end DESC";
  1467. }
  1468. }
  1469. else {
  1470. $volume_quantity_sql = " ORDER BY price_quantity_start";
  1471. }
  1472. // Get the price array
  1473. $price = $this->getPriceByShopperGroup( $product_id, $shopper_group_id, $check_multiple_prices, $volume_quantity_sql );
  1474. if( !$price && $product_parent_id ) {
  1475. // If this is a child product and it has not price, get the price of the parent product
  1476. $price = $this->getPriceByShopperGroup( $product_parent_id, $shopper_group_id, $check_multiple_prices, $volume_quantity_sql );
  1477. if( !$price ) {
  1478. $price = $this->getPriceByShopperGroup( $product_parent_id, $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'], $check_multiple_prices, $volume_quantity_sql );
  1479. }
  1480. }
  1481. elseif( !$price ) {
  1482. $price = $this->getPriceByShopperGroup( $product_id, $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'], $check_multiple_prices, $volume_quantity_sql );
  1483. }
  1484. return $price;
  1485. }
  1486. else {
  1487. return $GLOBALS['product_info'][$product_id]['price'];
  1488. }
  1489. }
  1490. /**
  1491. * Returns the price for a specific shopper group,
  1492. * Returns nothing, when the shopper group has no price
  1493. *
  1494. * @param int $product_id
  1495. * @param int $shopper_group_id
  1496. * @param boolean $check_multiple_prices
  1497. * @param string $additionalSQL
  1498. * @return mixed
  1499. */
  1500. function getPriceByShopperGroup( $product_id, $shopper_group_id, $check_multiple_prices=false, $additionalSQL='' ) {
  1501. global $auth;
  1502. static $resultcache = array();
  1503. $db = new ps_DB;
  1504. $vendor_id = $_SESSION['ps_vendor_id'];
  1505. if( empty( $shopper_group_id )) {
  1506. ps_shopper_group::makeDefaultShopperGroupInfo();
  1507. $shopper_group_id = $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'];
  1508. }
  1509. $whereClause='WHERE product_id=%s AND shopper_group_id=%s ';
  1510. $whereClause = sprintf( $whereClause, intval($product_id), intval($shopper_group_id) );
  1511. $q = "SELECT `product_price`, `product_price_id`, `product_currency` FROM `#__{vm}_product_price` $whereClause $additionalSQL";
  1512. $sig = sprintf("%u\n", crc32($q));
  1513. if( !isset($resultcache[$sig])) {
  1514. $db->query($q);
  1515. if( !$db->next_record() ) return false;
  1516. $price_info["product_price"]= $db->f("product_price") * ((100 - $auth["shopper_group_discount"])/100);
  1517. $price_info["product_currency"] = $db->f("product_currency");
  1518. $price_info["product_base_price"]= $db->f("product_price") * ((100 - $auth["shopper_group_discount"])/100);
  1519. $price_info["product_has_multiple_prices"] = $db->num_rows() > 1;
  1520. $price_info["product_price_id"] = $db->f("product_price_id");
  1521. $price_info["item"]=true;
  1522. $GLOBALS['product_info'][$product_id]['price'] = $price_info;
  1523. // Store the result for later
  1524. $resultcache[$sig] = $price_info;
  1525. return $GLOBALS['product_info'][$product_id]['price'];
  1526. }
  1527. else {
  1528. return $resultcache[$sig];
  1529. }
  1530. }
  1531. /**
  1532. * Adjusts the price from get_price for the selected attributes
  1533. * @author Nathan Hyde <nhyde@bigDrift.com>
  1534. * @author curlyroger from his post at <http://www.phpshop.org/phpbb/viewtopic.php?t=3052>
  1535. *
  1536. * @param int $product_id
  1537. * @param string $description
  1538. * @return array The adjusted price information
  1539. */
  1540. function get_adjusted_attribute_price ($product_id, $description='') {
  1541. global $mosConfig_secret;
  1542. $auth = $_SESSION['auth'];
  1543. $price = $this->get_price($product_id);
  1544. $base_price = $price["product_price"];
  1545. $setprice = 0;
  1546. $set_price = false;
  1547. $adjustment = 0;
  1548. // We must care for custom attribute fields! Their value can be freely given
  1549. // by the customer, so we mustn't include them into the price calculation
  1550. // Thanks to AryGroup@ua.fm for the good advice
  1551. if( empty( $_REQUEST["custom_attribute_fields"] )) {
  1552. if( !empty( $_SESSION["custom_attribute_fields"] )) {
  1553. $custom_attribute_fields = vmGet( $_SESSION, "custom_attribute_fields", Array() );
  1554. $custom_attribute_fields_check = vmGet( $_SESSION, "custom_attribute_fields_check", Array() );
  1555. }
  1556. else
  1557. $custom_attribute_fields = $custom_attribute_fields_check = Array();
  1558. }
  1559. else {
  1560. $custom_attribute_fields = $_SESSION["custom_attribute_fields"] = vmGet( $_REQUEST, "custom_attribute_fields", Array() );
  1561. $custom_attribute_fields_check = $_SESSION["custom_attribute_fields_check"]= vmGet( $_REQUEST, "custom_attribute_fields_check", Array() );
  1562. }
  1563. // if we've been given a description to deal with, get the adjusted price
  1564. if ($description != '') { // description is safe to use at this point cause it's set to ''
  1565. require_once(CLASSPATH.'ps_product_attribute.php');
  1566. $product_attributes = ps_product_attribute::getAdvancedAttributes($product_id, true);
  1567. $attribute_keys = explode( ";", $description );
  1568. for($i=0; $i < sizeof($attribute_keys); $i++ ) {
  1569. $temp_desc = $attribute_keys[$i];
  1570. $temp_desc = trim( $temp_desc );
  1571. // Get the key name (e.g. "Color" )
  1572. $this_key = substr( $temp_desc, 0, strpos($temp_desc, ":") );
  1573. $this_value = substr( $temp_desc, strpos($temp_desc, ":")+1 );
  1574. if( in_array( $this_key, $custom_attribute_fields )) {
  1575. if( @$custom_attribute_fields_check[$this_key] == md5( $mosConfig_secret.$this_key )) {
  1576. // the passed value is valid, don't use it for calculating prices
  1577. continue;
  1578. }
  1579. }
  1580. $this_value=str_replace("_"," ",$this_value);
  1581. if( isset( $product_attributes[$this_key]['values'][$this_value] )) {
  1582. $modifier = $product_attributes[$this_key]['values'][$this_value]['adjustment'];
  1583. $operand = $product_attributes[$this_key]['values'][$this_value]['operand'];
  1584. // if we have a number, allow the adjustment
  1585. if (true == is_numeric($modifier) ) {
  1586. $modifier = $GLOBALS['CURRENCY']->convert( $modifier, $price['product_currency'], $GLOBALS['product_currency'] );
  1587. // Now add or sub the modifier on
  1588. if ($operand=="+") {
  1589. $adjustment += $modifier;
  1590. }
  1591. else if ($operand=="-") {
  1592. $adjustment -= $modifier;
  1593. }
  1594. else if ($operand=='=') {
  1595. // NOTE: the +=, so if we have 2 sets they get added
  1596. // this could be moded to say, if we have a set_price, then
  1597. // calc the diff from the base price and start from there if we encounter
  1598. // another set price... just a thought.
  1599. $setprice += $modifier;
  1600. $set_price = true;
  1601. }
  1602. }
  1603. } else {
  1604. continue;
  1605. }
  1606. }
  1607. }
  1608. // no set price was set from the attribs
  1609. if ($set_price == false) {
  1610. $price["product_price"] = $base_price + ($adjustment)*(1 - ($auth["shopper_group_discount"]/100));
  1611. }
  1612. else {
  1613. // otherwise, set the price
  1614. // add the base price to the price set in the attributes
  1615. // then subtract the adjustment amount
  1616. // we could also just add the set_price to the adjustment... not sure on that one.
  1617. if (!empty($adjustment)) {
  1618. $setprice += $adjustment;
  1619. }
  1620. $setprice *= 1 - ($auth["shopper_group_discount"]/100);
  1621. $price["product_price"] = $setprice;
  1622. }
  1623. // don't let negative prices get by, set to 0
  1624. if ($price["product_price"] < 0) {
  1625. $price["product_price"] = 0;
  1626. }
  1627. // Get the DISCOUNT AMOUNT
  1628. $discount_info = $this->get_discount( $product_id );
  1629. $my_taxrate = $this->get_product_taxrate($product_id);
  1630. // If discounts are applied after tax, but prices are shown without tax,
  1631. // AND tax is EU mode and shopper is not in the EU,
  1632. // then ps_product::get_product_taxrate() returns 0, so $my_taxrate = 0.
  1633. // But, the discount still needs to be reduced by the shopper's tax rate, so we obtain it here:
  1634. if( PAYMENT_DISCOUNT_BEFORE != '1' && $auth["show_price_including_tax"] != 1 && !ps_checkout::tax_based_on_vendor_address() ) {
  1635. $db = new ps_DB;
  1636. $ps_vendor_id = $_SESSION["ps_vendor_id"];
  1637. require_once( CLASSPATH . 'ps_checkout.php' );
  1638. if (! ps_checkout::tax_based_on_vendor_address ()) {
  1639. if( $auth["user_id"] > 0 ) {
  1640. $q = "SELECT state, country FROM #__{vm}_user_info WHERE user_id='". $auth["user_id"] . "'";
  1641. $db->query($q);
  1642. $db->next_record();
  1643. $state = $db->f("state");
  1644. $country = $db->f("country");
  1645. $q = "SELECT tax_rate FROM #__{vm}_tax_rate WHERE tax_country='$country' ";
  1646. if( !empty($state)) {
  1647. $q .= "AND tax_state='$state'";
  1648. }
  1649. $db->query($q);
  1650. if ($db->next_record()) {
  1651. $my_taxrate = $db->f("tax_rate");
  1652. }
  1653. else {
  1654. $my_taxrate = 0;
  1655. }
  1656. }
  1657. else {
  1658. $my_taxrate = 0;
  1659. }
  1660. }
  1661. else {
  1662. if( empty( $_SESSION['taxrate'][$ps_vendor_id] )) {
  1663. // let's get the store's tax rate
  1664. $q = "SELECT `tax_rate` FROM #__{vm}_vendor, #__{vm}_tax_rate ";
  1665. $q .= "WHERE tax_country=vendor_country AND #__{vm}_vendor.vendor_id=1 ";
  1666. // !! Important !! take the highest available tax rate for the store's country
  1667. $q .= "ORDER BY `tax_rate` DESC";
  1668. $db->query($q);
  1669. if ($db->next_record()) {
  1670. $my_taxrate = $db->f("tax_rate");
  1671. }
  1672. else {
  1673. $my_taxrate = 0;
  1674. }
  1675. }
  1676. }
  1677. }
  1678. // Apply the discount
  1679. if( !empty($discount_info["amount"])) {
  1680. $undiscounted_price = $base_price;
  1681. switch( $discount_info["is_percent"] ) {
  1682. case 0:
  1683. if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
  1684. // If we subtract discounts BEFORE tax
  1685. // Subtract the whole discount
  1686. $price["product_price"] -= $discount_info["amount"];
  1687. }
  1688. else {
  1689. // But, if we subtract discounts AFTER tax
  1690. // Subtract the untaxed portion of the discount
  1691. $price["product_price"] -= $discount_info["amount"]/($my_taxrate + 1);
  1692. }
  1693. break;
  1694. case 1:
  1695. $price["product_price"] -= $price["product_price"]*($discount_info["amount"]/100);
  1696. break;
  1697. }
  1698. }
  1699. return $price;
  1700. }
  1701. /**
  1702. * This function can parse an "advanced / custom attribute"
  1703. * description like
  1704. * Size:big[+2.99]; Color:red[+0.99]
  1705. * and return the same string with values, tax added
  1706. * Size: big (+3.47), Color: red (+1.15)
  1707. *
  1708. * @param string $description
  1709. * @param int $product_id
  1710. * @return string The reformatted description
  1711. */
  1712. function getDescriptionWithTax( $description, $product_id=0 ) {
  1713. global $CURRENCY_DISPLAY, $mosConfig_secret;
  1714. require_once(CLASSPATH.'ps_product_attribute.php');
  1715. $auth = $_SESSION['auth'];
  1716. $description = stripslashes($description);
  1717. $description = str_replace("_"," ",$description);
  1718. // if we've been given a description to deal with, get the adjusted price
  1719. if ($description != '' && $auth["show_price_including_tax"] == 1 && $product_id != 0 ) {
  1720. $my_taxrate = $this->get_product_taxrate($product_id);
  1721. $price = $this->get_price( $product_id );
  1722. $product_currency = $price['product_currency'];
  1723. }
  1724. else {
  1725. $my_taxrate = 0.00;
  1726. $product_currency = '';
  1727. }
  1728. // We must care for custom attribute fields! Their value can be freely given
  1729. // by the customer, so we mustn't include them into the price calculation
  1730. // Thanks to AryGroup@ua.fm for the good advice
  1731. if( empty( $_REQUEST["custom_attribute_fields"] )) {
  1732. if( !empty( $_SESSION["custom_attribute_fields"] )) {
  1733. $custom_attribute_fields = vmGet( $_SESSION, "custom_attribute_fields", Array() );
  1734. $custom_attribute_fields_check = vmGet( $_SESSION, "custom_attribute_fields_check", Array() );
  1735. }
  1736. else {
  1737. $custom_attribute_fields = $custom_attribute_fields_check = Array();
  1738. }
  1739. }
  1740. else {
  1741. $custom_attribute_fields = $_SESSION["custom_attribute_fields"] = vmGet( $_REQUEST, "custom_attribute_fields", Array() );
  1742. $custom_attribute_fields_check = $_SESSION["custom_attribute_fields_check"]= vmGet( $_REQUEST, "custom_attribute_fields_check", Array() );
  1743. }
  1744. $product_attributes = ps_product_attribute::getAdvancedAttributes($product_id);
  1745. $attribute_keys = explode( ";", $description );
  1746. foreach( $attribute_keys as $temp_desc ) {
  1747. $finish = strpos($temp_desc,"]");
  1748. $temp_desc = trim( $temp_desc );
  1749. // Get the key name (e.g. "Color" )
  1750. $this_key = substr( $temp_desc, 0, strpos($temp_desc, ":") );
  1751. $this_value = substr( $temp_desc, strpos($temp_desc, ":")+1 );
  1752. if( in_array( $this_key, $custom_attribute_fields )) {
  1753. if( @$custom_attribute_fields_check[$this_key] == md5( $mosConfig_secret.$this_key )) {
  1754. // the passed value is valid, don't use it for calculating prices
  1755. continue;
  1756. }
  1757. }
  1758. $this_value = str_replace("_"," ",$this_value);
  1759. if( isset( $product_attributes[$this_key]['values'][$this_value] )) {
  1760. $modifier = $product_attributes[$this_key]['values'][$this_value]['adjustment'];
  1761. $operand = $product_attributes[$this_key]['values'][$this_value]['operand'];
  1762. $value_notax = $GLOBALS['CURRENCY']->convert( $modifier, $product_currency );
  1763. if( abs($value_notax) >0 ) {
  1764. $value_taxed = $value_notax * ($my_taxrate+1);
  1765. $temp_desc_new = str_replace( $operand.$modifier, $operand.' '.$CURRENCY_DISPLAY->getFullValue( $value_taxed ), $temp_desc );
  1766. $description = str_replace( $this_key.':'.$this_value,
  1767. $this_key.':'.$this_value.' ('.$operand.' '.$CURRENCY_DISPLAY->getFullValue( $value_taxed ).')',
  1768. $description);
  1769. }
  1770. $temp_desc = substr($temp_desc, $finish+1);
  1771. }
  1772. }
  1773. $description = str_replace("_"," ",$description);
  1774. $description = str_replace( $CURRENCY_DISPLAY->symbol, '@saved@', $description );
  1775. $description = str_replace( "[", " (", $description );
  1776. $description = str_replace( "]", ")", $description );
  1777. $description = str_replace( ":", ": ", $description );
  1778. $description = str_replace( ";", "<br/>", $description );
  1779. $description = str_replace( '@saved@', $CURRENCY_DISPLAY->symbol, $description );
  1780. return $description;
  1781. }
  1782. function calcEndUserprice( $product_id, $overrideShoppergroup ) {
  1783. global $VM_LANG, $CURRENCY_DISPLAY;
  1784. $auth = $_SESSION['auth'];
  1785. // Get the DISCOUNT AMOUNT
  1786. $discount_info = $this->get_discount( $product_id );
  1787. // Get the Price according to the quantity in the Cart
  1788. $price_info = $this->get_price( $product_id, false, $overrideShoppergroup );
  1789. if (isset($price_info["product_price_id"])) {
  1790. $base_price = $price_info["product_price"];
  1791. $price = $price_info["product_price"];
  1792. if ($auth["show_price_including_tax"] == 1) {
  1793. $my_taxrate = $this->get_product_taxrate($product_id);
  1794. $base_price += ($my_taxrate * $price);
  1795. }
  1796. else {
  1797. $my_taxrate = 0;
  1798. }
  1799. $tax = $my_taxrate * 100;
  1800. $price_info['tax_rate'] = $VM_LANG->_('PHPSHOP_TAX_LIST_RATE').': '.$tax.'%';
  1801. // Calculate discount
  1802. if( !empty($discount_info["amount"])) {
  1803. switch( $discount_info["is_percent"] ) {
  1804. case 0:
  1805. $base_price -= $discount_info["amount"];
  1806. $price_info['discount_info'] = $VM_LANG->_('PHPSHOP_PRODUCT_DISCOUNT_LBL').': '.$CURRENCY_DISPLAY->getFullValue($discount_info['amount']);
  1807. break;
  1808. case 1:
  1809. $base_price *= (100 - $discount_info["amount"])/100;
  1810. $price_info['discount_info'] = $VM_LANG->_('PHPSHOP_PRODUCT_DISCOUNT_LBL').': '.$discount_info['amount'].'%';
  1811. break;
  1812. }
  1813. }
  1814. $price_info['product_price'] = $base_price;
  1815. }
  1816. return $price_info;
  1817. }
  1818. /**
  1819. * Function to calculate the price, apply discounts from the discount table
  1820. * and reformat the price
  1821. *
  1822. * @param int $product_id
  1823. * @param boolean $hide_tax Wether to show the text "(including X.X% tax)" or not
  1824. * @return string The formatted price
  1825. */
  1826. function show_price( $product_id, $hide_tax = false ) {
  1827. global $VM_LANG, $CURRENCY_DISPLAY,$vendor_mail;
  1828. $auth = $_SESSION['auth'];
  1829. $tpl = new $GLOBALS['VM_THEMECLASS']();
  1830. $product_name = htmlentities( $this->get_field($product_id, 'product_name'), ENT_QUOTES );
  1831. $tpl->set( 'product_id', $product_id );
  1832. $tpl->set( 'product_name', $product_name );
  1833. $tpl->set( 'vendor_mail', $vendor_mail );
  1834. $discount_info = $base_price = array();
  1835. $text_including_tax = '';
  1836. if( $auth['show_prices'] ) {
  1837. // Get the DISCOUNT AMOUNT
  1838. $discount_info = $this->get_discount( $product_id );
  1839. if( !$discount_info["is_percent"] && $discount_info["amount"] != 0 ) {
  1840. $discount_info["amount"] = $GLOBALS['CURRENCY']->convert($discount_info["amount"]);
  1841. }
  1842. // Get the Price according to the quantity in the Cart
  1843. $price_info = $this->get_price( $product_id );
  1844. $tpl->set( 'price_info', $price_info );
  1845. // Get the Base Price of the Product
  1846. $base_price_info = $this->get_price($product_id, true );
  1847. $tpl->set( 'base_price_info', $base_price_info );
  1848. if( $price_info === false ) {
  1849. $price_info = $base_price_info;
  1850. }
  1851. $html = "";
  1852. $undiscounted_price = 0;
  1853. if (isset($price_info["product_price_id"])) {
  1854. if( $base_price_info["product_price"]== $price_info["product_price"] ) {
  1855. $price = $base_price = $GLOBALS['CURRENCY']->convert( $base_price_info["product_price"], $price_info['product_currency'] );
  1856. } else {
  1857. $base_price = $GLOBALS['CURRENCY']->convert( $base_price_info["product_price"], $price_info['product_currency'] );
  1858. $price = $GLOBALS['CURRENCY']->convert( $price_info["product_price"], $price_info['product_currency'] );
  1859. }
  1860. if ($auth["show_price_including_tax"] == 1) {
  1861. $my_taxrate = $this->get_product_taxrate($product_id);
  1862. $base_price += ($my_taxrate * $base_price);
  1863. }
  1864. else {
  1865. $my_taxrate = 0;
  1866. }
  1867. // Calculate discount
  1868. if( !empty($discount_info["amount"])) {
  1869. $undiscounted_price = $base_price;
  1870. switch( $discount_info["is_percent"] ) {
  1871. case 0:
  1872. // If we subtract discounts BEFORE tax
  1873. if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
  1874. // and if our prices are shown with tax
  1875. if( $auth["show_price_including_tax"] == 1) {
  1876. // then we add tax to the (untaxed) discount
  1877. $discount_info['amount'] += ($my_taxrate*$discount_info['amount']);
  1878. }
  1879. // but if our prices are shown without tax
  1880. // we just leave the (untaxed) discount amount as it is
  1881. }
  1882. // But, if we subtract discounts AFTER tax
  1883. // and if our prices are shown with tax
  1884. // we just leave the (untaxed) discount amount as it is
  1885. // but if prices are shown without tax
  1886. // we just leave the (untaxed) discount amount as it is
  1887. // even though this is not really a good combination of settings
  1888. $base_price -= $discount_info["amount"];
  1889. break;
  1890. case 1:
  1891. $base_price *= (100 - $discount_info["amount"])/100;
  1892. break;
  1893. }
  1894. }
  1895. $text_including_tax = "";
  1896. if (!empty($my_taxrate)) {
  1897. $tax = $my_taxrate * 100;
  1898. // only show "including x % tax" when it shall
  1899. // not be hidden
  1900. if( !$hide_tax && $auth["show_price_including_tax"] == 1 && VM_PRICE_SHOW_INCLUDINGTAX) {
  1901. $text_including_tax = $VM_LANG->_('PHPSHOP_INCLUDING_TAX');
  1902. eval ("\$text_including_tax = \"$text_including_tax\";");
  1903. }
  1904. }
  1905. // Check if we need to display a Table with all Quantity <=> Price Relationships
  1906. if( $base_price_info["product_has_multiple_prices"] && !$hide_tax ) {
  1907. $db = new ps_DB;
  1908. // Quantity Discount Table
  1909. $q = "SELECT product_price, product_currency, price_quantity_start, price_quantity_end
  1910. FROM #__{vm}_product_price
  1911. WHERE product_id='$product_id'
  1912. AND shopper_group_id='".$auth["shopper_group_id"]."'
  1913. ORDER BY price_quantity_start";
  1914. $db->query( $q );
  1915. // $prices_table = "<table align=\"right\">
  1916. $prices_table = "<table width=\"100%\">
  1917. <thead><tr class=\"sectiontableheader\">
  1918. <th>".$VM_LANG->_('PHPSHOP_CART_QUANTITY')."</th>
  1919. <th>".$VM_LANG->_('PHPSHOP_CART_PRICE')."</th>
  1920. </tr></thead>
  1921. <tbody>";
  1922. $i = 1;
  1923. if ($db->num_rows()==0) {
  1924. // get the vendor ID
  1925. $q = "SELECT vendor_id FROM #__{vm}_product WHERE product_id='$product_id'";
  1926. $db->setQuery($q); $db->query();
  1927. $db->next_record();
  1928. $vendor_id = $db->f("vendor_id");
  1929. // get the default shopper group ID
  1930. $q = "SELECT shopper_group_id FROM #__{vm}_shopper_group WHERE `vendor_id`='$vendor_id' AND `default`='1'";
  1931. $db->setQuery($q); $db->query();
  1932. $db->next_record();
  1933. $default_shopper_group_id = $db->f("shopper_group_id");
  1934. // get the current shopper group discount
  1935. $q = "SELECT * FROM #__{vm}_shopper_group WHERE shopper_group_id=" . $auth["shopper_group_id"];
  1936. $db->setQuery($q); $db->query();
  1937. $db->next_record();
  1938. $shopper_group_discount = $db->f("shopper_group_discount");
  1939. // check for prices in default shopper group
  1940. $q = "SELECT product_price, price_quantity_start, price_quantity_end FROM #__{vm}_product_price
  1941. WHERE product_id='$product_id' AND shopper_group_id='".$default_shopper_group_id."' ORDER BY price_quantity_start";
  1942. $db->query( $q );
  1943. while( $db->next_record() ) {
  1944. $prices_table .= "<tr class=\"sectiontableentry$i\"><td>".$db->f("price_quantity_start")." - ".$db->f("price_quantity_end")."</td>";
  1945. $prices_table .= "<td>";
  1946. if (!empty($my_taxrate))
  1947. $prices_table .= $CURRENCY_DISPLAY->getFullValue( ($my_taxrate+1)*$db->f("product_price")*((100-$shopper_group_discount)/100) );
  1948. else
  1949. $prices_table .= $CURRENCY_DISPLAY->getFullValue( $db->f("product_price")*((100-$shopper_group_discount)/100) );
  1950. $prices_table .= "</td></tr>";
  1951. $i == 1 ? $i++ : $i--;
  1952. }
  1953. } else {
  1954. // get the current shopper group discount
  1955. $dbsg = new ps_DB();
  1956. $q = "SELECT * FROM #__{vm}_shopper_group WHERE shopper_group_id=" . $auth["shopper_group_id"];
  1957. $dbsg->setQuery($q);
  1958. $dbsg->query();
  1959. $dbsg->next_record();
  1960. $shopper_group_discount = $dbsg->f("shopper_group_discount");
  1961. while( $db->next_record() ) {
  1962. $price = $GLOBALS['CURRENCY']->convert( $db->f("product_price"), $db->f("product_currency") );
  1963. $prices_table .= "<tr class=\"sectiontableentry$i\"><td>".$db->f("price_quantity_start")." - ".$db->f("price_quantity_end")."</td>";
  1964. $prices_table .= "<td>";
  1965. if (!empty($my_taxrate)) {
  1966. $prices_table .= $CURRENCY_DISPLAY->getFullValue( ($my_taxrate+1)*$price*((100-$shopper_group_discount)/100) );
  1967. }
  1968. else {
  1969. $prices_table .= $CURRENCY_DISPLAY->getFullValue( $price*((100-$shopper_group_discount)/100) );
  1970. }
  1971. $prices_table .= "</td></tr>";
  1972. $i == 1 ? $i++ : $i--;
  1973. }
  1974. }
  1975. $prices_table .= "</tbody></table>";
  1976. if( @$_REQUEST['page'] != "shop.product_details" ) {
  1977. $html .= vmToolTip( $prices_table );
  1978. }
  1979. else
  1980. $html .= $prices_table;
  1981. }
  1982. }
  1983. }
  1984. $tpl->set( 'discount_info', $discount_info );
  1985. $tpl->set( 'text_including_tax', $text_including_tax );
  1986. $tpl->set( 'undiscounted_price', @$undiscounted_price );
  1987. $tpl->set( 'base_price', $base_price );
  1988. $tpl->set( 'price_table', $html);
  1989. return $tpl->fetch( 'common/price.tpl.php');
  1990. }
  1991. /**
  1992. * Get the information about the discount for a product
  1993. *
  1994. * @param int $product_id
  1995. * @return array The discount information
  1996. */
  1997. function get_discount( $product_id ) {
  1998. global $mosConfig_lifetime;
  1999. // We use the Session now to store the discount info for
  2000. // each product. But this info can change regularly,
  2001. // so we check if the session time has expired
  2002. if( empty( $_SESSION['product_sess'][$product_id]['discount_info'] )
  2003. || (time() - $_SESSION['product_sess'][$product_id]['discount_info']['create_time'] )>$mosConfig_lifetime) {
  2004. $db = new ps_DB;
  2005. $starttime = time();
  2006. $year = date('Y');
  2007. $month = date('n');
  2008. $day = date('j');
  2009. // get the beginning time of today
  2010. $endofday = mktime(0, 0, 0, $month, $day, $year) - 1440;
  2011. // Get the DISCOUNT AMOUNT
  2012. $q = "SELECT amount,is_percent FROM #__{vm}_product,#__{vm}_product_discount ";
  2013. $q .= "WHERE product_id='$product_id' AND (start_date<='$starttime' OR start_date=0) AND (end_date>='$endofday' OR end_date=0) ";
  2014. $q .= "AND product_discount_id=discount_id";
  2015. $db->query( $q );
  2016. if( $db->next_record() ) {
  2017. $discount_info["amount"] = $db->f("amount");
  2018. $discount_info["is_percent"] = $db->f("is_percent");
  2019. $no_discount = false;
  2020. }
  2021. else {
  2022. $discount_info["amount"] = 0;
  2023. $discount_info["is_percent"] = 0;
  2024. $no_discount = true;
  2025. }
  2026. if ($no_discount) {
  2027. $q = "SELECT product_parent_id FROM #__{vm}_product WHERE product_id=$product_id";
  2028. $db->query($q);
  2029. if($db->next_record()) {
  2030. $q = "SELECT amount,is_percent FROM #__{vm}_product,#__{vm}_product_discount ";
  2031. $q .= "WHERE product_id='".$db->f("product_parent_id")."' AND (start_date<='$starttime' OR start_date=0) AND (end_date>='$endofday' OR end_date=0) ";
  2032. $q .= "AND product_discount_id=discount_id";
  2033. $db->query( $q );
  2034. if( $db->next_record() ) {
  2035. $discount_info["amount"] = $db->f("amount");
  2036. $discount_info["is_percent"] = $db->f("is_percent");
  2037. }
  2038. }
  2039. }
  2040. $discount_info['create_time'] = time();
  2041. $_SESSION['product_sess'][$product_id]['discount_info'] = $discount_info;
  2042. return $discount_info;
  2043. }
  2044. else
  2045. return $_SESSION['product_sess'][$product_id]['discount_info'];
  2046. }
  2047. /**
  2048. * display a snapshot of a product based on the product sku.
  2049. * This was written to provide a quick way to display a product inside of modules
  2050. *
  2051. * @param string $product_sku The SKU identifying the product
  2052. * @param boolean $show_price Show the product price?
  2053. * @param boolean $show_addtocart Show the add-to-cart link?
  2054. * @param boolean $show_product_name Show the product name?
  2055. */
  2056. function show_snapshot($product_sku, $show_price=true, $show_addtocart=true, $show_product_name = true ) {
  2057. echo $this->product_snapshot( $product_sku, $show_price, $show_addtocart, $show_product_name );
  2058. }
  2059. /**
  2060. * Returns HTML code for a snapshot of a product based on the product sku.
  2061. * This was written to provide a quick way to display a product inside of modules
  2062. *
  2063. * @param string $product_sku The SKU identifying the product
  2064. * @param boolean $show_price Show the product price?
  2065. * @param boolean $show_addtocart Show the add-to-cart link?
  2066. * @param boolean $show_product_name Show the product name?
  2067. */
  2068. function product_snapshot( $product_sku, $show_price=true, $show_addtocart=true, $show_product_name = true ) {
  2069. global $sess, $mm_action_url;
  2070. $db = new ps_DB;
  2071. require_once(CLASSPATH.'ps_product_category.php');
  2072. $ps_product_category = new ps_product_category;
  2073. $q = "SELECT product_id, product_name, product_parent_id, product_thumb_image FROM #__{vm}_product WHERE product_sku='$product_sku'";
  2074. $db->query( $q );
  2075. if ($db->next_record()) {
  2076. $product_id = $db->f("product_id" );
  2077. $tpl = new $GLOBALS['VM_THEMECLASS']();
  2078. $cid = $ps_product_category->get_cid( $product_id );
  2079. $tpl->set( 'product_id', $product_id);
  2080. $tpl->set( 'product_name', $db->f("product_name") );
  2081. $tpl->set( 'show_product_name', $show_product_name );
  2082. if ($db->f("product_parent_id")) {
  2083. $url = "?page=shop.product_details&category_id=$cid&flypage=".$this->get_flypage($db->f("product_parent_id"));
  2084. $url .= "&product_id=" . $db->f("product_parent_id");
  2085. } else {
  2086. $url = "?page=shop.product_details&category_id=$cid&flypage=".$this->get_flypage($db->f("product_id"));
  2087. $url .= "&product_id=" . $db->f("product_id");
  2088. }
  2089. $product_link = $sess->url($mm_action_url. "index.php" . $url);
  2090. $tpl->set( 'product_link', $product_link );
  2091. $tpl->set( 'product_thumb_image', $db->f("product_thumb_image"), "alt=\"".$db->f("product_name")."\"");
  2092. if (_SHOW_PRICES == '1' && $show_price) {
  2093. // Show price, but without "including X% tax"
  2094. $price = $this->show_price( $db->f("product_id"), true );
  2095. $tpl->set( 'price', $price );
  2096. }
  2097. if (USE_AS_CATALOGUE != 1 && $show_addtocart && isset( $GLOBALS['product_info'][$product_id]['price']['product_price_id'] )) {
  2098. $url = "?page=shop.cart&func=cartAdd&product_id=" . $db->f("product_id");
  2099. $addtocart_link = $sess->url($mm_action_url. "index.php" . $url);
  2100. $tpl->set( 'addtocart_link', $addtocart_link );
  2101. }
  2102. return $tpl->fetch( 'common/productsnapshot.tpl.php');
  2103. }
  2104. return '';
  2105. }
  2106. /**
  2107. * Use this function if you need the weight of a product
  2108. *
  2109. * @param int $prod_id
  2110. * @return int The weight of the product
  2111. */
  2112. function get_weight($prod_id) {
  2113. return (float)$this->get_field( $prod_id, "product_weight");
  2114. }
  2115. /**
  2116. * Print the availability HTML code for product $prod_id
  2117. *
  2118. * @param int $prod_id
  2119. */
  2120. function show_availability($prod_id) {
  2121. echo $this->get_availability($prod_id);
  2122. }
  2123. /**
  2124. * Returns the availability information as HTML code
  2125. * @author soeren
  2126. * @param int $prod_id
  2127. * @return string
  2128. */
  2129. function get_availability($prod_id) {
  2130. $html = '';
  2131. $availArr = $this->get_availability_data( $prod_id );
  2132. if( !empty( $availArr )) {
  2133. $tpl = vmTemplate::getInstance();
  2134. $tpl->set( 'product_id', $prod_id );
  2135. $tpl->set( 'product_available_date', $availArr['product_available_date'] );
  2136. $tpl->set( 'product_availability', $availArr['product_availability'] );
  2137. $tpl->set( 'product_in_stock', $availArr['product_in_stock'] );
  2138. $html = $tpl->fetch( 'common/availability.tpl.php');
  2139. }
  2140. return $html;
  2141. }
  2142. /**
  2143. * Retrieves the data related to availability information
  2144. *
  2145. * @param int $prod_id
  2146. * @return array
  2147. */
  2148. function get_availability_data( $prod_id) {
  2149. $is_parent = $this->parent_has_children( $prod_id );
  2150. $availArr = array();
  2151. if( !$is_parent ) {
  2152. $availArr['product_id'] = $prod_id;
  2153. $availArr['product_available_date'] = $this->get_field( $prod_id, 'product_available_date');
  2154. $availArr['product_availability'] = $this->get_field( $prod_id, 'product_availability');
  2155. $availArr['product_in_stock'] = $this->get_field( $prod_id, 'product_in_stock');
  2156. }
  2157. return $availArr;
  2158. }
  2159. /**
  2160. * Modifies the product_publish field and toggles it from Y to N or N to Y
  2161. * for product $d['product_id']
  2162. * @deprecated
  2163. * @param int $d $d['task'] must be "publish" or "unpublish"
  2164. * @return unknown
  2165. */
  2166. function product_publish( &$d ) {
  2167. $this->handlePublishState( $d );
  2168. return;
  2169. }
  2170. /**
  2171. * Function to check if a product exists (and is published)
  2172. * @static
  2173. * @param int $product_id
  2174. * @param boolean $check_publishstate
  2175. * @return boolean
  2176. */
  2177. function product_exists( $product_id, $check_publishstate=true ) {
  2178. $db = new ps_DB();
  2179. $q = "SELECT product_id FROM #__{vm}_product WHERE ";
  2180. $q .= "product_id = ".(int)$product_id;
  2181. if( $check_publishstate ) {
  2182. $q.= ' AND product_publish=\'Y\'';
  2183. }
  2184. $db->query ( $q );
  2185. return $db->num_rows() > 0;
  2186. }
  2187. /**
  2188. * Assembles the $child_options variable for storage in the product table
  2189. *
  2190. * @param unknown_type $d
  2191. * @return unknown
  2192. */
  2193. function set_child_options( $d ) {
  2194. if($d["product_parent_id"] !=0) {
  2195. $child_options = null;
  2196. } else {
  2197. $child_options = vmrequest::getYesOrNo('display_use_parent').","
  2198. .vmGet( $d, 'product_list', 'N' ).","
  2199. .vmrequest::getYesOrNo('display_headers').","
  2200. .vmrequest::getYesOrNo('product_list_child').","
  2201. .vmrequest::getYesOrNo('product_list_type').","
  2202. .vmrequest::getYesOrNo('display_desc').","
  2203. .vmrequest::getVar('desc_width').","
  2204. .vmrequest::getVar('attrib_width').","
  2205. .vmrequest::getWord('child_class_sfx');
  2206. }
  2207. return $child_options;
  2208. }
  2209. function &get_child_options( $product_id ) {
  2210. $child_options= array();
  2211. $child_options_string = ps_product::get_field( $product_id, 'child_options', true );
  2212. $fields=explode(',',$child_options_string);
  2213. if( !empty( $fields)) {
  2214. $child_options['display_use_parent'] =array_shift($fields);
  2215. $child_options['product_list'] =array_shift($fields);
  2216. $child_options['display_header'] =array_shift($fields);
  2217. $child_options['product_list_child'] =array_shift($fields);
  2218. $child_options['product_list_type'] =array_shift($fields);
  2219. $child_options['ddesc'] =array_shift($fields);
  2220. $child_options['display_desc'] =& $child_options['ddesc'];
  2221. $child_options['dw'] =array_shift($fields);
  2222. $child_options['desc_width'] =& $child_options['dw'];
  2223. $child_options['aw'] =array_shift($fields);
  2224. $child_options['attrib_width'] =& $child_options['aw'];
  2225. $child_options['class_suffix'] =array_shift($fields);
  2226. $child_options['child_class_sfx'] =& $child_options['class_suffix'];
  2227. }
  2228. return $child_options;
  2229. }
  2230. /**
  2231. * Assembles the string "quantity_options" for storage in the product table
  2232. *
  2233. * @param array $d
  2234. * @return string
  2235. */
  2236. function set_quantity_options( &$d ) {
  2237. return vmGet($d,'quantity_box').","
  2238. .vmRequest::getInt('quantity_start').","
  2239. .vmRequest::getInt('quantity_end').","
  2240. .vmRequest::getInt('quantity_step');
  2241. }
  2242. /**
  2243. * Disassembles the comma-separated "quantity_options" string
  2244. * and creates an array with associative indices
  2245. *
  2246. * @param int $product_id
  2247. * @return array
  2248. */
  2249. function &get_quantity_options( $product_id ) {
  2250. $quantity_options = array('quantity_start' => 0, 'quantity_end' => 0, 'quantity_step' => 1 );
  2251. $quantity_options_string = ps_product::get_field($product_id, 'quantity_options');
  2252. $fields = explode(',', $quantity_options_string );
  2253. if( !empty( $fields )&& sizeof($fields) > 1 ) {
  2254. $quantity_options['quantity_box'] = $fields[0];
  2255. $quantity_options['display_type'] = $fields[0];
  2256. $quantity_options['quantity_start'] = $fields[1];
  2257. $quantity_options['quantity_end'] = $fields[2];
  2258. $quantity_options['quantity_step'] = $fields[3];
  2259. }
  2260. return $quantity_options;
  2261. }
  2262. /**
  2263. * Retrieves the maximum and minimum quantity for the product specified by $product_id
  2264. *
  2265. * @param int $product_id
  2266. * @return array
  2267. */
  2268. function product_order_levels($product_id) {
  2269. $min_order=0;
  2270. $max_order=0;
  2271. $product_order_levels = ps_product::get_field( $product_id, 'product_order_levels');
  2272. $product_parent_id = ps_product::get_field( $product_id, 'product_parent_id');
  2273. if($product_order_levels != ',') {
  2274. $order_levels = $product_order_levels;
  2275. $levels = explode(",",$order_levels);
  2276. $min_order = array_shift($levels);
  2277. $max_order = array_shift($levels);
  2278. }
  2279. else if($product_parent_id > 0) {
  2280. //check parent if product_parent_id != 0
  2281. $product_order_levels = ps_product::get_field( $product_parent_id, 'product_order_levels');
  2282. $product_parent_id = ps_product::get_field( $product_parent_id, 'product_parent_id');
  2283. if($product_order_levels != ",") {
  2284. $order_levels = $product_order_levels;
  2285. $levels = explode(",",$order_levels);
  2286. $min_order = array_shift($levels);
  2287. $max_order = array_shift($levels);
  2288. }
  2289. }
  2290. return array($min_order,$max_order);
  2291. }
  2292. function featuredProducts($random, $products, $categories) {
  2293. global $VM_LANG;
  2294. require_once( CLASSPATH . 'ps_product_attribute.php');
  2295. $ps_product_attribute = new ps_product_attribute();
  2296. $db = new ps_DB;
  2297. $tpl = new $GLOBALS['VM_THEMECLASS']();
  2298. $category_id = null;
  2299. if($categories) {
  2300. $category_id = vmRequest::getInt('category_id');
  2301. }
  2302. if ( $category_id ) {
  2303. $q = "SELECT DISTINCT product_sku,#__{vm}_product.product_id,product_name,product_s_desc,product_thumb_image, product_full_image, product_in_stock, product_url FROM #__{vm}_product, #__{vm}_product_category_xref, #__{vm}_category WHERE \n";
  2304. $q .= "(#__{vm}_product.product_parent_id='' OR #__{vm}_product.product_parent_id='0') \n";
  2305. $q .= "AND #__{vm}_product.product_id=#__{vm}_product_category_xref.product_id \n";
  2306. $q .= "AND #__{vm}_category.category_id=#__{vm}_product_category_xref.category_id \n";
  2307. $q .= "AND #__{vm}_category.category_id='$category_id' \n";
  2308. $q .= "AND #__{vm}_product.product_publish='Y' \n";
  2309. $q .= "AND #__{vm}_product.product_special='Y' \n";
  2310. if( CHECK_STOCK && PSHOP_SHOW_OUT_OF_STOCK_PRODUCTS != "1") {
  2311. $q .= " AND product_in_stock > 0 \n";
  2312. }
  2313. $q .= 'ORDER BY RAND() LIMIT 0, '.(int)$products;
  2314. }
  2315. else {
  2316. $q = "SELECT DISTINCT product_sku,product_id,product_name,product_s_desc,product_thumb_image, product_full_image, product_in_stock, product_url FROM #__{vm}_product WHERE ";
  2317. $q .= "(#__{vm}_product.product_parent_id='' OR #__{vm}_product.product_parent_id='0') AND vendor_id='".$_SESSION['ps_vendor_id']."' ";
  2318. $q .= "AND #__{vm}_product.product_publish='Y' ";
  2319. $q .= "AND #__{vm}_product.product_special='Y' ";
  2320. if( CHECK_STOCK && PSHOP_SHOW_OUT_OF_STOCK_PRODUCTS != "1") {
  2321. $q .= " AND product_in_stock > 0 ";
  2322. }
  2323. $q .= 'ORDER BY RAND() LIMIT 0, '.(int)$products;
  2324. }
  2325. $db->query($q);
  2326. // Output using template
  2327. if( $db->num_rows() > 0 ) {
  2328. $i = 0;
  2329. $featured_products = array();
  2330. while($db->next_record()) {
  2331. $flypage = $this->get_flypage($db->f("product_id"));
  2332. $featured_products[$i]['product_sku'] = $db->f("product_sku");
  2333. $featured_products[$i]['product_name'] = $db->f("product_name");
  2334. $price = "";
  2335. if (_SHOW_PRICES == '1') {
  2336. // Show price, but without "including X% tax"
  2337. $price = $this->show_price( $db->f("product_id"), false );
  2338. }
  2339. $featured_products[$i]['product_price'] = $price;
  2340. $featured_products[$i]['product_s_desc'] = $db->f("product_s_desc");
  2341. $featured_products[$i]['product_url'] = $db->f("product_url");
  2342. $featured_products[$i]['product_thumb'] = $db->f("product_thumb_image");
  2343. $featured_products[$i]['product_full_image'] = $db->f("product_full_image");
  2344. $featured_products[$i]['product_id'] = $db->f("product_id");
  2345. $featured_products[$i]['flypage'] = $flypage;
  2346. $featured_products[$i]['form_addtocart'] = "";
  2347. if (USE_AS_CATALOGUE != '1' && $price != ""
  2348. && !stristr( $price, $VM_LANG->_('PHPSHOP_PRODUCT_CALL') )
  2349. && !$this->product_has_attributes( $db->f('product_id'), true )
  2350. && $tpl->get_cfg( 'showAddtocartButtonOnProductList' ) ) {
  2351. $tpl->set( 'i', $i );
  2352. $tpl->set( 'product_id', $db->f('product_id') );
  2353. $tpl->set( 'ps_product_attribute', $ps_product_attribute );
  2354. $tpl->set( 'product_in_stock', $db->f('product_in_stock'));
  2355. $featured_products[$i]['form_addtocart'] = $tpl->fetch( 'browse/includes/addtocart_form.tpl.php' );
  2356. $featured_products[$i]['has_addtocart'] = true;
  2357. }
  2358. $i++;
  2359. }
  2360. $tpl->set( 'featured_products', $featured_products );
  2361. return $tpl->fetch( 'common/featuredProducts.tpl.php');
  2362. }
  2363. }
  2364. function latestProducts($random, $products) {
  2365. return "";
  2366. }
  2367. function addRecentProduct($product_id,$category_id,$maxviewed) {
  2368. global $recentproducts;
  2369. //Check to see if we alread have recent
  2370. if($_SESSION['recent']['idx'] !=0) {
  2371. for($i=0;$i<$_SESSION['recent']['idx'];$i++){
  2372. //Check if it already exists and remove and reorder array
  2373. if($_SESSION['recent'][$i]['product_id']==$product_id) {
  2374. for($k=$i;$k<$_SESSION['recent']['idx']-1;$k++){
  2375. $_SESSION['recent'][$k]=$_SESSION['recent'][$k+1];
  2376. }
  2377. array_pop($_SESSION['recent']);
  2378. $_SESSION['recent']['idx']--;
  2379. }
  2380. }
  2381. }
  2382. // add product to recently viewed
  2383. $_SESSION['recent'][$_SESSION['recent']['idx']]['product_id'] = $product_id;
  2384. $_SESSION['recent'][$_SESSION['recent']['idx']]['category_id'] = $category_id;
  2385. $_SESSION['recent']['idx']++;
  2386. //Check to see if we have reached are limit and remove first item
  2387. if($_SESSION['recent']['idx'] > $maxviewed+1) {
  2388. for($k=0;$k<$_SESSION['recent']['idx']-1;$k++){
  2389. $_SESSION['recent'][$k]=$_SESSION['recent'][$k+1];
  2390. }
  2391. array_pop($_SESSION['recent']);
  2392. $_SESSION['recent']['idx']--;
  2393. }
  2394. }
  2395. function recentProducts($product_id,$maxitems) {
  2396. global $db, $VM_LANG, $sess;
  2397. if( $maxitems == 0 ) return;
  2398. $recentproducts = $_SESSION['recent'];
  2399. //No recent products so return empty
  2400. if($recentproducts['idx'] == 0) {
  2401. //return "";
  2402. }
  2403. $tpl = new $GLOBALS['VM_THEMECLASS']();
  2404. $db = new ps_DB;
  2405. $dbp = new ps_DB;
  2406. $k=0;
  2407. $recent = array();
  2408. // Iterate through loop backwards (newest to oldest)
  2409. for($i=$recentproducts['idx']-1;$i >= 0;$i--) {
  2410. //Check if on current product and don't display
  2411. if($recentproducts[$i]['product_id']== $product_id){
  2412. continue;
  2413. }
  2414. // If we have not reached max products add the next product
  2415. if($k < $maxitems+1) {
  2416. $prod_id = $recentproducts[$i]['product_id'];
  2417. $category_id = $recentproducts[$i]['category_id'];
  2418. $q = "SELECT product_name, category_name, c.category_flypage,product_s_desc,product_thumb_image ";
  2419. $q .= "FROM #__{vm}_product as p,#__{vm}_category as c,#__{vm}_product_category_xref as cx ";
  2420. $q .= "WHERE p.product_id = '$prod_id' ";
  2421. $q .= "AND c.category_id = '$category_id' ";
  2422. $q .= "AND p.product_id = cx.product_id ";
  2423. $q .= "AND c.category_id=cx.category_id LIMIT 0,1";
  2424. $db->query( $q );
  2425. if(!$this->is_product($prod_id )) {
  2426. $prod_id_p = $this->get_field($prod_id,"product_parent_id");
  2427. $q = "SELECT product_name,category_name, c.category_flypage,product_s_desc,product_thumb_image ";
  2428. $q .= "FROM #__{vm}_product as p,#__{vm}_category as c,#__{vm}_product_category_xref as cx ";
  2429. $q .= "WHERE p.product_id = '$prod_id_p' ";
  2430. $q .= "AND c.category_id = '$category_id' ";
  2431. $q .= "AND p.product_id = cx.product_id ";
  2432. $q .= "AND c.category_id=cx.category_id LIMIT 0,1";
  2433. $dbp->query( $q );
  2434. }
  2435. $recent[$k]['product_s_desc'] = $db->f("product_s_desc");
  2436. if($recent[$k]['product_s_desc']=="" && !empty($prod_id_p)) {
  2437. $recent[$k]['product_s_desc'] = $dbp->f("product_s_desc");
  2438. }
  2439. $flypage = $db->f("category_flypage");
  2440. if(empty($flypage) && !empty($prod_id_p))
  2441. $flypage = $dbp->sf("category_flypage");
  2442. if( empty( $flypage )) {
  2443. $flypage = FLYPAGE;
  2444. }
  2445. $flypage = str_replace( 'shop.', '', $flypage);
  2446. $flypage = stristr( $flypage, '.tpl') ? $flypage : $flypage . '.tpl';
  2447. $recent[$k]['product_url'] = $sess->url("page=shop.product_details&amp;product_id=$prod_id&amp;category_id=$category_id&amp;flypage=$flypage");
  2448. $recent[$k]['category_url'] = $sess->url("page=shop.browse&amp;category_id=$category_id");
  2449. $recent[$k]['product_name'] = $db->f("product_name");
  2450. if($recent[$k]['product_name']=="" && !empty($prod_id_p)) {
  2451. $recent[$k]['product_name'] = $dbp->f("product_name");
  2452. }
  2453. $recent[$k]['category_name'] = $db->f("category_name");
  2454. if($recent[$k]['category_name']=="" && !empty($prod_id_p)) {
  2455. $recent[$k]['category_name'] = $dbp->f("category_name");
  2456. }
  2457. $recent[$k]['product_thumb_image'] = $db->f("product_thumb_image");
  2458. if($recent[$k]['product_thumb_image']=="" && !empty($prod_id_p)) {
  2459. $recent[$k]['product_thumb_image'] = $dbp->f("product_thumb_image");
  2460. }
  2461. $k++;
  2462. }
  2463. }
  2464. if($k == 0) {
  2465. return "";
  2466. }
  2467. $tpl->set("recent_products",$recent);
  2468. return $tpl->fetch( 'common/recent.tpl.php' );
  2469. }
  2470. } // ENd of CLASS ps_product
  2471. ?>