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'] = 'done'; } $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 .= '
' . $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 .= '
' . $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(); } }