Add module antadisconfigurator + specific

This commit is contained in:
Michael RICOIS 2017-06-08 12:23:50 +02:00
parent 3c9a8fa5cf
commit 21cd725307
28 changed files with 4404 additions and 2 deletions

View File

@ -43,7 +43,8 @@ class Adapter_ProductPriceCalculator
$with_ecotax = true,
$use_group_reduction = true,
Context $context = null,
$use_customer_price = true
$use_customer_price = true,
$id_configurator = null
) {
return Product::getPriceStatic(
$id_product,
@ -62,7 +63,8 @@ class Adapter_ProductPriceCalculator
$with_ecotax,
$use_group_reduction,
$context,
$use_customer_price
$use_customer_price,
$id_configurator
);
}
}

View File

@ -0,0 +1,49 @@
# Install
# Documentation
* Configuration d'une liste d'options (group)
- type, reference, name
* Configuration des valeurs
- name, price, position
* Association au produit
- Group : required, position
- Impact : name with price
Lors de la sélection du choix des options pour le panier
Group avec texte libre (text, date)
optgroup-{id} = value
Group avec option "list"
optgroup-{id} = {id_optimpact}
Attention les valeurs du groupes peuvent être multiples
Désactivé le panier en ajax
Attention même id_cart - id_product mais options différentes
Associations ps_cart_product <= id_configurator
ALTER TABLE `ps_cart_product` ADD `id_configurator` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id_product_attribute`;
+ ajouter id_configurator a PRIMARY KEY
Associations ps_order_detail <= id_configurator
Table stockage des options id_configurator | id_product | id_product_configurator_opt_group | value | price
Modifier l'Adapter
Adapter_ProductPriceCalculator
* Panier
- Stockage des valeurs selectionnées dans une table
id + id_option + value + price
* TODO
- Afficher le détail des options choisies dans le panier, dans la commande
- Question : dans le stockage des options quid des éléments text, date, fichier
id_option_group + id_option + value + priceImpact

View File

@ -0,0 +1,155 @@
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
require_once dirname(__FILE__).'/classes/ConfiguratorOptGroup.php';
require_once dirname(__FILE__).'/classes/ConfiguratorOpt.php';
require_once dirname(__FILE__).'/classes/ProductConfiguratorOptGroup.php';
require_once dirname(__FILE__).'/classes/ProductConfiguratorOptImpact.php';
class AntadisConfigurator extends Module
{
public function __construct()
{
$this->name = 'antadisconfigurator';
$this->tab = 'front_office_features';
$this->version = '0.1';
$this->author = 'antadis';
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Product Options');
$this->description = $this->l('Add options on product');
$this->ps_versions_compliancy = array('min' => '1.6', 'max' => _PS_VERSION_);
$this->confirmUninstall = $this->l('Are you sure you want to uninstall this module?');
}
public function install()
{
// Call install parent method
if (!parent::install()) {
return false;
}
// Menu
if (!$this->installTab('AdminCatalog', 'AdminAntadisConfigurator', 'Configurator Options')) {
return false;
}
// Install AdminAntadisConfiguratorProduct
if (!$this->installTab('AdminCatalog', 'AdminAntadisConfiguratorProduct', 'Configurator Product', 0)) {
return false;
}
// Hook
if (!$this->registerHook('displayAdminProductsExtra') ||
!$this->registerHook('displayProductTabContent') ||
!$this->registerHook('displayProductForm')) {
return false;
}
// All went well!
return true;
}
public function uninstall()
{
// Call uninstall parent method
if (!parent::uninstall()) {
return false;
}
// Menu
if (!$this->uninstallTab('AdminAntadisConfigurator') ||
!$this->uninstallTab('AdminAntadisConfiguratorProduct')) {
return false;
}
// Hook
if (!$this->unregisterHook('displayAdminProductsExtra') ||
!$this->unregisterHook('displayProductTabContent') ||
!$this->unregisterHook('displayProductForm')) {
return false;
}
// All went well!
return true;
}
public function getContent()
{
$ajax_hook = Tools::getValue('ajax_hook');
if ($ajax_hook != '') {
$ajax_method = 'hook'.ucfirst($ajax_hook);
if (method_exists($this, $ajax_method)) {
die($this->{$ajax_method}(array()));
}
}
$controller = $this->getHookController('getContent');
return $controller->run();
}
public function installTab($parent, $class_name, $name, $active = 1)
{
// Create new admin tab
$tab = new Tab();
$tab->id_parent = (int)Tab::getIdFromClassName($parent);
$tab->name = array();
foreach (Language::getLanguages(true) as $lang) {
$tab->name[$lang['id_lang']] = $name;
$tab->class_name = $class_name;
$tab->module = $this->name;
$tab->active = $active;
return $tab->add();
}
}
public function uninstallTab($class_name)
{
// Retrieve Tab ID
$id_tab = (int)Tab::getIdFromClassName($class_name);
// Load tab
$tab = new Tab((int)$id_tab);
// Delete it
return $tab->delete();
}
public function getHookController($hook_name)
{
// Include the controller file
require_once(dirname(__FILE__).'/controllers/hook/'. $hook_name.'.php');
// Build dynamically the controller name
$controller_name = $this->name.$hook_name.'Controller';
// Instantiate controller
$controller = new $controller_name($this, __FILE__, $this->_path);
// Return the controller
return $controller;
}
public function hookDisplayAdminProductsExtra($params)
{
$controller = $this->getHookController('displayAdminProductsExtra');
return $controller->run();
}
public function hookDisplayProductTabContent($params)
{
$controller = $this->getHookController('displayProductTabContent');
return $controller->run();
}
public function hookDisplayProductForm($params)
{
$controller = $this->getHookController('displayProductForm');
return $controller->run();
}
}

View File

@ -0,0 +1,123 @@
<?php
class ConfiguratorOpt extends ObjectModel
{
public $id_configurator_opt_group;
public $price;
public $position;
public $name;
public static $definition = array(
'table' => 'configurator_opt',
'primary' => 'id_configurator_opt',
'multilang' => true,
'fields' => array(
'id_configurator_opt_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'price' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true),
'position' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
),
);
/**
* Move an attribute inside its group
* @param bool $way Up (1) or Down (0)
* @param int $position
* @return bool Update result
*/
public function updatePosition($way, $position)
{
if (!$id_configurator_opt_group = (int)Tools::getValue('id_configurator_opt_group')) {
$id_configurator_opt_group = (int)$this->id_configurator_opt_group;
}
$sql = '
SELECT a.`id_configurator_opt`, a.`position`, a.`id_configurator_opt_group`
FROM `'._DB_PREFIX_.'configurator_opt` a
WHERE a.`id_configurator_opt_group` = '.(int)$id_configurator_opt_group.'
ORDER BY a.`position` ASC';
if (!$res = Db::getInstance()->executeS($sql)) {
return false;
}
foreach ($res as $attribute) {
if ((int)$attribute['id_configurator_opt'] == (int)$this->id) {
$moved_attribute = $attribute;
}
}
if (!isset($moved_attribute) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
$res1 = Db::getInstance()->execute('
UPDATE `'._DB_PREFIX_.'configurator_opt`
SET `position`= `position` '.($way ? '- 1' : '+ 1').'
WHERE `position`
'.($way
? '> '.(int)$moved_attribute['position'].' AND `position` <= '.(int)$position
: '< '.(int)$moved_attribute['position'].' AND `position` >= '.(int)$position).'
AND `id_configurator_opt_group`='.(int)$moved_attribute['id_configurator_opt_group']
);
$res2 = Db::getInstance()->execute('
UPDATE `'._DB_PREFIX_.'configurator_opt`
SET `position` = '.(int)$position.'
WHERE `id_configurator_opt` = '.(int)$moved_attribute['id_configurator_opt'].'
AND `id_configurator_opt_group`='.(int)$moved_attribute['id_configurator_opt_group']
);
return ($res1 && $res2);
}
/**
* Reorder attribute position in group $id_attribute_group.
* Call it after deleting an attribute from a group.
*
* @param int $id_attribute_group
* @param bool $use_last_attribute
* @return bool $return
*/
public function cleanPositions($id_configurator_opt_group, $use_last = true)
{
Db::getInstance()->execute('SET @i = -1', false);
$sql = 'UPDATE `'._DB_PREFIX_.'configurator_opt` SET `position` = @i:=@i+1 WHERE';
if ($use_last) {
$sql .= ' `id_configurator_opt` != '.(int)$this->id.' AND';
}
$sql .= ' `id_configurator_opt_group` = '.(int)$id_configurator_opt_group.' ORDER BY `position` ASC';
$return = Db::getInstance()->execute($sql);
}
/**
* getHigherPosition
*
* Get the higher attribute position from a group attribute
*
* @param int $id_attribute_group
* @return int $position
*/
public static function getHigherPosition($id_configurator_opt_group)
{
$sql = 'SELECT MAX(`position`)
FROM `'._DB_PREFIX_.'configurator_opt`
WHERE id_configurator_opt_group = '.(int)$id_configurator_opt_group;
$position = DB::getInstance()->getValue($sql);
return (is_numeric($position)) ? $position : -1;
}
}

View File

@ -0,0 +1,65 @@
<?php
class ConfiguratorOptGroup extends ObjectModel
{
public $id_configurator_opt_group;
public $type;
public $reference;
public $name;
public static $definition = array(
'table' => 'configurator_opt_group',
'primary' => 'id_configurator_opt_group',
'multilang' => true,
'fields' => array(
'type' => array('type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true),
'reference' => array('type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
),
'associations' => array(
'options' => array('type' => self::HAS_MANY, 'field' => 'id_configurator_opt_group', 'object' => 'ConfiguratorOpt', 'association' => 'configurator_opt'),
),
);
public function getConfiguratorOptName()
{
$id_lang = (int)Context::getContext()->language->id;
return $this->name[$id_lang];
}
public static function getAll()
{
$id_lang = (int)Context::getContext()->language->id;
$opts = Db::getInstance()->executeS('
SELECT cog.*, cogl.name
FROM `'._DB_PREFIX_.'configurator_opt_group` cog, `'._DB_PREFIX_.'configurator_opt_group_lang` cogl
WHERE cog.id_configurator_opt_group = cogl.id_configurator_opt_group AND cogl.id_lang = '.(int)$id_lang);
return $opts;
}
public function delete()
{
// Supprimer les Opt associés
}
public static function getValues($id_configurator_opt_group)
{
$id_lang = (int)Context::getContext()->language->id;
$sql = 'SELECT co.*, col.name
FROM `'._DB_PREFIX_.'configurator_opt` co
LEFT JOIN `'._DB_PREFIX_.'configurator_opt_lang` col ON co.`id_configurator_opt` = col.`id_configurator_opt`
WHERE co.`id_configurator_opt_group` = '.(int)$id_configurator_opt_group.' AND col.`id_lang` = '.(int)$id_lang;
$result = Db::getInstance()->executeS($sql);
return $result;
}
}

View File

@ -0,0 +1,65 @@
<?php
class ProductConfiguratorOptGroup extends ObjectModel
{
public $id_product_configurator_opt_group;
public $id_product;
public $id_configurator_opt_group;
public $required;
public $position;
public static $definition = array(
'table' => 'product_configurator_opt_group',
'primary' => 'id_product_configurator_opt_group',
'multilang' => false,
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_configurator_opt_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'required' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
'position' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
),
);
public static function getProductConfiguratorOptGroup($id_product)
{
$id_lang = (int)Context::getContext()->language->id;
$opts = Db::getInstance()->executeS('
SELECT pcog.*, col.`name` FROM `'._DB_PREFIX_.'product_configurator_opt_group` pcog, `'._DB_PREFIX_.'configurator_opt_group_lang` col
WHERE pcog.`id_product` = '.(int)$id_product.'
AND pcog.`id_configurator_opt_group` = col.`id_configurator_opt_group`
AND col.`id_lang` = '.(int)$id_lang.'
ORDER BY pcog.`position` ASC');
return $opts;
}
public function getConfiguratorOptGroupName()
{
$id_lang = (int)Context::getContext()->language->id;
return Db::getInstance()->getValue('
SELECT `name`
FROM `'._DB_PREFIX_.'configurator_opt_group_lang`
WHERE `id_configurator_opt_group` = '.(int)$this->id_configurator_opt_group.' AND `id_lang` = '.(int)$id_lang);
}
/**
* getHigherPosition
*
* Get the higher position
*
* @param int $id_product
* @return int $position
*/
public static function getHigherPosition($id_product)
{
$sql = 'SELECT MAX(`position`)
FROM `'._DB_PREFIX_.'product_configurator_opt_group`
WHERE id_product = '.(int)$id_product;
$position = DB::getInstance()->getValue($sql);
return (is_numeric($position)) ? $position : -1;
}
}

View File

@ -0,0 +1,34 @@
<?php
class ProductConfiguratorOptImpact extends ObjectModel
{
public $id_product_configurator_opt_impact;
public $id_product;
public $id_product_configurator_opt_group;
public $id_configurator_opt;
public $price;
public static $definition = array(
'table' => 'product_configurator_opt_impact',
'primary' => 'id_product_configurator_opt_impact',
'multilang' => false,
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_product_configurator_opt_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_configurator_opt' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'price' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true),
),
);
public static function getProductOptImpact($id_product)
{
$context = Context::getContext();
$result = Db::getInstance()->executeS('
SELECT pcoi.*, co.*, col.name FROM `ps_product_configurator_opt_impact` pcoi, ps_configurator_opt co
LEFT JOIN ps_configurator_opt_lang col ON (col.id_configurator_opt = co.id_configurator_opt AND col.id_lang = 1)
WHERE pcoi.id_product = 1 AND pcoi.id_configurator_opt = co.id_configurator_opt
ORDER BY position ASC');
return $result;
}
}

View File

@ -0,0 +1,468 @@
<?php
class AdminAntadisConfiguratorController extends ModuleAdminController
{
public function __construct()
{
$this->table = 'configurator_opt_group';
$this->className = 'ConfiguratorOptGroup';
$this->lang = true;
// Set fields list
$this->fields_list = array(
'id_configurator_opt_group' => array(
'title' => $this->l('ID'),
'align' => 'center',
'width' => 25
),
'name' => array(
'title' => $this->l('Nom')
),
'type' => array(
'title' => $this->l('Type')
),
'reference' => array(
'title' => $this->l('Reference')
),
);
// Enable bootstrap
$this->bootstrap = true;
// Call of the parent constructor method
parent::__construct();
}
/**
* AdminController::renderList() override
* @see AdminController::renderList()
*/
public function renderList()
{
$this->addRowAction('view');
$this->addRowAction('edit');
$this->addRowAction('delete');
return parent::renderList();
}
public function renderView()
{
// Afficher la liste des valeurs de l'option
if (($id = Tools::getValue('id_configurator_opt_group'))) {
$this->table = 'configurator_opt';
$this->className = 'ConfiguratorOpt';
$this->identifier = 'id_configurator_opt';
$this->position_identifier = 'id_configurator_opt';
$this->list_id = 'values';
$this->lang = true;
$this->context->smarty->assign(array(
'current' => self::$currentIndex.'&id_configurator_opt_group='.(int)$id.'&viewconfigurator_opt_group'
));
if (!Validate::isLoadedObject($obj = new ConfiguratorOptGroup((int)$id))) {
$this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
return;
}
/*if ($obj->type == 'label') {
$tpl = $this->context->smarty->createTemplate(dirname(__FILE__).'/../../views/templates/admin/antadisconfiguratorView.tpl');
$tpl->assign('message', $this->l("Les options de type '' ne peuvent avoir de valeurs."));
return $tpl->fetch();
}*/
$this->fields_list = array(
'id_configurator_opt' => array(
'title' => $this->l('ID'),
'align' => 'center',
'class' => 'fixed-width-xs'
),
'name' => array(
'title' => $this->l('Name'),
'width' => 'auto',
),
'price' => array(
'title' => $this->l('Price'),
'type' => 'price',
),
);
switch($obj->type) {
case 'radio':
case 'checkbox':
case 'select':
$this->fields_list['position'] = array(
'title' => $this->l('Position'),
'filter_key' => 'a!position',
'position' => 'position',
'class' => 'fixed-width-md'
);
break;
}
$this->_where = 'AND a.`id_configurator_opt_group` = '.(int)$id;
$this->_orderBy = 'position';
$this->addRowAction('edit');
$this->addRowAction('delete');
self::$currentIndex = self::$currentIndex.'&id_configurator_opt_group='.(int)$id.'&viewconfigurator_opt_group';
$this->processFilter();
return parent::renderList();
}
}
/**
* AdminController::init() override
* @see AdminController::init()
*/
public function init()
{
if (Tools::isSubmit('updateconfigurator_opt')) {
$this->display = 'editValues';
} elseif (Tools::isSubmit('submitAddconfigurator_opt')) {
$this->display = 'editValues';
} elseif (Tools::isSubmit('submitAddconfigurator_opt_group')) {
$this->display = 'add';
}
parent::init();
}
public function initContent()
{
$this->initToolbar();
$this->initPageHeaderToolbar();
if ($this->display == 'edit' || $this->display == 'add') {
if (!($this->object = $this->loadObject(true))) {
return;
}
$this->content .= $this->renderForm();
} elseif ($this->display == 'editValues') {
if (!$this->object = new ConfiguratorOpt((int)Tools::getValue('id_configurator_opt'))) {
return;
}
$this->content .= $this->renderFormOpt();
} elseif ($this->display != 'view' && !$this->ajax) {
$this->content .= $this->renderList();
//$this->content .= $this->renderOptions();
} elseif ($this->display == 'view' && !$this->ajax) {
$this->content = $this->renderView();
}
$this->context->smarty->assign(array(
'table' => $this->table,
'current' => self::$currentIndex,
'token' => $this->token,
'content' => $this->content,
'url_post' => self::$currentIndex.'&token='.$this->token,
'show_page_header_toolbar' => $this->show_page_header_toolbar,
'page_header_toolbar_title' => $this->page_header_toolbar_title,
'page_header_toolbar_btn' => $this->page_header_toolbar_btn
));
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_configurator_opt_group'] = array(
'href' => self::$currentIndex.'&addconfigurator_opt_group&token='.$this->token,
'desc' => $this->l('Add New Option', null, null, false),
'icon' => 'process-icon-new'
);
}
parent::initPageHeaderToolbar();
}
public function initToolbar()
{
switch ($this->display) {
case 'add':
case 'edit':
case 'editValues':
// Default save button - action dynamically handled in javascript
$this->toolbar_btn['save'] = array(
'href' => '#',
'desc' => $this->l('Save')
);
$this->toolbar_btn['back'] = array(
'href' => self::$currentIndex.'&token='.$this->token,
'desc' => $this->l('Back to list', null, null, false)
);
break;
break;
case 'view':
$this->toolbar_btn['new'] = array(
'href' => self::$currentIndex.'&updateconfigurator_opt&id_configurator_opt_group='.(int)Tools::getValue('id_configurator_opt_group').'&token='.$this->token,
'desc' => $this->l('Add New Value', null, null, false),
'class' => 'toolbar-new'
);
$this->toolbar_btn['back'] = array(
'href' => self::$currentIndex.'&token='.$this->token,
'desc' => $this->l('Back to list', null, null, false)
);
break;
default: // list
$this->toolbar_btn['new'] = array(
'href' => self::$currentIndex.'&add'.$this->table.'&token='.$this->token,
'desc' => $this->l('Add New Option', null, null, false)
);
break;
}
}
public function initProcess()
{
$this->setTypeOpt();
if (Tools::getIsset('viewconfigurator_opt_group')) {
$this->list_id = 'values';
if (isset($_POST['submitReset'.$this->list_id])) {
$this->processResetFilters();
}
} else {
$this->list_id = 'group';
}
parent::initProcess();
if ($this->table == 'configurator_opt') {
$this->display = 'editValues';
$this->id_configurator_opt = (int)Tools::getValue('id_configurator_opt');
}
}
protected function setTypeOpt()
{
if (Tools::isSubmit('updateconfigurator_opt') || Tools::isSubmit('deleteconfigurator_opt') ||
Tools::isSubmit('submitAddconfigurator_opt')) {
$this->table = 'configurator_opt';
$this->className = 'ConfiguratorOpt';
$this->identifier = 'id_configurator_opt';
if ($this->display == 'edit') {
$this->display = 'editValues';
}
}
}
/**
* Form option
* {@inheritDoc}
* @see AdminControllerCore::renderForm()
*/
public function renderForm()
{
$this->table = 'configurator_opt_group';
$this->identifier = 'id_configurator_opt_group';
$this->show_form_cancel_button = true;
$this->fields_form = array(
'legend' => array('title' => $this->l('Add / Edit')),
'input' => array(
array('type' => 'text', 'label' => $this->l('Name'), 'name' => 'name', 'required' => true, 'lang' => true),
array('type' => 'select', 'label' => $this->l('Type'), 'name' => 'type', 'required' => true,
'options' => array(
'id' => 'id',
'name' => 'name',
'query' => array(
array('id' => 'text', 'name' => 'Texte saisie'),
array('id' => 'date', 'name' => 'Date'),
array('id' => 'select', 'name' => 'Selection'),
array('id' => 'radio', 'name' => 'Choix unique'),
array('id' => 'checkbox', 'name' => 'Choix multiple'),
),
),
),
array('type' => 'text', 'label' => $this->l('Reference'), 'name' => 'reference', 'required' => true),
),
'submit' => array('title' => $this->l('Save'))
);
return parent::renderForm();
}
/**
* Form option value
* @return string
*/
public function renderFormOpt()
{
$this->table = 'configurator_opt';
$this->identifier = 'id_configurator_opt';
$this->show_form_cancel_button = true;
$this->fields_form = array(
'legend' => array('title' => $this->l('Add / Edit')),
'input' => array(
array(
'type' => 'hidden',
'name' => 'id_configurator_opt_group',
'required' => true,
),
array(
'type' => 'text',
'label' => $this->l('Name'),
'name' => 'name',
'required' => true,
'lang' => true
),
array(
'type' => 'text',
'label' => $this->l('Value'),
'name' => 'value',
'required' => true,
),
array(
'type' => 'text',
'label' => $this->l('Price'),
'name' => 'price',
'required' => true
),
),
'submit' => array(
'title' => $this->l('Save')
),
);
$this->fields_value['id_configurator_opt_group'] = (int)Tools::getValue('id_configurator_opt_group');
// Override var of Controller
$this->table = 'configurator_opt';
$this->className = 'ConfiguratorOpt';
$this->identifier = 'id_configurator_opt';
$this->lang = true;
// Create object Attribute
if (!$obj = new ConfiguratorOpt((int)Tools::getValue($this->identifier))) {
return;
}
return parent::renderForm();
}
public function postProcess()
{
if (!Tools::getValue($this->identifier) && Tools::getValue('id_configurator_opt') && !Tools::getValue('attributeOrderby')) {
// Override var of Controller
$this->table = 'configurator_opt';
$this->className = 'ConfiguratorOpt';
$this->identifier = 'id_configurator_opt';
}
// If Opt, load object
if (Tools::getValue('updateconfigurator_opt') || Tools::isSubmit('deleteconfigurator_opt') || Tools::isSubmit('submitAddconfigurator_opt')) {
if (!$object = new ConfiguratorOpt((int)Tools::getValue($this->identifier))) {
$this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
}
if (Tools::getValue('position') !== false && Tools::getValue('id_configurator_opt')) {
$_POST['id_configurator_opt_group'] = $object->id_configurator_opt_group;
if (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position'))) {
$this->errors[] = Tools::displayError('Failed to update the position.');
} else {
Tools::redirectAdmin(self::$currentIndex.'&conf=5&token='.Tools::getAdminTokenLite('AdminAntadisConfigurator').'#details_details_'.$object->id_configurator_opt_group);
}
} elseif (Tools::isSubmit('deleteconfigurator_opt') && Tools::getValue('id_configurator_opt')) {
if (!$object->delete()) {
$this->errors[] = Tools::displayError('Failed to delete the attribute.');
} else {
Tools::redirectAdmin(self::$currentIndex.'&conf=1&token='.Tools::getAdminTokenLite('AdminAntadisConfigurator'));
}
} elseif (Tools::isSubmit('submitAddconfigurator_opt')) {
Hook::exec('actionObjectConfiguratorOptAddBefore');
$this->action = 'save';
$id_configurator_opt = (int)Tools::getValue('id_configurator_opt');
// Adding last position to the attribute if not exist
if ($id_configurator_opt <= 0) {
$sql = 'SELECT `position`+1
FROM `'._DB_PREFIX_.'configurator_opt`
WHERE `id_configurator_opt_group` = '.(int)Tools::getValue('id_configurator_opt_group').'
ORDER BY position DESC';
// set the position of the new group attribute in $_POST for postProcess() method
$_POST['position'] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
}
$_POST['id_parent'] = 0;
$this->processSave($this->token);
}
} else {
parent::postProcess();
}
}
public function processSave()
{
if ($this->display == 'add' || $this->display == 'edit') {
$this->identifier = 'id_configurator_opt_group';
}
if (!$this->id_object) {
return $this->processAdd();
} else {
return $this->processUpdate();
}
}
public function processDelete()
{
Tools::dieObject("@todo : Gérer la suppression avec les sous éléments");
}
public function processPosition()
{
if (Tools::getIsset('viewconfigurator_opt_group')) {
$object = new ConfiguratorOpt((int)Tools::getValue('id_configurator_opt'));
self::$currentIndex = self::$currentIndex.'&viewconfigurator_opt_group';
}
if (!Validate::isLoadedObject($object)) {
$this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
} elseif (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position'))) {
$this->errors[] = Tools::displayError('Failed to update the position.');
} else {
$id_identifier_str = ($id_identifier = (int)Tools::getValue($this->identifier)) ? '&'.$this->identifier.'='.$id_identifier : '';
$redirect = self::$currentIndex.'&'.$this->table.'Orderby=position&'.$this->table.'Orderway=asc&conf=5'.$id_identifier_str.'&token='.$this->token;
$this->redirect_after = $redirect;
}
return $object;
}
/**
* Modify position
*/
public function ajaxProcessUpdatePositions()
{
$way = (int)Tools::getValue('way');
$id_configurator_opt = (int)Tools::getValue('id_configurator_opt');
$id_configurator_opt_group = (int)Tools::getValue('id_configurator_opt_group');
$positions = Tools::getValue('configurator_opt');
if (is_array($positions)) {
foreach ($positions as $position => $value) {
$pos = explode('_', $value);
if ((isset($pos[1]) && isset($pos[2])) && (int)$pos[2] === $id_configurator_opt) {
if ($opt = new ConfiguratorOpt((int)$pos[2])) {
if (isset($position) && $opt->updatePosition($way, $position)) {
echo 'ok position '.(int)$position.' for values '.(int)$pos[2].'\r\n';
} else {
echo '{"hasError" : true, "errors" : "Can not update the '.(int)$id_configurator_opt.' values to position '.(int)$position.' "}';
}
} else {
echo '{"hasError" : true, "errors" : "The ('.(int)$id_configurator_opt.') values cannot be loaded"}';
}
break;
}
}
}
}
}

View File

@ -0,0 +1,188 @@
<?php
class AdminAntadisConfiguratorProductController extends ModuleAdminController
{
public function __construct()
{
$this->module = 'antadisconfigurator';
$this->context = Context::getContext();
$this->table = 'product_configurator_opt_impact';
$this->className = 'ProductConfiguratorOptImpact';
// Enable bootstrap
$this->bootstrap = true;
// Call of the parent constructor method
parent::__construct();
}
/**
* Associe l'option et les valeurs au produit
*/
public function ajaxProcessAdd()
{
$id_configurator_opt_group = Tools::getValue('id_configurator_opt_group');
$id_product = Tools::getValue('id_product');
// Associer OptGroup
$result = Db::getInstance()->insert('product_configurator_opt_group', array(
'id_product' => $id_product,
'id_configurator_opt_group' => $id_configurator_opt_group,
'required' => 0,
'position' => ProductConfiguratorOptGroup::getHigherPosition($id_product) + 1,
));
if ($result) {
$id_product_configurator_opt_group = Db::getInstance()->Insert_ID();
// Get OptGroup values
$values = ConfiguratorOptGroup::getValues($id_configurator_opt_group);
// Associer OptImpact
foreach($values as $v) {
$impact = new ProductConfiguratorOptImpact();
$impact->id_product = $id_product;
$impact->id_product_configurator_opt_group = $id_product_configurator_opt_group;
$impact->id_configurator_opt = $v['id_configurator_opt'];
$impact->price = $v['price'];
$impact->save();
}
$return = array(
'result' => $result,
);
} else {
$return = array(
'hasError' => true,
'errors' => $this->l("Insert error : ".Db::getInstance()->getMsgError()),
);
}
echo Tools::jsonEncode($return);
}
/**
* Set required status
*/
public function ajaxProcessStatusProductConfiguratorOptGroup()
{
if (!$id_product_configurator_opt_group = Tools::getValue('id_product_configurator_opt_group')) {
die(Tools::jsonEncode(array('success' => false, 'error' => 'true', 'text' => $this->l('Failed to update the required status'))));
} else {
$optGroup = new ProductConfiguratorOptGroup((int)$id_product_configurator_opt_group);
if (Validate::isLoadedObject($optGroup)) {
$optGroup->required = $optGroup->required == 1 ? 0 : 1;
$optGroup->save() ?
die(Tools::jsonEncode(array('success' => true, 'text' => $this->l('The required status has been updated successfully')))) :
die(Tools::jsonEncode(array('success' => false, 'error' => true, 'text' => $this->l('Failed to update the required status'))));
}
}
}
public function processDelete()
{
Tools::d("@todo : Gérer la suppression");
}
/**
* Display impact list
* {@inheritDoc}
* @see AdminControllerCore::renderList()
*/
public function renderList()
{
$id_product = Tools::getValue('id_product');
$id_product_configurator_opt_group = Tools::getValue('id_product_configurator_opt_group');
$optGroup = new ProductConfiguratorOptGroup($id_product_configurator_opt_group);
$this->toolbar_title = Product::getProductName($id_product) . ' - ' . $optGroup->getConfiguratorOptGroupName() . ' - Options Impact';
$this->toolbar_btn['back'] = array(
'href' => $this->context->link->getAdminLink('AdminProducts', true).'&updateproduct&id_product='.(int)$id_product,
'desc' => $this->l('Back to product', null, null, false)
);
// Set fields list
$this->fields_list = array(
'id_product_configurator_opt_impact' => array(
'title' => $this->l('ID'),
'align' => 'center',
'width' => 25,
'search' => false,
),
'name' => array(
'title' => $this->l('Value'),
'search' => false,
),
'price' => array(
'title' => $this->l('Price impact'),
'search' => false,
'type' => 'price',
),
);
$this->_select = 'col.`name` AS name';
$this->_join = '
LEFT JOIN `'._DB_PREFIX_.'configurator_opt_lang` col ON (col.`id_configurator_opt` = a.`id_configurator_opt` AND col.`id_lang` = '.(int)$this->context->language->id.')';
$this->_where = ' AND a.`id_product` = '.(int)$id_product.' AND a.`id_product_configurator_opt_group` = '.(int)$id_product_configurator_opt_group;
self::$currentIndex = self::$currentIndex.'&id_product='.$id_product;
$this->addRowAction('edit');
return parent::renderList();
}
/**
* Change price impact
* {@inheritDoc}
* @see AdminControllerCore::renderForm()
*/
public function renderForm()
{
$id_product_configurator_opt_impact= Tools::getValue('id_product_configurator_opt_impact');
$optImpact = new ProductConfiguratorOptImpact($id_product_configurator_opt_impact);
$this->fields_form = array(
'legend' => array('title' => $this->l('Edit price impact')),
'input' => array(
'id_product_configurator_opt_impact' => array(
'type' => 'hidden',
'name' => 'id_product_configurator_opt_impact',
),
'id_product' => array(
'type' => 'hidden',
'name' => 'id_product',
),
'id_product_configurator_opt_group' => array(
'type' => 'hidden',
'name' => 'id_product_configurator_opt_group',
),
'id_configurator_opt' => array(
'type' => 'hidden',
'name' => 'id_configurator_opt',
),
'price' => array(
'type' => 'text',
'label' => $this->l('Price'),
'name' => 'price',
'required' => true,
),
),
'submit' => array('title' => $this->l('Save')),
);
// Override cancel
self::$currentIndex = $this->context->link->getAdminLink('AdminAntadisConfiguratorProduct', false).
'&id_product='.$optImpact->id_product.'&id_product_configurator_opt_group='.$optImpact->id_product_configurator_opt_group;
$this->fields_value = array(
'id_product_configurator_opt_impact' => $optImpact->id_product_configurator_opt_impact,
'id_product' => $optImpact->id_product,
'id_product_configurator_opt_group' => $optImpact->id_product_configurator_opt_group,
'id_configurator_opt' => $optImpact->id_configurator_opt,
'price' => $optImpact->price,
);
return parent::renderForm();
}
}

View File

@ -0,0 +1,81 @@
<?php
class AntadisConfiguratorDisplayAdminProductsExtraController
{
/**
* @var ModuleAdminController
*/
protected $module;
/**
* @var Context
*/
protected $context;
public function __construct($module, $file, $path)
{
$this->file = $file;
$this->module = $module;
$this->context = Context::getContext();
$this->_path = $path;
}
public function run()
{
// Display list of option
$id_product = (int)Tools::getValue('id_product');
$fields_list = array(
'id_product_configurator_opt_group' => array(
'title' => $this->module->l('ID')
),
'name' => array(
'title' => $this->module->l('Option Name'),
),
'required' => array(
'title' => $this->module->l('Required'),
'active' => 'status',
'type' => 'bool',
'ajax' => true,
),
'position' => array(
'title' => $this->module->l('Position'),
'filter_key' => 'a!position',
'position' => 'position',
'class' => 'fixed-width-md'
),
);
// Get option group
$productOptGroupList = ProductConfiguratorOptGroup::getProductConfiguratorOptGroup($id_product);
// Helper list
$helper = new HelperList();
$helper->simple_header = true;
$helper->bootstrap = true;
$helper->table = 'product_configurator_opt_group';
$helper->identifier = 'id_product_configurator_opt_group';
$helper->title = $this->module->l('Product Options');
$helper->position_identifier = 'id_product_configurator_opt_group';
$helper->orderBy = 'position';
$helper->actions = array('delete');
$helper->token = Tools::getAdminTokenLite('AdminAntadisConfiguratorProduct');
$helper->currentIndex = $this->context->link->getAdminLink('AdminAntadisConfiguratorProduct').'&id_product='.(int)$id_product;
// Available options
$optGroupList = ConfiguratorOptGroup::getAll();
$this->context->smarty->assign(array(
'token' => Tools::getAdminTokenLite('AdminAntadisConfiguratorProduct'),
'id_product' => $id_product,
'OptGroupList' => $optGroupList,
'OptGroupListFormLink' => $this->context->link->getAdminLink('AdminProducts').'&id_product='.(int)$id_product,
'ProductOptGroupList' => $helper->generateList($productOptGroupList, $fields_list),
));
return $this->module->display($this->file, 'displayAdminProductsExtra.tpl');
}
}

View File

@ -0,0 +1,47 @@
<?php
class AntadisConfiguratorDisplayProductFormController
{
/**
* @var ModuleAdminController
*/
protected $module;
/**
* @var Context
*/
protected $context;
public function __construct($module, $file, $path)
{
$this->file = $file;
$this->module = $module;
$this->context = Context::getContext();
$this->_path = $path;
}
public function run()
{
$id_product = Tools::getValue('id_product');
// Get Product
// Get Options associées au produit
// Tableau Multi Niveau
// => Option Group : label / name / ref
// => Liste des Valeurs : name / value / price
$optGroupList = Product::getConfiguratorOptGroup($id_product);
$optImpact = Product::getConfiguratorOptImpact($id_product);
$optImpactList = array();
if (count($optImpact) > 0) {
foreach ($optImpact as $o) {
$optImpactList[$o['id_product_configurator_opt_group']][] = $o;
}
}
$this->context->smarty->assign('token', Tools::getToken(false));
$this->context->smarty->assign('optGroupList', $optGroupList);
$this->context->smarty->assign('optImpactList', $optImpactList);
return $this->module->display($this->file, 'displayProductForm.tpl');
}
}

View File

@ -0,0 +1,52 @@
<?php
class AntadisConfiguratorDisplayProductTabContentController
{
/**
* @var ModuleAdminController
*/
protected $module;
/**
* @var Context
*/
protected $context;
public function __construct($module, $file, $path)
{
$this->file = $file;
$this->module = $module;
$this->context = Context::getContext();
$this->_path = $path;
}
public function run()
{
$isActivated = false;
if($isActivated === true) {
$id_product = Tools::getValue('id_product');
// Get Product
// Get Options associées au produit
// Tableau Multi Niveau
// => Option Group : label / name / ref
// => Liste des Valeurs : name / value / price
$optGroupList = Product::getConfiguratorOptGroup($id_product);
$optImpact = Product::getConfiguratorOptImpact($id_product);
$optImpactList = array();
if (count($optImpact) > 0) {
foreach ($optImpact as $o) {
$optImpactList[$o['id_product_configurator_opt_group']][] = $o;
}
}
$this->context->smarty->assign('token', Tools::getToken(false));
$this->context->smarty->assign('optGroupList', $optGroupList);
$this->context->smarty->assign('optImpactList', $optImpactList);
return $this->module->display($this->file, 'displayProductTabContent.tpl');
}
return '';
}
}

View File

@ -0,0 +1,17 @@
<?php
class AntadisConfiguratorGetContentController
{
public function __construct($module, $file, $path)
{
$this->file = $file;
$this->module = $module;
$this->context = Context::getContext(); $this->_path = $path;
}
public function run()
{
$html = $this->module->display($this->file, 'getContent.tpl');
return $html;
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2016 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2016 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,782 @@
{*
* 2007-2016 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2016 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*}
{include file="$tpl_dir./errors.tpl"}
{if $errors|@count == 0}
{if !isset($priceDisplayPrecision)}
{assign var='priceDisplayPrecision' value=2}
{/if}
{if !$priceDisplay || $priceDisplay == 2}
{assign var='productPrice' value=$product->getPrice(true, $smarty.const.NULL, 6)}
{assign var='productPriceWithoutReduction' value=$product->getPriceWithoutReduct(false, $smarty.const.NULL)}
{elseif $priceDisplay == 1}
{assign var='productPrice' value=$product->getPrice(false, $smarty.const.NULL, 6)}
{assign var='productPriceWithoutReduction' value=$product->getPriceWithoutReduct(true, $smarty.const.NULL)}
{/if}
<div itemscope itemtype="https://schema.org/Product">
<meta itemprop="url" content="{$link->getProductLink($product)}">
<div class="primary_block row">
{if !$content_only}
<div class="container">
<div class="top-hr"></div>
</div>
{/if}
{if isset($adminActionDisplay) && $adminActionDisplay}
<div id="admin-action" class="container">
<p class="alert alert-info">{l s='This product is not visible to your customers.'}
<input type="hidden" id="admin-action-product-id" value="{$product->id}" />
<a id="publish_button" class="btn btn-default button button-small" href="#">
<span>{l s='Publish'}</span>
</a>
<a id="lnk_view" class="btn btn-default button button-small" href="#">
<span>{l s='Back'}</span>
</a>
</p>
<p id="admin-action-result"></p>
</div>
{/if}
{if isset($confirmation) && $confirmation}
<p class="confirmation">
{$confirmation}
</p>
{/if}
<!-- left infos-->
<div class="pb-left-column col-xs-12 col-sm-4 col-md-5">
<!-- product img-->
<div id="image-block" class="clearfix">
{if $product->new}
<span class="new-box">
<span class="new-label">{l s='New'}</span>
</span>
{/if}
{if $product->on_sale}
<span class="sale-box no-print">
<span class="sale-label">{l s='Sale!'}</span>
</span>
{elseif $product->specificPrice && $product->specificPrice.reduction && $productPriceWithoutReduction > $productPrice}
<span class="discount">{l s='Reduced price!'}</span>
{/if}
{if $have_image}
<span id="view_full_size">
{if $jqZoomEnabled && $have_image && !$content_only}
<a class="jqzoom" title="{if !empty($cover.legend)}{$cover.legend|escape:'html':'UTF-8'}{else}{$product->name|escape:'html':'UTF-8'}{/if}" rel="gal1" href="{$link->getImageLink($product->link_rewrite, $cover.id_image, 'thickbox_default')|escape:'html':'UTF-8'}">
<img itemprop="image" src="{$link->getImageLink($product->link_rewrite, $cover.id_image, 'large_default')|escape:'html':'UTF-8'}" title="{if !empty($cover.legend)}{$cover.legend|escape:'html':'UTF-8'}{else}{$product->name|escape:'html':'UTF-8'}{/if}" alt="{if !empty($cover.legend)}{$cover.legend|escape:'html':'UTF-8'}{else}{$product->name|escape:'html':'UTF-8'}{/if}"/>
</a>
{else}
<img id="bigpic" itemprop="image" src="{$link->getImageLink($product->link_rewrite, $cover.id_image, 'large_default')|escape:'html':'UTF-8'}" title="{if !empty($cover.legend)}{$cover.legend|escape:'html':'UTF-8'}{else}{$product->name|escape:'html':'UTF-8'}{/if}" alt="{if !empty($cover.legend)}{$cover.legend|escape:'html':'UTF-8'}{else}{$product->name|escape:'html':'UTF-8'}{/if}" width="{$largeSize.width}" height="{$largeSize.height}"/>
{if !$content_only}
<span class="span_link no-print">{l s='View larger'}</span>
{/if}
{/if}
</span>
{else}
<span id="view_full_size">
<img itemprop="image" src="{$img_prod_dir}{$lang_iso}-default-large_default.jpg" id="bigpic" alt="" title="{$product->name|escape:'html':'UTF-8'}" width="{$largeSize.width}" height="{$largeSize.height}"/>
{if !$content_only}
<span class="span_link">
{l s='View larger'}
</span>
{/if}
</span>
{/if}
</div> <!-- end image-block -->
{if isset($images) && count($images) > 0}
<!-- thumbnails -->
<div id="views_block" class="clearfix {if isset($images) && count($images) < 2}hidden{/if}">
{if isset($images) && count($images) > 2}
<span class="view_scroll_spacer">
<a id="view_scroll_left" class="" title="{l s='Other views'}" href="javascript:{ldelim}{rdelim}">
{l s='Previous'}
</a>
</span>
{/if}
<div id="thumbs_list">
<ul id="thumbs_list_frame">
{if isset($images)}
{foreach from=$images item=image name=thumbnails}
{assign var=imageIds value="`$product->id`-`$image.id_image`"}
{if !empty($image.legend)}
{assign var=imageTitle value=$image.legend|escape:'html':'UTF-8'}
{else}
{assign var=imageTitle value=$product->name|escape:'html':'UTF-8'}
{/if}
<li id="thumbnail_{$image.id_image}"{if $smarty.foreach.thumbnails.last} class="last"{/if}>
<a{if $jqZoomEnabled && $have_image && !$content_only} href="javascript:void(0);" rel="{literal}{{/literal}gallery: 'gal1', smallimage: '{$link->getImageLink($product->link_rewrite, $imageIds, 'large_default')|escape:'html':'UTF-8'}',largeimage: '{$link->getImageLink($product->link_rewrite, $imageIds, 'thickbox_default')|escape:'html':'UTF-8'}'{literal}}{/literal}"{else} href="{$link->getImageLink($product->link_rewrite, $imageIds, 'thickbox_default')|escape:'html':'UTF-8'}" data-fancybox-group="other-views" class="fancybox{if $image.id_image == $cover.id_image} shown{/if}"{/if} title="{$imageTitle}">
<img class="img-responsive" id="thumb_{$image.id_image}" src="{$link->getImageLink($product->link_rewrite, $imageIds, 'cart_default')|escape:'html':'UTF-8'}" alt="{$imageTitle}" title="{$imageTitle}"{if isset($cartSize)} height="{$cartSize.height}" width="{$cartSize.width}"{/if} itemprop="image" />
</a>
</li>
{/foreach}
{/if}
</ul>
</div> <!-- end thumbs_list -->
{if isset($images) && count($images) > 2}
<a id="view_scroll_right" title="{l s='Other views'}" href="javascript:{ldelim}{rdelim}">
{l s='Next'}
</a>
{/if}
</div> <!-- end views-block -->
<!-- end thumbnails -->
{/if}
{if isset($images) && count($images) > 1}
<p class="resetimg clear no-print">
<span id="wrapResetImages" style="display: none;">
<a href="{$link->getProductLink($product)|escape:'html':'UTF-8'}" data-id="resetImages">
<i class="icon-repeat"></i>
{l s='Display all pictures'}
</a>
</span>
</p>
{/if}
</div> <!-- end pb-left-column -->
<!-- end left infos-->
<!-- center infos -->
<div class="pb-center-column col-xs-12 col-sm-4">
{if $product->online_only}
<p class="online_only">{l s='Online only'}</p>
{/if}
<h1 itemprop="name">{$product->name|escape:'html':'UTF-8'}</h1>
<p id="product_reference"{if empty($product->reference) || !$product->reference} style="display: none;"{/if}>
<label>{l s='Reference:'} </label>
<span class="editable" itemprop="sku"{if !empty($product->reference) && $product->reference} content="{$product->reference}"{/if}>{if !isset($groups)}{$product->reference|escape:'html':'UTF-8'}{/if}</span>
</p>
{if !$product->is_virtual && $product->condition}
<p id="product_condition">
<label>{l s='Condition:'} </label>
{if $product->condition == 'new'}
<link itemprop="itemCondition" href="https://schema.org/NewCondition"/>
<span class="editable">{l s='New product'}</span>
{elseif $product->condition == 'used'}
<link itemprop="itemCondition" href="https://schema.org/UsedCondition"/>
<span class="editable">{l s='Used'}</span>
{elseif $product->condition == 'refurbished'}
<link itemprop="itemCondition" href="https://schema.org/RefurbishedCondition"/>
<span class="editable">{l s='Refurbished'}</span>
{/if}
</p>
{/if}
{if $product->description_short || $packItems|@count > 0}
<div id="short_description_block">
{if $product->description_short}
<div id="short_description_content" class="rte align_justify" itemprop="description">{$product->description_short}</div>
{/if}
{if $product->description}
<p class="buttons_bottom_block">
<a href="javascript:{ldelim}{rdelim}" class="button">
{l s='More details'}
</a>
</p>
{/if}
<!--{if $packItems|@count > 0}
<div class="short_description_pack">
<h3>{l s='Pack content'}</h3>
{foreach from=$packItems item=packItem}
<div class="pack_content">
{$packItem.pack_quantity} x <a href="{$link->getProductLink($packItem.id_product, $packItem.link_rewrite, $packItem.category)|escape:'html':'UTF-8'}">{$packItem.name|escape:'html':'UTF-8'}</a>
<p>{$packItem.description_short}</p>
</div>
{/foreach}
</div>
{/if}-->
</div> <!-- end short_description_block -->
{/if}
{if ($display_qties == 1 && !$PS_CATALOG_MODE && $PS_STOCK_MANAGEMENT && $product->available_for_order)}
<!-- number of item in stock -->
<p id="pQuantityAvailable"{if $product->quantity <= 0} style="display: none;"{/if}>
<span id="quantityAvailable">{$product->quantity|intval}</span>
<span {if $product->quantity > 1} style="display: none;"{/if} id="quantityAvailableTxt">{l s='Item'}</span>
<span {if $product->quantity == 1} style="display: none;"{/if} id="quantityAvailableTxtMultiple">{l s='Items'}</span>
</p>
{/if}
<!-- availability or doesntExist -->
<p id="availability_statut"{if !$PS_STOCK_MANAGEMENT || ($product->quantity <= 0 && !$product->available_later && $allow_oosp) || ($product->quantity > 0 && !$product->available_now) || !$product->available_for_order || $PS_CATALOG_MODE} style="display: none;"{/if}>
{*<span id="availability_label">{l s='Availability:'}</span>*}
<span id="availability_value" class="label{if $product->quantity <= 0 && !$allow_oosp} label-danger{elseif $product->quantity <= 0} label-warning{else} label-success{/if}">{if $product->quantity <= 0}{if $PS_STOCK_MANAGEMENT && $allow_oosp}{$product->available_later}{else}{l s='This product is no longer in stock'}{/if}{elseif $PS_STOCK_MANAGEMENT}{$product->available_now}{/if}</span>
</p>
{if $PS_STOCK_MANAGEMENT}
{if !$product->is_virtual}{hook h="displayProductDeliveryTime" product=$product}{/if}
<p class="warning_inline" id="last_quantities"{if ($product->quantity > $last_qties || $product->quantity <= 0) || $allow_oosp || !$product->available_for_order || $PS_CATALOG_MODE} style="display: none"{/if} >{l s='Warning: Last items in stock!'}</p>
{/if}
<p id="availability_date"{if ($product->quantity > 0) || !$product->available_for_order || $PS_CATALOG_MODE || !isset($product->available_date) || $product->available_date < $smarty.now|date_format:'%Y-%m-%d'} style="display: none;"{/if}>
<span id="availability_date_label">{l s='Availability date:'}</span>
<span id="availability_date_value">{if Validate::isDate($product->available_date)}{dateFormat date=$product->available_date full=false}{/if}</span>
</p>
<!-- Out of stock hook -->
<div id="oosHook"{if $product->quantity > 0} style="display: none;"{/if}>
{$HOOK_PRODUCT_OOS}
</div>
{if isset($HOOK_EXTRA_RIGHT) && $HOOK_EXTRA_RIGHT}{$HOOK_EXTRA_RIGHT}{/if}
{if !$content_only}
<!-- usefull links-->
<ul id="usefull_link_block" class="clearfix no-print">
{if $HOOK_EXTRA_LEFT}{$HOOK_EXTRA_LEFT}{/if}
<li class="print">
<a href="javascript:print();">
{l s='Print'}
</a>
</li>
</ul>
{/if}
</div>
<!-- end center infos-->
<!-- pb-right-column-->
<div class="pb-right-column col-xs-12 col-sm-4 col-md-3">
{if ($product->show_price && !isset($restricted_country_mode)) || isset($groups) || $product->reference || (isset($HOOK_PRODUCT_ACTIONS) && $HOOK_PRODUCT_ACTIONS)}
<!-- add to cart form-->
<form id="buy_block"{if $PS_CATALOG_MODE && !isset($groups) && $product->quantity > 0} class="hidden"{/if} action="{$link->getPageLink('cart')|escape:'html':'UTF-8'}" method="post">
<!-- hidden datas -->
<p class="hidden">
<input type="hidden" name="token" value="{$static_token}" />
<input type="hidden" name="id_product" value="{$product->id|intval}" id="product_page_product_id" />
<input type="hidden" name="add" value="1" />
<input type="hidden" name="id_product_attribute" id="idCombination" value="" />
</p>
<div class="box-info-product">
<div class="content_prices clearfix">
{if $product->show_price && !isset($restricted_country_mode) && !$PS_CATALOG_MODE}
<!-- prices -->
<div>
<p class="our_price_display" itemprop="offers" itemscope itemtype="https://schema.org/Offer">{strip}
{if $product->quantity > 0}<link itemprop="availability" href="https://schema.org/InStock"/>{/if}
{if $priceDisplay >= 0 && $priceDisplay <= 2}
<span id="our_price_display" class="price" itemprop="price" content="{$productPrice}">{convertPrice price=$productPrice|floatval}</span>
{if $tax_enabled && ((isset($display_tax_label) && $display_tax_label == 1) || !isset($display_tax_label))}
{if $priceDisplay == 1} {l s='tax excl.'}{else} {l s='tax incl.'}{/if}
{/if}
<meta itemprop="priceCurrency" content="{$currency->iso_code}" />
{hook h="displayProductPriceBlock" product=$product type="price"}
{/if}
{/strip}</p>
<p id="reduction_percent" {if $productPriceWithoutReduction <= 0 || !$product->specificPrice || $product->specificPrice.reduction_type != 'percentage'} style="display:none;"{/if}>{strip}
<span id="reduction_percent_display">
{if $product->specificPrice && $product->specificPrice.reduction_type == 'percentage'}-{$product->specificPrice.reduction*100}%{/if}
</span>
{/strip}</p>
<p id="reduction_amount" {if $productPriceWithoutReduction <= 0 || !$product->specificPrice || $product->specificPrice.reduction_type != 'amount' || $product->specificPrice.reduction|floatval ==0} style="display:none"{/if}>{strip}
<span id="reduction_amount_display">
{if $product->specificPrice && $product->specificPrice.reduction_type == 'amount' && $product->specificPrice.reduction|floatval !=0}
-{convertPrice price=$productPriceWithoutReduction|floatval-$productPrice|floatval}
{/if}
</span>
{/strip}</p>
<p id="old_price"{if (!$product->specificPrice || !$product->specificPrice.reduction)} class="hidden"{/if}>{strip}
{if $priceDisplay >= 0 && $priceDisplay <= 2}
{hook h="displayProductPriceBlock" product=$product type="old_price"}
<span id="old_price_display"><span class="price">{if $productPriceWithoutReduction > $productPrice}{convertPrice price=$productPriceWithoutReduction|floatval}{/if}</span>{if $productPriceWithoutReduction > $productPrice && $tax_enabled && $display_tax_label == 1} {if $priceDisplay == 1}{l s='tax excl.'}{else}{l s='tax incl.'}{/if}{/if}</span>
{/if}
{/strip}</p>
{if $priceDisplay == 2}
<br />
<span id="pretaxe_price">{strip}
<span id="pretaxe_price_display">{convertPrice price=$product->getPrice(false, $smarty.const.NULL)}</span> {l s='tax excl.'}
{/strip}</span>
{/if}
</div> <!-- end prices -->
{if $packItems|@count && $productPrice < $product->getNoPackPrice()}
<p class="pack_price">{l s='Instead of'} <span style="text-decoration: line-through;">{convertPrice price=$product->getNoPackPrice()}</span></p>
{/if}
{if $product->ecotax != 0}
<p class="price-ecotax">{l s='Including'} <span id="ecotax_price_display">{if $priceDisplay == 2}{$ecotax_tax_exc|convertAndFormatPrice}{else}{$ecotax_tax_inc|convertAndFormatPrice}{/if}</span> {l s='for ecotax'}
{if $product->specificPrice && $product->specificPrice.reduction}
<br />{l s='(not impacted by the discount)'}
{/if}
</p>
{/if}
{if !empty($product->unity) && $product->unit_price_ratio > 0.000000}
{math equation="pprice / punit_price" pprice=$productPrice punit_price=$product->unit_price_ratio assign=unit_price}
<p class="unit-price"><span id="unit_price_display">{convertPrice price=$unit_price}</span> {l s='per'} {$product->unity|escape:'html':'UTF-8'}</p>
{hook h="displayProductPriceBlock" product=$product type="unit_price"}
{/if}
{/if} {*close if for show price*}
{hook h="displayProductPriceBlock" product=$product type="weight" hook_origin='product_sheet'}
{hook h="displayProductPriceBlock" product=$product type="after_price"}
<div class="clear"></div>
</div> <!-- end content_prices -->
<div class="product_attributes clearfix">
<!-- quantity wanted -->
{if !$PS_CATALOG_MODE}
<p id="quantity_wanted_p"{if (!$allow_oosp && $product->quantity <= 0) || !$product->available_for_order || $PS_CATALOG_MODE} style="display: none;"{/if}>
<label for="quantity_wanted">{l s='Quantity'}</label>
<input type="number" min="1" name="qty" id="quantity_wanted" class="text" value="{if isset($quantityBackup)}{$quantityBackup|intval}{else}{if $product->minimal_quantity > 1}{$product->minimal_quantity}{else}1{/if}{/if}" />
<a href="#" data-field-qty="qty" class="btn btn-default button-minus product_quantity_down">
<span><i class="icon-minus"></i></span>
</a>
<a href="#" data-field-qty="qty" class="btn btn-default button-plus product_quantity_up">
<span><i class="icon-plus"></i></span>
</a>
<span class="clearfix"></span>
</p>
{/if}
<!-- minimal quantity wanted -->
<p id="minimal_quantity_wanted_p"{if $product->minimal_quantity <= 1 || !$product->available_for_order || $PS_CATALOG_MODE} style="display: none;"{/if}>
{l s='The minimum purchase order quantity for the product is'} <b id="minimal_quantity_label">{$product->minimal_quantity}</b>
</p>
{if isset($groups)}
<!-- attributes -->
<div id="attributes">
<div class="clearfix"></div>
{foreach from=$groups key=id_attribute_group item=group}
{if $group.attributes|@count}
<fieldset class="attribute_fieldset">
<label class="attribute_label" {if $group.group_type != 'color' && $group.group_type != 'radio'}for="group_{$id_attribute_group|intval}"{/if}>{$group.name|escape:'html':'UTF-8'}&nbsp;</label>
{assign var="groupName" value="group_$id_attribute_group"}
<div class="attribute_list">
{if ($group.group_type == 'select')}
<select name="{$groupName}" id="group_{$id_attribute_group|intval}" class="form-control attribute_select no-print">
{foreach from=$group.attributes key=id_attribute item=group_attribute}
<option value="{$id_attribute|intval}"{if (isset($smarty.get.$groupName) && $smarty.get.$groupName|intval == $id_attribute) || $group.default == $id_attribute} selected="selected"{/if} title="{$group_attribute|escape:'html':'UTF-8'}">{$group_attribute|escape:'html':'UTF-8'}</option>
{/foreach}
</select>
{elseif ($group.group_type == 'color')}
<ul id="color_to_pick_list" class="clearfix">
{assign var="default_colorpicker" value=""}
{foreach from=$group.attributes key=id_attribute item=group_attribute}
{assign var='img_color_exists' value=file_exists($col_img_dir|cat:$id_attribute|cat:'.jpg')}
<li{if $group.default == $id_attribute} class="selected"{/if}>
<a href="{$link->getProductLink($product)|escape:'html':'UTF-8'}" id="color_{$id_attribute|intval}" name="{$colors.$id_attribute.name|escape:'html':'UTF-8'}" class="color_pick{if ($group.default == $id_attribute)} selected{/if}"{if !$img_color_exists && isset($colors.$id_attribute.value) && $colors.$id_attribute.value} style="background:{$colors.$id_attribute.value|escape:'html':'UTF-8'};"{/if} title="{$colors.$id_attribute.name|escape:'html':'UTF-8'}">
{if $img_color_exists}
<img src="{$img_col_dir}{$id_attribute|intval}.jpg" alt="{$colors.$id_attribute.name|escape:'html':'UTF-8'}" title="{$colors.$id_attribute.name|escape:'html':'UTF-8'}" width="20" height="20" />
{/if}
</a>
</li>
{if ($group.default == $id_attribute)}
{$default_colorpicker = $id_attribute}
{/if}
{/foreach}
</ul>
<input type="hidden" class="color_pick_hidden" name="{$groupName|escape:'html':'UTF-8'}" value="{$default_colorpicker|intval}" />
{elseif ($group.group_type == 'radio')}
<ul>
{foreach from=$group.attributes key=id_attribute item=group_attribute}
<li>
<input type="radio" class="attribute_radio" name="{$groupName|escape:'html':'UTF-8'}" value="{$id_attribute}" {if ($group.default == $id_attribute)} checked="checked"{/if} />
<span>{$group_attribute|escape:'html':'UTF-8'}</span>
</li>
{/foreach}
</ul>
{/if}
</div> <!-- end attribute_list -->
</fieldset>
{/if}
{/foreach}
</div> <!-- end attributes -->
{/if}
</div> <!-- end product_attributes -->
{hook h='displayProductForm' mod='antadisconfigurator'}
<div class="box-cart-bottom">
<div{if (!$allow_oosp && $product->quantity <= 0) || !$product->available_for_order || (isset($restricted_country_mode) && $restricted_country_mode) || $PS_CATALOG_MODE} class="unvisible"{/if}>
<p id="add_to_cart" class="buttons_bottom_block no-print">
<button type="submit" name="Submit" class="exclusive">
<span>{if $content_only && (isset($product->customization_required) && $product->customization_required)}{l s='Customize'}{else}{l s='Add to cart'}{/if}</span>
</button>
</p>
</div>
{if isset($HOOK_PRODUCT_ACTIONS) && $HOOK_PRODUCT_ACTIONS}{$HOOK_PRODUCT_ACTIONS}{/if}
</div> <!-- end box-cart-bottom -->
</div> <!-- end box-info-product -->
</form>
{/if}
</div> <!-- end pb-right-column-->
</div> <!-- end primary_block -->
{if !$content_only}
{if (isset($quantity_discounts) && count($quantity_discounts) > 0)}
<!-- quantity discount -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='Volume discounts'}</h3>
<div id="quantityDiscount">
<table class="std table-product-discounts">
<thead>
<tr>
<th>{l s='Quantity'}</th>
<th>{if $display_discount_price}{l s='Price'}{else}{l s='Discount'}{/if}</th>
<th>{l s='You Save'}</th>
</tr>
</thead>
<tbody>
{foreach from=$quantity_discounts item='quantity_discount' name='quantity_discounts'}
{if $quantity_discount.price >= 0 || $quantity_discount.reduction_type == 'amount'}
{$realDiscountPrice=$quantity_discount.base_price|floatval-$quantity_discount.real_value|floatval}
{else}
{$realDiscountPrice=$quantity_discount.base_price|floatval*(1 - $quantity_discount.reduction)|floatval}
{/if}
<tr class="quantityDiscount_{$quantity_discount.id_product_attribute}" data-real-discount-value="{convertPrice price = $realDiscountPrice}" data-discount-type="{$quantity_discount.reduction_type}" data-discount="{$quantity_discount.real_value|floatval}" data-discount-quantity="{$quantity_discount.quantity|intval}">
<td>
{$quantity_discount.quantity|intval}
</td>
<td>
{if $quantity_discount.price >= 0 || $quantity_discount.reduction_type == 'amount'}
{if $display_discount_price}
{if $quantity_discount.reduction_tax == 0 && !$quantity_discount.price}
{convertPrice price = $productPriceWithoutReduction|floatval-($productPriceWithoutReduction*$quantity_discount.reduction_with_tax)|floatval}
{else}
{convertPrice price=$productPriceWithoutReduction|floatval-$quantity_discount.real_value|floatval}
{/if}
{else}
{convertPrice price=$quantity_discount.real_value|floatval}
{/if}
{else}
{if $display_discount_price}
{if $quantity_discount.reduction_tax == 0}
{convertPrice price = $productPriceWithoutReduction|floatval-($productPriceWithoutReduction*$quantity_discount.reduction_with_tax)|floatval}
{else}
{convertPrice price = $productPriceWithoutReduction|floatval-($productPriceWithoutReduction*$quantity_discount.reduction)|floatval}
{/if}
{else}
{$quantity_discount.real_value|floatval}%
{/if}
{/if}
</td>
<td>
<span>{l s='Up to'}</span>
{if $quantity_discount.price >= 0 || $quantity_discount.reduction_type == 'amount'}
{$discountPrice=$productPriceWithoutReduction|floatval-$quantity_discount.real_value|floatval}
{else}
{$discountPrice=$productPriceWithoutReduction|floatval-($productPriceWithoutReduction*$quantity_discount.reduction)|floatval}
{/if}
{$discountPrice=$discountPrice * $quantity_discount.quantity}
{$qtyProductPrice=$productPriceWithoutReduction|floatval * $quantity_discount.quantity}
{convertPrice price=$qtyProductPrice - $discountPrice}
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</section>
{/if}
{if isset($features) && $features}
<!-- Data sheet -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='Data sheet'}</h3>
<table class="table-data-sheet">
{foreach from=$features item=feature}
<tr class="{cycle values="odd,even"}">
{if isset($feature.value)}
<td>{$feature.name|escape:'html':'UTF-8'}</td>
<td>{$feature.value|escape:'html':'UTF-8'}</td>
{/if}
</tr>
{/foreach}
</table>
</section>
<!--end Data sheet -->
{/if}
{if isset($product) && $product->description}
<!-- More info -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='More info'}</h3>
<!-- full description -->
<div class="rte">{$product->description}</div>
</section>
<!--end More info -->
{/if}
{if isset($packItems) && $packItems|@count > 0}
<section id="blockpack">
<h3 class="page-product-heading">{l s='Pack content'}</h3>
{include file="$tpl_dir./product-list.tpl" products=$packItems}
</section>
{/if}
{if (isset($HOOK_PRODUCT_TAB) && $HOOK_PRODUCT_TAB) || (isset($HOOK_PRODUCT_TAB_CONTENT) && $HOOK_PRODUCT_TAB_CONTENT)}
<!--HOOK_PRODUCT_TAB -->
<section class="page-product-box">
{$HOOK_PRODUCT_TAB}
{if isset($HOOK_PRODUCT_TAB_CONTENT) && $HOOK_PRODUCT_TAB_CONTENT}{$HOOK_PRODUCT_TAB_CONTENT}{/if}
</section>
<!--end HOOK_PRODUCT_TAB -->
{/if}
{if isset($accessories) && $accessories}
<!--Accessories -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='Accessories'}</h3>
<div class="block products_block accessories-block clearfix">
<div class="block_content">
<ul id="bxslider" class="bxslider clearfix">
{foreach from=$accessories item=accessory name=accessories_list}
{if ($accessory.allow_oosp || $accessory.quantity_all_versions > 0 || $accessory.quantity > 0) && $accessory.available_for_order && !isset($restricted_country_mode)}
{assign var='accessoryLink' value=$link->getProductLink($accessory.id_product, $accessory.link_rewrite, $accessory.category)}
<li class="item product-box ajax_block_product{if $smarty.foreach.accessories_list.first} first_item{elseif $smarty.foreach.accessories_list.last} last_item{else} item{/if} product_accessories_description">
<div class="product_desc">
<a href="{$accessoryLink|escape:'html':'UTF-8'}" title="{$accessory.legend|escape:'html':'UTF-8'}" class="product-image product_image">
<img class="lazyOwl" src="{$link->getImageLink($accessory.link_rewrite, $accessory.id_image, 'home_default')|escape:'html':'UTF-8'}" alt="{$accessory.legend|escape:'html':'UTF-8'}" width="{$homeSize.width}" height="{$homeSize.height}"/>
</a>
<div class="block_description">
<a href="{$accessoryLink|escape:'html':'UTF-8'}" title="{l s='More'}" class="product_description">
{$accessory.description_short|strip_tags|truncate:25:'...'}
</a>
</div>
</div>
<div class="s_title_block">
<h5 class="product-name">
<a href="{$accessoryLink|escape:'html':'UTF-8'}">
{$accessory.name|truncate:20:'...':true|escape:'html':'UTF-8'}
</a>
</h5>
{if $accessory.show_price && !isset($restricted_country_mode) && !$PS_CATALOG_MODE}
<span class="price">
{if $priceDisplay != 1}
{displayWtPrice p=$accessory.price}
{else}
{displayWtPrice p=$accessory.price_tax_exc}
{/if}
{hook h="displayProductPriceBlock" product=$accessory type="price"}
</span>
{/if}
{hook h="displayProductPriceBlock" product=$accessory type="after_price"}
</div>
<div class="clearfix" style="margin-top:5px">
{if !$PS_CATALOG_MODE && ($accessory.allow_oosp || $accessory.quantity > 0) && isset($add_prod_display) && $add_prod_display == 1}
<div class="no-print">
<a class="exclusive button ajax_add_to_cart_button" href="{$link->getPageLink('cart', true, NULL, "qty=1&amp;id_product={$accessory.id_product|intval}&amp;token={$static_token}&amp;add")|escape:'html':'UTF-8'}" data-id-product="{$accessory.id_product|intval}" title="{l s='Add to cart'}">
<span>{l s='Add to cart'}</span>
</a>
</div>
{/if}
</div>
</li>
{/if}
{/foreach}
</ul>
</div>
</div>
</section>
<!--end Accessories -->
{/if}
{if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if}
<!-- description & features -->
{if (isset($product) && $product->description) || (isset($features) && $features) || (isset($accessories) && $accessories) || (isset($HOOK_PRODUCT_TAB) && $HOOK_PRODUCT_TAB) || (isset($attachments) && $attachments) || isset($product) && $product->customizable}
{if isset($attachments) && $attachments}
<!--Download -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='Download'}</h3>
{foreach from=$attachments item=attachment name=attachements}
{if $smarty.foreach.attachements.iteration %3 == 1}<div class="row">{/if}
<div class="col-lg-4">
<h4><a href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html':'UTF-8'}">{$attachment.name|escape:'html':'UTF-8'}</a></h4>
<p class="text-muted">{$attachment.description|escape:'html':'UTF-8'}</p>
<a class="btn btn-default btn-block" href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html':'UTF-8'}">
<i class="icon-download"></i>
{l s="Download"} ({Tools::formatBytes($attachment.file_size, 2)})
</a>
<hr />
</div>
{if $smarty.foreach.attachements.iteration %3 == 0 || $smarty.foreach.attachements.last}</div>{/if}
{/foreach}
</section>
<!--end Download -->
{/if}
{if isset($product) && $product->customizable}
<!--Customization -->
<section class="page-product-box">
<h3 class="page-product-heading">{l s='Product customization'}</h3>
<!-- Customizable products -->
<form method="post" action="{$customizationFormTarget}" enctype="multipart/form-data" id="customizationForm" class="clearfix">
<p class="infoCustomizable">
{l s='After saving your customized product, remember to add it to your cart.'}
{if $product->uploadable_files}
<br />
{l s='Allowed file formats are: GIF, JPG, PNG'}{/if}
</p>
{if $product->uploadable_files|intval}
<div class="customizableProductsFile">
<h5 class="product-heading-h5">{l s='Pictures'}</h5>
<ul id="uploadable_files" class="clearfix">
{counter start=0 assign='customizationField'}
{foreach from=$customizationFields item='field' name='customizationFields'}
{if $field.type == 0}
<li class="customizationUploadLine{if $field.required} required{/if}">{assign var='key' value='pictures_'|cat:$product->id|cat:'_'|cat:$field.id_customization_field}
{if isset($pictures.$key)}
<div class="customizationUploadBrowse">
<img src="{$pic_dir}{$pictures.$key}_small" alt="" />
<a href="{$link->getProductDeletePictureLink($product, $field.id_customization_field)|escape:'html':'UTF-8'}" title="{l s='Delete'}" >
<img src="{$img_dir}icon/delete.gif" alt="{l s='Delete'}" class="customization_delete_icon" width="11" height="13" />
</a>
</div>
{/if}
<div class="customizationUploadBrowse form-group">
<label class="customizationUploadBrowseDescription">
{if !empty($field.name)}
{$field.name}
{else}
{l s='Please select an image file from your computer'}
{/if}
{if $field.required}<sup>*</sup>{/if}
</label>
<input type="file" name="file{$field.id_customization_field}" id="img{$customizationField}" class="form-control customization_block_input {if isset($pictures.$key)}filled{/if}" />
</div>
</li>
{counter}
{/if}
{/foreach}
</ul>
</div>
{/if}
{if $product->text_fields|intval}
<div class="customizableProductsText">
<h5 class="product-heading-h5">{l s='Text'}</h5>
<ul id="text_fields">
{counter start=0 assign='customizationField'}
{foreach from=$customizationFields item='field' name='customizationFields'}
{if $field.type == 1}
<li class="customizationUploadLine{if $field.required} required{/if}">
<label for ="textField{$customizationField}">
{assign var='key' value='textFields_'|cat:$product->id|cat:'_'|cat:$field.id_customization_field}
{if !empty($field.name)}
{$field.name}
{/if}
{if $field.required}<sup>*</sup>{/if}
</label>
<textarea name="textField{$field.id_customization_field}" class="form-control customization_block_input" id="textField{$customizationField}" rows="3" cols="20">{strip}
{if isset($textFields.$key)}
{$textFields.$key|stripslashes}
{/if}
{/strip}</textarea>
</li>
{counter}
{/if}
{/foreach}
</ul>
</div>
{/if}
<p id="customizedDatas">
<input type="hidden" name="quantityBackup" id="quantityBackup" value="" />
<input type="hidden" name="submitCustomizedDatas" value="1" />
<button class="button btn btn-default button button-small" name="saveCustomization">
<span>{l s='Save'}</span>
</button>
<span id="ajax-loader" class="unvisible">
<img src="{$img_ps_dir}loader.gif" alt="loader" />
</span>
</p>
</form>
<p class="clear required"><sup>*</sup> {l s='required fields'}</p>
</section>
<!--end Customization -->
{/if}
{/if}
{/if}
</div> <!-- itemscope product wrapper -->
{strip}
{if isset($smarty.get.ad) && $smarty.get.ad}
{addJsDefL name=ad}{$base_dir|cat:$smarty.get.ad|escape:'html':'UTF-8'}{/addJsDefL}
{/if}
{if isset($smarty.get.adtoken) && $smarty.get.adtoken}
{addJsDefL name=adtoken}{$smarty.get.adtoken|escape:'html':'UTF-8'}{/addJsDefL}
{/if}
{addJsDef allowBuyWhenOutOfStock=$allow_oosp|boolval}
{addJsDef availableNowValue=$product->available_now|escape:'quotes':'UTF-8'}
{addJsDef availableLaterValue=$product->available_later|escape:'quotes':'UTF-8'}
{addJsDef attribute_anchor_separator=$attribute_anchor_separator|escape:'quotes':'UTF-8'}
{addJsDef attributesCombinations=$attributesCombinations}
{addJsDef currentDate=$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'}
{if isset($combinations) && $combinations}
{addJsDef combinations=$combinations}
{addJsDef combinationsFromController=$combinations}
{addJsDef displayDiscountPrice=$display_discount_price}
{addJsDefL name='upToTxt'}{l s='Up to' js=1}{/addJsDefL}
{/if}
{if isset($combinationImages) && $combinationImages}
{addJsDef combinationImages=$combinationImages}
{/if}
{addJsDef customizationId=$id_customization}
{addJsDef customizationFields=$customizationFields}
{addJsDef default_eco_tax=$product->ecotax|floatval}
{addJsDef displayPrice=$priceDisplay|intval}
{addJsDef ecotaxTax_rate=$ecotaxTax_rate|floatval}
{if isset($cover.id_image_only)}
{addJsDef idDefaultImage=$cover.id_image_only|intval}
{else}
{addJsDef idDefaultImage=0}
{/if}
{addJsDef img_ps_dir=$img_ps_dir}
{addJsDef img_prod_dir=$img_prod_dir}
{addJsDef id_product=$product->id|intval}
{addJsDef jqZoomEnabled=$jqZoomEnabled|boolval}
{addJsDef maxQuantityToAllowDisplayOfLastQuantityMessage=$last_qties|intval}
{addJsDef minimalQuantity=$product->minimal_quantity|intval}
{addJsDef noTaxForThisProduct=$no_tax|boolval}
{if isset($customer_group_without_tax)}
{addJsDef customerGroupWithoutTax=$customer_group_without_tax|boolval}
{else}
{addJsDef customerGroupWithoutTax=false}
{/if}
{if isset($group_reduction)}
{addJsDef groupReduction=$group_reduction|floatval}
{else}
{addJsDef groupReduction=false}
{/if}
{addJsDef oosHookJsCodeFunctions=Array()}
{addJsDef productHasAttributes=isset($groups)|boolval}
{addJsDef productPriceTaxExcluded=($product->getPriceWithoutReduct(true)|default:'null' - $product->ecotax)|floatval}
{addJsDef productPriceTaxIncluded=($product->getPriceWithoutReduct(false)|default:'null' - $product->ecotax * (1 + $ecotaxTax_rate / 100))|floatval}
{addJsDef productBasePriceTaxExcluded=($product->getPrice(false, null, 6, null, false, false) - $product->ecotax)|floatval}
{addJsDef productBasePriceTaxExcl=($product->getPrice(false, null, 6, null, false, false)|floatval)}
{addJsDef productBasePriceTaxIncl=($product->getPrice(true, null, 6, null, false, false)|floatval)}
{addJsDef productReference=$product->reference|escape:'html':'UTF-8'}
{addJsDef productAvailableForOrder=$product->available_for_order|boolval}
{addJsDef productPriceWithoutReduction=$productPriceWithoutReduction|floatval}
{addJsDef productPrice=$productPrice|floatval}
{addJsDef productUnitPriceRatio=$product->unit_price_ratio|floatval}
{addJsDef productShowPrice=(!$PS_CATALOG_MODE && $product->show_price)|boolval}
{addJsDef PS_CATALOG_MODE=$PS_CATALOG_MODE}
{if $product->specificPrice && $product->specificPrice|@count}
{addJsDef product_specific_price=$product->specificPrice}
{else}
{addJsDef product_specific_price=array()}
{/if}
{if $display_qties == 1 && $product->quantity}
{addJsDef quantityAvailable=$product->quantity}
{else}
{addJsDef quantityAvailable=0}
{/if}
{addJsDef quantitiesDisplayAllowed=$display_qties|boolval}
{if $product->specificPrice && $product->specificPrice.reduction && $product->specificPrice.reduction_type == 'percentage'}
{addJsDef reduction_percent=$product->specificPrice.reduction*100|floatval}
{else}
{addJsDef reduction_percent=0}
{/if}
{if $product->specificPrice && $product->specificPrice.reduction && $product->specificPrice.reduction_type == 'amount'}
{addJsDef reduction_price=$product->specificPrice.reduction|floatval}
{else}
{addJsDef reduction_price=0}
{/if}
{if $product->specificPrice && $product->specificPrice.price}
{addJsDef specific_price=$product->specificPrice.price|floatval}
{else}
{addJsDef specific_price=0}
{/if}
{addJsDef specific_currency=($product->specificPrice && $product->specificPrice.id_currency)|boolval} {* TODO: remove if always false *}
{addJsDef stock_management=$PS_STOCK_MANAGEMENT|intval}
{addJsDef taxRate=$tax_rate|floatval}
{addJsDefL name=doesntExist}{l s='This combination does not exist for this product. Please select another combination.' js=1}{/addJsDefL}
{addJsDefL name=doesntExistNoMore}{l s='This product is no longer in stock' js=1}{/addJsDefL}
{addJsDefL name=doesntExistNoMoreBut}{l s='with those attributes but is available with others.' js=1}{/addJsDefL}
{addJsDefL name=fieldRequired}{l s='Please fill in all the required fields before saving your customization.' js=1}{/addJsDefL}
{addJsDefL name=uploading_in_progress}{l s='Uploading in progress, please be patient.' js=1}{/addJsDefL}
{addJsDefL name='product_fileDefaultHtml'}{l s='No file selected' js=1}{/addJsDefL}
{addJsDefL name='product_fileButtonHtml'}{l s='Choose File' js=1}{/addJsDefL}
{/strip}
{/if}

View File

@ -0,0 +1,151 @@
{*
* 2007-2016 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2016 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*}
<tr id="product_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}{if !empty($product.gift)}_gift{/if}" class="cart_item{if isset($productLast) && $productLast && (!isset($ignoreProductLast) || !$ignoreProductLast)} last_item{/if}{if isset($productFirst) && $productFirst} first_item{/if}{if isset($customizedDatas.$productId.$productAttributeId) AND $quantityDisplayed == 0} alternate_item{/if} address_{$product.id_address_delivery|intval} {if $odd}odd{else}even{/if}">
<td class="cart_product">
<a href="{$link->getProductLink($product.id_product, $product.link_rewrite, $product.category, null, null, $product.id_shop, $product.id_product_attribute, false, false, true)|escape:'html':'UTF-8'}"><img src="{$link->getImageLink($product.link_rewrite, $product.id_image, 'small_default')|escape:'html':'UTF-8'}" alt="{$product.name|escape:'html':'UTF-8'}" {if isset($smallSize)}width="{$smallSize.width}" height="{$smallSize.height}" {/if} /></a>
</td>
<td class="cart_description">
{capture name=sep} : {/capture}
{capture}{l s=' : '}{/capture}
<p class="product-name"><a href="{$link->getProductLink($product.id_product, $product.link_rewrite, $product.category, null, null, $product.id_shop, $product.id_product_attribute, false, false, true)|escape:'html':'UTF-8'}">{$product.name|escape:'html':'UTF-8'}</a></p>
{if $product.reference}<small class="cart_ref">{l s='SKU'}{$smarty.capture.default}{$product.reference|escape:'html':'UTF-8'}</small>{/if}
{if isset($product.attributes) && $product.attributes}<small><a href="{$link->getProductLink($product.id_product, $product.link_rewrite, $product.category, null, null, $product.id_shop, $product.id_product_attribute, false, false, true)|escape:'html':'UTF-8'}">{$product.attributes|@replace: $smarty.capture.sep:$smarty.capture.default|escape:'html':'UTF-8'}</a></small>{/if}
{if isset($product.configurator_opt)}
{foreach item=group from=$product.configurator_opt}
<small>
{$group['name']} :
{foreach item=i from=$group['value'] name=opt}
{$i} {if !$smarty.foreach.opt.last},{/if}
{/foreach}
</small>
{/foreach}
{/if}
</td>
{if $PS_STOCK_MANAGEMENT}
<td class="cart_avail"><span class="label{if $product.quantity_available <= 0 && isset($product.allow_oosp) && !$product.allow_oosp} label-danger{elseif $product.quantity_available <= 0} label-warning{else} label-success{/if}">{if $product.quantity_available <= 0}{if isset($product.allow_oosp) && $product.allow_oosp}{if isset($product.available_later) && $product.available_later}{$product.available_later}{else}{l s='In Stock'}{/if}{else}{l s='Out of stock'}{/if}{else}{if isset($product.available_now) && $product.available_now}{$product.available_now}{else}{l s='In Stock'}{/if}{/if}</span>{if !$product.is_virtual}{hook h="displayProductDeliveryTime" product=$product}{/if}</td>
{/if}
<td class="cart_unit" data-title="{l s='Unit price'}">
<ul class="price text-right" id="product_price_{$product.id_product}_{$product.id_product_attribute}{if $quantityDisplayed > 0}_nocustom{/if}_{$product.id_address_delivery|intval}{if !empty($product.gift)}_gift{/if}">
{if !empty($product.gift)}
<li class="gift-icon">{l s='Gift!'}</li>
{else}
{if !$priceDisplay}
<li class="price{if isset($product.is_discounted) && $product.is_discounted && isset($product.reduction_applies) && $product.reduction_applies} special-price{/if}">{convertPrice price=$product.price_wt}</li>
{else}
<li class="price{if isset($product.is_discounted) && $product.is_discounted && isset($product.reduction_applies) && $product.reduction_applies} special-price{/if}">{convertPrice price=$product.price}</li>
{/if}
{if isset($product.is_discounted) && $product.is_discounted && isset($product.reduction_applies) && $product.reduction_applies}
<li class="price-percent-reduction small">
{if !$priceDisplay}
{if isset($product.reduction_type) && $product.reduction_type == 'amount'}
{assign var='priceReduction' value=($product.price_wt - $product.price_without_specific_price)}
{assign var='symbol' value=$currency->sign}
{else}
{assign var='priceReduction' value=(($product.price_without_specific_price - $product.price_wt)/$product.price_without_specific_price) * 100 * -1}
{assign var='symbol' value='%'}
{/if}
{else}
{if isset($product.reduction_type) && $product.reduction_type == 'amount'}
{assign var='priceReduction' value=($product.price - $product.price_without_specific_price)}
{assign var='symbol' value=$currency->sign}
{else}
{assign var='priceReduction' value=(($product.price_without_specific_price - $product.price)/$product.price_without_specific_price) * -100}
{assign var='symbol' value='%'}
{/if}
{/if}
{if $symbol == '%'}
&nbsp;{$priceReduction|string_format:"%.2f"|regex_replace:"/[^\d]0+$/":""}{$symbol}&nbsp;
{else}
&nbsp;{convertPrice price=$priceReduction}&nbsp;
{/if}
</li>
<li class="old-price">{convertPrice price=$product.price_without_specific_price}</li>
{/if}
{/if}
</ul>
</td>
<td class="cart_quantity text-center" data-title="{l s='Quantity'}">
{if (isset($cannotModify) && $cannotModify == 1)}
<span>
{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}
{$product.customizationQuantityTotal}
{else}
{$product.cart_quantity-$quantityDisplayed}
{/if}
</span>
{else}
{if isset($customizedDatas.$productId.$productAttributeId) AND $quantityDisplayed == 0}
<span id="cart_quantity_custom_{$product.id_product}_{$product.id_product_attribute}_{$product.id_address_delivery|intval}" >{$product.customizationQuantityTotal}</span>
{/if}
{if !isset($customizedDatas.$productId.$productAttributeId) OR $quantityDisplayed > 0}
<input type="hidden" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}" name="quantity_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}_hidden" />
<input size="2" type="text" autocomplete="off" class="cart_quantity_input form-control grey" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}" name="quantity_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}" />
<div class="cart_quantity_button clearfix">
{if $product.minimal_quantity < ($product.cart_quantity-$quantityDisplayed) OR $product.minimal_quantity <= 1}
<a rel="nofollow" class="cart_quantity_down btn btn-default button-minus" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}" href="{$link->getPageLink('cart', true, NULL, "add=1&amp;id_product={$product.id_product|intval}&amp;ipa={$product.id_product_attribute|intval}&amp;id_address_delivery={$product.id_address_delivery|intval}&amp;op=down&amp;token={$token_cart}")|escape:'html':'UTF-8'}" title="{l s='Subtract'}">
<span><i class="icon-minus"></i></span>
</a>
{else}
<a class="cart_quantity_down btn btn-default button-minus disabled" href="#" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}" title="{l s='You must purchase a minimum of %d of this product.' sprintf=$product.minimal_quantity}">
<span><i class="icon-minus"></i></span>
</a>
{/if}
<a rel="nofollow" class="cart_quantity_up btn btn-default button-plus" id="cart_quantity_up_{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}" href="{$link->getPageLink('cart', true, NULL, "add=1&amp;id_product={$product.id_product|intval}&amp;ipa={$product.id_product_attribute|intval}&amp;id_address_delivery={$product.id_address_delivery|intval}&amp;token={$token_cart}")|escape:'html':'UTF-8'}" title="{l s='Add'}"><span><i class="icon-plus"></i></span></a>
</div>
{/if}
{/if}
</td>
{if !isset($noDeleteButton) || !$noDeleteButton}
<td class="cart_delete text-center" data-title="{l s='Delete'}">
{if (!isset($customizedDatas.$productId.$productAttributeId) OR $quantityDisplayed > 0) && empty($product.gift)}
<div>
<a rel="nofollow" title="{l s='Delete'}" class="cart_quantity_delete" id="{$product.id_product}_{$product.id_product_attribute}_{if $quantityDisplayed > 0}nocustom{else}0{/if}_{$product.id_address_delivery|intval}" href="{$link->getPageLink('cart', true, NULL, "delete=1&amp;id_product={$product.id_product|intval}&amp;ipa={$product.id_product_attribute|intval}&amp;id_address_delivery={$product.id_address_delivery|intval}&amp;token={$token_cart}")|escape:'html':'UTF-8'}"><i class="icon-trash"></i></a>
</div>
{else}
{/if}
</td>
{/if}
<td class="cart_total" data-title="{l s='Total'}">
<span class="price" id="total_product_price_{$product.id_product}_{$product.id_product_attribute}{if $quantityDisplayed > 0}_nocustom{/if}_{$product.id_address_delivery|intval}{if !empty($product.gift)}_gift{/if}">
{if !empty($product.gift)}
<span class="gift-icon">{l s='Gift!'}</span>
{else}
{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}
{if !$priceDisplay}{displayPrice price=$product.total_customization_wt}{else}{displayPrice price=$product.total_customization}{/if}
{else}
{if !$priceDisplay}{displayPrice price=$product.total_wt}{else}{displayPrice price=$product.total}{/if}
{/if}
{/if}
</span>
{hook h='displayCartExtraProductActions' product=$product}
</td>
</tr>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,483 @@
<?php
class Product extends ProductCore
{
/**
* Product with configuration
* @param int $id_product
* @return boolean
*/
public static function hasConfiguratorOpt($id_product)
{
$result = Db::getInstance()->getValue('
SELECT count(*) AS nb
FROM `'._DB_PREFIX_.'product_configurator_opt_group` pcog,
`'._DB_PREFIX_.'configurator_opt_group` cog
WHERE pcog.`id_product` = '.(int)$id_product.'
AND cog.`id_configurator_opt_group` = pcog.`id_configurator_opt_group`');
return ( $result > 0 ? true : false );
}
/**
* Retourne les groupes options
* @param int $id_product
* @return array|false|NULL|mysqli_result|PDOStatement|resource
*/
public static function getConfiguratorOptGroup($id_product)
{
$context = Context::getContext();
$result = Db::getInstance()->executeS('
SELECT pcog.*, col.`name`, cog.`type`
FROM `'._DB_PREFIX_.'product_configurator_opt_group` pcog,
`'._DB_PREFIX_.'configurator_opt_group` cog,
`'._DB_PREFIX_.'configurator_opt_group_lang` col
WHERE pcog.`id_product` = '.(int)$id_product.'
AND cog.`id_configurator_opt_group` = col.`id_configurator_opt_group`
AND pcog.`id_configurator_opt_group` = col.`id_configurator_opt_group`
AND col.`id_lang` = '.(int)$context->language->id.'
ORDER BY pcog.`position` ASC');
return $result;
}
/**
* Retourne les impacts
* @param int $id_product
* @return array|false|NULL|mysqli_result|PDOStatement|resource
*/
public static function getConfiguratorOptImpact($id_product)
{
$context = Context::getContext();
$result = Db::getInstance()->executeS('
SELECT pcoi.`id_product_configurator_opt_impact`, pcoi.`id_product_configurator_opt_group`, pcoi.`id_configurator_opt`, col.`name`, pcoi.`price`, co.`position`
FROM `'._DB_PREFIX_.'product_configurator_opt_impact` pcoi
LEFT JOIN `'._DB_PREFIX_.'configurator_opt` co ON co.`id_configurator_opt` = pcoi.`id_configurator_opt`
LEFT JOIN `'._DB_PREFIX_.'configurator_opt_lang` col
ON (col.`id_configurator_opt` = pcoi.`id_configurator_opt` AND col.`id_lang` = '.(int)$context->language->id.')
WHERE pcoi.`id_product` = '.(int)$id_product.'
ORDER BY co.`position` ASC');
return $result;
}
/**
* Calcul du prix d'impact suivant les éléments selectionnés
*/
public function getConfiguratorImpactPrice($params)
{
$priceImpact = 0;
// List id_product_configurator_opt_impact
$impactId = array();
if (count($params) > 0) {
foreach ($params as $p) {
if (is_numeric($p['id_product_configurator_opt_impact'])) {
$impactId[] = $p['id_product_configurator_opt_impact'];
} elseif (is_array($p['id_product_configurator_opt_impact'])) {
foreach ($p['id_product_configurator_opt_impact'] as $v) {
$impactId[] = $v;
}
}
}
$sql = 'SELECT price FROM `'._DB_PREFIX_.'product_configurator_opt_impact` where id_product = '.$this->id.
' AND id_product_configurator_opt_impact IN('.join(',', $impactId).')';
$result = Db::getInstance()->executeS($sql);
if (count($result) > 0) {
foreach ($result as $item) {
$priceImpact += $item['price'];
}
}
}
return $priceImpact;
}
public function getConfiguratorOptSelected($id_configurator)
{
$sql = 'SELECT id_product_configurator_opt_group, id_product_configurator_opt_impact FROM ps_configurator_storage WHERE id_configurator = '.(int)$id_configurator;
$optSelected = Db::getInstance()->executeS($sql);
if (count($optSelected) > 0) {
}
}
/**
* Price calculation / Get product price
*
* @param int $id_shop Shop id
* @param int $id_product Product id
* @param int $id_product_attribute Product attribute id
* @param int $id_country Country id
* @param int $id_state State id
* @param string $zipcode
* @param int $id_currency Currency id
* @param int $id_group Group id
* @param int $quantity Quantity Required for Specific prices : quantity discount application
* @param bool $use_tax with (1) or without (0) tax
* @param int $decimals Number of decimals returned
* @param bool $only_reduc Returns only the reduction amount
* @param bool $use_reduc Set if the returned amount will include reduction
* @param bool $with_ecotax insert ecotax in price output.
* @param null $specific_price If a specific price applies regarding the previous parameters,
* this variable is filled with the corresponding SpecificPrice object
* @param bool $use_group_reduction
* @param int $id_customer
* @param bool $use_customer_price
* @param int $id_cart
* @param int $real_quantity
* @return float Product price
**/
public static function priceCalculation($id_shop, $id_product, $id_product_attribute, $id_country, $id_state, $zipcode, $id_currency,
$id_group, $quantity, $use_tax, $decimals, $only_reduc, $use_reduc, $with_ecotax, &$specific_price, $use_group_reduction,
$id_customer = 0, $use_customer_price = true, $id_cart = 0, $real_quantity = 0, $id_configurator = null)
{
static $address = null;
static $context = null;
if ($address === null) {
$address = new Address();
}
if ($context == null) {
$context = Context::getContext()->cloneContext();
}
if ($id_shop !== null && $context->shop->id != (int)$id_shop) {
$context->shop = new Shop((int)$id_shop);
}
if (!$use_customer_price) {
$id_customer = 0;
}
if ($id_product_attribute === null) {
$id_product_attribute = Product::getDefaultAttribute($id_product);
}
$cache_id = (int)$id_product.'-'.(int)$id_shop.'-'.(int)$id_currency.'-'.(int)$id_country.'-'.$id_state.'-'.$zipcode.'-'.(int)$id_group.
'-'.(int)$quantity.'-'.(int)$id_product_attribute.
'-'.(int)$with_ecotax.'-'.(int)$id_customer.'-'.(int)$use_group_reduction.'-'.(int)$id_cart.'-'.(int)$real_quantity.
'-'.($only_reduc?'1':'0').'-'.($use_reduc?'1':'0').'-'.($use_tax?'1':'0').'-'.(int)$decimals;
if($id_configurator !== null) {
$cache_id.= '-'.$id_configurator;
}
// reference parameter is filled before any returns
$specific_price = SpecificPrice::getSpecificPrice(
(int)$id_product,
$id_shop,
$id_currency,
$id_country,
$id_group,
$quantity,
$id_product_attribute,
$id_customer,
$id_cart,
$real_quantity
);
if (isset(self::$_prices[$cache_id])) {
/* Affect reference before returning cache */
if (isset($specific_price['price']) && $specific_price['price'] > 0) {
$specific_price['price'] = self::$_prices[$cache_id];
}
return self::$_prices[$cache_id];
}
// fetch price & attribute price
$cache_id_2 = $id_product.'-'.$id_shop;
if (!isset(self::$_pricesLevel2[$cache_id_2])) {
$sql = new DbQuery();
$sql->select('product_shop.`price`, product_shop.`ecotax`');
$sql->from('product', 'p');
$sql->innerJoin('product_shop', 'product_shop', '(product_shop.id_product=p.id_product AND product_shop.id_shop = '.(int)$id_shop.')');
$sql->where('p.`id_product` = '.(int)$id_product);
if (Combination::isFeatureActive()) {
$sql->select('IFNULL(product_attribute_shop.id_product_attribute,0) id_product_attribute, product_attribute_shop.`price` AS attribute_price, product_attribute_shop.default_on');
$sql->leftJoin('product_attribute_shop', 'product_attribute_shop', '(product_attribute_shop.id_product = p.id_product AND product_attribute_shop.id_shop = '.(int)$id_shop.')');
} else {
$sql->select('0 as id_product_attribute');
}
$res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
if (is_array($res) && count($res)) {
foreach ($res as $row) {
$array_tmp = array(
'price' => $row['price'],
'ecotax' => $row['ecotax'],
'attribute_price' => (isset($row['attribute_price']) ? $row['attribute_price'] : null)
);
self::$_pricesLevel2[$cache_id_2][(int)$row['id_product_attribute']] = $array_tmp;
if (isset($row['default_on']) && $row['default_on'] == 1) {
self::$_pricesLevel2[$cache_id_2][0] = $array_tmp;
}
}
}
}
if (!isset(self::$_pricesLevel2[$cache_id_2][(int)$id_product_attribute])) {
return;
}
$result = self::$_pricesLevel2[$cache_id_2][(int)$id_product_attribute];
if (!$specific_price || $specific_price['price'] < 0) {
$price = (float)$result['price'];
} else {
$price = (float)$specific_price['price'];
}
// convert only if the specific price is in the default currency (id_currency = 0)
if (!$specific_price || !($specific_price['price'] >= 0 && $specific_price['id_currency'])) {
$price = Tools::convertPrice($price, $id_currency);
if (isset($specific_price['price']) && $specific_price['price'] >= 0) {
$specific_price['price'] = $price;
}
}
// Attribute price
if (is_array($result) && (!$specific_price || !$specific_price['id_product_attribute'] || $specific_price['price'] < 0)) {
$attribute_price = Tools::convertPrice($result['attribute_price'] !== null ? (float)$result['attribute_price'] : 0, $id_currency);
// If you want the default combination, please use NULL value instead
if ($id_product_attribute !== false) {
$price += $attribute_price;
}
}
// Tax
$address->id_country = $id_country;
$address->id_state = $id_state;
$address->postcode = $zipcode;
$tax_manager = TaxManagerFactory::getManager($address, Product::getIdTaxRulesGroupByIdProduct((int)$id_product, $context));
$product_tax_calculator = $tax_manager->getTaxCalculator();
// Add Tax
if ($use_tax) {
$price = $product_tax_calculator->addTaxes($price);
}
// Eco Tax
if (($result['ecotax'] || isset($result['attribute_ecotax'])) && $with_ecotax) {
$ecotax = $result['ecotax'];
if (isset($result['attribute_ecotax']) && $result['attribute_ecotax'] > 0) {
$ecotax = $result['attribute_ecotax'];
}
if ($id_currency) {
$ecotax = Tools::convertPrice($ecotax, $id_currency);
}
if ($use_tax) {
// reinit the tax manager for ecotax handling
$tax_manager = TaxManagerFactory::getManager(
$address,
(int)Configuration::get('PS_ECOTAX_TAX_RULES_GROUP_ID')
);
$ecotax_tax_calculator = $tax_manager->getTaxCalculator();
$price += $ecotax_tax_calculator->addTaxes($ecotax);
} else {
$price += $ecotax;
}
}
// Configurator add price impact
if ($id_configurator != null) {
$sql = 'SELECT price FROM `'._DB_PREFIX_.'configurator_storage` WHERE id_configurator = '.(int)$id_configurator.
' AND id_product = '.(int)$id_product;
$priceResult = Db::getInstance()->executeS($sql);
if (count($priceResult) > 0) {
foreach($priceResult as $p) {
$price+= $p['price'];
}
}
}
// Reduction
$specific_price_reduction = 0;
if (($only_reduc || $use_reduc) && $specific_price) {
if ($specific_price['reduction_type'] == 'amount') {
$reduction_amount = $specific_price['reduction'];
if (!$specific_price['id_currency']) {
$reduction_amount = Tools::convertPrice($reduction_amount, $id_currency);
}
$specific_price_reduction = $reduction_amount;
// Adjust taxes if required
if (!$use_tax && $specific_price['reduction_tax']) {
$specific_price_reduction = $product_tax_calculator->removeTaxes($specific_price_reduction);
}
if ($use_tax && !$specific_price['reduction_tax']) {
$specific_price_reduction = $product_tax_calculator->addTaxes($specific_price_reduction);
}
} else {
$specific_price_reduction = $price * $specific_price['reduction'];
}
}
if ($use_reduc) {
$price -= $specific_price_reduction;
}
// Group reduction
if ($use_group_reduction) {
$reduction_from_category = GroupReduction::getValueForProduct($id_product, $id_group);
if ($reduction_from_category !== false) {
$group_reduction = $price * (float)$reduction_from_category;
} else { // apply group reduction if there is no group reduction for this category
$group_reduction = (($reduc = Group::getReductionByIdGroup($id_group)) != 0) ? ($price * $reduc / 100) : 0;
}
$price -= $group_reduction;
}
if ($only_reduc) {
return Tools::ps_round($specific_price_reduction, $decimals);
}
$price = Tools::ps_round($price, $decimals);
if ($price < 0) {
$price = 0;
}
self::$_prices[$cache_id] = $price;
return self::$_prices[$cache_id];
}
/**
* Override product price
*/
public static function getPriceStatic($id_product, $usetax = true, $id_product_attribute = null, $decimals = 6, $divisor = null,
$only_reduc = false, $usereduc = true, $quantity = 1, $force_associated_tax = false, $id_customer = null, $id_cart = null,
$id_address = null, &$specific_price_output = null, $with_ecotax = true, $use_group_reduction = true, Context $context = null,
$use_customer_price = true, $id_configurator = null)
{
if (!$context) {
$context = Context::getContext();
}
$cur_cart = $context->cart;
if ($divisor !== null) {
Tools::displayParameterAsDeprecated('divisor');
}
if (!Validate::isBool($usetax) || !Validate::isUnsignedId($id_product)) {
die(Tools::displayError());
}
// Initializations
$id_group = null;
if ($id_customer) {
$id_group = Customer::getDefaultGroupId((int)$id_customer);
}
if (!$id_group) {
$id_group = (int)Group::getCurrent()->id;
}
// If there is cart in context or if the specified id_cart is different from the context cart id
if (!is_object($cur_cart) || (Validate::isUnsignedInt($id_cart) && $id_cart && $cur_cart->id != $id_cart)) {
/*
* When a user (e.g., guest, customer, Google...) is on PrestaShop, he has already its cart as the global (see /init.php)
* When a non-user calls directly this method (e.g., payment module...) is on PrestaShop, he does not have already it BUT knows the cart ID
* When called from the back office, cart ID can be inexistant
*/
if (!$id_cart && !isset($context->employee)) {
die(Tools::displayError());
}
$cur_cart = new Cart($id_cart);
// Store cart in context to avoid multiple instantiations in BO
if (!Validate::isLoadedObject($context->cart)) {
$context->cart = $cur_cart;
}
}
$cart_quantity = 0;
if ((int)$id_cart) {
$cache_id = 'Product::getPriceStatic_'.(int)$id_product.'-'.(int)$id_cart;
if (!Cache::isStored($cache_id) || ($cart_quantity = Cache::retrieve($cache_id) != (int)$quantity)) {
$sql = 'SELECT SUM(`quantity`)
FROM `'._DB_PREFIX_.'cart_product`
WHERE `id_product` = '.(int)$id_product.'
AND `id_cart` = '.(int)$id_cart;
$cart_quantity = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
Cache::store($cache_id, $cart_quantity);
} else {
$cart_quantity = Cache::retrieve($cache_id);
}
}
$id_currency = Validate::isLoadedObject($context->currency) ? (int)$context->currency->id : (int)Configuration::get('PS_CURRENCY_DEFAULT');
// retrieve address informations
$id_country = (int)$context->country->id;
$id_state = 0;
$zipcode = 0;
if (!$id_address && Validate::isLoadedObject($cur_cart)) {
$id_address = $cur_cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
}
if ($id_address) {
$address_infos = Address::getCountryAndState($id_address);
if ($address_infos['id_country']) {
$id_country = (int)$address_infos['id_country'];
$id_state = (int)$address_infos['id_state'];
$zipcode = $address_infos['postcode'];
}
} elseif (isset($context->customer->geoloc_id_country)) {
$id_country = (int)$context->customer->geoloc_id_country;
$id_state = (int)$context->customer->id_state;
$zipcode = $context->customer->postcode;
}
if (Tax::excludeTaxeOption()) {
$usetax = false;
}
if ($usetax != false
&& !empty($address_infos['vat_number'])
&& $address_infos['id_country'] != Configuration::get('VATNUMBER_COUNTRY')
&& Configuration::get('VATNUMBER_MANAGEMENT')) {
$usetax = false;
}
if (is_null($id_customer) && Validate::isLoadedObject($context->customer)) {
$id_customer = $context->customer->id;
}
$return = Product::priceCalculation(
$context->shop->id,
$id_product,
$id_product_attribute,
$id_country,
$id_state,
$zipcode,
$id_currency,
$id_group,
$quantity,
$usetax,
$decimals,
$only_reduc,
$usereduc,
$with_ecotax,
$specific_price_output,
$use_group_reduction,
$id_customer,
$use_customer_price,
$id_cart,
$cart_quantity,
$id_configurator
);
return $return ;
}
}

View File

@ -0,0 +1,5 @@
<?php
class AdminOrdersController extends AdminController
{
}

View File

@ -0,0 +1,248 @@
<?php
class CartController extends CartControllerCore
{
protected $id_configurator;
protected function parseConfigurator()
{
// @todo : in real time is it a good solution to get id before write items OR get from ps_cart_product and update it quickly
$this->id_configurator = Db::getInstance()->getValue('SELECT (MAX(`id_configurator`)+1) FROM `'._DB_PREFIX_.'configurator_storage`');
if ($this->id_configurator === null) {
$this->id_configurator = 1;
}
// Create array of option to retrieve price impact
$detectOptGroup = 'optgroup-';
$postValues = Tools::getAllValues();
if (count($postValues) > 0) {
$sqlValues = array();
foreach($postValues as $p => $v) {
if (!empty($v)) {
if (substr($p, 0, strlen($detectOptGroup)) === $detectOptGroup) {
if (is_array($v) && count($v) > 0) {
foreach ($v as $vArray) {
$price = Db::getInstance()->getValue('SELECT price FROM ps_product_configurator_opt_impact WHERE id_product_configurator_opt_impact = '.(int)$vArray);
$sqlValues[] = '('.$this->id_configurator.', '.$this->id_product.', '.substr($p, strlen($detectOptGroup)).', '.pSQL($vArray).', '.$price.')';
}
} else {
$price = Db::getInstance()->getValue('SELECT price FROM ps_product_configurator_opt_impact WHERE id_product_configurator_opt_impact = '.(int)$v);
$sqlValues[] = '('.$this->id_configurator.', '.$this->id_product.', '.substr($p, strlen($detectOptGroup)).', '.pSQL($v).', '.$price.')';
}
}
}
}
if (count($sqlValues) > 0) {
$result = Db::getInstance()->execute('
INSERT INTO `'._DB_PREFIX_.'configurator_storage` (`id_configurator`, `id_product`, `id_product_configurator_opt_group`, `id_product_configurator_opt_impact`, `price`)
VALUES '.implode(',', $sqlValues)
);
// update cart_product
if ($result) {
Db::getInstance()->update('cart_product', array(
'id_configurator' => (int)$this->id_configurator),
'id_cart='.(int)$this->context->cart->id.
' AND id_product='.(int)$this->id_product.
' AND id_address_delivery='.(int)$this->id_address_delivery.
' AND id_shop='.(int)$this->context->cart->id_shop.
' AND id_product_attribute='.(int)$this->id_product_attribute.
' AND quantity='.(int)$this->qty
.' AND id_configurator = 0');
}
}
}
}
protected function processDeleteProductInCart()
{
$customization_product = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'customization`
WHERE `id_cart` = '.(int)$this->context->cart->id.' AND `id_product` = '.(int)$this->id_product.' AND `id_customization` != '.(int)$this->customization_id);
if (count($customization_product)) {
$product = new Product((int)$this->id_product);
if ($this->id_product_attribute > 0) {
$minimal_quantity = (int)Attribute::getAttributeMinimalQty($this->id_product_attribute);
} else {
$minimal_quantity = (int)$product->minimal_quantity;
}
$total_quantity = 0;
foreach ($customization_product as $custom) {
$total_quantity += $custom['quantity'];
}
if ($total_quantity < $minimal_quantity) {
$this->ajaxDie(Tools::jsonEncode(array(
'hasError' => true,
'errors' => array(sprintf(Tools::displayError('You must add %d minimum quantity', !Tools::getValue('ajax')), $minimal_quantity)),
)));
}
}
if ($this->context->cart->deleteProduct($this->id_product, $this->id_product_attribute, $this->customization_id, $this->id_address_delivery)) {
Hook::exec('actionAfterDeleteProductInCart', array(
'id_cart' => (int)$this->context->cart->id,
'id_product' => (int)$this->id_product,
'id_product_attribute' => (int)$this->id_product_attribute,
'customization_id' => (int)$this->customization_id,
'id_address_delivery' => (int)$this->id_address_delivery
));
if (!Cart::getNbProducts((int)$this->context->cart->id)) {
$this->context->cart->setDeliveryOption(null);
$this->context->cart->gift = 0;
$this->context->cart->gift_message = '';
$this->context->cart->update();
}
}
$removed = CartRule::autoRemoveFromCart();
CartRule::autoAddToCart();
if (count($removed) && (int)Tools::getValue('allow_refresh')) {
$this->ajax_refresh = true;
}
}
protected function processChangeProductInCart()
{
$mode = (Tools::getIsset('update') && $this->id_product) ? 'update' : 'add';
if ($this->qty == 0) {
$this->errors[] = Tools::displayError('Null quantity.', !Tools::getValue('ajax'));
} elseif (!$this->id_product) {
$this->errors[] = Tools::displayError('Product not found', !Tools::getValue('ajax'));
}
$product = new Product($this->id_product, true, $this->context->language->id);
if (!$product->id || !$product->active || !$product->checkAccess($this->context->cart->id_customer)) {
$this->errors[] = Tools::displayError('This product is no longer available.', !Tools::getValue('ajax'));
return;
}
$qty_to_check = $this->qty;
$cart_products = $this->context->cart->getProducts();
if (is_array($cart_products)) {
foreach ($cart_products as $cart_product) {
if ((!isset($this->id_product_attribute) || $cart_product['id_product_attribute'] == $this->id_product_attribute) &&
(isset($this->id_product) && $cart_product['id_product'] == $this->id_product)) {
$qty_to_check = $cart_product['cart_quantity'];
if (Tools::getValue('op', 'up') == 'down') {
$qty_to_check -= $this->qty;
} else {
$qty_to_check += $this->qty;
}
break;
}
}
}
// Check product quantity availability
if ($this->id_product_attribute) {
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !Attribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
$this->errors[] = Tools::displayError('There isn\'t enough product in stock.', !Tools::getValue('ajax'));
}
} elseif ($product->hasAttributes()) {
$minimumQuantity = ($product->out_of_stock == 2) ? !Configuration::get('PS_ORDER_OUT_OF_STOCK') : !$product->out_of_stock;
$this->id_product_attribute = Product::getDefaultAttribute($product->id, $minimumQuantity);
// @todo do something better than a redirect admin !!
if (!$this->id_product_attribute) {
Tools::redirectAdmin($this->context->link->getProductLink($product));
} elseif (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !Attribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
$this->errors[] = Tools::displayError('There isn\'t enough product in stock.', !Tools::getValue('ajax'));
}
} elseif (!$product->checkQty($qty_to_check)) {
$this->errors[] = Tools::displayError('There isn\'t enough product in stock.', !Tools::getValue('ajax'));
}
// If no errors, process product addition
if (!$this->errors && $mode == 'add') {
// Add cart if no cart found
if (!$this->context->cart->id) {
if (Context::getContext()->cookie->id_guest) {
$guest = new Guest(Context::getContext()->cookie->id_guest);
$this->context->cart->mobile_theme = $guest->mobile_theme;
}
$this->context->cart->add();
if ($this->context->cart->id) {
$this->context->cookie->id_cart = (int)$this->context->cart->id;
}
}
// Check customizable fields
if (!$product->hasAllRequiredCustomizableFields() && !$this->customization_id) {
$this->errors[] = Tools::displayError('Please fill in all of the required fields, and then save your customizations.', !Tools::getValue('ajax'));
}
if (!$this->errors) {
$cart_rules = $this->context->cart->getCartRules();
$available_cart_rules = CartRule::getCustomerCartRules($this->context->language->id, (isset($this->context->customer->id) ? $this->context->customer->id : 0), true, true, true, $this->context->cart, false, true);
// Configurator options
$this->parseConfigurator();
// Send configurator id
$update_quantity = $this->context->cart->updateQty(
$this->qty,
$this->id_product,
$this->id_product_attribute,
$this->customization_id,
Tools::getValue('op', 'up'),
$this->id_address_delivery,
null,
true,
$this->id_configurator
);
if ($update_quantity < 0) {
// If product has attribute, minimal quantity is set with minimal quantity of attribute
$minimal_quantity = ($this->id_product_attribute) ? Attribute::getAttributeMinimalQty($this->id_product_attribute) : $product->minimal_quantity;
$this->errors[] = sprintf(Tools::displayError('You must add %d minimum quantity', !Tools::getValue('ajax')), $minimal_quantity);
} elseif (!$update_quantity) {
$this->errors[] = Tools::displayError('You already have the maximum quantity available for this product.', !Tools::getValue('ajax'));
} elseif ((int)Tools::getValue('allow_refresh')) {
// If the cart rules has changed, we need to refresh the whole cart
$cart_rules2 = $this->context->cart->getCartRules();
if (count($cart_rules2) != count($cart_rules)) {
$this->ajax_refresh = true;
} elseif (count($cart_rules2)) {
$rule_list = array();
foreach ($cart_rules2 as $rule) {
$rule_list[] = $rule['id_cart_rule'];
}
foreach ($cart_rules as $rule) {
if (!in_array($rule['id_cart_rule'], $rule_list)) {
$this->ajax_refresh = true;
break;
}
}
} else {
$available_cart_rules2 = CartRule::getCustomerCartRules($this->context->language->id, (isset($this->context->customer->id) ? $this->context->customer->id : 0), true, true, true, $this->context->cart, false, true);
if (count($available_cart_rules2) != count($available_cart_rules)) {
$this->ajax_refresh = true;
} elseif (count($available_cart_rules2)) {
$rule_list = array();
foreach ($available_cart_rules2 as $rule) {
$rule_list[] = $rule['id_cart_rule'];
}
foreach ($cart_rules2 as $rule) {
if (!in_array($rule['id_cart_rule'], $rule_list)) {
$this->ajax_refresh = true;
break;
}
}
}
}
}
}
}
$removed = CartRule::autoRemoveFromCart();
CartRule::autoAddToCart();
if (count($removed) && (int)Tools::getValue('allow_refresh')) {
$this->ajax_refresh = true;
}
}
}

View File

@ -0,0 +1,54 @@
<?php
class ProductController extends ProductControllerCore
{
protected $optValues = array();
protected $impactPrice;
public function init()
{
parent::init();
if ($this->ajax && Tools::getValue('impactprice', 0)) {
if (Validate::isLoadedObject($this->product)) {
$qty = Tools::getValue('qty', 1);
// @todo : Attributes & Quantity
$productPrice = $this->product->getPrice(true, null, 6 , null, false, true, $qty);
} else {
return;
}
// Create array of option to retrieve price impact
$detectOptGroup = 'optgroup-';
$postValues = Tools::getAllValues();
$this->optValues = array();
$impact = 0;
if (count($postValues) > 0) {
foreach($postValues as $p => $v) {
if (!empty($v)) {
if (substr($p, 0, strlen($detectOptGroup)) === $detectOptGroup) {
$this->optValues[] = array(
'id_product_configurator_opt_group' => substr($p, strlen($detectOptGroup)),
'id_product_configurator_opt_impact' => $v
);
}
}
}
$impact = $this->product->getConfiguratorImpactPrice($this->optValues);
}
$this->impactPrice = $productPrice + $impact;
}
}
public function postProcess()
{
if ($this->ajax && Tools::getValue('impactprice', 0)) {
$return = array(
'priceDisplay' => Tools::displayPrice($this->impactPrice),
'price' => $this->impactPrice,
);
die(Tools::jsonEncode($return));
}
}
}

View File

@ -0,0 +1,47 @@
{*Ajouter une OptGroup*}
<div id="product-configurator-opt" class="panel product-tab">
<div class="panel-heading">{l s='Add options for this product'}</div>
<div class="panel-body">
<form id="product-configurator-opt-add" name="product-configurator-opt-add" class="form-inline" method="post" data-token="{$token}" action="{$OptGroupListFormLink}">
<input type="hidden" name="id_product" value="{$id_product}"/>
<div class="form-group">
<select name="id_configurator_opt_group" class="form-control">
<option value="">-</option>
{foreach $OptGroupList as $l}
<option value="{$l.id_configurator_opt_group}">{$l.name}</option>
{/foreach}
</select>
</div>
<button type="submit" class="btn btn-default pull-right">{l s="Add New option"}</button>
</form>
</div>
</div>
<script>
$('form#product-configurator-opt-add').submit(function(){
var postData = {
//required parameters
ajax : true,
controller : 'AdminAntadisConfiguratorProduct',
action : 'Add',
token : $(this).attr('data-token'),
//additional parameters to your controller
id_configurator_opt_group : $('select[name=id_configurator_opt_group]').val(),
id_product: $('input[name=id_product]').val(),
};
$.post('ajax-tab.php', postData, function (data) {}, 'json')
.done(function(){
window.location.href = window.location.href;
})
.fail(function(){
// Display error message
});
return false;
});
</script>
{*Gestion des OptGroup*}
{$ProductOptGroupList}

View File

@ -0,0 +1,53 @@
{if $optGroupList|@count > 0}
<h3 class="page-product-heading" id="antadisconfigurator-content-tab">{l s='Options' mod='antadisconfigurator'}</h3>
<input type="hidden" name="id_product" value="{$product->id}" />
{foreach item=optGroup from=$optGroupList}
<h4>{$optGroup['name']}</h4> {*visible*}
{if $optGroup['type'] == 'text'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<input type="text" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="" placeholder="{$optImpact['name']}" />
{/foreach}
{elseif $optGroup['type'] == 'date'}
<label>{$optGroup['value']}</label>
<input type="date" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="" />
{elseif $optGroup['type'] == 'radio'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<label>
<input type="radio" data-url="./?controller=product&impactprice=1&ajax=1" class="configurator-opt-click" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="{$optImpact['id_product_configurator_opt_impact']}" />
{$optImpact['name']}
</label>
{/foreach}
{elseif $optGroup['type'] == 'checkbox'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<label>
<input type="checkbox" data-url="./?controller=product&impactprice=1&ajax=1" class="configurator-opt-click" name="optgroup-{$optGroup['id_product_configurator_opt_group']}[]" value="{$optImpact['id_product_configurator_opt_impact']}" />
{$optImpact['name']}
</label>
{/foreach}
{elseif $optGroup['type'] == 'select'}
<select class="configurator-opt-change" data-url="./?controller=product&impactprice=1&ajax=1" name="optgroup-{$optGroup['id_product_configurator_opt_group']}">
<option value="">-</option>
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<option value="{$optImpact['id_product_configurator_opt_impact']}">{$optImpact['name']}</option>
{/foreach}
</select>
{/if}
{/foreach}
<script>
$('.configurator-opt-change').on('change', function(e) {
console.log($('form#buy_block').serialize());
$.post($(this).data('url'), $('form#buy_block').serialize(), function (data) {
console.log(data);
$('span#our_price_display').replaceWith('<span id="our_price_display" class="price" itemprop="price" content="'+data.price+'">'+data.priceDisplay+'</span>');
}, 'json').fail(function(){});
});
$('.configurator-opt-click').on('click', function(e){
console.log($('form#buy_block').serialize());
$.post($(this).data('url'), $('form#buy_block').serialize(), function (data) {
console.log(data);
$('span#our_price_display').replaceWith('<span id="our_price_display" class="price" itemprop="price" content="'+data.price+'">'+data.priceDisplay+'</span>');
}, 'json').fail(function(){});
})
</script>
{/if}

View File

@ -0,0 +1,76 @@
{if $optGroupList|@count > 0}
<h3 class="page-product-heading" id="antadisconfigurator-content-tab">{l s='Options' mod='antadisconfigurator'}</h3>
<form name="configurator-opt" method="post" action="./">
<input type="hidden" name="controller" value="product"/>
<input type="hidden" name="token" value="{$token}" />
<input type="hidden" name="ajax" value="1" />
<input type="hidden" name="impactprice" value="1" />
<input type="hidden" name="id_product" value="{$product->id}" />
{foreach item=optGroup from=$optGroupList}
<h4>{$optGroup['name']}</h4> {*visible*}
{if $optGroup['type'] == 'text'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<input type="text" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="" placeholder="{$optImpact['name']}" />
{/foreach}
{elseif $optGroup['type'] == 'date'}
<label>{$optGroup['value']}</label>
<input type="date" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="" />
{elseif $optGroup['type'] == 'radio'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<label>
<input type="radio" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}" value="{$optImpact['id_product_configurator_opt_impact']}" />
{$optImpact['name']}
</label>
{/foreach}
{elseif $optGroup['type'] == 'checkbox'}
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<label>
<input type="checkbox" class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}[]" value="{$optImpact['id_product_configurator_opt_impact']}" />
{$optImpact['name']}
</label>
{/foreach}
{elseif $optGroup['type'] == 'select'}
<select class="configurator-opt" name="optgroup-{$optGroup['id_product_configurator_opt_group']}">
<option value="">-</option>
{foreach item=optImpact from=$optImpactList[$optGroup['id_product_configurator_opt_group']]}
<option value="{$optImpact['id_product_configurator_opt_impact']}">{$optImpact['name']}</option>
{/foreach}
</select>
{/if}
{/foreach}
</form>
<script>
$('.configurator-opt').on('change', function(e) {
if ($(this).attr('type') == 'radio' ||
$(this).attr('type') == 'checkbox' ||
$(this).attr('type') == 'input') {
return;
}
if ($(this).val() == "") {
return;
}
$('form[name=configurator-opt]').submit();
});
$('.configurator-opt').on('click', function(e){
if ($(this).attr('type') == 'text' ||
$(this).attr('type') == 'select') {
return;
}
if ($(this).val() == "") {
return;
}
$('form[name=configurator-opt]').submit();
})
$('form[name=configurator-opt]').on('submit', function(e) {
console.log($(this).serialize());
$.post($(this).attr('action'), $(this).serialize(), function (data) {
console.log(data);
$('span#our_price_display').replaceWith('<span id="our_price_display" class="price" itemprop="price" content="'+data.price+'">'+data.priceDisplay+'</span>');
}, 'json').fail(function(){ /* Display error message */ });
return false;
});
</script>
{/if}

View File

@ -0,0 +1 @@
No Config

View File

@ -384,6 +384,9 @@
</div> <!-- end attributes -->
{/if}
</div> <!-- end product_attributes -->
{hook h='displayProductForm' mod='antadisconfigurator'}
<div class="box-cart-bottom">
<div{if (!$allow_oosp && $product->quantity <= 0) || !$product->available_for_order || (isset($restricted_country_mode) && $restricted_country_mode) || $PS_CATALOG_MODE} class="unvisible"{/if}>
<p id="add_to_cart" class="buttons_bottom_block no-print">

View File

@ -32,6 +32,18 @@
<p class="product-name"><a href="{$link->getProductLink($product.id_product, $product.link_rewrite, $product.category, null, null, $product.id_shop, $product.id_product_attribute, false, false, true)|escape:'html':'UTF-8'}">{$product.name|escape:'html':'UTF-8'}</a></p>
{if $product.reference}<small class="cart_ref">{l s='SKU'}{$smarty.capture.default}{$product.reference|escape:'html':'UTF-8'}</small>{/if}
{if isset($product.attributes) && $product.attributes}<small><a href="{$link->getProductLink($product.id_product, $product.link_rewrite, $product.category, null, null, $product.id_shop, $product.id_product_attribute, false, false, true)|escape:'html':'UTF-8'}">{$product.attributes|@replace: $smarty.capture.sep:$smarty.capture.default|escape:'html':'UTF-8'}</a></small>{/if}
{if isset($product.configurator_opt)}
{foreach item=group from=$product.configurator_opt}
<small>
{$group['name']} :
{foreach item=i from=$group['value'] name=opt}
{$i} {if !$smarty.foreach.opt.last},{/if}
{/foreach}
</small>
{/foreach}
{/if}
</td>
{if $PS_STOCK_MANAGEMENT}
<td class="cart_avail"><span class="label{if $product.quantity_available <= 0 && isset($product.allow_oosp) && !$product.allow_oosp} label-danger{elseif $product.quantity_available <= 0} label-warning{else} label-success{/if}">{if $product.quantity_available <= 0}{if isset($product.allow_oosp) && $product.allow_oosp}{if isset($product.available_later) && $product.available_later}{$product.available_later}{else}{l s='In Stock'}{/if}{else}{l s='Out of stock'}{/if}{else}{if isset($product.available_now) && $product.available_now}{$product.available_now}{else}{l s='In Stock'}{/if}{/if}</span>{if !$product.is_virtual}{hook h="displayProductDeliveryTime" product=$product}{/if}</td>