<?php
/* Copyright (C) 2007-2020  Laurent Destailleur <eldy@users.sourceforge.net>
 * Copyright (C) 2014       Juanjo Menent       <jmenent@2byte.es>
 * Copyright (C) 2015       Florian Henry       <florian.henry@open-concept.pro>
 * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
 * Copyright (C) 2016       Pierre-Henry Favre  <phf@atm-consulting.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * \file    htdocs/multicurrency/class/multicurrency.class.php
 * \ingroup multicurrency
 * \brief   This file is a CRUD class file (Create/Read/Update/Delete) for multicurrency
 */

// Put here all includes required by your class file
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';


/**
 * Class Currency
 *
 * Put here description of your class
 * @see CommonObject
 */
class MultiCurrency extends CommonObject
{
	/**
	 * @var string Id to identify managed objects
	 */
	public $element = 'multicurrency';

	/**
	 * @var string Name of table without prefix where object is stored
	 */
	public $table_element = 'multicurrency';

	/**
	 * @var string Name of table without prefix where object is stored
	 */
	public $table_element_line = "multicurrency_rate";

	/**
	 * @var CurrencyRate[] rates
	 */
	public $rates = array();

	/**
	 * @var mixed Sample property 1
	 */
	public $id;

	/**
	 * @var mixed Sample property 1
	 */
	public $code;

	/**
	 * @var mixed Sample property 2
	 */
	public $name;

	/**
	 * @var int Entity
	 */
	public $entity;

	/**
	 * @var mixed Sample property 2
	 */
	public $date_create;

	/**
	 * @var mixed Sample property 2
	 */
	public $fk_user;

	/**
	 * @var mixed Sample property 2
	 */
	public $rate;


	/**
	 * Constructor
	 *
	 * @param DoliDb $db Database handler
	 */
	public function __construct(DoliDB $db)
	{
		$this->db = $db;
	}

	/**
	 * Create object into database
	 *
	 * @param  User $user      	User that creates
	 * @param  bool $trigger 	true=launch triggers after, false=disable triggers
	 * @return int 				Return integer <0 if KO, Id of created object if OK
	 */
	public function create(User $user, $trigger = true)
	{
		global $conf, $langs;

		dol_syslog('MultiCurrency::create', LOG_DEBUG);

		$error = 0;

		if (self::checkCodeAlreadyExists($this->code)) {
			$error++;
			$this->errors[] = $langs->trans('multicurrency_code_already_added');
			return -1;
		}

		if (empty($this->entity) || $this->entity <= 0) {
			$this->entity = $conf->entity;
		}
		$now = dol_now();

		// Insert request
		$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element."(";
		$sql .= ' code,';
		$sql .= ' name,';
		$sql .= ' entity,';
		$sql .= ' date_create,';
		$sql .= ' fk_user';
		$sql .= ') VALUES (';
		$sql .= " '".$this->db->escape($this->code)."',";
		$sql .= " '".$this->db->escape($this->name)."',";
		$sql .= " ".((int) $this->entity).",";
		$sql .= " '".$this->db->idate($now)."',";
		$sql .= " ".((int) $user->id);
		$sql .= ')';

		$this->db->begin();

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('MultiCurrency::create '.join(',', $this->errors), LOG_ERR);
		}

		if (!$error) {
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
			$this->date_create = $now;
			$this->fk_user = $user->id;

			if ($trigger) {
				$result = $this->call_trigger('CURRENCY_CREATE', $user);
				if ($result < 0) {
					$error++;
				}
			}
		}

		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return $this->id;
		}
	}

	/**
	 * Load object in memory from the database
	 *
	 * @param int    $id  		Id object
	 * @param string $code 		code
	 * @return int 				Return integer <0 if KO, 0 if not found, >0 if OK
	 */
	public function fetch($id, $code = null)
	{
		dol_syslog('MultiCurrency::fetch', LOG_DEBUG);

		global $conf;

		$sql = "SELECT";
		$sql .= ' c.rowid, c.name, c.code, c.entity, c.date_create, c.fk_user';
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' AS c';
		if (!empty($code)) {
			$sql .= ' WHERE c.code = \''.$this->db->escape($code).'\' AND c.entity = '.$conf->entity;
		} else {
			$sql .= ' WHERE c.rowid = '.((int) $id);
		}

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);

		if ($resql) {
			$numrows = $this->db->num_rows($resql);
			if ($numrows) {
				$obj = $this->db->fetch_object($resql);

				$this->id = $obj->rowid;
				$this->name = $obj->name;
				$this->code = $obj->code;
				$this->entity = $obj->entity;
				$this->date_create = $obj->date_create;
				$this->fk_user = $obj->fk_user;

				$this->fetchAllCurrencyRate();
				$this->getRate();
			}
			$this->db->free($resql);

			if ($numrows) {
				return 1;
			} else {
				return 0;
			}
		} else {
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('MultiCurrency::fetch '.join(',', $this->errors), LOG_ERR);

			return -1;
		}
	}

	/**
	 * Load all rates in object from the database
	 *
	 * @return int Return integer <0 if KO, >=0 if OK
	 */
	public function fetchAllCurrencyRate()
	{
		$sql = "SELECT cr.rowid";
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as cr';
		$sql .= ' WHERE cr.fk_multicurrency = '.((int) $this->id);
		$sql .= ' ORDER BY cr.date_sync DESC';

		$this->rates = array();

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if ($resql) {
			$num = $this->db->num_rows($resql);

			while ($obj = $this->db->fetch_object($resql)) {
				$rate = new CurrencyRate($this->db);
				$rate->fetch($obj->rowid);

				$this->rates[] = $rate;
			}
			$this->db->free($resql);

			return $num;
		} else {
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('MultiCurrency::fetchAllCurrencyRate '.join(',', $this->errors), LOG_ERR);

			return -1;
		}
	}

	/**
	 * Update object into database
	 *
	 * @param  User $user      	User that modifies
	 * @param  bool $trigger 	true=launch triggers after, false=disable triggers
	 * @return int 				Return integer <0 if KO, >0 if OK
	 */
	public function update(User $user, $trigger = true)
	{
		$error = 0;

		dol_syslog('MultiCurrency::update', LOG_DEBUG);

		// Clean parameters
		$this->name = trim($this->name);
		$this->code = trim($this->code);

		// Check parameters
		if (empty($this->code)) {
			$error++;
			dol_syslog('MultiCurrency::update $this->code can not be empty', LOG_ERR);

			return -1;
		}

		// Update request
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
		$sql .= " name = '".$this->db->escape($this->name)."',";
		$sql .= " code = '".$this->db->escape($this->code)."'";
		$sql .= " WHERE rowid = ".((int) $this->id);

		$this->db->begin();

		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('MultiCurrency::update '.join(',', $this->errors), LOG_ERR);
		}

		if (!$error && $trigger) {
			$result = $this->call_trigger('CURRENCY_MODIFY', $user);
			if ($result < 0) {
				$error++;
			}
		}

		// Commit or rollback
		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return 1;
		}
	}

	/**
	 * Delete object in database
	 *
	 * @param	User	$user		User making the deletion
	 * @param 	bool 	$trigger 	true=launch triggers after, false=disable triggers
	 * @return 	int 				Return integer <0 if KO, >0 if OK
	 */
	public function delete($user, $trigger = true)
	{
		dol_syslog('MultiCurrency::delete', LOG_DEBUG);

		$error = 0;

		$this->db->begin();

		if ($trigger) {
			$result = $this->call_trigger('CURRENCY_DELETE', $user);
			if ($result < 0) {
				$error++;
			}
		}

		if (!$error) {
			// Delete all rates before
			if (!$this->deleteRates()) {
				$error++;
				$this->errors[] = 'Error '.$this->db->lasterror();
				dol_syslog('Currency::delete  '.join(',', $this->errors), LOG_ERR);
			}

			$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
			$sql .= " WHERE rowid = ".((int) $this->id);

			dol_syslog(__METHOD__, LOG_DEBUG);
			$resql = $this->db->query($sql);
			if (!$resql) {
				$error++;
				$this->errors[] = 'Error '.$this->db->lasterror();
				dol_syslog('MultiCurrency::delete '.join(',', $this->errors), LOG_ERR);
			}
		}

		// Commit or rollback
		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return 1;
		}
	}

	/**
	 * Delete rates in database
	 *
	 * @return bool false if KO, true if OK
	 */
	public function deleteRates()
	{
		global $user;

		foreach ($this->rates as &$rate) {
			if ($rate->delete($user) <= 0) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Add a Rate into database
	 *
	 * @param double	$rate	rate value
	 * @return int 				-1 if KO, 1 if OK
	 */
	public function addRate($rate)
	{
		$currencyRate = new CurrencyRate($this->db);
		$currencyRate->rate = price2num($rate);

		if ($currencyRate->create($this->id) > 0) {
			$this->rate = $currencyRate;
			return 1;
		} else {
			$this->rate = null;
			$this->errors = $currencyRate->errors;
			return -1;
		}
	}

	/**
	 * Try get label of code in llx_currency then add rate.
	 *
	 * @param	string	$code	currency code
	 * @param	double	$rate	new rate
	 * @return 	int 			-1 if KO, 1 if OK, 2 if label found and OK
	 */
	public function addRateFromDolibarr($code, $rate)
	{
		global $user;

		$currency = new MultiCurrency($this->db);
		$currency->code = $code;
		$currency->name = $code;

		$sql = "SELECT label FROM ".MAIN_DB_PREFIX."c_currencies WHERE code_iso = '".$this->db->escape($code)."'";

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if ($resql && ($line = $this->db->fetch_object($resql))) {
			$currency->name = $line->label;
		}

		if ($currency->create($user) > 0) {
			$currency->addRate($rate);

			if (!empty($line)) {
				return 2;
			} else {
				return 1;
			}
		}

		return -1;
	}

	/**
	 * Add new entry into llx_multicurrency_rate
	 *
	 * @param double	$rate	rate value
	 * @return int Return integer <0 if KO, >0 if OK
	 */
	public function updateRate($rate)
	{
		return $this->addRate($rate);
	}

	/**
	 * Fetch CurrencyRate object in $this->rate
	 *
	 * @return int Return integer <0 if KO, 0 if not found, >0 if OK
	 */
	public function getRate()
	{
		$sql = "SELECT cr.rowid";
		$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element_line." as cr";
		$sql .= " WHERE cr.fk_multicurrency = ".((int) $this->id);
		$sql .= " AND cr.date_sync = (SELECT MAX(cr2.date_sync) FROM ".MAIN_DB_PREFIX.$this->table_element_line." AS cr2 WHERE cr2.fk_multicurrency = ".((int) $this->id).")";

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if ($resql && ($obj = $this->db->fetch_object($resql))) {
			$this->rate = new CurrencyRate($this->db);
			return $this->rate->fetch($obj->rowid);
		}

		return -1;
	}

	/**
	 * Get id of currency from code
	 *
	 * @param  DoliDB	$dbs	    object db
	 * @param  string	$code	    code value search
	 *
	 * @return int                 0 if not found, >0 if OK
	 */
	public static function getIdFromCode($dbs, $code)
	{
		global $conf;

		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."multicurrency WHERE code = '".$dbs->escape($code)."' AND entity = ".((int) $conf->entity);

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $dbs->query($sql);
		if ($resql && $obj = $dbs->fetch_object($resql)) {
			return $obj->rowid;
		} else {
			return 0;
		}
	}

	/**
	 * Get id and rate of currency from code
	 *
	 * @param DoliDB	$dbs	        Object db
	 * @param string	$code	        Code value search
	 * @param integer	$date_document	Date from document (propal, order, invoice, ...)
	 *
	 * @return 	array	[0] => id currency
	 *						[1] => rate
	 */
	public static function getIdAndTxFromCode($dbs, $code, $date_document = '')
	{
		global $conf;

		$sql1 = "SELECT m.rowid, mc.rate FROM ".MAIN_DB_PREFIX."multicurrency m";

		$sql1 .= ' LEFT JOIN '.MAIN_DB_PREFIX.'multicurrency_rate mc ON (m.rowid = mc.fk_multicurrency)';
		$sql1 .= " WHERE m.code = '".$dbs->escape($code)."'";
		$sql1 .= " AND m.entity IN (".getEntity('multicurrency').")";
		$sql2 = '';
		if (getDolGlobalString('MULTICURRENCY_USE_RATE_ON_DOCUMENT_DATE') && !empty($date_document)) {	// Use last known rate compared to document date
			$tmparray = dol_getdate($date_document);
			$sql2 .= " AND mc.date_sync <= '".$dbs->idate(dol_mktime(23, 59, 59, $tmparray['mon'], $tmparray['mday'], $tmparray['year'], true))."'";
		}
		$sql3 = " ORDER BY mc.date_sync DESC LIMIT 1";

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $dbs->query($sql1.$sql2.$sql3);

		if ($resql && $obj = $dbs->fetch_object($resql)) {
			return array($obj->rowid, $obj->rate);
		} else {
			if (getDolGlobalString('MULTICURRENCY_USE_RATE_ON_DOCUMENT_DATE')) {
				$resql = $dbs->query($sql1.$sql3);
				if ($resql && $obj = $dbs->fetch_object($resql)) {
					return array($obj->rowid, $obj->rate);
				}
			}

			return array(0, 1);
		}
	}

	/**
	 * Get the conversion of amount with invoice rate
	 *
	 * @param	int				$fk_facture				Id of invoice
	 * @param	double			$amount					amount to convert
	 * @param	string			$way					'dolibarr' mean the amount is in dolibarr currency
	 * @param	string			$table					'facture' or 'facture_fourn'
	 * @param	float|null		$invoice_rate			Invoice rate if known (to avoid to make the getInvoiceRate call)
	 * @return	double|boolean 							amount converted or false if conversion fails
	 */
	public static function getAmountConversionFromInvoiceRate($fk_facture, $amount, $way = 'dolibarr', $table = 'facture', $invoice_rate = null)
	{
		if (!is_null($invoice_rate)) {
			$multicurrency_tx = $invoice_rate;
		} else {
			$multicurrency_tx = self::getInvoiceRate($fk_facture, $table);
		}

		if ($multicurrency_tx) {
			if ($way == 'dolibarr') {
				return price2num($amount * $multicurrency_tx, 'MU');
			} else {
				return price2num($amount / $multicurrency_tx, 'MU');
			}
		} else {
			return false;
		}
	}

	/**
	 *  Get current invoite rate
	 *
	 *  @param	int 		$fk_facture 	id of facture
	 *  @param 	string 		$table 			facture or facture_fourn
	 *  @return float|bool					Rate of currency or false if error
	 */
	public static function getInvoiceRate($fk_facture, $table = 'facture')
	{
		global $db;

		$sql = "SELECT multicurrency_tx FROM ".MAIN_DB_PREFIX.$table." WHERE rowid = ".((int) $fk_facture);

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $db->query($sql);
		if ($resql && ($line = $db->fetch_object($resql))) {
			return $line->multicurrency_tx;
		}

		return false;
	}

	/**
	 * With free account we can't set source then recalcul all rates to force another source.
	 * This modify the array &$TRate.
	 *
	 * @param   stdClass	$TRate	Object containing all currencies rates
	 * @return	int					-1 if KO, 0 if nothing, 1 if OK
	 */
	public function recalculRates(&$TRate)
	{
		global $conf;

		if ($conf->currency != getDolGlobalString('MULTICURRENCY_APP_SOURCE')) {
			$alternate_source = 'USD'.$conf->currency;
			if (!empty($TRate->$alternate_source)) {
				$coef = 1 / $TRate->$alternate_source;
				foreach ($TRate as $attr => &$rate) {
					$rate *= $coef;
				}
				$TRate->USDUSD = $coef;
				return 1;
			}

			return -1; // Alternate souce not found
		}

		return 0; // Nothing to do
	}

	/**
	 * Sync rates from API
	 *
	 * @param 	string  $key                Key to use. Come from getDolGlobalString("MULTICURRENCY_APP_ID")
	 * @param   int     $addifnotfound      Add if not found
	 * @param   string  $mode				"" for standard use, "cron" to use it in a cronjob
	 * @return  int							Return integer <0 if KO, >0 if OK, if mode = "cron" OK is 0
	 */
	public function syncRates($key, $addifnotfound = 0, $mode = "")
	{
		global $conf, $db, $langs;

		if (!getDolGlobalString('MULTICURRENCY_DISABLE_SYNC_CURRENCYLAYER')) {
			if ($mode == "cron") {
				$this->output = $langs->trans('Use of API for currency update is disabled by option MULTICURRENCY_DISABLE_SYNC_CURRENCYLAYER');
			} else {
				setEventMessages($langs->trans('Use of API for currency update is disabled by option MULTICURRENCY_DISABLE_SYNC_CURRENCYLAYER'), null, 'errors');
			}
			return -1;
		}

		if (empty($key)) {
			$key = getDolGlobalString("MULTICURRENCY_APP_ID");
		}

		include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';

		$urlendpoint = 'http://api.currencylayer.com/live?access_key='.$key;
		$urlendpoint .= '&source=' . (!getDolGlobalString('MULTICURRENCY_APP_SOURCE') ? 'USD' : $conf->global->MULTICURRENCY_APP_SOURCE);

		dol_syslog("Call url endpoint ".$urlendpoint);

		$resget = getURLContent($urlendpoint);

		if ($resget['content']) {
			$response = $resget['content'];
			$response = json_decode($response);

			if ($response->success) {
				$TRate = $response->quotes;
				//$timestamp = $response->timestamp;

				if ($this->recalculRates($TRate) >= 0) {
					foreach ($TRate as $currency_code => $rate) {
						$code = substr($currency_code, 3, 3);
						$obj = new MultiCurrency($db);
						if ($obj->fetch(0, $code) > 0) {
							$obj->updateRate($rate);
						} elseif ($addifnotfound) {
							$this->addRateFromDolibarr($code, $rate);
						}
					}
				}

				if ($mode == "cron") {
					return 0;
				}
				return 1;
			} else {
				dol_syslog("Failed to call endpoint ".$response->error->info, LOG_WARNING);
				if ($mode == "cron") {
					$this->output = $langs->trans('multicurrency_syncronize_error', $response->error->info);
				} else {
					setEventMessages($langs->trans('multicurrency_syncronize_error', $response->error->info), null, 'errors');
				}
				return -1;
			}
		} else {
			return -1;
		}
	}

	/**
	 * Check in database if the current code already exists
	 *
	 * @param	string	$code 	current code to search
	 * @return	boolean         True if exists, false if not exists
	 */
	public function checkCodeAlreadyExists($code)
	{
		$currencytmp = new MultiCurrency($this->db);
		if ($currencytmp->fetch('', $code) > 0) {
			return true;
		} else {
			return false;
		}
	}
}


/**
 * Class CurrencyRate
 */
class CurrencyRate extends CommonObjectLine
{
	/**
	 * @var string Id to identify managed objects
	 */
	public $element = 'multicurrency_rate';

	/**
	 * @var string Name of table without prefix where object is stored
	 */
	public $table_element = 'multicurrency_rate';

	/**
	 * @var int ID
	 */
	public $id;

	/**
	 * @var double Rate
	 */
	public $rate;

	/**
	 * @var double Rate Indirect
	 */
	public $rate_indirect;

	/**
	 * @var integer    Date synchronisation
	 */
	public $date_sync;

	/**
	 * @var int Id of currency
	 */
	public $fk_multicurrency;

	/**
	 * @var int Id of entity
	 */
	public $entity;


	/**
	 * Constructor
	 *
	 * @param DoliDb $db Database handler
	 */
	public function __construct(DoliDB $db)
	{
		$this->db = $db;
	}

	/**
	 * Create object into database
	 *
	 * @param  int	$fk_multicurrency	Id of currency
	 * @param  bool	$trigger			true=launch triggers after, false=disable triggers
	 * @return int 						Return integer <0 if KO, Id of created object if OK
	 */
	public function create($fk_multicurrency, $trigger = true)
	{
		global $conf, $user;

		dol_syslog('CurrencyRate::create', LOG_DEBUG);

		$error = 0;
		$this->rate = price2num($this->rate);
		if (empty($this->entity) || $this->entity <= 0) {
			$this->entity = $conf->entity;
		}
		$now = empty($this->date_sync) ? dol_now() : $this->date_sync;

		// Insert request
		$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element."(";
		$sql .= ' rate,';
		$sql .= ' rate_indirect,';
		$sql .= ' date_sync,';
		$sql .= ' fk_multicurrency,';
		$sql .= ' entity';
		$sql .= ') VALUES (';
		$sql .= ' '.((float) $this->rate).',';
		$sql .= ' '.((float) $this->rate_indirect).',';
		$sql .= " '".$this->db->idate($now)."',";
		$sql .= " ".((int) $fk_multicurrency).",";
		$sql .= " ".((int) $this->entity);
		$sql .= ')';

		$this->db->begin();

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('CurrencyRate::create '.join(',', $this->errors), LOG_ERR);
		}

		if (!$error) {
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
			$this->fk_multicurrency = $fk_multicurrency;
			$this->date_sync = $now;

			if ($trigger) {
				$result = $this->call_trigger('CURRENCYRATE_CREATE', $user);
				if ($result < 0) {
					$error++;
				}
			}
		}

		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return $this->id;
		}
	}

	/**
	 * Load object in memory from the database
	 *
	 * @param 	int    $id  Id object
	 * @return 	int 		Return integer <0 if KO, 0 if not found, >0 if OK
	 */
	public function fetch($id)
	{
		dol_syslog('CurrencyRate::fetch', LOG_DEBUG);

		$sql = "SELECT cr.rowid, cr.rate, cr.rate_indirect, cr.date_sync, cr.fk_multicurrency, cr.entity";
		$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." AS cr";
		$sql .= " WHERE cr.rowid = ".((int) $id);

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if ($resql) {
			$numrows = $this->db->num_rows($resql);
			if ($numrows) {
				$obj = $this->db->fetch_object($resql);

				$this->id = $obj->rowid;
				$this->rate = $obj->rate;
				$this->rate_indirect = $obj->rate_indirect;
				$this->date_sync = $this->db->jdate($obj->date_sync);
				$this->fk_multicurrency = $obj->fk_multicurrency;
				$this->entity = $obj->entity;
			}
			$this->db->free($resql);

			if ($numrows) {
				return 1;
			} else {
				return 0;
			}
		} else {
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('CurrencyRate::fetch '.join(',', $this->errors), LOG_ERR);

			return -1;
		}
	}

	/**
	 * Update object into database
	 *
	 * @param  bool $trigger	true=launch triggers after, false=disable triggers
	 * @return int 				Return integer <0 if KO, >0 if OK
	 */
	public function update($trigger = true)
	{
		global $user;

		$error = 0;

		dol_syslog('CurrencyRate::update', LOG_DEBUG);

		$this->rate = price2num($this->rate);

		// Update request
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
		$sql .= "SET rate = ".((float) $this->rate);
		if (!empty($this->date_sync)) {
			$sql .= ", date_sync = '".$this->db->idate($this->date_sync)."'";
		}
		if (!empty($this->fk_multicurrency)) {
			$sql .= ', fk_multicurrency = '.((int) $this->fk_multicurrency);
		}
		$sql .= " WHERE rowid =".((int) $this->id);

		$this->db->begin();

		dol_syslog(__METHOD__, LOG_DEBUG);
		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = 'Error '.$this->db->lasterror();
			dol_syslog('CurrencyRate::update '.join(',', $this->errors), LOG_ERR);
		}

		if (!$error && $trigger) {
			$result = $this->call_trigger('CURRENCYRATE_MODIFY', $user);
			if ($result < 0) {
				$error++;
			}
		}

		// Commit or rollback
		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return 1;
		}
	}

	/**
	 * Delete object in database
	 *
	 * @param	User	$user		User making the deletion
	 * @param 	bool 	$trigger 	true=launch triggers after, false=disable triggers
	 * @return 	int 				Return integer <0 if KO, >0 if OK
	 */
	public function delete($user, $trigger = true)
	{
		dol_syslog('CurrencyRate::delete', LOG_DEBUG);

		$error = 0;

		$this->db->begin();

		if ($trigger) {
			$result = $this->call_trigger('CURRENCYRATE_DELETE', $user);
			if ($result < 0) {
				$error++;
			}
		}

		if (!$error) {
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element;
			$sql .= ' WHERE rowid='.((int) $this->id);

			dol_syslog(__METHOD__, LOG_DEBUG);
			$resql = $this->db->query($sql);
			if (!$resql) {
				$error++;
				$this->errors[] = 'Error '.$this->db->lasterror();
				dol_syslog('CurrencyRate::delete '.join(',', $this->errors), LOG_ERR);
			}
		}

		// Commit or rollback
		if ($error) {
			$this->db->rollback();

			return -1 * $error;
		} else {
			$this->db->commit();

			return 1;
		}
	}
}
