Jump to content

Prestashop currency rates out of date


mattf10

Recommended Posts

I use the Prestashop web service to update my currency rates.  I notice that the rates have not changed in over a week.  Yes, I realise that the service is provided "as is", but the system does not have a way to download rates from other sources.

 

The xml file with the rates comes from here: http://api.prestashop.com/xml/currencies.xml.

 

Can someone please investigate why this file is not being updated?

Link to comment
Share on other sites

Have the same problem! I'm reading too for an solution..

Yes, I also noticed this 2 weeks ago. This XML file is not changing at all. So where is the Prestashop currency service? No service at all to provide the wrong rates, even dangerous!

 

We should have an option to select xml files from Europpean Central bank, Yahoo, Bloomberg or similar. Prestashop uses its own proprietary format here, compatible to nothing.

Link to comment
Share on other sites

You certainly can create your own currency process, to pull in data from another source, and then run that through cron. 

Well I certainly could write my own shop software, if I had another life to live. Then I also would for sure know how to do it.

Seriously, what is the purpose to provide a currency service, which is not updating its XML file for weeks, just because someone is on holidays?

And render all shops which rely on it as broken? Just useless.

Link to comment
Share on other sites

i agree, currency rates service from the begining is useless, maybe it is because there is no tech standard to share currency rates. Each bank / government do this in a other way so this is why it is hard to develop universal solution :(  I've got module that updates the currency rates with european central bank and it works very well, and currencies are updated day by day, with no excuses;)

Link to comment
Share on other sites

i agree, currency rates service from the begining is useless, maybe it is because there is no tech standard to share currency rates. Each bank / government do this in a other way so this is why it is hard to develop universal solution :(

Well I would not say that. It would be quite useful, if you would just update it evry day. We all agree, that the exchange rates fluctuate a little bit during the day. We also agree that different providers have a slightly different rate due to realtime effects and buffering latency. The difference in between all providers is usually less than 0.1% in average during the day time.

 

Due to the fact, that the Prestashop service has ceased to update ists XML file weeks ago (whatever problem this has caused, I don't know) the current rates deviate as much as 5% from reality.

 

If Prestashop wants to provide a failsafe service, it is quite easy to write a redundant cronjob, which crosschecks the five major currencies authencity and does a failover handling.

In the extreme case just puts the XML file offline, if there is no backup server, so the webshop admin out there knows: Right now there is a problem with currency exchange rates, have to do it myself until fixed. We all know that routers break down in summertime, or any other bad things happen. That is not the main problem here. There are always solutions. They just do not exist yet.

 

For guys like you this is done in minutes, I am pretty sure of that. For guys like me it would take a lifetime. I feel lke the window cleaner with the calculator who asks Einstein if he can give him a hand....

Link to comment
Share on other sites

currency rates from prestashop are different than currency rates in my country (in my country for  taxation purposes we need to use currency rates from national bank called NBP). Usage of internal ps currency rates is against the law, so they are useless in context of my country

Link to comment
Share on other sites

currency rates from prestashop are different than currency rates in my country (in my country for  taxation purposes we need to use currency rates from national bank called NBP). Usage of internal ps currency rates is against the law, so they are useless in context of my country

I see your problem. Right now, the rates from Prestashop are not updated, do you know what is the reason for this?

Can you solve your problem with prestashop rates + correction factor/formula? Maybe NBP does the same, somehow.

Anyway, as Prestashop rates are static now, this is also useless.

Link to comment
Share on other sites

Well I certainly could write my own shop software, if I had another life to live. Then I also would for sure know how to do it.

Seriously, what is the purpose to provide a currency service, which is not updating its XML file for weeks, just because someone is on holidays?

And render all shops which rely on it as broken? Just useless.

there are those that bitch and complain and get nothing accomplished...

And there are those that take action and move forward...

 

which one do you want to be?

Link to comment
Share on other sites

You are right, there is no honor in bitching and complaining. I just focus on my strenghts, and those are today definitely not in writing software. There were times when I was writing C++, Delphi, and assembler. There were times when I was helping "stupid" guys (like me today) free of charge, just for the honor of helping, forgiving them their ignorance. Those times are gone. You can say my brain is now to slow for this. I can live with that, bro. Today the number of people who deliver fantastic plugins free of charge, just for the honor of it, are very limited. They still exist, also in this forum, and they may just provide a solution to the problem, forgiving me my own ignorance today.

 

But how about you? You as a Prestashop Superstar you have no solution either? That makes me think now. Shall I call myself Superstar too?

I can ask you: you want to belong to a third class? Those who take action and still getting nothing accomplished?

 

Maybe your reply will give some insight which class you belong to.

 

Edit:

 

Right now I have solved it by a new currency.php in classes directory, for Prestashop 1.6.

It takes its rates from ECB bank. And it just works!

 

I would have expected this solution from you SUPERSTAR, but anyway I know enough guys who just talk a lot....

 

here is the code:

--------------------------------

<?php
/*
* 2007-2016 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.or...ses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
*  @author PrestaShop SA <[email protected]>
*  @copyright  2007-2016 PrestaShop SA
*  @license    http://opensource.or...ses/osl-3.0.php  Open Software License (OSL 3.0)
*  International Registered Trademark & Property of PrestaShop SA
*/

class CurrencyCore extends ObjectModel
{
    public $id;

    /** @var string Name */
    public $name;

    /** @var string Iso code */
    public $iso_code;

    /** @var string Iso code numeric */
    public $iso_code_num;

    /** @var string Symbol for short display */
    public $sign;

    /** @var int bool used for displaying blank between sign and price */
    public $blank;

    /** @var string exchange rate from euros */
    public $conversion_rate;

    /** @var bool True if currency has been deleted (staying in database as deleted) */
    public $deleted = 0;

    /** @var int ID used for displaying prices */
    public $format;

    /** @var int bool Display decimals on prices */
    public $decimals;

    /** @var int bool active */
    public $active;

    /**
     * @see ObjectModel::$definition
     */
    public static $definition = array(
        'table' => 'currency',
        'primary' => 'id_currency',
        'multilang_shop' => true,
        'fields' => array(
            'name' =>            array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
            'iso_code' =>        array('type' => self::TYPE_STRING, 'validate' => 'isLanguageIsoCode', 'required' => true, 'size' => 3),
            'iso_code_num' =>    array('type' => self::TYPE_STRING, 'validate' => 'isNumericIsoCode', 'size' => 3),
            'blank' =>            array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'sign' =>            array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 8),
            'format' =>        array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
            'decimals' =>        array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
            'conversion_rate' =>array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat', 'required' => true, 'shop' => true),
            'deleted' =>        array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'active' =>        array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
        ),
    );

    /** @var array Currency cache */
    protected static $currencies = array();
    protected static $countActiveCurrencies = array();

    protected $webserviceParameters = array(
        'objectsNodeName' => 'currencies',
    );

    /**
     * contains the sign to display before price, according to its format
     * @var string
     */
    public $prefix = null;
    /**
     * contains the sign to display after price, according to its format
     * @var string
     */
    public $suffix = null;

    public function __construct($id = null, $id_lang = null, $id_shop = null)
    {
        parent::__construct($id, $id_lang, $id_shop);
        // prefix and suffix are convenient shortcut for displaying
        // price sign before or after the price number
        $this->prefix =    $this->format % 2 != 0 ? $this->sign.' ' : '';
        $this->suffix =    $this->format % 2 == 0 ? ' '.$this->sign : '';
        if (!$this->conversion_rate) {
            $this->conversion_rate = 1;
        }
    }
    /**
     * Overriding check if currency rate is not empty and if currency with the same iso code already exists.
     * If it's true, currency is not added.
     *
     * @see ObjectModelCore::add()
     */
    public function add($autodate = true, $nullValues = false)
    {
        if ((float)$this->conversion_rate <= 0) {
            return false;
        }
        return Currency::exists($this->iso_code, $this->iso_code_num) ? false : parent::add($autodate, $nullValues);
    }

    public function update($autodate = true, $nullValues = false)
    {
        if ((float)$this->conversion_rate <= 0) {
            return false;
        }
        return parent::update($autodate, $nullValues);
    }

    /**
     * Check if a curency already exists.
     *
     * @param int|string $iso_code int for iso code number string for iso code
     * @return bool
     */
    public static function exists($iso_code, $iso_code_num, $id_shop = 0)
    {
        if (is_int($iso_code)) {
            $id_currency_exists = Currency::getIdByIsoCodeNum((int)$iso_code_num, (int)$id_shop);
        } else {
            $id_currency_exists = Currency::getIdByIsoCode($iso_code, (int)$id_shop);
        }

        if ($id_currency_exists) {
            return true;
        } else {
            return false;
        }
    }

    public function deleteSelection($selection)
    {
        if (!is_array($selection)) {
            return false;
        }

        $res = array();
        foreach ($selection as $id) {
            $obj = new Currency((int)$id);
            $res[$id] = $obj->delete();
        }

        foreach ($res as $value) {
            if (!$value) {
                return false;
            }
        }
        return true;
    }

    public function delete()
    {
        if ($this->id == Configuration::get('PS_CURRENCY_DEFAULT')) {
            $result = Db::getInstance()->getRow('SELECT `id_currency` FROM '._DB_PREFIX_.'currency WHERE `id_currency` != '.(int)($this->id).' AND `deleted` = 0');
            if (!$result['id_currency']) {
                return false;
            }
            Configuration::updateValue('PS_CURRENCY_DEFAULT', $result['id_currency']);
        }
        $this->deleted = 1;
        return $this->update();
    }

    /**
     * Return formated sign
     *
     * @param string $side left or right
     * @return string formated sign
     */
    public function getSign($side = null)
    {
        if (!$side) {
            return $this->sign;
        }
        $formated_strings = array(
            'left' => $this->sign.' ',
            'right' => ' '.$this->sign
        );

        $formats = array(
            1 => array('left' => &$formated_strings['left'], 'right' => ''),
            2 => array('left' => '', 'right' => &$formated_strings['right']),
            3 => array('left' => &$formated_strings['left'], 'right' => ''),
            4 => array('left' => '', 'right' => &$formated_strings['right']),
            5 => array('left' => '', 'right' => &$formated_strings['right'])
        );
        if (isset($formats[$this->format][$side])) {
            return ($formats[$this->format][$side]);
        }
        return $this->sign;
    }

    /**
     * Return available currencies
     *
     * @return array Currencies
     */
    public static function getCurrencies($object = false, $active = true, $group_by = false)
    {
        $tab = Db::getInstance()->executeS('
        SELECT *
        FROM `'._DB_PREFIX_.'currency` c
        '.Shop::addSqlAssociation('currency', 'c').
        ' WHERE `deleted` = 0'.
        ($active ? ' AND c.`active` = 1' : '').
        ($group_by ? ' GROUP BY c.`id_currency`' : '').
        ' ORDER BY `name` ASC');
        if ($object) {
            foreach ($tab as $key => $currency) {
                $tab[$key] = Currency::getCurrencyInstance($currency['id_currency']);
            }
        }
        return $tab;
    }

    public static function getCurrenciesByIdShop($id_shop = 0)
    {
        return Db::getInstance()->executeS('
        SELECT *
        FROM `'._DB_PREFIX_.'currency` c
        LEFT JOIN `'._DB_PREFIX_.'currency_shop` cs ON (cs.`id_currency` = c.`id_currency`)
        '.($id_shop ? ' WHERE cs.`id_shop` = '.(int)$id_shop : '').'
        ORDER BY `name` ASC');
    }


    public static function getPaymentCurrenciesSpecial($id_module, $id_shop = null)
    {
        if (is_null($id_shop)) {
            $id_shop = Context::getContext()->shop->id;
        }

        $sql = 'SELECT *
                FROM '._DB_PREFIX_.'module_currency
                WHERE id_module = '.(int)$id_module.'
                    AND id_shop ='.(int)$id_shop;
        return Db::getInstance()->getRow($sql);
    }

    public static function getPaymentCurrencies($id_module, $id_shop = null)
    {
        if (is_null($id_shop)) {
            $id_shop = Context::getContext()->shop->id;
        }

        $sql = 'SELECT c.*
                FROM `'._DB_PREFIX_.'module_currency` mc
                LEFT JOIN `'._DB_PREFIX_.'currency` c ON c.`id_currency` = mc.`id_currency`
                WHERE c.`deleted` = 0
                    AND mc.`id_module` = '.(int)$id_module.'
                    AND c.`active` = 1
                    AND mc.id_shop = '.(int)$id_shop.'
                ORDER BY c.`name` ASC';
        return Db::getInstance()->executeS($sql);
    }

    public static function checkPaymentCurrencies($id_module, $id_shop = null)
    {
        if (empty($id_module)) {
            return false;
        }

        if (is_null($id_shop)) {
            $id_shop = Context::getContext()->shop->id;
        }

        $sql = 'SELECT *
                FROM `'._DB_PREFIX_.'module_currency`
                WHERE `id_module` = '.(int)$id_module.'
                    AND `id_shop` = '.(int)$id_shop;
        return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
    }

    public static function getCurrency($id_currency)
    {
        return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
        SELECT *
        FROM `'._DB_PREFIX_.'currency`
        WHERE `deleted` = 0
        AND `id_currency` = '.(int)($id_currency));
    }

    /**
     * @param $iso_code
     * @param int $id_shop
     * @return int
     */
    public static function getIdByIsoCode($iso_code, $id_shop = 0)
    {
        $cache_id = 'Currency::getIdByIsoCode_'.pSQL($iso_code).'-'.(int)$id_shop;
        if (!Cache::isStored($cache_id)) {
            $query = Currency::getIdByQuery($id_shop);
            $query->where('iso_code = \''.pSQL($iso_code).'\'');

            $result = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query->build());
            Cache::store($cache_id, $result);
            return $result;
        }
        return Cache::retrieve($cache_id);
    }

    /**
     * @param $iso_code_num
     * @param int $id_shop
     * @return int
     */
    public static function getIdByIsoCodeNum($iso_code_num, $id_shop = 0)
    {
        $query = Currency::getIdByQuery($id_shop);
        $query->where('iso_code_num = \''.pSQL($iso_code_num).'\'');

        return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query->build());
    }

    /**
     * @param int $id_shop
     * @return DbQuery
     */
    public static function getIdByQuery($id_shop = 0)
    {
        $query = new DbQuery();
        $query->select('c.id_currency');
        $query->from('currency', 'c');
        $query->where('deleted = 0');

        if (Shop::isFeatureActive() && $id_shop > 0) {
            $query->leftJoin('currency_shop', 'cs', 'cs.id_currency = c.id_currency');
            $query->where('id_shop = '.(int)$id_shop);
        }
        return $query;
    }

    /**
     * Refresh the currency exchange rate
     * The XML file define exchange rate for each from a default currency ($isoCodeSource).
     *
     * @param SimpleXMLElement $data XML content which contains all the exchange rates
     * @param string $isoCodeSource The default currency used in the XML file
     * @param Currency $defaultCurrency The default currency object
     */
    public function refreshCurrency($data, $isoCodeSource, $defaultCurrency)
    {
        // fetch the exchange rate of the default currency
        $exchange_rate = 1;
        $tmp = $this->conversion_rate;
        if ($defaultCurrency->iso_code != $isoCodeSource) {
            foreach ($data->Cube->Cube->Cube as $currency) {
                if ($currency['currency'] == $defaultCurrency->iso_code) {
                    $exchange_rate = round((float)$currency['rate'], 6);
                    break;
                }
            }
        }

        if ($defaultCurrency->iso_code == $this->iso_code) {
            $this->conversion_rate = 1;
        } else {
            if ($this->iso_code == $isoCodeSource) {
                $rate = 1;
            } else {
                foreach ($data->Cube->Cube->Cube as $obj) {
                    if ($this->iso_code == strval($obj['currency'])) {
                        $rate = (float)$obj['rate'];
                        break;
                    }
                }
            }

            if (isset($rate)) {
                $this->conversion_rate = round($rate / $exchange_rate, 6);
            }
        }

        if ($tmp != $this->conversion_rate) {
            $this->update();
        }
    }

    public static function getDefaultCurrency()
    {
        $id_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT');
        if ($id_currency == 0) {
            return false;
        }

        return new Currency($id_currency);
    }

    public static function refreshCurrencies()
    {
        // Parse
        if (!$feed = Tools::simplexml_load_file('http://www.ecb.europ...fxref-daily.xml')) {
            return Tools::displayError('Cannot parse feed.');
        }

 
        // Default feed currency (EUR)
        $isoCodeSource = 'EUR';

        if (!$default_currency = Currency::getDefaultCurrency()) {
            return Tools::displayError('No default currency');
        }

        $currencies = Currency::getCurrencies(true, false, true);
        foreach ($currencies as $currency) {
            /** @var Currency $currency */
            if ($currency->id != $default_currency->id) {
                $currency->refreshCurrency($feed, $isoCodeSource, $default_currency);
            }
        }
    }

    /**
     * Get current currency
     *
     * @deprecated as of 1.5 use $context->currency instead
     * @return Currency
     */
    public static function getCurrent()
    {
        Tools::displayAsDeprecated();
        return Context::getContext()->currency;
    }

    public static function getCurrencyInstance($id)
    {
        if (!isset(self::$currencies[$id])) {
            self::$currencies[(int)($id)] = new Currency($id);
        }
        return self::$currencies[(int)($id)];
    }

    public function getConversationRate()
    {
        return $this->id != (int)Configuration::get('PS_CURRENCY_DEFAULT') ? $this->conversion_rate : 1;
    }

    public static function countActiveCurrencies($id_shop = null)
    {
        if ($id_shop === null) {
            $id_shop = (int)Context::getContext()->shop->id;
        }

        if (!isset(self::$countActiveCurrencies[$id_shop])) {
            self::$countActiveCurrencies[$id_shop] = Db::getInstance()->getValue('
                SELECT COUNT(DISTINCT c.id_currency) FROM `'._DB_PREFIX_.'currency` c
                LEFT JOIN '._DB_PREFIX_.'currency_shop cs ON (cs.id_currency = c.id_currency AND cs.id_shop = '.(int)$id_shop.')
                WHERE c.`active` = 1
            ');
        }
        return self::$countActiveCurrencies[$id_shop];
    }

    public static function isMultiCurrencyActivated($id_shop = null)
    {
        return (Currency::countActiveCurrencies($id_shop) > 1);
    }
}
 

Edited by [email protected] (see edit history)
  • Thanks 1
Link to comment
Share on other sites

Edit:  I see someone beat me to it!  Great minds think alike I guess.
 
In the end, I got tired of waiting and fixed the problem myself.  I'm sharing the code here in case anyone finds it useful.  The code below works for me, but I make no guarantees.  Use it at your own risk.
 
I'm using rates from the European Central Bank ( http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml ).  It doesn't contain as many rates as the original Prestashop version, but it has enough to suit my purposes.
 
If you'd like to use this, create a file called "Currency.php" and place it in the "/override/classes/" directory.   If you use "cron_currency_update", it will call the method in this file instead.
 
class Currency extends CurrencyCore
{   
    /**
     * Refresh the currency exchange rate
     * The XML file define exchange rate for each from a default currency ($isoCodeSource).
     *
     * @param SimpleXMLElement $data XML content which contains all the exchange rates
     * @param string $isoCodeSource The default currency used in the XML file
     * @param Currency $defaultCurrency The default currency object
     */
    public function refreshCurrency($data, $isoCodeSource, $defaultCurrency)
    {    
    // fetch the exchange rate of the default currency
        $exchange_rate = 1;
        $tmp = $this->conversion_rate;
        if ($defaultCurrency->iso_code != $isoCodeSource) {
            foreach ($data->Cube as $currency) {
                if ($currency['currency'] == $defaultCurrency->iso_code) {
                    $exchange_rate = round((float)$currency['rate'], 6);
                    break;
                }
            }
        }
 
        if ($defaultCurrency->iso_code == $this->iso_code) {
            $this->conversion_rate = 1;
        } else {
            if ($this->iso_code == $isoCodeSource) {
                $rate = 1;
            } else {
                foreach ($data->Cube as $obj) {
                    if ($this->iso_code == strval($obj['currency'])) {
                        $rate = (float)$obj['rate'];
                        break;
                    }
                }
            }
 
            if (isset($rate)) {
                $this->conversion_rate = round($rate / $exchange_rate, 6);
            }
        }
 
        if ($tmp != $this->conversion_rate) {
            $this->update();
        }
    }
 
    public static function refreshCurrencies()
    {    
        // Prestashop no longer provide currency rates from their API
        // get them from the European Central Bank
        if (!$feed = Tools::simplexml_load_file('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')) {
            return Tools::displayError('Cannot parse feed.');
        }
 
        // Default feed currency - this is always EUR and not provided in the feed: hardcode it
        $isoCodeSource = 'EUR';
 
        // returns the shop default - we want this to be EUR
        //if (!$default_currency = Currency::getDefaultCurrency()) {
        $default_currency_id = Currency::getIdByIsoCode($isoCodeSource);
        if (!$default_currency = Currency::getCurrency($default_currency_id)) {
        
   return Tools::displayError('No default currency');

        }

 
        $currencies = Currency::getCurrencies(true, false, true);
        foreach ($currencies as $currency) {
            /** @var Currency $currency */
            if ($currency->id != $default_currency->id) {
                // in our xml, the rates are nested in two "Cube" elements
                if ( isset($feed->Cube, $feed->Cube->Cube) )
                    $currency->refreshCurrency($feed->Cube->Cube, $isoCodeSource, $default_currency);
            }
        }
    }
}

Edited by mattf10 (see edit history)
  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...