Current path: home/webdevt/www/schtroumpf.fr/modules/an_productattributes/
?? Go up: /home/webdevt/www/schtroumpf.fr/modules
<?php
/**
* 2022 Anvanto
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
*
* @author Anvanto <anvantoco@gmail.com>
* @copyright 2022 Anvanto
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
*/
if (!defined('_PS_VERSION_')) {
exit;
}
class an_productattributes extends Module
{
const PREFIX = "an_pa_";
public static $defaulOutOfStock = [
'en' => 'Out of stock',
'fr' => 'Rupture de stock',
'es' => 'Fuera de stock',
'pl' => 'Obecnie brak na stanie',
'it' => 'Non disponibile',
'nl' => 'Niet op voorraad',
'de' => 'Nicht auf Lager'
];
public function __construct()
{
$this->name = 'an_productattributes';
$this->tab = 'front_office_features';
$this->version = '2.1.6';
$this->author = 'anvanto';
$this->need_instance = 0;
$this->ps_versions_compliancy = array('min' => '1.7', 'max' => _PS_VERSION_);
$this->bootstrap = true;
$this->module_key = '1bb121b65a00e449ce328f7e363687fe';
$this->addons_product_id = '44990';
$this->url_rate = 'https://bit.ly/3siRxJE';
$this->url_contact_us = 'http://bit.ly/2Xz7kVE';
parent::__construct();
$this->displayName = $this->l('Show attribute combinations in products list & Add to cart');
$this->description = $this->l('Display product combinations, discounts, availability and the «Add to cart» button — make users able to make a purchase and continue browsing the catalog. Configure view type and customize combination captions and labels.');
$this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
}
public function install()
{
Configuration::updateValue(self::PREFIX.'type_view', 'standart');
Configuration::updateValue(self::PREFIX.'color_type_view', 'color');
Configuration::updateValue(self::PREFIX.'display_add_to_cart', 1);
Configuration::updateValue(self::PREFIX.'display_quantity', 1);
Configuration::updateValue(self::PREFIX.'display_labels', 0);
Configuration::updateValue(self::PREFIX.'product_miniature', '.js-product-miniature');
Configuration::updateValue(self::PREFIX.'thumbnail_container', '.thumbnail-container');
Configuration::updateValue(self::PREFIX.'price', '.price');
Configuration::updateValue(self::PREFIX.'regular_price', '.regular-price');
Configuration::updateValue(self::PREFIX.'product_price_and_shipping', '.product-price-and-shipping');
Configuration::updateValue(self::PREFIX.'separator', ' / ');
Configuration::updateValue(self::PREFIX.'background_sold_out', '#ffc427');
Configuration::updateValue(self::PREFIX.'color_sold_out', '#ffffff');
Configuration::updateValue(self::PREFIX.'background_sale', '#e53d60');
Configuration::updateValue(self::PREFIX.'color_sale', '#ffffff');
Configuration::updateValue(self::PREFIX.'display_prices', 1);
$languages = Language::getLanguages(false);
$title_OutOfStock = [];
foreach ($languages as $lang) {
if (isset(self::$defaulOutOfStock[$lang['iso_code']])){
$title_OutOfStock[$lang['id_lang']] = self::$defaulOutOfStock[$lang['iso_code']];
} else {
$title_OutOfStock[$lang['id_lang']] = self::$defaulOutOfStock['en'];
}
}
Configuration::updateValue(self::PREFIX.'text_out_of_stock', $title_OutOfStock);
Configuration::updateValue(self::PREFIX.'txt_out_of_stock', $title_OutOfStock);
if (parent::install()) {
return $this->registerHook('displayHeader')
&& $this->registerHook('displayProductPriceBlock')
&& $this->registerHook('displayBackOfficeHeader');
}
return false;
}
public function uninstall()
{
$paramList = $this->getParamList();
$this->deleteParams($paramList);
return parent::uninstall();
}
public function getParamList()
{
return array(
'type_view',
'color_type_view',
'display_add_to_cart',
'display_quantity',
'display_labels',
'product_miniature',
'thumbnail_container',
'price',
'regular_price',
'product_price_and_shipping',
'separator',
'background_sold_out',
'color_sold_out',
'background_sale',
'color_sale',
'display_prices',
'text_out_of_stock',
'txt_out_of_stock',
);
}
public function getParam($key, $id_lang = 0)
{
return Configuration::get(self::PREFIX.$key, $id_lang);
}
protected function deleteParams($keys)
{
foreach ($keys as $key) {
$this->deleteParam($key);
}
}
protected function deleteParam($key)
{
return Configuration::deleteByName(self::PREFIX.$key);
}
protected function updateParam($key, $value)
{
return Configuration::updateValue(self::PREFIX.$key, $value);
}
public function getConfig(){
$config = array();
$paramList = $this->getParamList();
foreach ($paramList as $key) {
$config[$key] = $this->getParam($key);
}
$config['text_out_of_stock'] = Configuration::get(self::PREFIX.'txt_out_of_stock', $this->context->language->id);
if (!$config['text_out_of_stock'] && isset(self::$defaulOutOfStock[$this->context->language->iso_code])){
$config['text_out_of_stock'] = self::$defaulOutOfStock[$this->context->language->iso_code];
} else if (!$config['text_out_of_stock']) {
$config['text_out_of_stock'] = $this->l('Out of stock');
}
$config['txt_out_of_stock'] = $config['text_out_of_stock'];
return $config;
}
public function hookBackOfficeHeader($params)
{
$this->hookDisplayBackOfficeHeader($params);
}
public function hookDisplayBackOfficeHeader($params)
{
if (in_array($this->name, array(Tools::getValue('configure', ''), Tools::getValue('module_name', '')))) {
$this->context->controller->addJquery();
$this->context->controller->addJS($this->_path.'views/js/back.js');
}
}
public function hookDisplayHeader($params)
{
$config = array();
$paramList = $this->getParamList();
foreach ($paramList as $key) {
$config[$key] = $this->getParam($key);
}
$this->context->smarty->assign('config', json_encode($config));
$this->context->controller->addCSS($this->_path .'views/css/front.css');
$this->context->controller->addJS($this->_path .'views/js/front.js');
return $this->display(__FILE__, 'views/templates/front/js_header.tpl');
}
public function hookDisplayProductPriceBlock($params)
{
/*
$pageName = $this->context->controller->getPageName();
*/
if (($params['type'] == 'weight' || $params['type'] == 'overriding_prices') && !isset($params['hook_origin'])){
$productObj = new Product($params['product']['id_product'], false, (int)$this->context->language->id);
$product = $this->productPrepare($productObj);
}
if ($params['type'] == 'overriding_prices' && !isset($params['hook_origin'])){
$prices = $this->getPrices($productObj, $product);
$this->context->smarty->assign('prices', $prices);
return $this->display(__FILE__, 'views/templates/front/overriding-price.tpl');
}
if ($params['type'] == 'weight' && !isset($params['hook_origin'])){
if ($this->getParam('type_view') == 'select'){
// Select
$attributeCombinations = array();
$productData = array();
$attributeCombinations = $productObj->getAttributeCombinations((int)$this->context->language->id);
$productData = $this->attributeCombinationForSelect($attributeCombinations, $productObj, $product);
$attributeGroupsSelect = array();
foreach ($attributeCombinations as $item){
$attributeGroupsSelect[$item['id_product_attribute']][] = array(
'id_attribute_group' => $item['id_attribute_group'],
'id_attribute' => $item['id_attribute'],
);
}
$this->context->smarty->assign('attributeGroups', json_encode($attributeGroupsSelect));
$this->context->smarty->assign('productData', $productData);
} else {
// Standart
$product_full = Product::getProductProperties($this->context->language->id, $product);
$attributesGroups = $this->assignAttributesGroups($productObj, $product_full);
if (isset($attributesGroups['groups'])){
$this->context->smarty->assign('groups', $attributesGroups['groups']);
} else {
$this->context->smarty->assign('groups', array());
}
}
// Images
$combinationImages = $productObj->getCombinationImages($this->context->language->id);
$this->context->smarty->assign('combinationImages', $combinationImages);
if (isset($combinationImages[$product['id_product_attribute']]) ){
$coverPhoto = Context::getContext()->link->getImageLink(
$product['link_rewrite'],
$combinationImages[$product['id_product_attribute']]['0']['id_image'],
ImageType::getFormattedName('home')
);
$this->context->smarty->assign('coverPhoto', $coverPhoto);
}
$orderOutOfStock = (bool)Configuration::get('PS_STOCK_MANAGEMENT') && !Product::isAvailableWhenOutOfStock($product['out_of_stock']);
$this->context->smarty->assign(array(
'config' => $this->getConfig(),
'cart' => $this->context->link->getPageLink('cart'),
'token' => Tools::getToken(false),
'productId' => (int) $productObj->id,
'id_product_attribute' => $product['id_product_attribute'],
'product_link_rewrite' => $product['link_rewrite'],
'order_out_of_stock' => $orderOutOfStock,
'quantity' => $product['quantity'],
'quantity_wanted' => $product['quantity_wanted'],
'minimal_quantity' => $product['minimal_quantity'],
'availableForOrder' => intval($product['availableForOrder']),
));
return $this->display(__FILE__, 'views/templates/front/productattributes.tpl');
}
}
public function attributeCombinationForSelect($attributeCombinations, $productObj)
{
$productsAttr = array();
foreach ($attributeCombinations as $attributeCombination) {
$product = $this->productPrepare($productObj, $attributeCombination['id_product_attribute']);
$prices = $this->getPrices($productObj, $product);
$productsAttr[$attributeCombination['id_product_attribute']]['availableForOrder'] = $product['availableForOrder'];
$productsAttr[$attributeCombination['id_product_attribute']]['default_on'] = (int)$attributeCombination['default_on'];
$productsAttr[$attributeCombination['id_product_attribute']]['prices'] = $prices;
$productsAttr[$attributeCombination['id_product_attribute']]['comb'][] = $attributeCombination;
}
return $productsAttr;
}
public function getPrices($productObj, $product){
$prices = array();
$taxConfiguration = new TaxConfiguration();
if ($taxConfiguration->includeTaxes()) {
$price = $productObj->getPrice(true, $product['id_product_attribute'], 2);
$prices['regular_price_amount'] = $productObj->getPriceWithoutReduct(true, $product['id_product_attribute'], 2);
} else {
$price = $productObj->getPrice(false, $product['id_product_attribute'], 2);
$prices['regular_price_amount'] = $productObj->getPriceWithoutReduct(false, $product['id_product_attribute'], 2);
}
$productProperties = Product::getProductProperties($this->context->language->id, $product, $this->context);
if ($productProperties['specific_prices']) {
$prices['has_discount'] = (0 != $productProperties['reduction']);
$prices['discount_type'] = $productProperties['specific_prices']['reduction_type'];
$prices['discount_percentage'] = -round(100 * $productProperties['specific_prices']['reduction']).'%';
$prices['discount_percentage_absolute'] = round(100 * $productProperties['specific_prices']['reduction']).'%';
$prices['discount_amount'] = Tools::displayPrice($productProperties['reduction']);
$prices['regular_price'] = Tools::displayPrice($productProperties['price_without_reduction']);
}
$prices['price_amount'] = $price;
$prices['price'] = Tools::displayPrice($price);
return $prices;
}
public function productPrepare($productObj, $id_product_attribute = false, $ajax = false)
{
$productObj->loadStockData();
$product = (array)$productObj;
if ($ajax){
$product['id_product_attribute'] = $this->getIdProductAttributeByRequestOrGroup($productObj, $productObj->id);
}
if (!isset($product['id_product_attribute'])){
$product['id_product_attribute'] = false;
}
if ($id_product_attribute || (!$product['id_product_attribute'] && $id_product_attribute)){
$product['id_product_attribute'] = $id_product_attribute;
}
if (!$id_product_attribute && !$ajax ){
$product['id_product_attribute'] = Product::getDefaultAttribute($productObj->id);
}
$product['minimal_quantity'] = $this->getProductMinimalQuantity($productObj, $product);
$product['images'] = $productObj->getImages($this->context->language->id);
$product['id_product'] = (int) $productObj->id;
$product['out_of_stock'] = (int) $productObj->out_of_stock;
$product['new'] = (int) $productObj->new;
$product['id_category_default'] = '';
$product['link_rewrite'] = $this->checkLinkRewrite($productObj->link_rewrite);
$product['ean13'] = $productObj->ean13;
$product['price'] = $productObj->price;
$product['wholesale_price'] = $productObj->wholesale_price;
$product['unit_price_ratio'] = $productObj->unit_price_ratio;
$product['link'] = $this->context->link->getProductLink(
(int) $product['id_product'],
$product['link_rewrite'],
null,
$product['ean13'],
$this->context->language->id,
null,
$product['id_product_attribute']
);
$product['quantity'] = Product::getQuantity(
(int) $productObj->id,
(int) $product['id_product_attribute'],
isset($product['cache_is_pack']) ? $product['cache_is_pack'] : null
);
// $product['minimal_quantity'] = $this->getProductMinimalQuantity($productObj, $product);
$product['quantity_wanted'] = $this->getRequiredQuantity($productObj, $product);
$product['availableForOrder'] = $this->isAvailableForOrder($product);
return $product;
}
public function isAvailableForOrder($product)
{
$availableForOrder = true;
// If Enable stock management = off and Some Produc -> Options -> Available for order -> off
if (!(bool)Configuration::get('PS_STOCK_MANAGEMENT') && !$product['available_for_order']){
return false;
}
if ((bool)Configuration::get('PS_STOCK_MANAGEMENT') &&
!an_productattributes::isAvailableWhenOutOfStock($product['out_of_stock']) &&
isset($product['quantity_wanted']) && ($product['quantity'] <= 0 || $product['quantity'] < $product['quantity_wanted'])
){
$availableForOrder = false;
}
//customizable
// Учесть вариант, когда костомизация заполнена
$customization_required = false;
if (isset($product['customizable']) && $product['customizable'] && Customization::isFeatureActive()) {
if (count(Product::getRequiredCustomizableFieldsStatic((int)$product['id_product']))){
$customization_required = true;
}
}
if ($customization_required){
$availableForOrder = false;
}
return $availableForOrder;
}
public static function isAvailableWhenOutOfStock($out_of_stock)
{
/** @TODO 1.5.0 Update of STOCK_MANAGEMENT & ORDER_OUT_OF_STOCK */
$ps_stock_management = Configuration::get('PS_STOCK_MANAGEMENT');
if (!$ps_stock_management) {
return true;
}
$ps_order_out_of_stock = Configuration::get('PS_ORDER_OUT_OF_STOCK');
return (int) $out_of_stock == 2 ? (int) $ps_order_out_of_stock : (int) $out_of_stock;
}
/**
* Assign template vars related to attribute groups and colors.
*/
public function assignAttributesGroups($product, $product_for_template = null)
{
$colors = [];
$groups = [];
$this->combinations = [];
// @todo (RM) should only get groups and not all declination ?
$attributes_groups = $product->getAttributesGroups($this->context->language->id);
if (is_array($attributes_groups) && $attributes_groups) {
$combination_images = $product->getCombinationImages($this->context->language->id);
$combination_prices_set = array();
foreach ($attributes_groups as $k => $row) {
// Color management
if (isset($row['is_color_group']) && $row['is_color_group'] && (isset($row['attribute_color']) && $row['attribute_color']) || (file_exists(_PS_COL_IMG_DIR_ . $row['id_attribute'] . '.jpg'))) {
$colors[$row['id_attribute']]['value'] = $row['attribute_color'];
$colors[$row['id_attribute']]['name'] = $row['attribute_name'];
if (!isset($colors[$row['id_attribute']]['attributes_quantity'])) {
$colors[$row['id_attribute']]['attributes_quantity'] = 0;
}
$colors[$row['id_attribute']]['attributes_quantity'] += (int) $row['quantity'];
}
if (!isset($groups[$row['id_attribute_group']])) {
$groups[$row['id_attribute_group']] = array(
'group_name' => $row['group_name'],
'name' => $row['public_group_name'],
'group_type' => $row['group_type'],
'default' => -1,
);
}
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']] = array(
'name' => $row['attribute_name'],
'html_color_code' => $row['attribute_color'],
'texture' => (@filemtime(_PS_COL_IMG_DIR_ . $row['id_attribute'] . '.jpg')) ? _THEME_COL_DIR_ . $row['id_attribute'] . '.jpg' : '',
'selected' => (isset($product_for_template['attributes'][$row['id_attribute_group']]['id_attribute']) && $product_for_template['attributes'][$row['id_attribute_group']]['id_attribute'] == $row['id_attribute']) ? true : false,
'id_product_attribute' => $row['id_product_attribute'],
);
if (!isset($combination_images[$row['id_product_attribute']][0]['id_image'])) {
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['id_image'] = -1;
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['legend'] = '';
} else {
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['id_image'] = (int) $combination_images[$row['id_product_attribute']][0]['id_image'];
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['legend'] = $combination_images[$row['id_product_attribute']][0]['legend'];
}
$groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['img_url'] = $this->context->link->getImageLink(
$product->link_rewrite, $groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']]['id_image'], 'small_default'
);
//$product.attributes.$id_attribute_group.id_attribute eq $id_attribute
if ($row['default_on'] && $groups[$row['id_attribute_group']]['default'] == -1) {
$groups[$row['id_attribute_group']]['default'] = (int) $row['id_attribute'];
}
if (!isset($groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']])) {
$groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] = 0;
}
$groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] += (int) $row['quantity'];
$this->combinations[$row['id_product_attribute']]['attributes_values'][$row['id_attribute_group']] = $row['attribute_name'];
$this->combinations[$row['id_product_attribute']]['attributes'][] = (int) $row['id_attribute'];
$this->combinations[$row['id_product_attribute']]['price'] = (float) $row['price'];
// Call getPriceStatic in order to set $combination_specific_price
if (!isset($combination_prices_set[(int) $row['id_product_attribute']])) {
$combination_specific_price = null;
Product::getPriceStatic((int) $product->id, false, $row['id_product_attribute'], 6, null, false, true, 1, false, null, null, null, $combination_specific_price);
$combination_prices_set[(int) $row['id_product_attribute']] = true;
$this->combinations[$row['id_product_attribute']]['specific_price'] = $combination_specific_price;
}
$this->combinations[$row['id_product_attribute']]['ecotax'] = (float) $row['ecotax'];
$this->combinations[$row['id_product_attribute']]['weight'] = (float) $row['weight'];
$this->combinations[$row['id_product_attribute']]['quantity'] = (int) $row['quantity'];
$this->combinations[$row['id_product_attribute']]['reference'] = $row['reference'];
$this->combinations[$row['id_product_attribute']]['unit_impact'] = $row['unit_price_impact'];
$this->combinations[$row['id_product_attribute']]['minimal_quantity'] = $row['minimal_quantity'];
if ($row['available_date'] != '0000-00-00' && Validate::isDate($row['available_date'])) {
$this->combinations[$row['id_product_attribute']]['available_date'] = $row['available_date'];
$this->combinations[$row['id_product_attribute']]['date_formatted'] = Tools::displayDate($row['available_date']);
} else {
$this->combinations[$row['id_product_attribute']]['available_date'] = $this->combinations[$row['id_product_attribute']]['date_formatted'] = '';
}
if (!isset($combination_images[$row['id_product_attribute']][0]['id_image'])) {
$this->combinations[$row['id_product_attribute']]['id_image'] = -1;
} else {
$this->combinations[$row['id_product_attribute']]['id_image'] = (int) $combination_images[$row['id_product_attribute']][0]['id_image'];
}
}
// wash attributes list depending on available attributes depending on selected preceding attributes
$current_selected_attributes = array();
$count = 0;
foreach ($groups as &$group) {
++$count;
if ($count > 1) {
//find attributes of current group, having a possible combination with current selected
$id_product_attributes = array(0);
$query = 'SELECT pac.`id_product_attribute`
FROM `' . _DB_PREFIX_ . 'product_attribute_combination` pac
INNER JOIN `' . _DB_PREFIX_ . 'product_attribute` pa ON pa.id_product_attribute = pac.id_product_attribute
WHERE id_product = ' . (int)$product->id . ' AND id_attribute IN (' . implode(',', array_map('intval', $current_selected_attributes)) . ')
GROUP BY id_product_attribute
HAVING COUNT(id_product) = ' . count($current_selected_attributes);
if ($results = Db::getInstance()->executeS($query)) {
foreach ($results as $row) {
$id_product_attributes[] = $row['id_product_attribute'];
}
}
/* $id_attributes = Db::getInstance()->executeS('SELECT `id_attribute` FROM `' . _DB_PREFIX_ . 'product_attribute_combination` pac2
WHERE `id_product_attribute` IN (' . implode(',', array_map('intval', $id_product_attributes)) . ')
AND id_attribute NOT IN (' . implode(',', array_map('intval', $current_selected_attributes)) . ')'); */
$id_attributes = Db::getInstance()->executeS('SELECT pac2.`id_attribute` FROM `' . _DB_PREFIX_ . 'product_attribute_combination` pac2' .
((!Product::isAvailableWhenOutOfStock($product->out_of_stock) && 0 == Configuration::get('PS_DISP_UNAVAILABLE_ATTR')) ?
' INNER JOIN `' . _DB_PREFIX_ . 'stock_available` pa ON pa.id_product_attribute = pac2.id_product_attribute
WHERE pa.quantity > 0 AND ' :
' WHERE ') .
'pac2.`id_product_attribute` IN (' . implode(',', array_map('intval', $id_product_attributes)) . ')
AND pac2.id_attribute NOT IN (' . implode(',', array_map('intval', $current_selected_attributes)) . ')');
foreach ($id_attributes as $k => $row) {
$id_attributes[$k] = (int) $row['id_attribute'];
}
foreach ($group['attributes'] as $key => $attribute) {
if (!in_array((int) $key, $id_attributes)) {
unset($group['attributes'][$key]);
unset($group['attributes_quantity'][$key]);
}
}
}
//find selected attribute or first of group
$index = 0;
$current_selected_attribute = 0;
foreach ($group['attributes'] as $key => $attribute) {
if ($index === 0) {
$current_selected_attribute = $key;
}
if ($attribute['selected']) {
$current_selected_attribute = $key;
break;
}
}
if ($current_selected_attribute > 0) {
$current_selected_attributes[] = $current_selected_attribute;
}
}
// wash attributes list (if some attributes are unavailables and if allowed to wash it)
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && Configuration::get('PS_DISP_UNAVAILABLE_ATTR') == 0) {
foreach ($groups as &$group) {
// foreach ($group['attributes_quantity'] as $key => &$quantity) {
foreach ($group['attributes_quantity'] as $key => $quantity) {
if ($quantity <= 0) {
unset($group['attributes'][$key]);
}
}
}
foreach ($colors as $key => $color) {
if ($color['attributes_quantity'] <= 0) {
unset($colors[$key]);
}
}
}
foreach ($this->combinations as $id_product_attribute => $comb) {
$attribute_list = '';
foreach ($comb['attributes'] as $id_attribute) {
$attribute_list .= '\'' . (int) $id_attribute . '\',';
}
$attribute_list = rtrim($attribute_list, ',');
$this->combinations[$id_product_attribute]['list'] = $attribute_list;
}
unset($group);
$return = [];
$return['groups'] = $groups;
$return['combinations'] = $this->combinations;
$return['combination_images'] = $combination_images;
return $return;
} else {
return [];
}
}
private function getIdProductAttributeByGroup($id_product)
{
$groups = Tools::getValue('group');
if (empty($groups)) {
return null;
}
return (int) $this->getIdProductAttributeByIdAttributes(
$id_product,
$groups,
true
);
}
public static function getIdProductAttributeByIdAttributes($idProduct, $idAttributes, $findBest = false)
{
$idProduct = (int) $idProduct;
// var_dump($idAttributes);
if (!is_array($idAttributes) && is_numeric($idAttributes)) {
$idAttributes = array((int) $idAttributes);
}
if (!is_array($idAttributes) || empty($idAttributes)) {
throw new PrestaShopException(
sprintf(
'Invalid parameter $idAttributes with value: "%s"',
print_r($idAttributes, true)
)
);
}
$idAttributesImploded = implode(',', array_map('intval', $idAttributes));
$idProductAttribute = Db::getInstance()->getValue('
SELECT
pac.`id_product_attribute`
FROM
`' . _DB_PREFIX_ . 'product_attribute_combination` pac
INNER JOIN `' . _DB_PREFIX_ . 'product_attribute` pa ON pa.id_product_attribute = pac.id_product_attribute
WHERE
pa.id_product = ' . (int)$idProduct . '
AND pac.id_attribute IN (' .pSQL($idAttributesImploded) . ')
GROUP BY
pac.`id_product_attribute`
HAVING
COUNT(pa.id_product) = ' . count($idAttributes)
);
if ($idProductAttribute === false && $findBest) {
//find the best possible combination
//first we order $idAttributes by the group position
$orderred = array();
$result = Db::getInstance()->executeS('
SELECT
a.`id_attribute`
FROM
`' . _DB_PREFIX_ . 'attribute` a
INNER JOIN `' . _DB_PREFIX_ . 'attribute_group` g ON a.`id_attribute_group` = g.`id_attribute_group`
WHERE
a.`id_attribute` IN (' .pSQL($idAttributesImploded) . ')
ORDER BY
g.`position` ASC'
);
foreach ($result as $row) {
$orderred[] = $row['id_attribute'];
}
while ($idProductAttribute === false && count($orderred) > 0) {
array_pop($orderred);
$idProductAttribute = Db::getInstance()->getValue('
SELECT
pac.`id_product_attribute`
FROM
`' . _DB_PREFIX_ . 'product_attribute_combination` pac
INNER JOIN `' . _DB_PREFIX_ . 'product_attribute` pa ON pa.id_product_attribute = pac.id_product_attribute
WHERE
pa.id_product = ' . (int) $idProduct . '
AND pac.id_attribute IN (' . implode(',', array_map('intval', $orderred)) . ')
GROUP BY
pac.id_product_attribute
HAVING
COUNT(pa.id_product) = ' . count($orderred)
);
}
}
if (empty($idProductAttribute)) {
throw new PrestaShopObjectNotFoundException('Can not retrieve the id_product_attribute for product id ' . $idProduct);
}
return $idProductAttribute;
}
public function getIdProductAttributeByRequestOrGroup($product, $id_product)
{
$requestedIdProductAttribute = (int) Tools::getValue('id_product_attribute');
$groupIdProductAttribute = $this->getIdProductAttributeByGroup($id_product);
$requestedIdProductAttribute = null !== $groupIdProductAttribute ? $groupIdProductAttribute : $requestedIdProductAttribute;
return $this->tryToGetAvailableIdProductAttribute($product, $requestedIdProductAttribute);
}
public function tryToGetAvailableIdProductAttribute($product, $checkedIdProductAttribute)
{
if (!Configuration::get('PS_DISP_UNAVAILABLE_ATTR')) {
$productCombinations = $product->getAttributeCombinations();
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock)) {
$availableProductAttributes = array_filter(
$productCombinations,
function ($elem) {
return $elem['quantity'] > 0;
}
);
} else {
$availableProductAttributes = $productCombinations;
}
$availableProductAttribute = array_filter(
$availableProductAttributes,
function ($elem) use ($checkedIdProductAttribute) {
return $elem['id_product_attribute'] == $checkedIdProductAttribute;
}
);
if (empty($availableProductAttribute) && count($availableProductAttributes)) {
// if selected combination is NOT available ($availableProductAttribute) but they are other alternatives ($availableProductAttributes), then we'll try to get the closest.
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock)) {
// first lets get information of the selected combination.
$checkProductAttribute = array_filter(
$productCombinations,
function ($elem) use ($checkedIdProductAttribute) {
return $elem['id_product_attribute'] == $checkedIdProductAttribute || (!$checkedIdProductAttribute && $elem['default_on']);
}
);
if (count($checkProductAttribute)) {
// now lets find other combinations for the selected attributes.
$alternativeProductAttribute = [];
foreach ($checkProductAttribute as $key => $attribute) {
$alternativeAttribute = array_filter(
$availableProductAttributes,
function ($elem) use ($attribute) {
return $elem['id_attribute'] == $attribute['id_attribute'] && !$elem['is_color_group'];
}
);
foreach ($alternativeAttribute as $key => $value) {
$alternativeProductAttribute[$key] = $value;
}
}
if (count($alternativeProductAttribute)) {
// if alternative combination is found, order the list by quantity to use the one with more stock.
usort($alternativeProductAttribute, function ($a, $b) {
if ($a['quantity'] == $b['quantity']) {
return 0;
}
return ($a['quantity'] > $b['quantity']) ? -1 : 1;
});
return (int) array_shift($alternativeProductAttribute)['id_product_attribute'];
}
}
}
return (int) array_shift($availableProductAttributes)['id_product_attribute'];
}
}
return $checkedIdProductAttribute;
}
public function findProductCombinationById($product, $combinationId)
{
$foundCombination = null;
$combinations = $product->getAttributesGroups($this->context->language->id);
foreach ($combinations as $combination) {
if ((int) ($combination['id_product_attribute']) === $combinationId) {
$foundCombination = $combination;
break;
}
}
return $foundCombination;
}
public function getProductMinimalQuantity($product, $productArray)
{
$minimal_quantity = 1;
if (isset($productArray['id_product_attribute']) && $productArray['id_product_attribute']) {
$combination = $this->findProductCombinationById($product, $productArray['id_product_attribute']);
if (isset($combination['minimal_quantity']) && $combination['minimal_quantity']) {
$minimal_quantity = $combination['minimal_quantity'];
}
} else {
$minimal_quantity = $product->minimal_quantity;
}
return $minimal_quantity;
}
/**
* @param $product
*
* @return int
*/
public function getRequiredQuantity($product, $productArray)
{
$requiredQuantity = (int) Tools::getValue('quantity_wanted', $this->getProductMinimalQuantity($product, $productArray));
if ($requiredQuantity < $productArray['minimal_quantity']) {
$requiredQuantity = $productArray['minimal_quantity'];
}
return $requiredQuantity;
}
public function getAttributesGroups($id_lang, $id_product, $groupIdProductAttribute = '')
{
if (!Combination::isFeatureActive()) {
return array();
}
$filterByGroupIdProductAttribute = '';
if ($groupIdProductAttribute != ''){
$filterByGroupIdProductAttribute = 'AND pac.`id_product_attribute` = ' . (int) $groupIdProductAttribute . '';
}
$sql = 'SELECT ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, agl.`public_name` AS public_group_name,
a.`id_attribute`, al.`name` AS attribute_name, a.`color` AS attribute_color, product_attribute_shop.`id_product_attribute`,
IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`price`, product_attribute_shop.`ecotax`, product_attribute_shop.`weight`,
product_attribute_shop.`default_on`, pa.`reference`, product_attribute_shop.`unit_price_impact`,
product_attribute_shop.`minimal_quantity`, product_attribute_shop.`available_date`, ag.`group_type`
FROM `' . _DB_PREFIX_ . 'product_attribute` pa
' . Shop::addSqlAssociation('product_attribute', 'pa') . '
' . Product::sqlStock('pa', 'pa') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_combination` pac ON (pac.`id_product_attribute` = pa.`id_product_attribute`)
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a ON (a.`id_attribute` = pac.`id_attribute`)
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group` ag ON (ag.`id_attribute_group` = a.`id_attribute_group`)
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute`)
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group`)
' . Shop::addSqlAssociation('attribute', 'a') . '
WHERE pa.`id_product` = ' . (int) $id_product . '
AND al.`id_lang` = ' . (int) $id_lang . '
AND agl.`id_lang` = ' . (int) $id_lang . '
' . pSQL($filterByGroupIdProductAttribute) . '
GROUP BY id_attribute_group, id_product_attribute
ORDER BY ag.`position` ASC, a.`position` ASC, agl.`name` ASC';
return Db::getInstance()->executeS($sql);
}
/**
* Allow to check if $link_rewrite is an array or not and only return a valid value
*
* @param array|string $link_rewrite
*
* @return string
*/
public function checkLinkRewrite($link_rewrite)
{
$link_rewrite = $link_rewrite;
if (is_array($link_rewrite)) {
$filteredArray = array_filter($link_rewrite);
$link_rewrite = current($filteredArray);
}
return $link_rewrite;
}
public function checkPage(){
$pageName = $this->context->controller->getPageName();
}
/// CONTENT
public function getContent()
{
$output = null;
if (Tools::isSubmit('submit'.$this->name)) {
$output = $this->getSubmitOutput();
}
$this->context->smarty->assign('theme', $this->getThemeInfo());
return $this->display(__FILE__, 'views/templates/admin/config_top.tpl').$output.$this->displayForm();
}
protected function getSubmitOutput()
{
$params = array();
$paramList = $this->getParamList();
foreach ($paramList as $key) {
$params[$key] = Tools::getValue(self::PREFIX.$key);
}
if (Tools::getValue('an_pa_type_view') == 'select' && !$this->isReceivedParamsValid($params)) {
return $this->displayError($this->l('Invalid Configuration value'));
}
$languages = Language::getLanguages(false);
foreach ($paramList as $key) {
if ($key == 'txt_out_of_stock'){
$value = [];
foreach ($languages as $language){
$value[$language['id_lang']] = Tools::getValue(self::PREFIX.'txt_out_of_stock_' . $language['id_lang']);
}
Configuration::updateValue(self::PREFIX.'txt_out_of_stock', $value);
} else {
$this->updateParam($key, $params[$key]);
}
}
return $this->displayConfirmation($this->l('Settings updated'));
}
public function displayForm()
{
$default_lang = (int)Configuration::get('PS_LANG_DEFAULT');
$fields_form = array();
$fields_form[0]['form'] = array(
'legend' => array(
'title' => $this->l('Settings'),
),
'input' => array(
array(
'type' => 'radio',
'label' => 'View type',
'name' => self::PREFIX.'type_view',
'class' => 'an-pa-type-view',
'values' => array(
array(
'id' => 'type_standart',
'value' => 'standart',
'label' => 'Standard'
),
array(
'id' => 'type_select',
'value' => 'select',
'label' => 'Select'
),
array(
'id' => 'type_none',
'value' => 'None',
'label' => 'None'
)
)
),
array(
'type' => 'radio',
'label' => 'Color view type',
'name' => self::PREFIX.'color_type_view',
'class' => 'an-pa-type-view-standart',
'values' => array(
array(
'id' => 'color_type_view_color',
'value' => 'color',
'label' => 'Color'
),
array(
'id' => 'color_type_view_image',
'value' => 'image',
'label' => 'Product photo (if the combination does not have a photo, it will have a color or texture)'
),
array(
'id' => 'color_type_view_only_image',
'value' => 'only_image',
'label' => 'Only product photo'
),
)
),
array(
'type' => 'text',
'label' => 'Separator',
'name' => self::PREFIX.'separator',
'values' => ' / ',
'col' => '5',
'class' => 'an-pa-type-select',
),
array(
'type' => 'color',
'label' => '«Sold Out» background',
'name' => self::PREFIX.'background_sold_out',
'values' => '#ffc427',
'class' => 'an-pa-type-select',
),
array(
'type' => 'color',
'label' => '«Sold Out» color',
'name' => self::PREFIX.'color_sold_out',
'values' => '#ffffff',
'class' => 'an-pa-type-select',
),
array(
'type' => 'color',
'label' => '«Sale» background',
'name' => self::PREFIX.'background_sale',
'values' => '#e53d60',
'class' => 'an-pa-type-select',
),
array(
'type' => 'color',
'label' => '«Sale» color',
'name' => self::PREFIX.'color_sale',
'values' => '#ffffff',
'class' => 'an-pa-type-select',
),
array(
'type' => 'switch',
'label' => $this->l('Display combination price'),
'name' => self::PREFIX.'display_prices',
'is_bool' => true,
'class' => 'an-pa-type-select',
'values' => array(
array(
'id' => 'is_enabled_on',
'value' => 1
),
array(
'id' => 'is_enabled_off',
'value' => 0
)
),
),
array(
'type' => 'switch',
'label' => $this->l('Display attribute public name'),
'name' => self::PREFIX.'display_labels',
'is_bool' => true,
'values' => array(
array(
'id' => 'is_enabled_on',
'value' => 1
),
array(
'id' => 'is_enabled_off',
'value' => 0
)
),
),
array(
'type' => 'switch',
'label' => $this->l('Display «Add to cart»'),
'name' => self::PREFIX.'display_add_to_cart',
'is_bool' => true,
'class' => 'an-pa-display_add_to_cart',
'values' => array(
array(
'id' => 'is_enabled_on',
'value' => 1
),
array(
'id' => 'is_enabled_off',
'value' => 0
)
),
),
array(
'type' => 'switch',
'label' => $this->l('Display quantity'),
'name' => self::PREFIX.'display_quantity',
'is_bool' => true,
'values' => array(
array(
'id' => 'is_enabled_on',
'value' => 1
),
array(
'id' => 'is_enabled_off',
'value' => 0
)
),
),
array(
'type' => 'text',
'label' => 'Text for "Out of stock"',
'name' => self::PREFIX.'txt_out_of_stock',
'lang' => true,
'col' => '5',
),
),
'submit' => array(
'title' => $this->l('Save'),
)
);
$fields_form[1]['form'] = array(
'legend' => array(
'title' => $this->l('Advanced options'),
),
'input' => array(
array(
'type' => 'text',
'label' => 'Product miniature class',
'name' => self::PREFIX.'product_miniature',
'values' => '.js-product-miniature',
'col' => '5',
),
array(
'type' => 'text',
'label' => 'Thumbnail-container class',
'name' => self::PREFIX.'thumbnail_container',
'values' => '.thumbnail-container',
'col' => '5',
),
array(
'type' => 'text',
'label' => 'Product-price-and-shipping class',
'name' => self::PREFIX.'product_price_and_shipping',
'values' => '.product-price-and-shipping',
'col' => '5',
),
array(
'type' => 'text',
'label' => 'Price class',
'name' => self::PREFIX.'price',
'values' => '.price',
'col' => '5',
),
array(
'type' => 'text',
'label' => 'Regular-price class',
'name' => self::PREFIX.'regular_price',
'values' => '.regular-price',
'col' => '5',
),
),
'submit' => array(
'title' => $this->l('Save'),
)
);
$helper = new HelperForm();
$helper->module = $this;
$helper->name_controller = $this->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;
$helper->default_form_language = $default_lang;
$helper->allow_employee_form_lang = $default_lang;
$helper->title = $this->displayName;
$helper->show_toolbar = true;
$helper->toolbar_scroll = true;
$helper->submit_action = 'submit'.$this->name;
$languages = $this->context->controller->getLanguages();
$helper->tpl_vars = [
'uri' => $this->getPathUri(),
'languages' => $languages,
'id_language' => $this->context->language->id
];
$helper->toolbar_btn = array(
'save' =>
array(
'desc' => $this->l('Save'),
'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
'&token='.Tools::getAdminTokenLite('AdminModules'),
),
'back' => array(
'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
'desc' => $this->l('Back to list')
)
);
$paramList = $this->getParamList();
foreach ($paramList as $key) {
if ($key == 'txt_out_of_stock'){
$value = [];
foreach ($languages as $language){
$value[$language['id_lang']] = Configuration::get(self::PREFIX.'txt_out_of_stock', $language['id_lang']);
}
$helper->fields_value[self::PREFIX.$key] = $value;
} else {
$helper->fields_value[self::PREFIX.$key] = $this->getParam($key);
}
}
return $helper->generateForm($fields_form);
}
protected function isReceivedParamsValid($params)
{
return Validate::isString($params['type_view'])
&& $this->isColor($params['background_sold_out'])
&& $this->isColor($params['color_sold_out'])
&& $this->isColor($params['background_sale'])
&& $this->isColor($params['color_sale']);
}
protected function isColor($string){
$string = htmlspecialchars($string);
$customvalarray = str_split('#0123456789aAbBcCdDeEfF');
foreach (str_split($string) as $val) {
if (!in_array($val, $customvalarray)) {
return false;
}
}
return true;
}
public function getThemeInfo()
{
$theme = [];
$themeFileJson = _PS_THEME_DIR_.'/config/theme.json';
if (Tools::file_exists_no_cache($themeFileJson)) {
$theme = (array)json_decode(Tools::file_get_contents($themeFileJson), 1);
}
if (!isset($theme['url_contact_us']) || $theme['url_contact_us'] == ''){
$urlContactUs = 'https://addons.prestashop.com/contact-form.php';
if (isset($theme['addons_id']) && $theme['addons_id'] != ''){
$urlContactUs .= '?id_product=' .$theme['addons_id'];
} elseif (isset($this->url_contact_us) && $this->url_contact_us != ''){
$urlContactUs = $this->url_contact_us;
} elseif (isset($this->addons_product_id) && $this->addons_product_id != ''){
$urlContactUs .= '?id_product=' .$this->addons_product_id;
}
$theme['url_contact_us'] = $urlContactUs;
}
if (!isset($theme['url_rate']) || $theme['url_rate'] == ''){
$urlRate = 'https://addons.prestashop.com/ratings.php';
if (isset($theme['addons_id']) && $theme['addons_id'] != ''){
$urlRate .= '?id_product=' .$theme['addons_id'];
} elseif (isset($this->url_rate) && $this->url_rate != ''){
$urlRate = $this->url_rate;
} elseif (isset($this->addons_product_id) && $this->addons_product_id != ''){
$urlRate .= '?id_product=' .$this->addons_product_id;
}
$theme['url_rate'] = $urlRate;
}
return $theme;
}
}