diff --git a/htdocs/core/modules/modMultiCurrency.class.php b/htdocs/core/modules/modMultiCurrency.class.php index 47788fa44c8..e15999c9c12 100644 --- a/htdocs/core/modules/modMultiCurrency.class.php +++ b/htdocs/core/modules/modMultiCurrency.class.php @@ -175,6 +175,19 @@ class modMultiCurrency extends DolibarrModules // Main menu entries $this->menu = array(); // List of menus to add $r = 0; + $this->menu[$r]=array( 'fk_menu'=>'fk_mainmenu=tools', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode + 'type'=>'left', // This is a Left menu entry + 'titre'=>$langs->trans('MulticurrencyRateSetup'), + 'mainmenu'=>'', + 'leftmenu'=>'multicurrency', + 'url'=>'/multicurrency/multicurrency_rates.php', + 'langs'=>'multicurrency', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory. + 'position'=>100, + 'enabled'=>'$conf->multicurrency->enabled', // Define condition to show or hide menu entry. Use '$conf->multicurrency->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected. + 'perms'=>'1', // Use 'perms'=>'$user->rights->multicurrency->level1->level2' if you want your menu with a permission rules + 'target'=>'', + 'user'=>0); // 0=Menu for internal users, 1=external users, 2=both + $r++; // Add here entries to declare new menus // diff --git a/htdocs/langs/en_US/multicurrency.lang b/htdocs/langs/en_US/multicurrency.lang index bfcbd11fb7c..cd06f337bf0 100644 --- a/htdocs/langs/en_US/multicurrency.lang +++ b/htdocs/langs/en_US/multicurrency.lang @@ -20,3 +20,17 @@ MulticurrencyPaymentAmount=Payment amount, original currency AmountToOthercurrency=Amount To (in currency of receiving account) CurrencyRateSyncSucceed=Currency rate synchronization done successfuly MULTICURRENCY_USE_CURRENCY_ON_DOCUMENT=Use the currency of the document for online payments + +ErrorCallbackNotFound=Callback not found: %s +MulticurrencyErrorCouldNotCreateRate=Unable to create exchange rate "%s" for currency "%s" +MulticurrencyErrorCurrencyCodeNotFound=Currency code not found: "%s" +MulticurrencyErrorCouldNotFetchRate=Exchange rate #%d not found +CurrencyRateSetup=Multi-currency exchange rates +MulticurrencyRateSetup=Edit exchange rates +MulticurrencyRateDeleted=Exchange rate deleted +MulticurrencyDateSync=Date +MulticurrencyRate=Exchange rate +MulticurrencyCode=Currency code +MulticurrencyEntity=Entity +MulticurrencyRateSaved=Exchange rate saved +UnknownAction=Unknown action : "%s" diff --git a/htdocs/langs/fr_FR/multicurrency.lang b/htdocs/langs/fr_FR/multicurrency.lang index 73dd1aa7cf6..d6fd1cc71bd 100644 --- a/htdocs/langs/fr_FR/multicurrency.lang +++ b/htdocs/langs/fr_FR/multicurrency.lang @@ -18,3 +18,17 @@ MulticurrencyReceived=Reçu, devise originale MulticurrencyRemainderToTake=Montant restant, devise d'origine MulticurrencyPaymentAmount=Montant du règlement (devise d'origine) AmountToOthercurrency=Montant destination (en devise du compte de réception) + +ErrorCallbackNotFound=Callback introuvable : %s +MulticurrencyErrorCouldNotCreateRate=Impossible de créer le taux "%s" sur la devise "%s" +MulticurrencyErrorCurrencyCodeNotFound=Code devise introuvable : "%s" +MulticurrencyErrorCouldNotFetchRate=Taux de change %d introuvable +CurrencyRateSetup=Taux de change multi-devise +MulticurrencyRateSetup=Modifier les taux de change +MulticurrencyRateDeleted=Taux de change supprimé +MulticurrencyDateSync=Date +MulticurrencyRate=Taux de change +MulticurrencyCode=Code devise +MulticurrencyEntity=Entité +MulticurrencyRateSaved=Taux de change enregistré +UnknownAction=Action non reconnue : "%s" diff --git a/htdocs/multicurrency/class/multicurrency.class.php b/htdocs/multicurrency/class/multicurrency.class.php index 2ce3277ae89..105416c7140 100644 --- a/htdocs/multicurrency/class/multicurrency.class.php +++ b/htdocs/multicurrency/class/multicurrency.class.php @@ -756,7 +756,8 @@ class CurrencyRate extends CommonObjectLine $error = 0; $this->rate = price2num($this->rate); if (empty($this->entity) || $this->entity <= 0) $this->entity = $conf->entity; - $now = date('Y-m-d H:i:s'); + // if no date defined on object, use current date + if (empty($this->date_sync)) $this->date_sync = date('Y-m-d H:i:s'); // Insert request $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'('; @@ -766,9 +767,9 @@ class CurrencyRate extends CommonObjectLine $sql .= ' entity'; $sql .= ') VALUES ('; $sql .= ' '.$this->rate.','; - $sql .= ' \''.$now.'\','; - $sql .= ' \''.$fk_multicurrency.'\','; - $sql .= ' \''.$this->entity.'\''; + $sql .= ' \'' . $this->date_sync . '\','; + $sql .= ' \'' . $fk_multicurrency . '\','; + $sql .= ' \'' . $this->entity . '\''; $sql .= ')'; $this->db->begin(); @@ -864,9 +865,10 @@ class CurrencyRate extends CommonObjectLine $this->rate = price2num($this->rate); // Update request - $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET'; - $sql .= ' rate='.$this->rate; - $sql .= ' WHERE rowid='.$this->id; + $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET'; + $sql .= ' rate=' . $this->rate; + if ($this->date_sync) $sql .= ', date_sync="' . $this->db->escape($this->date_sync) . '"'; + $sql .= ' WHERE rowid=' . $this->id; $this->db->begin(); diff --git a/htdocs/multicurrency/multicurrency_rates.php b/htdocs/multicurrency/multicurrency_rates.php new file mode 100644 index 00000000000..5671a0bab28 --- /dev/null +++ b/htdocs/multicurrency/multicurrency_rates.php @@ -0,0 +1,774 @@ +. + */ + +/** + * \file htdocs/multicurrency/multicurrency_rates.php + * \ingroup multicurrency + * \brief Shows an exchange rate editor + */ + +$res=@include("../main.inc.php"); // For root directory + +/** + * @var User $user + * @var DoliDB $db + */ + +//require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; +//require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; +//require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +require_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php'; +dol_include_once('/multicompany/class/actions_multicompany.class.php', 'ActionsMulticompany'); +/** @var Translate $langs */ +$langs->loadLangs(array( + "errors", + "admin", + "main", + "multicurrency")); + + + +if (!$user->admin) +{ + accessforbidden(); +} + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +/** @var HookManager $hookmanager */ +$hookmanager->initHooks(array('multicurrency_rates')); + +// Load translation files required by the page + +$action = GETPOST('action', 'alpha') ?GETPOST('action', 'alpha') : 'view'; + +// column definition +$TVisibleColumn = array( + 'rate.date_sync' + => array('callback' => 'Date'), + 'rate.rate' + => array('callback' => 'Number'), + 'currency.code' + => array('callback' => 'CurrencyCode'), + // 'rate.entity' + // => array('callback' => 'Entity'), +); + +/* + * Actions + */ +_completeColumns($db, $TVisibleColumn); +_handleActions($db, $TVisibleColumn); +exit; + +function _handleActions($db, $TVisibleColumn) { + global $langs; + $action = GETPOST('action', 'alpha'); + if (empty($action)) $action = 'view'; + + $callbackName = '_action' . _camel($action); + if (!function_exists($callbackName)) { + setEventMessages($langs->trans('UnknownAction', $action), array(), 'errors'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; + } + call_user_func($callbackName, $db, $TVisibleColumn); +} + +/** + * @param DoliDB $db + * @param array $TVisibleColumn + * @param string $mode + * @param int|null $targetId ID of the row targeted for edition, deletion, etc. + */ +function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { + global $langs; + $title = $langs->trans('CurrencyRateSetup'); + $limit = 1000; + + $TSQLFilter = array(); + foreach ($TVisibleColumn as $colSelect => $colParam) { + if (isset($colParam['filter_value']) && !empty($colParam['filter_value'])) { + $cbName = '_getSQLFilter' . ucfirst($colParam['callback']); + if (function_exists($cbName)) { + $sqlFilter = call_user_func($cbName, $db, $colParam); + } else { + $sqlFilter = ' AND ' . $colParam['name'] . ' = ' + . '"' . $db->escape($colParam['filter_value']) . '"'; + } + $TSQLFilter[] = $sqlFilter; + } + } + + $sql = /** @lang SQL */ + 'SELECT rate.rowid, ' . join(', ', array_keys($TVisibleColumn)) . ' FROM ' . MAIN_DB_PREFIX . 'multicurrency_rate rate' + . ' LEFT JOIN ' . MAIN_DB_PREFIX . 'multicurrency currency ON rate.fk_multicurrency = currency.rowid' + . ' WHERE rate.entity IN (' . getEntity('multicurrency') . ')' + . (count($TSQLFilter) ? join('', $TSQLFilter) : '') + . ' ORDER BY rate.date_sync DESC' + . ' LIMIT ' . intval($limit); + $resql = $db->query($sql); + if (!$resql) { + setEventMessages($db->lasterror, array(), 'errors'); + $num_rows = 0; + } else { + $num_rows = $db->num_rows($resql); + } + + llxHeader(); + echo load_fiche_titre($title); + + echo ''; + + echo ''; + echo '' + . '' + . '' + . ''; + + + // Formulaire des filtres de recherche + echo ''; + echo ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + echo ''; + } + echo ''; + echo ''; + + // En-têtes de colonnes + echo ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + echo ''; + } + echo ''; + echo ''; + echo ''; + + // formulaire d'ajout ('new') + echo ''; + echo ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + echo ''; + } + // entire form is inside cell because HTML does not allow forms inside tables unless they are inside cells + echo ''; + echo ''; + echo ''; + + // lignes + echo ''; + if (!$num_rows) { + echo ''; + $colspan = count($TVisibleColumn); + $colspan += 1; // account for the action column + echo ''; + echo ''; + } + for ($i = 0; $i < $num_rows; $i++) { + $obj = $db->fetch_object($resql); + $objId = intval($obj->rowid); + $row_is_in_edit_mode = ($mode === 'modify' && $objId === $targetId); + $form_update_name = "form-update-" . $objId; + if (!$obj) { break; } + echo ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + $rawValue = $obj->{$colParam['name']}; + $displayMode = 'view'; + if ($row_is_in_edit_mode) { $displayMode = 'modify'; } + $cellContent = _getCellContent($rawValue, $colParam, $displayMode, $form_update_name); + echo ''; + } + + echo ''; + echo ''; + } + echo ''; + echo '
'; + echo _getCellContent( + $colParam['filter_value'], + $colParam, + 'search', + 'form-filter' + ); + echo '' + . '
' + . '' + . '' + . '' + . '
' + . '
'; + echo $langs->trans('Multicurrency' . _camel(ucfirst($colParam['name']))); + echo '
'; + // show an empty input + echo _getCellContent('', $colParam, 'new', 'form-add-new'); + echo '' + .'
' + . _formHiddenInputs($TVisibleColumn) + .'' + .'
' + .'
' . $langs->trans('NoResults') . '
' . $cellContent . ''; + // save form (for the row in edit mode) + if ($row_is_in_edit_mode) { + echo '
' + . _formHiddenInputs($TVisibleColumn) + . '' + . '' + . '' + . '
'; + } + + // edit + delete buttons (for rows not in edit mode) + else { + echo '
' + . _formHiddenInputs($TVisibleColumn) + . '' + . '' + . '' + . '
'; + echo '
' + . _formHiddenInputs($TVisibleColumn) + . '' + . '' + . '' + . '
'; + } + echo '
'; + + + // End of page + llxFooter(); + $db->close(); +} + +/** + * Calls a specialized callback depending on $colParam['callback'] (or a default one + * if not set or found) to return a representation of $rawValue depending on $mode: + * + * @param mixed $rawValue A raw value (as returned by the SQL handler) + * @param array $colParam Information about the kind of value (date, price, etc.) + * @param string $mode 'view', => returns the value for end user display + * 'modify', => returns a form to modify the value + * 'new', => returns a form to put the value in a new record + * 'raw', => does nothing (returns the raw value) + * 'text' => returns a text-only version of the value + * (for text-only exports etc.) + * @param string|null $formId HTML id of the form on which to attach the input in + * 'modify' and 'new' modes + * @return string + */ +function _getCellContent($rawValue, $colParam, $mode='view', $formId=NULL) { + if ($mode === 'raw') return $rawValue; + $callback = _cellContentCallbackName($colParam); + return call_user_func($callback, $rawValue, $mode, $colParam['name'], $formId); +} + +/** + * @param $rawValue + * @param string $mode + * @param string $inputName + * @return string + * @see _getCellContent() + */ +function _getCellDefault($rawValue, $mode='view', $inputName='', $formId=NULL) { + switch ($mode) { + case 'view': + return dol_escape_htmltag($rawValue); + case 'modify': case 'new': + $inputAttributes = array( + 'value' => $rawValue, + 'name' => $inputName, + ); + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} + return _tagWithAttributes('input', $inputAttributes); + case 'raw': + return $rawValue; + case 'text': + return strip_tags($rawValue); + case 'search': + return ''; + } + return $rawValue; +} + +/** + * @param $rawValue + * @param string $mode + * @param string $inputName + * @return string + * @see _getCellContent() + */ +function _getCellDate($rawValue, $mode='view', $inputName='', $formId=NULL) { + global $db; + switch ($mode) { + case 'view': + $tms = $db->jdate($rawValue); + $dateFormat = '%d/%m/%Y %H:%M'; + $dateFormat = ''; + return dol_print_date($tms, $dateFormat); + case 'modify': case 'new': + $inputAttributes = array( + 'type' => 'date', + 'value' => preg_replace('/^(.*?) .*/', '$1', $rawValue), + 'name' => $inputName, + ); + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} + return _tagWithAttributes('input', $inputAttributes); + case 'raw': + return $rawValue; + case 'text': + return strip_tags($rawValue); + case 'search': + $select = _tagWithAttributes('select', array( + 'form' => $formId, + 'name' => 'search_' . $inputName + )); + $y = intval(dol_print_date(dol_now(), '%Y')); + $emptyOptParams = array('value' => ''); + if (empty($rawValue)) { $emptyOptParams['selected'] = 'selected'; } + $options = array(_tagWithAttributes('option', $emptyOptParams)); + $options += array_map(function($i) use ($rawValue) { + $optParams = array('value' => $i); + if ($rawValue == $i) $optParams['selected'] = 'selected'; + return _tagWithAttributes('option', $optParams) . $i . ''; + }, range($y-10, $y+1)); + return $select . join("\n", $options) . ''; + } + return $rawValue; +} + +/** + * @param $rawValue + * @param string $mode + * @param string $inputName + * @return string + * @see _getCellContent() + */ +function _getCellNumber($rawValue, $mode='view', $inputName='', $formId=NULL) { + switch ($mode) { + case 'view': + return price($rawValue); + case 'modify': case 'new': + $inputAttributes = array( + 'value' => $rawValue, + 'name' => $inputName, + 'placeholder' => '0,00', + 'pattern' => '\d+(?:[.,]\d+)?', + 'required' => 'required', + ); + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} + return _tagWithAttributes('input', $inputAttributes); + case 'raw': + return $rawValue; + case 'text': + return strip_tags($rawValue); + case 'search': + return ''; + } + return $rawValue; +} + +/** + * @param $rawValue + * @param string $mode + * @param string $inputName + * @return string + */ +function _getCellCurrencyCode($rawValue, $mode='view', $inputName='', $formId=NULL) { + global $db, $langs; + if ($formId) $formId = htmlspecialchars($formId, ENT_QUOTES); + $form = new Form($db); + switch ($mode) { + case 'view': case 'modify': // 'modify' because the currency code is read-only + return $langs->cache_currencies[$rawValue]['label'] . ' (' . $langs->getCurrencySymbol($rawValue) . ')'; + case 'new': + $select = $form->selectMultiCurrency($rawValue, $inputName, 1); + if ($formId) { + // add form attribute to the output of selectCurrency + $select = preg_replace( + '/^trans('ErrorCallbackNotFound', $cbName), + 'warnings' + ); + } + } + return $cellContentCallback; +} + +/** + * Returns an opening (or self-closing) tag with the (escaped) requested attributes + * + * Example: _tagWithAttributes('input', ['name' => 'test', 'value' => '"hello"']) + * => '' + * + * + * @param string $tagName + * @param array $TAttribute [$attrName => $attrVal] + * @return string + */ +function _tagWithAttributes($tagName, $TAttribute) { + $selfClosing = in_array($tagName, array('area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr')); + $tag = '<' . $tagName; + foreach ($TAttribute as $attrName => $attrVal) { + $tag .= ' ' . $attrName . '="' . str_replace("\n", " ", htmlspecialchars($attrVal, ENT_QUOTES)) . '"'; + } + $tag .= $selfClosing ? ' />' : ' >'; + return $tag; +} + +/** + * Returns the name of the column in the object returned by DoliDB::fetch_object + * + * Example: + * $colSelect = 'abcd' => 'abcd' // no table name, no alias + * 'table.xyz AS abcd' => 'abcd' // with table name + * 'table.abcd' => 'abcd' // with table name and alias + * 'xyz AS abcd' => 'xyz AS abcd' // not handled: alias without table name + * @param string $colSelect + * @return string + */ +function _columnAlias($colSelect) { + // the regexp replacement does this: + // 'table.abcd AS efgh' => 'efgh' + // regexp explanation: + // '.*\.`?' => not captured: anything, then a dot, then an optional backtick; + // '([^ `]+)`?' => capture 1: anything that doesn't have a space or a backtick (then an optional, uncaptured backtick) + // '(?:.....)?' => non-capturing: makes whatever is inside the parentheses optional + // '\s+as\s+`?' => not captured: whitespace, then 'AS', then whitespace, then an optional backtick + // '([^ `]+)' => capture 2: anything that doesn't have a space or a backtick + return preg_replace_callback( + '/^.*\.`?([^ `]+)`?(?:\s+as\s+`?([^ `]+)`?)?/i', + function ($m) { return isset($m[2]) ? $m[2] : $m[1]; }, + $colSelect + ); +} + +/** + * Returns $str in camel case ("snake_case_versus_camel_case" => 'snakeCaseVersusCamelCase') + * @param $str + * @return string|string[]|null + */ +function _camel($str) { + return preg_replace_callback('/_(.)?/', function($m) { return ucfirst($m[1]); }, $str); +} + + +/** + * Default: view all currency rates + * @param DoliDB $db + */ +function _actionView($db, $TVisibleColumn) { + _mainView($db, $TVisibleColumn, 'view', intval(GETPOST('id', 'int'))); +} + +function _actionFilter($db, $TVisibleColumn) { + _mainView($db, $TVisibleColumn); +} + +function _actionRemoveFilters($db, $TVisibleColumn) { + foreach ($TVisibleColumn as $colSelect => &$colParam) { + if (isset($colParam['filter_value'])) { + unset($colParam['filter_value']); + } + } + unset($colParam); + _mainView($db, $TVisibleColumn); +} + +/** + * Add a new currency rate + * @param DoliDB $db + */ +function _actionAdd($db, $TVisibleColumn) { + global $langs, $conf; + $dateSync = GETPOST('date_sync', 'alpha'); + $rate = GETPOST('rate', 'int'); + $code = GETPOST('code', 'aZ09'); + $entity = intval($conf->entity); + $multiCurrency = new MultiCurrency($db); + $resfetch = $multiCurrency->fetch(null, $code); + if ($resfetch <= 0) { + setEventMessages($langs->trans('MulticurrencyErrorCurrencyCodeNotFound', $code), array(), 'errors'); + } else { + $mcRate = new CurrencyRate($db); + $mcRate->date_sync = $dateSync; + $mcRate->rate = $rate; + $mcRate->entity = $entity; + $rescreate = $mcRate->create($multiCurrency->id); + if ($rescreate <= 0) { + setEventMessages($langs->trans('MulticurrencyErrorCouldNotCreateRate', $rate, $code), array(), 'errors'); + } + } + _mainView($db, $TVisibleColumn, 'view'); +} + +/** + * Show a currency rate in edit mode + * @param DoliDB $db + */ +function _actionModify($db, $TVisibleColumn) { + $id = intval(GETPOST('id', 'int')); + _mainView($db, $TVisibleColumn, 'modify', $id); +} + +/** + * Saves a currency rate + * @param $db + */ +function _actionUpdate($db, $TVisibleColumn) { + global $langs; + $id = intval(GETPOST('id', 'int')); + $dateSync = GETPOST('date_sync', 'alpha'); + $rate = GETPOST('rate', 'int'); + $mcRate = new CurrencyRate($db); + $resfetch = $mcRate->fetch($id); + if ($resfetch <= 0) { + setEventMessages($langs->trans('MulticurrencyErrorCouldNotFetchRate', $id), array(), 'errors'); + } else { + $mcRate->date_sync = $dateSync; + $mcRate->rate = $rate; + $resupdate = $mcRate->update(); + if ($resupdate <= 0) { + setEventMessages($langs->trans($db->lasterror), array(), 'errors'); + } else { + setEventMessages($langs->trans('MulticurrencyRateSaved'), array(), 'mesgs'); + } + } + + _mainView($db, $TVisibleColumn); +} + +/** + * Show a confirm form prior to deleting a currency rate + * @param DoliDB $db + */ +function _actionDelete($db, $TVisibleColumn) { + global $langs; + global $delayedhtmlcontent; + $id = intval(GETPOST('id', 'int')); + $form = new Form($db); + $formParams = array( + 'id' => $id, + 'token' => newToken(), + ); + foreach ($TVisibleColumn as $colSelect => $colParam) { + if (isset($colParam['filter_value'])) { + $formParams['search_' . $colParam['name']] = $colParam['filter_value']; + } + } + if (isset($page)) $formParams['page'] = $page; + $formParams = http_build_query($formParams); + $delayedhtmlcontent .= $form->formconfirm( + $_SERVER["PHP_SELF"].'?'.$formParams, + $langs->trans('DeleteLine'), + $langs->trans('ConfirmDeleteLine'), + 'confirm_delete', + '', + 0, + 1 + ); + _mainView($db, $TVisibleColumn, 'view'); +} + +/** + * Delete a currency rate + * @param DoliDB $db + */ +function _actionConfirmDelete($db, $TVisibleColumn) { + global $langs; + $id = intval(GETPOST('id', 'int')); + if (empty($id)) { + setEventMessages($langs->trans('WrongID'), array(), 'errors'); + } else { + $mcRate = new CurrencyRate($db); + $resfetch = $mcRate->fetch($id); + if ($resfetch <= 0) { + setEventMessages($langs->trans('MulticurrencyErrorCouldNotFetchRate', $id), array(), 'errors'); + } else { + $resdelete = $mcRate->delete(); + if ($resdelete <= 0) { + setEventMessages($db->lasterror, array(), 'errors'); + } else { + setEventMessages($langs->trans('MulticurrencyRateDeleted'), array(), 'mesgs'); + } + } + } + _mainView($db, $TVisibleColumn, 'view'); +} + +/** + * Calls setEventMessages only if $message is not already stored for display + * + * @param string $message + * @param string $level 'errors', 'mesgs', 'warnings' + */ +function _setEventMessageOnce($message, $level='errors') { + if (!in_array($message, $_SESSION['dol_events'][$level])) { + setEventMessages($message, array(), $level); + } +} + +/** + * Completes the column definition array with values from HTTP query + * @param DoliDB $db + * @param array $TVisibleColumn + */ +function _completeColumns($db, &$TVisibleColumn) { + foreach ($TVisibleColumn as $colSelect => &$colParam) { + $colParam['name'] = _columnAlias($colSelect); + if (GETPOSTISSET('search_' . $colParam['name'])) { + $searchValue = GETPOST('search_' . $colParam['name']); + if (empty($searchValue)) continue; + $colParam['filter_value'] = $searchValue; + } + } + unset($colParam); +// $confirm = GETPOST('confirm', 'alpha'); +// $listoffset = GETPOST('listoffset'); +// $listlimit = GETPOST('listlimit') > 0 ?GETPOST('listlimit') : 1000; // To avoid too long dictionaries +// $sortfield = GETPOST("sortfield", 'alpha'); +// $sortorder = GETPOST("sortorder", 'alpha'); +// $page = GETPOST("page", 'int'); +// if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +// $offset = $listlimit * $page; +// $pageprev = $page - 1; +// $pagenext = $page + 1; +} + +/** + * @param DoliDB $db + * @param array $colParam + * @return string + */ +function _getSQLFilterNumber($db, $colParam) { + $filterVal = $colParam['filter_value']; + // apply price2num to every part of the string delimited by '<' or '>' + $filterVal = join( + '', + array_map( + 'price2num', + preg_split( + '/([><])/', + $filterVal, + -1, + PREG_SPLIT_DELIM_CAPTURE + ) + ) + ); + $sqlFilter = natural_search($colParam['name'], $filterVal, 1); + return $sqlFilter; +} + +function _getSQLFilterDate($db, $colParam) { + $year = intval($colParam['filter_value']); + $yearPlusOne = ($year+1) . '-01-01 00:00:00'; + $year .= '-01-01 00:00:00'; + $sqlFilter = ' AND (rate.date_sync > "' . $year . '"' + . ' AND rate.date_sync < "' . ($yearPlusOne) . '")'; + return $sqlFilter; +} + +/** + * Returns the hidden fields that need to be attached to all + * forms (such as search parameters). + * + * @param $TVisibleColumn + * @return string + */ +function _formHiddenInputs($TVisibleColumn) { + $ret = ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + if (isset($colParam['filter_value'])) { + $ret .= "\n" . _tagWithAttributes('input', array( + 'type' => 'hidden', + 'name' => 'search_' . $colParam['name'], + 'value' => $colParam['filter_value'], + )); + } + } + $ret .= "\n" . _tagWithAttributes('input', array( + 'type' => 'hidden', + 'name' => 'token', + 'value' => newToken() + )); + return $ret; +}