981 lines
38 KiB
PHP
981 lines
38 KiB
PHP
<?php
|
||
/**
|
||
*
|
||
* NOTICE OF LICENSE
|
||
*
|
||
* This source file is subject to the Apache License, Version 2.0
|
||
* that is bundled with this package in the file LICENSE.
|
||
* It is also available through the world-wide-web at this URL:
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* @author presta.studio
|
||
* @copyright Copyright (c) 2023 presta.studio
|
||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||
*/
|
||
|
||
if (!defined('_PS_VERSION_')) {
|
||
exit;
|
||
}
|
||
|
||
use Omnibus\Compatibility\PrestashopCompatibility;
|
||
|
||
class OmnibusEuFree extends Module
|
||
{
|
||
protected $config_form = false;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->name = 'omnibuseufree';
|
||
$this->tab = 'pricing_promotion';
|
||
$this->version = '1.0.2';
|
||
$this->author = 'presta.studio';
|
||
$this->need_instance = 1;
|
||
$this->bootstrap = true;
|
||
|
||
parent::__construct();
|
||
|
||
$this->displayName = $this->l('Omnibus Directive');
|
||
$this->description = $this->l('This module will help you meet the requirements of the EU Omnibus Directive in your store.');
|
||
|
||
$this->confirmUninstall = $this->l('Are you sure you want to uninstall this module? The price history will be deleted.');
|
||
|
||
$this->ps_versions_compliancy = array('min' => '1.7', 'max' => _PS_VERSION_);
|
||
}
|
||
|
||
public function install()
|
||
{
|
||
Configuration::updateValue('OMNIBUSEUFREE_INFORMATION_VERSION', 2);
|
||
Configuration::updateValue('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK', 1);
|
||
Configuration::updateValue('OMNIBUSEUFREE_CRON_STATUS', 2);
|
||
Configuration::updateValue('OMNIBUSEUFREE_DAYS', 30);
|
||
Configuration::updateValue('OMNIBUSEUFREE_HISTORY_DAYS', 60);
|
||
|
||
include(dirname(__FILE__) . '/sql/install.php');
|
||
|
||
return parent::install() &&
|
||
$this->registerHook('header') &&
|
||
$this->registerHook('displayBackOfficeHeader') &&
|
||
$this->registerHook('actionProductSave') &&
|
||
$this->registerHook('actionProductAttributeUpdate') &&
|
||
$this->registerHook('displayOmnibusEuFree') &&
|
||
$this->registerHook('displayProductPriceBlock') &&
|
||
$this->registerHook('displayAdminProductsExtra') &&
|
||
$this->installTab();
|
||
}
|
||
|
||
public function uninstall()
|
||
{
|
||
Configuration::deleteByName('OMNIBUSEUFREE_INFORMATION_VERSION');
|
||
Configuration::deleteByName('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK');
|
||
Configuration::deleteByName('OMNIBUSEUFREE_CRON_STATUS');
|
||
Configuration::deleteByName('OMNIBUSEUFREE_DAYS');
|
||
Configuration::deleteByName('OMNIBUSEUFREE_HISTORY_DAYS');
|
||
|
||
include(dirname(__FILE__) . '/sql/uninstall.php');
|
||
|
||
return parent::uninstall() &&
|
||
$this->uninstallTab();
|
||
}
|
||
|
||
private function installTab()
|
||
{
|
||
$tabId = (int) Tab::getIdFromClassName('OmnibusEuFreeController');
|
||
if (!$tabId) {
|
||
$tabId = null;
|
||
}
|
||
|
||
$tab = new Tab($tabId);
|
||
$tab->active = 1;
|
||
$tab->class_name = 'OmnibusEuFreeController';
|
||
$tab->route_name = 'admin_link_config';
|
||
$tab->icon = 'euro_symbol';
|
||
$tab->name = array();
|
||
foreach (Language::getLanguages() as $lang) {
|
||
$tab->name[$lang['id_lang']] = $this->l('Omnibus Directive');
|
||
}
|
||
$tab->id_parent = (int) Tab::getIdFromClassName('IMPROVE');
|
||
$tab->module = $this->name;
|
||
|
||
return $tab->save();
|
||
}
|
||
|
||
private function uninstallTab()
|
||
{
|
||
$tabId = (int) Tab::getIdFromClassName('OmnibusEuFreeController');
|
||
if (!$tabId) {
|
||
return true;
|
||
}
|
||
|
||
$tab = new Tab($tabId);
|
||
|
||
return $tab->delete();
|
||
}
|
||
|
||
protected function getLastMinimalPrice($id_product = null, $id_product_attribute = 0, $currency = null, $hasDiscountNow = null)
|
||
{
|
||
if (!isset($id_product)) {
|
||
throw new Exception('Missing parameter: $id_product');
|
||
}
|
||
|
||
if (!isset($currency)) {
|
||
$defaultCurrency = Currency::getDefaultCurrency();
|
||
$currency = (int)$defaultCurrency->id;
|
||
}
|
||
|
||
$info_version = (int) Configuration::get('OMNIBUSEUFREE_INFORMATION_VERSION', null, null, null, 2);
|
||
$NumberOfDays = (int) Configuration::get('OMNIBUSEUFREE_DAYS', null, null, null, 30);
|
||
|
||
// Jeżeli v2 ma sens tylko przy rabacie - jeśli nie ma rabatu, nie pokazuj
|
||
if ($info_version === 2) {
|
||
if ($hasDiscountNow === null) {
|
||
// pewne sprawdzenie: porównaj regular vs sale w tej walucie
|
||
$snap = $this->getPricesSnapshot((int)$id_product, (int)$id_product_attribute, (int)$currency);
|
||
$hasDiscountNow = (bool)$snap['has_discount'];
|
||
}
|
||
|
||
if (!$hasDiscountNow) {
|
||
return [];
|
||
}
|
||
|
||
// 1) data ostatniego wpisu (bieżący stan)
|
||
$sqlLast = new DbQuery();
|
||
$sqlLast->select('date_add');
|
||
$sqlLast->from('omnibus_eu_free');
|
||
$sqlLast->where('id_product = ' . (int)$id_product);
|
||
$sqlLast->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlLast->where('id_currency = ' . (int)$currency);
|
||
$sqlLast->where('is_default_currency = 1');
|
||
$sqlLast->where('is_last = 1');
|
||
$lastRow = Db::getInstance()->getRow($sqlLast);
|
||
|
||
if (empty($lastRow['date_add'])) {
|
||
return [];
|
||
}
|
||
|
||
$lastDate = $lastRow['date_add'];
|
||
|
||
// 2) znajdź ostatni moment przed lastDate, kiedy rabatu NIE było (has_discount=0)
|
||
$sqlPrevNoDisc = new DbQuery();
|
||
$sqlPrevNoDisc->select('MAX(date_add) AS prev_no_disc');
|
||
$sqlPrevNoDisc->from('omnibus_eu_free');
|
||
$sqlPrevNoDisc->where('id_product = ' . (int)$id_product);
|
||
$sqlPrevNoDisc->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlPrevNoDisc->where('id_currency = ' . (int)$currency);
|
||
$sqlPrevNoDisc->where('is_default_currency = 1');
|
||
$sqlPrevNoDisc->where('date_add < "' . pSQL($lastDate) . '"');
|
||
$sqlPrevNoDisc->where('has_discount = 0');
|
||
$rowPrev = Db::getInstance()->getRow($sqlPrevNoDisc);
|
||
|
||
// 3) start rabatu = pierwszy rekord z has_discount=1 po prev_no_disc
|
||
// jeśli nigdy nie było has_discount=0, start = pierwszy rekord w historii z has_discount=1
|
||
if (!empty($rowPrev['prev_no_disc'])) {
|
||
$sqlStart = new DbQuery();
|
||
$sqlStart->select('MIN(date_add) AS disc_start');
|
||
$sqlStart->from('omnibus_eu_free');
|
||
$sqlStart->where('id_product = ' . (int)$id_product);
|
||
$sqlStart->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlStart->where('id_currency = ' . (int)$currency);
|
||
$sqlStart->where('is_default_currency = 1');
|
||
$sqlStart->where('has_discount = 1');
|
||
$sqlStart->where('date_add > "' . pSQL($rowPrev['prev_no_disc']) . '"');
|
||
$rowStart = Db::getInstance()->getRow($sqlStart);
|
||
} else {
|
||
$sqlStart = new DbQuery();
|
||
$sqlStart->select('MIN(date_add) AS disc_start');
|
||
$sqlStart->from('omnibus_eu_free');
|
||
$sqlStart->where('id_product = ' . (int)$id_product);
|
||
$sqlStart->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlStart->where('id_currency = ' . (int)$currency);
|
||
$sqlStart->where('is_default_currency = 1');
|
||
$sqlStart->where('has_discount = 1');
|
||
$rowStart = Db::getInstance()->getRow($sqlStart);
|
||
}
|
||
|
||
if (empty($rowStart['disc_start'])) {
|
||
return [];
|
||
}
|
||
|
||
$discStart = $rowStart['disc_start'];
|
||
|
||
// okno 30 dni przed startem rabatu
|
||
$startDateTime = (new DateTime($discStart))->modify('-' . $NumberOfDays . ' days')->format('Y-m-d H:i:s');
|
||
|
||
// minimalna cena REGULARNA w oknie przed rabatem
|
||
$sqlMin = new DbQuery();
|
||
$sqlMin->select('MIN(price_regular) AS price');
|
||
$sqlMin->from('omnibus_eu_free');
|
||
$sqlMin->where('id_product = ' . (int)$id_product);
|
||
$sqlMin->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlMin->where('id_currency = ' . (int)$currency);
|
||
$sqlMin->where('is_default_currency = 1');
|
||
$sqlMin->where('date_add >= "' . pSQL($startDateTime) . '"');
|
||
$sqlMin->where('date_add < "' . pSQL($discStart) . '"');
|
||
|
||
$minRow = Db::getInstance()->getRow($sqlMin);
|
||
|
||
if (empty($minRow) || !isset($minRow['price']) || $minRow['price'] === null) {
|
||
|
||
$sqlStartPrice = new DbQuery();
|
||
$sqlStartPrice->select('price_regular AS price');
|
||
$sqlStartPrice->from('omnibus_eu_free');
|
||
$sqlStartPrice->where('id_product = ' . (int)$id_product);
|
||
$sqlStartPrice->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sqlStartPrice->where('id_currency = ' . (int)$currency);
|
||
$sqlStartPrice->where('is_default_currency = 1');
|
||
$sqlStartPrice->where('has_discount = 1');
|
||
$sqlStartPrice->orderBy('date_add ASC, id_omnibuseufree ASC');
|
||
|
||
$startPriceRow = Db::getInstance()->getRow($sqlStartPrice, false);
|
||
|
||
return (!empty($startPriceRow) && isset($startPriceRow['price'])) ? $startPriceRow : [];
|
||
}
|
||
|
||
return (!empty($minRow) && isset($minRow['price'])) ? $minRow : [];
|
||
}
|
||
|
||
// v1: najniższa z ostatnich X dni (tu możesz liczyć po regularnej albo po sale – zależnie od interpretacji)
|
||
$date = new DateTime();
|
||
$date->modify('-' . $NumberOfDays . ' days');
|
||
$CutOffDate = $date->format('Y-m-d');
|
||
|
||
$sql = new DbQuery();
|
||
$sql->select('MIN(price_sale) AS price');
|
||
$sql->from('omnibus_eu_free');
|
||
$sql->where('id_product = ' . (int)$id_product);
|
||
$sql->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sql->where('id_currency = ' . (int)$currency);
|
||
$sql->where('is_default_currency = 1');
|
||
$sql->where('date_add >= "'.$CutOffDate.' 00:00:00"');
|
||
|
||
$row = Db::getInstance()->getRow($sql);
|
||
|
||
return (!empty($row) && isset($row['price'])) ? $row : [];
|
||
}
|
||
|
||
|
||
public function hookDisplayOmnibusEuFree($params)
|
||
{
|
||
$currency = $this->context->currency;
|
||
$lastMinimalPrice = $this->getLastMinimalPrice(
|
||
(int)$params['product']['id_product'],
|
||
(int)$params['product']['id_product_attribute'],
|
||
(int)$currency->id,
|
||
(bool)$params['product']['has_discount']
|
||
);
|
||
|
||
if (!empty($lastMinimalPrice)) {
|
||
$minimalPrice = PrestashopCompatibility::formatPrice(_PS_VERSION_, $lastMinimalPrice['price'], $currency);
|
||
}
|
||
else {
|
||
$minimalPrice = null;
|
||
}
|
||
|
||
$this->context->smarty->assign([
|
||
'OmnibuseufreeInfoVersion' => (int) Configuration::get('OMNIBUSEUFREE_INFORMATION_VERSION', null, null, null, 2),
|
||
'OmnibuseufreeProductPriceMin' => $minimalPrice,
|
||
'OmnibuseufreeProductPriceCurrent' => $params['product']['price'],
|
||
'OmnibuseufreeProductDiscount' => (bool) $params['product']['has_discount'],
|
||
'OmnibuseufreeNumberOfDays' => (int) Configuration::get('OMNIBUSEUFREE_DAYS', null, null, null, 30),
|
||
]);
|
||
|
||
return $this->display(__FILE__, '/views/templates/hook/presta_studio_omnibus_price.tpl');
|
||
}
|
||
|
||
public function hookDisplayProductPriceBlock($params)
|
||
{
|
||
return ($params['type'] == 'after_price' && Configuration::get('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK') == 1) ? $this->hookDisplayOmnibusEuFree($params) : '';
|
||
}
|
||
|
||
public function hookDisplayAdminProductsExtra($params)
|
||
{
|
||
$OmnibusData = array();
|
||
$product = new Product((int) $params['id_product']);
|
||
$hasCombinations = $product->hasCombinations();
|
||
$nameCache = array();
|
||
|
||
foreach ($this->getOmnibusData($params['id_product']) as $rowId => $rowValue) {
|
||
$OmnibusData[$rowId]['price_locale'] = '';
|
||
$OmnibusData[$rowId]['currency_iso_code'] = '';
|
||
$OmnibusData[$rowId]['name'] = '';
|
||
$OmnibusData[$rowId]['is_last_icon'] = '';
|
||
|
||
foreach ($rowValue as $key => $value) {
|
||
if (isset($OmnibusData[$rowId]['price']) && $key == 'id_currency') {
|
||
$currency = Currency::getCurrencyInstance((int) $value);
|
||
$OmnibusData[$rowId]['price_locale'] = PrestashopCompatibility::formatPrice(_PS_VERSION_, $OmnibusData[$rowId]['price'], $currency);
|
||
$OmnibusData[$rowId]['currency_iso_code'] = $currency->iso_code;
|
||
}
|
||
elseif ($key == 'id_product_attribute') {
|
||
$attrId = (int) $value;
|
||
if (!isset($nameCache[$attrId])) {
|
||
$nameCache[$attrId] = $product->getProductName((int) $params['id_product'], $attrId);
|
||
if ($hasCombinations && $attrId === 0) {
|
||
$nameCache[$attrId] .= ' ' . $this->l('(default combination)');
|
||
}
|
||
}
|
||
$OmnibusData[$rowId]['name'] = $nameCache[$attrId];
|
||
}
|
||
elseif ($key == 'is_last' && $value == 1) {
|
||
$OmnibusData[$rowId]['is_last_icon'] = '<span class="material-icons text-success">done</span>';
|
||
}
|
||
|
||
$OmnibusData[$rowId][$key] = $value;
|
||
}
|
||
}
|
||
|
||
$this->context->smarty->assign([
|
||
'OmnibuseufreeData' => $OmnibusData
|
||
]);
|
||
|
||
return $this->display(__FILE__, '/views/templates/admin/products-price-list.tpl');
|
||
}
|
||
|
||
public function hookHeader()
|
||
{
|
||
$this->context->controller->addCSS($this->_path . '/views/css/omnibuseufree-presta-studio.css');
|
||
}
|
||
|
||
public function hookDisplayBackOfficeHeader()
|
||
{
|
||
$this->context->controller->addJS($this->_path . 'views/js/back.js');
|
||
$this->context->controller->addCSS($this->_path . 'views/css/back.css');
|
||
}
|
||
|
||
public function hookActionProductSave($hook_params)
|
||
{
|
||
if (Module::isEnabled('omnibuseufree')) {
|
||
$id_product = (int) $hook_params['id_product'];
|
||
|
||
$combinationCount = (int) Db::getInstance()->getValue(
|
||
'SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'product_attribute` WHERE `id_product` = ' . $id_product
|
||
);
|
||
|
||
Product::flushPriceCache();
|
||
$this->addProductPrice($id_product);
|
||
|
||
if ($combinationCount <= 30) {
|
||
$this->addProductPriceWithCombinations($id_product);
|
||
}
|
||
}
|
||
}
|
||
|
||
public function hookActionProductAttributeUpdate()
|
||
{
|
||
if (Module::isEnabled('omnibuseufree')) {
|
||
$id_product = (int) Tools::getValue('id_product');
|
||
|
||
$combinationCount = (int) Db::getInstance()->getValue(
|
||
'SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'product_attribute` WHERE `id_product` = ' . $id_product
|
||
);
|
||
|
||
// Skip entirely for products with many combinations.
|
||
// hookActionProductSave already handles base price recording.
|
||
// This hook fires once PER combination (168x), so running here is wasteful.
|
||
if ($combinationCount > 30) {
|
||
return;
|
||
}
|
||
|
||
Product::flushPriceCache();
|
||
$this->addProductPrice($id_product);
|
||
$this->addProductPriceWithCombinations($id_product);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Load the configuration form
|
||
*/
|
||
public function getContent()
|
||
{
|
||
$confirmation = '';
|
||
if (((bool) Tools::isSubmit('submitOmnibusEuFreeModule')) == true) {
|
||
$confirmation = $this->postProcess();
|
||
}
|
||
|
||
$token = Configuration::get('OMNIBUSEUFREE_CRON_TOKEN', null, null, null, 'error');
|
||
|
||
$this->context->smarty->assign([
|
||
'module_dir' => $this->_path,
|
||
'lang' => $this->context->language->iso_code,
|
||
'update_prices' => Context::getContext()->link->getModuleLink('omnibuseufree', 'cron', array('type' => 1, 'token' => $token)),
|
||
'delete_outdated_data' => Context::getContext()->link->getModuleLink('omnibuseufree', 'cron', array('type' => 2, 'token' => $token)),
|
||
'cron_status' => Configuration::get('OMNIBUSEUFREE_CRON_STATUS', null, null, null, 2)
|
||
]);
|
||
|
||
$output = $this->context->smarty->fetch($this->local_path . 'views/templates/admin/configure.tpl');
|
||
|
||
return $confirmation . $output . $this->renderForm();
|
||
}
|
||
|
||
protected function renderForm()
|
||
{
|
||
$helper = new HelperForm();
|
||
|
||
$helper->show_toolbar = false;
|
||
$helper->table = $this->table;
|
||
$helper->module = $this;
|
||
$helper->default_form_language = $this->context->language->id;
|
||
$helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0);
|
||
|
||
$helper->identifier = $this->identifier;
|
||
$helper->submit_action = 'submitOmnibusEuFreeModule';
|
||
$helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false)
|
||
. '&configure=' . $this->name . '&tab_module=' . $this->tab . '&module_name=' . $this->name;
|
||
$helper->token = Tools::getAdminTokenLite('AdminModules');
|
||
|
||
$helper->tpl_vars = array(
|
||
'fields_value' => $this->getConfigFormValues(),
|
||
'languages' => $this->context->controller->getLanguages(),
|
||
'id_language' => $this->context->language->id,
|
||
);
|
||
|
||
return $helper->generateForm(array($this->getConfigForm()));
|
||
}
|
||
|
||
protected function getConfigForm()
|
||
{
|
||
$NumberOfDays = (int) Configuration::get('OMNIBUSEUFREE_DAYS', null, null, null, 30);
|
||
|
||
return array(
|
||
'form' => array(
|
||
'legend' => array(
|
||
'title' => $this->l('Settings'),
|
||
'icon' => 'icon-cogs',
|
||
),
|
||
'input' => array(
|
||
array(
|
||
'type' => 'switch',
|
||
'label' => $this->l('Delete outdated data'),
|
||
'name' => 'OMNIBUSEUFREE_OLD_DATA',
|
||
'is_bool' => true,
|
||
'values' => array(
|
||
array(
|
||
'id' => 'active_on',
|
||
'value' => true,
|
||
'label' => $this->l('Enabled')
|
||
),
|
||
array(
|
||
'id' => 'active_off',
|
||
'value' => false,
|
||
'label' => $this->l('Disabled')
|
||
)
|
||
),
|
||
),
|
||
array(
|
||
'type' => 'switch',
|
||
'label' => $this->l('Update prices'),
|
||
'name' => 'OMNIBUSEUFREE_UPDATE_DATABASE_PRICE',
|
||
'is_bool' => true,
|
||
'values' => array(
|
||
array(
|
||
'id' => 'active_on',
|
||
'value' => true,
|
||
'label' => $this->l('Enabled')
|
||
),
|
||
array(
|
||
'id' => 'active_off',
|
||
'value' => false,
|
||
'label' => $this->l('Disabled')
|
||
)
|
||
),
|
||
),
|
||
array(
|
||
'type' => 'select',
|
||
'label' => $this->l('Version:'),
|
||
'name' => 'OMNIBUSEUFREE_INFORMATION_VERSION',
|
||
'class' => 'omnibus-select-version',
|
||
'required' => true,
|
||
'options' => array(
|
||
'query' => array(
|
||
array(
|
||
'id_option' => 1,
|
||
'name' => sprintf($this->l('Lowest price in %d days'),$NumberOfDays)
|
||
),
|
||
array(
|
||
'id_option' => 2,
|
||
'name' => sprintf($this->l('Lowest price in %d days before discount'),$NumberOfDays)
|
||
),
|
||
),
|
||
'id' => 'id_option',
|
||
'name' => 'name'
|
||
)
|
||
),
|
||
array(
|
||
'type' => 'text',
|
||
'label' => $this->l('Number of days'),
|
||
'desc' => $this->l('Number of days for front-end lowest price display (Omnibus Directive).'),
|
||
'name' => 'OMNIBUSEUFREE_DAYS',
|
||
'class' => 'omnibus-input-days',
|
||
'maxlength' => '3',
|
||
'required' => true
|
||
),
|
||
array(
|
||
'type' => 'text',
|
||
'label' => $this->l('History retention (days)'),
|
||
'desc' => $this->l('How many days of price history to keep and display in admin. Older entries (except current price) will be deleted by CRON.'),
|
||
'name' => 'OMNIBUSEUFREE_HISTORY_DAYS',
|
||
'class' => 'omnibus-input-days',
|
||
'maxlength' => '4',
|
||
'required' => true
|
||
),
|
||
array(
|
||
'type' => 'switch',
|
||
'label' => $this->l('Display on product page'),
|
||
'name' => 'OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK',
|
||
'is_bool' => false,
|
||
'desc' => $this->l('Hook: ProductPriceBlock type: after_price'),
|
||
'values' => array(
|
||
array(
|
||
'id' => 'active_on',
|
||
'value' => 1,
|
||
'label' => $this->l('Enabled')
|
||
),
|
||
array(
|
||
'id' => 'active_off',
|
||
'value' => 2,
|
||
'label' => $this->l('Disabled')
|
||
)
|
||
),
|
||
),
|
||
array(
|
||
'type' => 'switch',
|
||
'label' => $this->l('CRON'),
|
||
'name' => 'OMNIBUSEUFREE_CRON_STATUS',
|
||
'is_bool' => false,
|
||
'desc' => $this->l('A new token will be generated if you change the CRON status'),
|
||
'values' => array(
|
||
array(
|
||
'id' => 'active_on',
|
||
'value' => 1,
|
||
'label' => $this->l('Enabled')
|
||
),
|
||
array(
|
||
'id' => 'active_off',
|
||
'value' => 2,
|
||
'label' => $this->l('Disabled')
|
||
)
|
||
),
|
||
),
|
||
),
|
||
'submit' => array(
|
||
'title' => $this->l('Save'),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Set values for the inputs.
|
||
*
|
||
* OMNIBUSEUFREE_CRON_STATUS since v1.0.2
|
||
* OMNIBUSEUFREE_DAYS since v1.0.2
|
||
*
|
||
*/
|
||
protected function getConfigFormValues()
|
||
{
|
||
return array(
|
||
'OMNIBUSEUFREE_OLD_DATA' => false,
|
||
'OMNIBUSEUFREE_UPDATE_DATABASE_PRICE' => false,
|
||
'OMNIBUSEUFREE_INFORMATION_VERSION' => Configuration::get('OMNIBUSEUFREE_INFORMATION_VERSION', null, null, null, 2),
|
||
'OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK' => Configuration::get('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK', null, null, null, 1),
|
||
'OMNIBUSEUFREE_CRON_STATUS' => Configuration::get('OMNIBUSEUFREE_CRON_STATUS', null, null, null, 2),
|
||
'OMNIBUSEUFREE_DAYS' => Configuration::get('OMNIBUSEUFREE_DAYS', null, null, null, 30),
|
||
'OMNIBUSEUFREE_HISTORY_DAYS' => Configuration::get('OMNIBUSEUFREE_HISTORY_DAYS', null, null, null, 60)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Save form data.
|
||
*/
|
||
protected function postProcess()
|
||
{
|
||
if (Configuration::get('OMNIBUSEUFREE_CRON_STATUS', null, null, null, 2) != Tools::getValue('OMNIBUSEUFREE_CRON_STATUS')) {
|
||
Configuration::updateValue('OMNIBUSEUFREE_CRON_TOKEN', Tools::hash(Tools::passwdGen(16)));
|
||
}
|
||
|
||
Configuration::updateValue('OMNIBUSEUFREE_INFORMATION_VERSION', (int) Tools::getValue('OMNIBUSEUFREE_INFORMATION_VERSION'));
|
||
Configuration::updateValue('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK', (int) Tools::getValue('OMNIBUSEUFREE_DISPLAY_PRODUCT_PRICE_BLOCK'));
|
||
Configuration::updateValue('OMNIBUSEUFREE_CRON_STATUS', (int) Tools::getValue('OMNIBUSEUFREE_CRON_STATUS'));
|
||
Configuration::updateValue('OMNIBUSEUFREE_DAYS', (int) Tools::getValue('OMNIBUSEUFREE_DAYS'));
|
||
Configuration::updateValue('OMNIBUSEUFREE_HISTORY_DAYS', max(1, (int) Tools::getValue('OMNIBUSEUFREE_HISTORY_DAYS')));
|
||
|
||
$confirmation = $this->l('The settings have been updated.');
|
||
|
||
if (Tools::getValue('OMNIBUSEUFREE_OLD_DATA')) {
|
||
$OldDataCounter = $this->removeOldDataFromOmnibusTable();
|
||
PrestaShopLogger::addLog('Omnibus Directive module by presta.studio - Deleted old data. Number of records deleted in database: ' . $OldDataCounter);
|
||
$confirmation .= '<br>' . $this->l('Deleted old data. Number of records deleted in database: ') . $OldDataCounter;
|
||
}
|
||
|
||
if (Tools::getValue('OMNIBUSEUFREE_UPDATE_DATABASE_PRICE')) {
|
||
$InsertDataCounter = $this->insertAllProductsToOmnibusTable();
|
||
PrestaShopLogger::addLog('Omnibus Directive module by presta.studio - Updated price history. Number of products checked: ' . $InsertDataCounter);
|
||
$confirmation .= '<br>' . $this->l('Updated price history. Number of products checked: ') . $InsertDataCounter;
|
||
}
|
||
|
||
return $this->displayConfirmation($confirmation);
|
||
}
|
||
|
||
protected function checkCurrencyConversionRate($id_product = null, $id_product_attribute = 0)
|
||
{
|
||
if (!isset($id_product)) {
|
||
throw new Exception('Missing parameter: $id_product');
|
||
}
|
||
|
||
$sql = new DbQuery();
|
||
$sql->select('id_currency, currency_conversion_rate');
|
||
$sql->from('omnibus_eu_free');
|
||
$sql->where('id_product = ' . (int) $id_product);
|
||
$sql->where('id_product_attribute = ' . (int) $id_product_attribute);
|
||
$sql->where('is_last = 1');
|
||
$SQLResult = Db::getInstance()->executeS($sql);
|
||
|
||
$ToChange = 0;
|
||
$OmnibusCurrencies = array();
|
||
|
||
foreach ($SQLResult as $row) {
|
||
$OmnibusCurrencies[$row['id_currency']] = $row['currency_conversion_rate'];
|
||
}
|
||
|
||
if (!empty($OmnibusCurrencies)) {
|
||
$currencies = Currency::getCurrencies();
|
||
$AvailableCurrencies = array();
|
||
|
||
foreach ($currencies as $currency) {
|
||
$AvailableCurrencies[$currency['id_currency']] = $currency['conversion_rate'];
|
||
}
|
||
|
||
foreach ($AvailableCurrencies as $CurrencyKey => $CurrencyValue) {
|
||
if (!isset($OmnibusCurrencies[$CurrencyKey]) || $CurrencyValue != $OmnibusCurrencies[$CurrencyKey]) {
|
||
$ToChange++;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
$ToChange++;
|
||
}
|
||
|
||
return $ToChange == 0 ? false : true;
|
||
}
|
||
|
||
protected function getLastSnapshot($id_product = 0, $id_product_attribute = 0, $id_currency = null)
|
||
{
|
||
$sql = new DbQuery();
|
||
$sql->select('price_regular, price_sale, has_discount, currency_conversion_rate');
|
||
$sql->from('omnibus_eu_free');
|
||
$sql->where('id_product = ' . (int)$id_product);
|
||
$sql->where('id_product_attribute = ' . (int)$id_product_attribute);
|
||
$sql->where('is_last = 1');
|
||
$sql->where('is_default_currency = 1');
|
||
|
||
if ($id_currency !== null) {
|
||
$sql->where('id_currency = ' . (int)$id_currency);
|
||
}
|
||
|
||
$sql->limit(1);
|
||
return Db::getInstance()->getRow($sql);
|
||
}
|
||
|
||
protected function getPricesSnapshot($id_product, $id_product_attribute, $id_currency)
|
||
{
|
||
$context = Context::getContext();
|
||
|
||
$oldCurrency = $context->currency;
|
||
if ((int)$id_currency > 0) {
|
||
$context->currency = new Currency((int)$id_currency);
|
||
}
|
||
|
||
// regularna (bez redukcji)
|
||
$regular = (float) Product::getPriceStatic(
|
||
(int)$id_product,
|
||
true,
|
||
(int)$id_product_attribute,
|
||
6,
|
||
null,
|
||
false,
|
||
false // use_reduction = false
|
||
);
|
||
|
||
// sprzedażowa (z redukcją)
|
||
$sale = (float) Product::getPriceStatic(
|
||
(int)$id_product,
|
||
true,
|
||
(int)$id_product_attribute,
|
||
6,
|
||
null,
|
||
false,
|
||
true // use_reduction = true
|
||
);
|
||
|
||
// przywróć walutę
|
||
$context->currency = $oldCurrency;
|
||
|
||
$hasDiscount = ($sale > 0 && $regular > 0 && ($sale + 0.0001) < $regular) ? 1 : 0;
|
||
$diff = $hasDiscount ? ($regular - $sale) : 0.0;
|
||
|
||
return [
|
||
'regular' => $regular,
|
||
'sale' => $sale,
|
||
'has_discount' => $hasDiscount,
|
||
'diff' => $diff,
|
||
];
|
||
}
|
||
|
||
|
||
protected function normalizeCurrency($currency)
|
||
{
|
||
if (is_array($currency)) {
|
||
return [
|
||
'id' => (int) ($currency['id_currency'] ?? $currency['id'] ?? 0),
|
||
'rate' => (float) ($currency['conversion_rate'] ?? $currency['rate'] ?? 0),
|
||
];
|
||
}
|
||
|
||
// obiekt Currency
|
||
return [
|
||
'id' => (int) ($currency->id ?? 0),
|
||
'rate' => (float) ($currency->conversion_rate ?? 0),
|
||
];
|
||
}
|
||
|
||
|
||
protected function addProductPrice($id_product = null)
|
||
{
|
||
if (!isset($id_product)) {
|
||
throw new Exception('Missing parameter: $id_product');
|
||
}
|
||
|
||
$id_product = (int)$id_product;
|
||
|
||
$currencies = Currency::getCurrencies(true, true);
|
||
$defaultCurrency = Currency::getDefaultCurrency();
|
||
|
||
// snapshot w default walucie do porównania (żeby nie dopisywać non stop)
|
||
$snapDefault = $this->getPricesSnapshot($id_product, 0, (int)$defaultCurrency->id);
|
||
|
||
if ((float)$snapDefault['sale'] != 0.0) {
|
||
$last = $this->getLastSnapshot($id_product, 0);
|
||
|
||
$check_currency = $this->checkCurrencyConversionRate($id_product, 0);
|
||
|
||
$changed =
|
||
empty($last)
|
||
|| abs((float)$last['price_regular'] - (float)$snapDefault['regular']) > 0.0001
|
||
|| abs((float)$last['price_sale'] - (float)$snapDefault['sale']) > 0.0001
|
||
|| (int)$last['has_discount'] !== (int)$snapDefault['has_discount']
|
||
|| (bool)$check_currency === true;
|
||
|
||
if ($changed) {
|
||
$this->clearLastPrice($id_product, 0);
|
||
|
||
foreach ($currencies as $currency) {
|
||
$c = $this->normalizeCurrency($currency);
|
||
|
||
$id_currency = (int)$c['id'];
|
||
$conversion_rate = (float)$c['rate'];
|
||
|
||
if ($id_currency <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$isDefaultCurrency = ($id_currency === (int)$defaultCurrency->id) ? 1 : 0;
|
||
|
||
$snap = $this->getPricesSnapshot($id_product, 0, $id_currency);
|
||
|
||
if ((float)$snap['sale'] <= 0.0) {
|
||
continue;
|
||
}
|
||
|
||
$this->insertToOmnibusTable(
|
||
$id_product,
|
||
0,
|
||
$snap,
|
||
$id_currency,
|
||
$isDefaultCurrency,
|
||
$conversion_rate
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
protected function addProductPriceWithCombinations($id_product = null)
|
||
{
|
||
if (!isset($id_product))
|
||
{
|
||
throw new Exception('Missing parameter: $id_product');
|
||
}
|
||
|
||
$id_product = (int)$id_product;
|
||
|
||
Product::flushPriceCache();
|
||
|
||
$defaultCurrency = Currency::getDefaultCurrency();
|
||
$id_default_currency = (int)$defaultCurrency->id;
|
||
|
||
// Najstabilniejsza lista kombinacji (bez powtórek)
|
||
$attributeRows = Product::getProductAttributesIds($id_product);
|
||
if (empty($attributeRows))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Lista walut (aktywnych)
|
||
$currencies = Currency::getCurrencies(true, true);
|
||
|
||
foreach ($attributeRows as $row)
|
||
{
|
||
$id_product_attribute = (int)$row['id_product_attribute'];
|
||
|
||
// Snapshot w default walucie do porównania zmian
|
||
$snapDefault = $this->getPricesSnapshot($id_product, $id_product_attribute, $id_default_currency);
|
||
|
||
if ((float)$snapDefault['sale'] <= 0.0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
$last = $this->getLastSnapshot($id_product, $id_product_attribute);
|
||
$check_currency = $this->checkCurrencyConversionRate($id_product, $id_product_attribute);
|
||
|
||
$changed =
|
||
empty($last)
|
||
|| abs((float)$last['price_regular'] - (float)$snapDefault['regular']) > 0.0001
|
||
|| abs((float)$last['price_sale'] - (float)$snapDefault['sale']) > 0.0001
|
||
|| (int)$last['has_discount'] !== (int)$snapDefault['has_discount']
|
||
|| (bool)$check_currency === true;
|
||
|
||
if (!$changed)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// Zerujemy is_last dla tej kombinacji (dla wszystkich walut)
|
||
$this->clearLastPrice($id_product, $id_product_attribute);
|
||
|
||
// Wstawiamy nowe snapshoty dla wszystkich walut
|
||
foreach ($currencies as $currency)
|
||
{
|
||
$c = $this->normalizeCurrency($currency);
|
||
$id_currency = (int)$c['id'];
|
||
$conversion_rate = (float)$c['rate'];
|
||
|
||
if ($id_currency <= 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
$isDefaultCurrency = ($id_currency === $id_default_currency) ? 1 : 0;
|
||
|
||
$snap = $this->getPricesSnapshot($id_product, $id_product_attribute, $id_currency);
|
||
|
||
if ((float)$snap['sale'] <= 0.0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
$this->insertToOmnibusTable(
|
||
$id_product,
|
||
$id_product_attribute,
|
||
$snap,
|
||
$id_currency,
|
||
$isDefaultCurrency,
|
||
$conversion_rate
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
protected function insertToOmnibusTable($id_product = 0, $id_product_attribute = 0, $snap = [], $currency = 0, $isDefaultCurrency = 0, $CurrencyConversionRate = 0)
|
||
{
|
||
return Db::getInstance()->insert('omnibus_eu_free', [
|
||
'id_product' => (int) $id_product,
|
||
'id_product_attribute' => (int) $id_product_attribute,
|
||
|
||
// zostaw stare price jako "sale" dla kompatybilności szablonu / starego kodu
|
||
'price' => pSQL($snap['sale']),
|
||
|
||
'price_regular' => pSQL($snap['regular']),
|
||
'price_sale' => pSQL($snap['sale']),
|
||
'has_discount' => (int) $snap['has_discount'],
|
||
'discount_diff' => pSQL($snap['diff']),
|
||
|
||
'id_currency' => (int) $currency,
|
||
'is_default_currency' => (int) $isDefaultCurrency,
|
||
'is_last' => 1,
|
||
'currency_conversion_rate' => pSQL($CurrencyConversionRate)
|
||
]);
|
||
}
|
||
|
||
protected function getLastPrice($id_product = 0, $id_product_attribute = 0)
|
||
{
|
||
$sql = new DbQuery();
|
||
$sql->select('price');
|
||
$sql->from('omnibus_eu_free');
|
||
$sql->where('id_product = ' . (int) $id_product);
|
||
$sql->where('id_product_attribute = ' . (int) $id_product_attribute);
|
||
$sql->where('is_last = 1');
|
||
$sql->where('is_default_currency = 1');
|
||
$sql->limit(1);
|
||
|
||
return Db::getInstance()->executeS($sql);
|
||
}
|
||
|
||
protected function getOmnibusData($id_product = null)
|
||
{
|
||
if (!isset($id_product)) {
|
||
throw new Exception('Missing parameter: $id_product');
|
||
}
|
||
|
||
$historyDays = (int) Configuration::get('OMNIBUSEUFREE_HISTORY_DAYS', null, null, null, 60);
|
||
|
||
$sql = new DbQuery();
|
||
$sql->select('*');
|
||
$sql->from('omnibus_eu_free');
|
||
$sql->where('id_product = ' . (int) $id_product);
|
||
$sql->where('(`is_last` = 1 OR `date_add` >= DATE_SUB(NOW(), INTERVAL ' . $historyDays . ' DAY))');
|
||
$sql->orderBy('is_default_currency DESC');
|
||
$sql->orderBy('id_currency ASC');
|
||
$sql->orderBy('id_product_attribute ASC');
|
||
$sql->orderBy('id_omnibuseufree DESC');
|
||
|
||
return Db::getInstance()->executeS($sql);
|
||
}
|
||
|
||
protected function clearLastPrice($id_product = 0, $id_product_attribute = 0)
|
||
{
|
||
$result = Db::getInstance()->update('omnibus_eu_free', array(
|
||
'is_last' => 0,
|
||
), 'id_product = ' . (int) $id_product . ' AND id_product_attribute = ' . (int) $id_product_attribute);
|
||
}
|
||
|
||
public function insertAllProductsToOmnibusTable()
|
||
{
|
||
$counter = 0;
|
||
$ProductClass = new Product();
|
||
$ProductClass->flushPriceCache();
|
||
$products = $ProductClass->getProducts($this->context->language->id, 0, 0, 'id_product', 'ASC');
|
||
|
||
foreach ($products as $product) {
|
||
$this->addProductPriceWithCombinations($product['id_product']);
|
||
$this->addProductPrice($product['id_product']);
|
||
$counter++;
|
||
}
|
||
|
||
return $counter;
|
||
}
|
||
|
||
public function removeOldDataFromOmnibusTable()
|
||
{
|
||
$numberOfDays = (int) Configuration::get('OMNIBUSEUFREE_HISTORY_DAYS', null, null, null, 60);
|
||
|
||
Db::getInstance()->execute(
|
||
'DELETE FROM `' . _DB_PREFIX_ . 'omnibus_eu_free`
|
||
WHERE `is_last` = 0
|
||
AND `date_add` < DATE_SUB(NOW(), INTERVAL ' . $numberOfDays . ' DAY)'
|
||
);
|
||
|
||
return (int) Db::getInstance()->Affected_Rows();
|
||
}
|
||
}
|