From 8dc6110c459b080b43476552ef96275d79111912 Mon Sep 17 00:00:00 2001 From: Tim Otte Date: Tue, 15 Sep 2020 11:33:22 +0200 Subject: [PATCH 01/77] Fixed a bug where a price equal zero would be replaced with the product price --- htdocs/comm/propal/card.php | 4 ++-- htdocs/commande/card.php | 2 +- htdocs/compta/facture/card.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index f275f16ee03..6ee54218adc 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -825,7 +825,7 @@ if (empty($reshook)) $error++; } - if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht == '' && $price_ht_devise == '') // Unit price can be 0 but not ''. Also price can be negative for proposal. + if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht === '' && $price_ht_devise === '') // Unit price can be 0 but not ''. Also price can be negative for proposal. { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors'); $error++; @@ -966,7 +966,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht is forced (ie: calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) { + if (isset($price_ht)) { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); } // On reevalue prix selon taux tva car taux tva transaction peut etre different diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 8cc3fd48aca..01db6a68fb9 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -820,7 +820,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht is forced (ie: calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) { + if (isset($price_ht)) { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 3d06f665e22..79936020cb4 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -2016,7 +2016,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht was forced (ie: from gui when calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) + if (isset($price_ht)) { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); From 0adca0a66036f3ef1edea969dfdfdc4bfb5445c4 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Mon, 21 Sep 2020 11:43:08 +0200 Subject: [PATCH 02/77] fix event creation same on all page --- htdocs/contact/agenda.php | 9 +++------ htdocs/core/class/html.formactions.class.php | 2 +- htdocs/societe/agenda.php | 9 +++------ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/htdocs/contact/agenda.php b/htdocs/contact/agenda.php index 7ac512bd32f..386847abe38 100644 --- a/htdocs/contact/agenda.php +++ b/htdocs/contact/agenda.php @@ -240,12 +240,9 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) $permok = $user->rights->agenda->myactions->create; if ((!empty($objthirdparty->id) || !empty($objcon->id)) && $permok) { - //$out.='trans("AddAnAction"),'filenew'); - //$out.=""; + if (is_object($objthirdparty) && get_class($objthirdparty) == 'Societe') $out .= '&originid='.$objthirdparty->id.($objthirdparty->id > 0 ? '&socid='.$objthirdparty->id : ''); + $out .= (!empty($objcon->id) ? '&contactid='.$objcon->id : '').'&origin=contact&originid='.$object->id.'&percentage=-1&backtopage='.urlencode($_SERVER['PHP_SELF'].($objcon->id > 0 ? '?id='.$objcon->id : '')); + $out .= '&datep='.dol_print_date(dol_now(), 'dayhourlog'); } diff --git a/htdocs/core/class/html.formactions.class.php b/htdocs/core/class/html.formactions.class.php index 8614d4ee433..1c359914bf0 100644 --- a/htdocs/core/class/html.formactions.class.php +++ b/htdocs/core/class/html.formactions.class.php @@ -202,7 +202,7 @@ class FormActions $newcardbutton = ''; if (!empty($conf->agenda->enabled) && !empty($user->rights->agenda->myactions->create)) { - $newcardbutton .= dolGetButtonTitle($langs->trans("AddEvent"), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/comm/action/card.php?action=create&datep='.dol_print_date(dol_now(), 'dayhourlog').'&origin='.$typeelement.'&originid='.$object->id.($object->socid > 0 ? '&socid='.$object->socid : ($socid > 0 ? '&socid='.$socid : '')).($projectid > 0 ? '&projectid='.$projectid : '').'&backtopage='.urlencode($urlbacktopage)); + $newcardbutton .= dolGetButtonTitle($langs->trans("AddEvent"), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/comm/action/card.php?action=create&datep='.dol_print_date(dol_now(), 'dayhourlog').'&origin='.$typeelement.'&originid='.$object->id.($object->socid > 0 ? '&socid='.$object->socid : ($socid > 0 ? '&socid='.$socid : '')).($projectid > 0 ? '&projectid='.$projectid : '').'&backtopage='.urlencode($urlbacktopage)); } diff --git a/htdocs/societe/agenda.php b/htdocs/societe/agenda.php index 61d473f5c43..0108ba0a607 100644 --- a/htdocs/societe/agenda.php +++ b/htdocs/societe/agenda.php @@ -139,12 +139,9 @@ if ($socid > 0) $permok = $user->rights->agenda->myactions->create; if ((!empty($objthirdparty->id) || !empty($objcon->id)) && $permok) { - //$out.='trans("AddAnAction"),'filenew'); - //$out.=""; + if (is_object($objthirdparty) && get_class($objthirdparty) == 'Societe') $out .= '&originid='.$objthirdparty->id.($objthirdparty->id > 0 ? '&socid='.$objthirdparty->id : '').'&backtopage='.urlencode($_SERVER['PHP_SELF'].($objthirdparty->id > 0 ? '?socid='.$objthirdparty->id : '')); + $out .= (!empty($objcon->id) ? '&contactid='.$objcon->id : '').'&percentage=-1'; + $out .= '&datep='.dol_print_date(dol_now(), 'dayhourlog'); } From 1efec17228ef6739d1af0b8a67b2ff7c45d65106 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Mon, 21 Sep 2020 11:53:09 +0200 Subject: [PATCH 03/77] remove commented code since 3 years --- htdocs/contact/agenda.php | 4 ---- htdocs/societe/agenda.php | 9 ++------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/htdocs/contact/agenda.php b/htdocs/contact/agenda.php index 386847abe38..1984097d3cb 100644 --- a/htdocs/contact/agenda.php +++ b/htdocs/contact/agenda.php @@ -245,10 +245,6 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) $out .= '&datep='.dol_print_date(dol_now(), 'dayhourlog'); } - - //print '
'; - //print '
'; - $newcardbutton = ''; if (!empty($conf->agenda->enabled)) { diff --git a/htdocs/societe/agenda.php b/htdocs/societe/agenda.php index 0108ba0a607..77953f73d7c 100644 --- a/htdocs/societe/agenda.php +++ b/htdocs/societe/agenda.php @@ -139,16 +139,11 @@ if ($socid > 0) $permok = $user->rights->agenda->myactions->create; if ((!empty($objthirdparty->id) || !empty($objcon->id)) && $permok) { - if (is_object($objthirdparty) && get_class($objthirdparty) == 'Societe') $out .= '&originid='.$objthirdparty->id.($objthirdparty->id > 0 ? '&socid='.$objthirdparty->id : '').'&backtopage='.urlencode($_SERVER['PHP_SELF'].($objthirdparty->id > 0 ? '?socid='.$objthirdparty->id : '')); - $out .= (!empty($objcon->id) ? '&contactid='.$objcon->id : '').'&percentage=-1'; + if (is_object($objthirdparty) && get_class($objthirdparty) == 'Societe') $out .= '&originid='.$objthirdparty->id.($objthirdparty->id > 0 ? '&socid='.$objthirdparty->id : '').'&backtopage='.urlencode($_SERVER['PHP_SELF'].($objthirdparty->id > 0 ? '?socid='.$objthirdparty->id : '')); + $out .= (!empty($objcon->id) ? '&contactid='.$objcon->id : '').'&percentage=-1'; $out .= '&datep='.dol_print_date(dol_now(), 'dayhourlog'); } - - //print '
'; - //print '
'; - - $newcardbutton = ''; if (!empty($conf->agenda->enabled)) { From bd4db13f9010f0302a273db581ab51694b02ac74 Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Mon, 21 Sep 2020 17:42:20 +0200 Subject: [PATCH 04/77] NEW: rate editor for multicurrency --- htdocs/multicurrency/multicurrency_rates.php | 592 +++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 htdocs/multicurrency/multicurrency_rates.php diff --git a/htdocs/multicurrency/multicurrency_rates.php b/htdocs/multicurrency/multicurrency_rates.php new file mode 100644 index 00000000000..6bf3d491c47 --- /dev/null +++ b/htdocs/multicurrency/multicurrency_rates.php @@ -0,0 +1,592 @@ +. + */ + + +/** + * \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'; +$langs->loadLangs(array("errors", "admin", "main", "companies", "resource", "holiday", "accountancy", "hrm", "orders", "contracts", "projects", "propal", "bills", "interventions")); + + + +if (!$user->admin) +{ + accessforbidden(); +} + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('admin')); + +// Load translation files required by the page + +$action = GETPOST('action', 'alpha') ?GETPOST('action', 'alpha') : 'view'; +$confirm = GETPOST('confirm', 'alpha'); +$id = GETPOST('id', 'int'); +$rowid = GETPOST('rowid', 'alpha'); +$entity = GETPOST('entity', 'int'); +$code = GETPOST('code', 'alpha'); + +$acts =array(); $actl =array(); +$acts[0] = "activate"; +$acts[1] = "disable"; +$actl[0] = img_picto($langs->trans("Disabled"), 'switch_off'); +$actl[1] = img_picto($langs->trans("Activated"), 'switch_on'); + +$listoffset = GETPOST('listoffset'); +$listlimit = GETPOST('listlimit') > 0 ?GETPOST('listlimit') : 1000; // To avoid too long dictionaries +$active = 1; + +$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; + + +// This page is a generic page to edit dictionaries +// Put here declaration of dictionaries properties + +// Sort order to show dictionary (0 is space). All other dictionaries (added by modules) will be at end of this. +$taborder = array(9, 0, 4, 3, 2, 0, 1, 8, 19, 16, 27, 38, 0, 5, 11, 0, 32, 33, 34, 0, 6, 0, 29, 0, 7, 24, 28, 17, 35, 36, 0, 10, 23, 12, 13, 0, 14, 0, 22, 20, 18, 21, 0, 15, 30, 0, 37, 0, 25, 0); + +// Name of SQL tables of dictionaries +$tabname = array(); +$tabname[1] = MAIN_DB_PREFIX."c_forme_juridique"; + +// Dictionary labels +$tablib = array(); +$tablib[1] = "DictionaryCompanyJuridicalType"; + +// Requests to extract data +$tabsql = array(); +$tabsql[1] = "SELECT f.rowid as rowid, f.code, f.libelle, c.code as country_code, c.label as country, f.active FROM ".MAIN_DB_PREFIX."c_forme_juridique as f, ".MAIN_DB_PREFIX."c_country as c WHERE f.fk_pays=c.rowid"; + +// Criteria to sort dictionaries +$tabsqlsort = array(); +$tabsqlsort[1] = "country ASC, code ASC"; + +// Field names in select result for dictionary display +$tabfield = array(); +$tabfield[1] = "code,libelle,country"; + +// Edit field names for editing a record +$tabfieldvalue = array(); +$tabfieldvalue[1] = "code,libelle,country"; + +// Field names in the table for inserting a record +$tabfieldinsert = array(); +$tabfieldinsert[1] = "code,libelle,fk_pays"; + +// Rowid name of field depending if field is autoincrement on or off.. +// Use "" if id field is "rowid" and has autoincrement on +// Use "nameoffield" if id field is not "rowid" or has not autoincrement on +$tabrowid = array(); +$tabrowid[1] = ""; + +// Condition to show dictionary in setup page +$tabcond = array(); +$tabcond[1] = (!empty($conf->societe->enabled)); + +// List of help for fields +$tabhelp = array(); +$tabhelp[1] = array('code'=>$langs->trans("EnterAnyCode")); +// List of check for fields (NOT USED YET) +$tabfieldcheck = array(); +$tabfieldcheck[1] = array(); + +// Defaut sortorder +if (empty($sortfield)) +{ + $tmp1 = explode(',', $tabsqlsort[$id]); + $tmp2 = explode(' ', $tmp1[0]); + $sortfield = preg_replace('/^.*\./', '', $tmp2[0]); +} + +/* + * Actions + */ + +_handleActions($db); +exit; + +/** + * Show fields in insert/edit mode + * + * @param array $fieldlist Array of fields + * @param Object $obj If we show a particular record, obj is filled with record fields + * @param string $tabname Name of SQL table + * @param string $context 'add'=Output field for the "add form", 'edit'=Output field for the "edit form", 'hide'=Output field for the "add form" but we dont want it to be rendered + * @return string '' or value of entity into table + */ +function fieldList($fieldlist, $obj = '', $tabname = '', $context = '') +{ + global $conf, $langs, $db, $mysoc; + global $form; + global $region_id; + global $elementList, $sourceList, $localtax_typeList; + + $formadmin = new FormAdmin($db); + $formcompany = new FormCompany($db); + $formaccounting = new FormAccounting($db); + + $withentity = ''; + + foreach ($fieldlist as $field => $value) + { + if ($fieldlist[$field] == 'entity') { + $withentity = $obj->{$fieldlist[$field]}; + continue; + } + + if (in_array($fieldlist[$field], array('code', 'libelle', 'type')) && $tabname == MAIN_DB_PREFIX."c_actioncomm" && in_array($obj->type, array('system', 'systemauto'))) + { + $hidden = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''); + print ''; + print ''; + print $langs->trans($hidden); + print ''; + } + elseif ($fieldlist[$field] == 'country') + { + if (in_array('region_id', $fieldlist)) + { + print ''; + print ''; + continue; + } // For state page, we do not show the country input (we link to region, not country) + print ''; + $fieldname = 'country'; + print $form->select_country((!empty($obj->country_code) ? $obj->country_code : (!empty($obj->country) ? $obj->country : '')), $fieldname, '', 28, 'maxwidth150 maxwidthonsmartphone'); + print ''; + } + elseif ($fieldlist[$field] == 'country_id') + { + if (!in_array('country', $fieldlist)) // If there is already a field country, we don't show country_id (avoid duplicate) + { + $country_id = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]} : 0); + print ''; + print ''; + print ''; + } + } + elseif ($fieldlist[$field] == 'region') + { + print ''; + $formcompany->select_region($region_id, 'region'); + print ''; + } + elseif ($fieldlist[$field] == 'region_id') + { + $region_id = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:0); + print ''; + print ''; + print ''; + } + elseif ($fieldlist[$field] == 'lang') + { + print ''; + print $formadmin->select_language($conf->global->MAIN_LANG_DEFAULT, 'lang'); + print ''; + } + // The type of the element (for contact types) + elseif ($fieldlist[$field] == 'element') + { + print ''; + print $form->selectarray('element', $elementList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); + print ''; + } + // The source of the element (for contact types) + elseif ($fieldlist[$field] == 'source') + { + print ''; + print $form->selectarray('source', $sourceList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); + print ''; + } + elseif ($fieldlist[$field] == 'private') + { + print ''; + print $form->selectyesno("private", (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); + print ''; + } + elseif ($fieldlist[$field] == 'type' && $tabname == MAIN_DB_PREFIX."c_actioncomm") + { + $type = (!empty($obj->type) ? $obj->type : 'user'); // Check if type is different of 'user' (external module) + print ''; + print $type.''; + print ''; + } + elseif ($fieldlist[$field] == 'type' && $tabname == MAIN_DB_PREFIX.'c_paiement') + { + print ''; + $select_list = array(0=>$langs->trans('PaymentTypeCustomer'), 1=>$langs->trans('PaymentTypeSupplier'), 2=>$langs->trans('PaymentTypeBoth')); + print $form->selectarray($fieldlist[$field], $select_list, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'2')); + print ''; + } + elseif ($fieldlist[$field] == 'recuperableonly' || $fieldlist[$field] == 'type_cdr' || $fieldlist[$field] == 'deductible' || $fieldlist[$field] == 'category_type') { + if ($fieldlist[$field] == 'type_cdr') print ''; + else print ''; + if ($fieldlist[$field] == 'type_cdr') { + print $form->selectarray($fieldlist[$field], array(0=>$langs->trans('None'), 1=>$langs->trans('AtEndOfMonth'), 2=>$langs->trans('CurrentNext')), (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); + } else { + print $form->selectyesno($fieldlist[$field], (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''), 1); + } + print ''; + } + elseif (in_array($fieldlist[$field], array('nbjour', 'decalage', 'taux', 'localtax1', 'localtax2'))) { + $class = "left"; + if (in_array($fieldlist[$field], array('taux', 'localtax1', 'localtax2'))) $class = "center"; // Fields aligned on right + print ''; + print ''; + print ''; + } + elseif (in_array($fieldlist[$field], array('libelle_facture'))) { + print ''; + $transfound = 0; + $transkey = ''; + // Special case for labels + if ($tabname == MAIN_DB_PREFIX.'c_payment_term') + { + $langs->load("bills"); + $transkey = "PaymentCondition".strtoupper($obj->code); + if ($langs->trans($transkey) != $transkey) + { + $transfound = 1; + print $form->textwithpicto($langs->trans($transkey), $langs->trans("GoIntoTranslationMenuToChangeThis")); + } + } + if (!$transfound) + { + print ''; + } + else { + print ''; + } + print ''; + } + elseif ($fieldlist[$field] == 'price' || preg_match('/^amount/i', $fieldlist[$field])) { + print ''; + } + elseif ($fieldlist[$field] == 'code' && isset($obj->{$fieldlist[$field]})) { + print ''; + } + elseif ($fieldlist[$field] == 'unit') { + print ''; + $units = array( + 'mm' => $langs->trans('SizeUnitmm'), + 'cm' => $langs->trans('SizeUnitcm'), + 'point' => $langs->trans('SizeUnitpoint'), + 'inch' => $langs->trans('SizeUnitinch') + ); + print $form->selectarray('unit', $units, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''), 0, 0, 0); + print ''; + } + // Le type de taxe locale + elseif ($fieldlist[$field] == 'localtax1_type' || $fieldlist[$field] == 'localtax2_type') + { + print ''; + print $form->selectarray($fieldlist[$field], $localtax_typeList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); + print ''; + } + elseif ($fieldlist[$field] == 'accountancy_code' || $fieldlist[$field] == 'accountancy_code_sell' || $fieldlist[$field] == 'accountancy_code_buy') + { + print ''; + if (!empty($conf->accounting->enabled)) + { + $fieldname = $fieldlist[$field]; + $accountancy_account = (!empty($obj->$fieldname) ? $obj->$fieldname : 0); + print $formaccounting->select_account($accountancy_account, '.'.$fieldlist[$field], 1, '', 1, 1, 'maxwidth200 maxwidthonsmartphone'); + } + else + { + $fieldname = $fieldlist[$field]; + print ''; + } + print ''; + } + elseif ($fieldlist[$field] == 'fk_tva') + { + print ''; + print $form->load_tva('fk_tva', $obj->taux, $mysoc, new Societe($db), 0, 0, '', false, -1); + print ''; + } + elseif ($fieldlist[$field] == 'fk_c_exp_tax_cat') + { + print ''; + print $form->selectExpenseCategories($obj->fk_c_exp_tax_cat); + print ''; + } + elseif ($fieldlist[$field] == 'fk_range') + { + print ''; + print $form->selectExpenseRanges($obj->fk_range); + print ''; + } + else + { + $fieldValue = isset($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''; + + if ($fieldlist[$field] == 'sortorder') + { + $fieldlist[$field] = 'position'; + } + + $classtd = ''; $class = ''; + if ($fieldlist[$field] == 'code') $class = 'maxwidth100'; + if (in_array($fieldlist[$field], array('dayrule', 'day', 'month', 'year', 'pos', 'use_default', 'affect', 'delay', 'position', 'sortorder', 'sens', 'category_type'))) $class = 'maxwidth50'; + if (in_array($fieldlist[$field], array('libelle', 'label', 'tracking'))) $class = 'quatrevingtpercent'; + print ''; + $transfound = 0; + $transkey = ''; + if (in_array($fieldlist[$field], array('label', 'libelle'))) // For label + { + // Special case for labels + if ($tabname == MAIN_DB_PREFIX.'c_civility') { + $transkey = "Civility".strtoupper($obj->code); + } + if ($tabname == MAIN_DB_PREFIX.'c_payment_term') { + $langs->load("bills"); + $transkey = "PaymentConditionShort".strtoupper($obj->code); + } + if ($transkey && $langs->trans($transkey) != $transkey) + { + $transfound = 1; + print $form->textwithpicto($langs->trans($transkey), $langs->trans("GoIntoTranslationMenuToChangeThis")); + } + } + if (!$transfound) + { + print ''; + } + else { + print ''; + } + print ''; + } + } + + return $withentity; +} + +function _handleActions($db) { + $action = GETPOST('action', 'alpha'); + if (empty($action)) $action = 'view'; + + $callbackName = '_action' . _camel($action); + if (!function_exists($callbackName)) { + setEventMessages('UnknownAction', array(), 'errors'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; + } + call_user_func($callbackName, $db); +} + +function _actionAdd($db) { + +} + +function _actionModify($db) { + $id = GETPOST('id', 'int'); + _showDictionary($db, 'modify', $id); +} + +function _actionView($db) { + _showDictionary($db, 'view', intval(GETPOST('id', 'int')))); +} + +function _actionDelete($db) { + global $langs; + global $delayedhtmlcontent; + $form = new Form($db); + $delayedhtmlcontent .= $form->formconfirm( + $_SERVER["PHP_SELF"].'?'.($page ? 'page='.$page.'&' : '').$paramwithsearch, + $langs->trans('DeleteLine'), + $langs->trans('ConfirmDeleteLine'), + 'confirm_delete', + '', + 0, + 1 + ); + _showDictionary($db, 'view', intval(GETPOST('id', 'int'))); +} + +function _actionConfirmDelete($db) { + $id = GETPOST('id', 'int'); + $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'multicurrency_rate' + .' WHERE rowid = ' . intval($id); + + _showDictionary($db, 'view'); +} + +function _showDictionary($db, $mode='view', $id=NULL) { + global $langs; + $form = new Form($db); + $formadmin = new FormAdmin($db); + $title = $langs->trans('CurrencyRateSetup'); + $limit = 123; + + $TVisibleColumn = array( + 'rate.date_sync' => array('Date'), + 'rate.rate' => array('Number'), + 'currency.code' => array('CurrencyCode'), + 'rate.entity' => array(), + ); + foreach ($TVisibleColumn as $colSelect => &$colParam) { + $colParam['name'] = _columnAlias($colSelect); + } + unset($colParam); + + $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 1 = 1' + . ' LIMIT ' . intval($limit); + $resql = $db->query($sql); + if (!$resql) { + llxFooter(); + return; + } + $num_rows = $db->num_rows($resql); + + llxHeader(); + echo load_fiche_titre($title, $linkback, $titlepicto); + + echo ''; + echo ''; + echo ''; + + // titres + foreach ($TVisibleColumn as $colSelect => $colParam) { + echo ''; + } + echo ''; + echo ''; + echo ''; + 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); + if (!$obj) { break; } + echo ''; + foreach ($TVisibleColumn as $colSelect => $colParam) { + $rawValue = $obj->{$colParam['name']}; + + // default callback for how to display the raw value + $cellContentCallback = '_getCellDefault'; + + // possible override in column definition + if (isset($colParam['callback'])) { + $cbName = $colParam['callback']; + // mandatory function name prefix: '_getCell' (some day, the callback may come from untrusted input) + if (strpos($cbName, '_getCell') !== 0) $cbName = '_getCell' . $cbName; + if (function_exists($cbName)) { $cellContentCallback = $cbName; } + } + + if ($mode === 'modify' && $obj->rowid === $id) { + $cellContent = call_user_func($cellContentCallback, $rawValue, 'modify', $colParam['name']); + } else { + $cellContent = call_user_func($cellContentCallback, $rawValue, 'view', $colParam['name']); + } + echo ''; + } + + echo ''; + echo ''; + } + echo ''; + echo '
'; + echo $langs->trans('multicurrencyColumn' . _camel(ucfirst($colParam['name']))); + echo '
' . $langs->trans('NoResults') . '
' . $cellContent . '' + . '' . img_picto('edit', 'edit') . '' + . '' . img_picto('delete', 'delete') . '' + . '
'; + + // End of page + llxFooter(); + $db->close(); +} + +/** + * @param $rawValue + * @param string $mode + * @param string $inputName + * @return string + */ +function _getcellDefault($rawValue, $mode='view', $inputName='') { + if ($mode === 'view') { + return dol_escape_htmltag($rawValue); + } elseif ($mode === 'modify') { + return ''; + } +} + +/** + * 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); +} From e373de91d96f666e78a9cc9f84abe531fbd1ea23 Mon Sep 17 00:00:00 2001 From: Tim Otte Date: Tue, 22 Sep 2020 14:43:09 +0200 Subject: [PATCH 05/77] Fixed a bug where a price equal zero would be replaced with the product price --- htdocs/comm/propal/card.php | 4 ++-- htdocs/commande/card.php | 2 +- htdocs/compta/facture/card.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index f275f16ee03..e824e365528 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -825,7 +825,7 @@ if (empty($reshook)) $error++; } - if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht == '' && $price_ht_devise == '') // Unit price can be 0 but not ''. Also price can be negative for proposal. + if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht === '' && $price_ht_devise === '') // Unit price can be 0 but not ''. Also price can be negative for proposal. { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors'); $error++; @@ -966,7 +966,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht is forced (ie: calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) { + if (!empty($price_ht) || $price_ht === '0') { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); } // On reevalue prix selon taux tva car taux tva transaction peut etre different diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 8cc3fd48aca..4cb01a030f4 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -820,7 +820,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht is forced (ie: calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) { + if (!empty($price_ht) || $price_ht === '0') { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 3d06f665e22..bac45f0f486 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -2016,7 +2016,7 @@ if (empty($reshook)) $tmpprodvat = price2num(preg_replace('/\s*\(.*\)/', '', $prod->tva_tx)); // if price ht was forced (ie: from gui when calculated by margin rate and cost price). TODO Why this ? - if (!empty($price_ht)) + if (!empty($price_ht) || $price_ht === '0') { $pu_ht = price2num($price_ht, 'MU'); $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU'); From b3f911f810282d3d4fe04886c1b5219d3b80eb14 Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Tue, 22 Sep 2020 16:10:28 +0200 Subject: [PATCH 06/77] WIP: rate editor --- htdocs/langs/en_US/multicurrency.lang | 8 + htdocs/langs/fr_FR/multicurrency.lang | 8 + htdocs/multicurrency/multicurrency_rates.php | 918 ++++++++++--------- 3 files changed, 523 insertions(+), 411 deletions(-) diff --git a/htdocs/langs/en_US/multicurrency.lang b/htdocs/langs/en_US/multicurrency.lang index 4dc04ff2622..8e4f1c24502 100644 --- a/htdocs/langs/en_US/multicurrency.lang +++ b/htdocs/langs/en_US/multicurrency.lang @@ -18,3 +18,11 @@ MulticurrencyReceived=Received, original currency MulticurrencyRemainderToTake=Remaining amount, original currency MulticurrencyPaymentAmount=Payment amount, original currency AmountToOthercurrency=Amount To (in currency of receiving account) + +ErrorCallbackNotFound=Callback not found: %s +CurrencyRateSetup=Multi-currency exchange rates +MulticurrencyRateDeleted=Exchange rate deleted +MulticurrencyDateSync=Date +MulticurrencyRate=Exchange rate +MulticurrencyCode=Currency code +MulticurrencyEntity=Entity diff --git a/htdocs/langs/fr_FR/multicurrency.lang b/htdocs/langs/fr_FR/multicurrency.lang index 73dd1aa7cf6..b1418c780ee 100644 --- a/htdocs/langs/fr_FR/multicurrency.lang +++ b/htdocs/langs/fr_FR/multicurrency.lang @@ -18,3 +18,11 @@ 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 +CurrencyRateSetup=Taux de change multi-devise +MulticurrencyRateDeleted=Taux de change supprimé +MulticurrencyDateSync=Date +MulticurrencyRate=Taux de change +MulticurrencyCode=Code devise +MulticurrencyEntity=Entité diff --git a/htdocs/multicurrency/multicurrency_rates.php b/htdocs/multicurrency/multicurrency_rates.php index 6bf3d491c47..e41f6ddc48b 100644 --- a/htdocs/multicurrency/multicurrency_rates.php +++ b/htdocs/multicurrency/multicurrency_rates.php @@ -31,7 +31,13 @@ $res=@include("../main.inc.php"); // For root directory 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'; -$langs->loadLangs(array("errors", "admin", "main", "companies", "resource", "holiday", "accountancy", "hrm", "orders", "contracts", "projects", "propal", "bills", "interventions")); +dol_include_once('/multicompany/class/actions_multicompany.class.php', 'ActionsMulticompany'); +/** @var Translate $langs */ +$langs->loadLangs(array( + "errors", + "admin", + "main", + "multicurrency")); @@ -41,7 +47,8 @@ if (!$user->admin) } // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('admin')); +/** @var HookManager $hookmanager */ +$hookmanager->initHooks(array('multicurrency_rates')); // Load translation files required by the page @@ -69,67 +76,9 @@ if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, $offset = $listlimit * $page; $pageprev = $page - 1; $pagenext = $page + 1; +// TODO: sorting, filtering, paginating -// This page is a generic page to edit dictionaries -// Put here declaration of dictionaries properties - -// Sort order to show dictionary (0 is space). All other dictionaries (added by modules) will be at end of this. -$taborder = array(9, 0, 4, 3, 2, 0, 1, 8, 19, 16, 27, 38, 0, 5, 11, 0, 32, 33, 34, 0, 6, 0, 29, 0, 7, 24, 28, 17, 35, 36, 0, 10, 23, 12, 13, 0, 14, 0, 22, 20, 18, 21, 0, 15, 30, 0, 37, 0, 25, 0); - -// Name of SQL tables of dictionaries -$tabname = array(); -$tabname[1] = MAIN_DB_PREFIX."c_forme_juridique"; - -// Dictionary labels -$tablib = array(); -$tablib[1] = "DictionaryCompanyJuridicalType"; - -// Requests to extract data -$tabsql = array(); -$tabsql[1] = "SELECT f.rowid as rowid, f.code, f.libelle, c.code as country_code, c.label as country, f.active FROM ".MAIN_DB_PREFIX."c_forme_juridique as f, ".MAIN_DB_PREFIX."c_country as c WHERE f.fk_pays=c.rowid"; - -// Criteria to sort dictionaries -$tabsqlsort = array(); -$tabsqlsort[1] = "country ASC, code ASC"; - -// Field names in select result for dictionary display -$tabfield = array(); -$tabfield[1] = "code,libelle,country"; - -// Edit field names for editing a record -$tabfieldvalue = array(); -$tabfieldvalue[1] = "code,libelle,country"; - -// Field names in the table for inserting a record -$tabfieldinsert = array(); -$tabfieldinsert[1] = "code,libelle,fk_pays"; - -// Rowid name of field depending if field is autoincrement on or off.. -// Use "" if id field is "rowid" and has autoincrement on -// Use "nameoffield" if id field is not "rowid" or has not autoincrement on -$tabrowid = array(); -$tabrowid[1] = ""; - -// Condition to show dictionary in setup page -$tabcond = array(); -$tabcond[1] = (!empty($conf->societe->enabled)); - -// List of help for fields -$tabhelp = array(); -$tabhelp[1] = array('code'=>$langs->trans("EnterAnyCode")); -// List of check for fields (NOT USED YET) -$tabfieldcheck = array(); -$tabfieldcheck[1] = array(); - -// Defaut sortorder -if (empty($sortfield)) -{ - $tmp1 = explode(',', $tabsqlsort[$id]); - $tmp2 = explode(' ', $tmp1[0]); - $sortfield = preg_replace('/^.*\./', '', $tmp2[0]); -} - /* * Actions */ @@ -137,264 +86,6 @@ if (empty($sortfield)) _handleActions($db); exit; -/** - * Show fields in insert/edit mode - * - * @param array $fieldlist Array of fields - * @param Object $obj If we show a particular record, obj is filled with record fields - * @param string $tabname Name of SQL table - * @param string $context 'add'=Output field for the "add form", 'edit'=Output field for the "edit form", 'hide'=Output field for the "add form" but we dont want it to be rendered - * @return string '' or value of entity into table - */ -function fieldList($fieldlist, $obj = '', $tabname = '', $context = '') -{ - global $conf, $langs, $db, $mysoc; - global $form; - global $region_id; - global $elementList, $sourceList, $localtax_typeList; - - $formadmin = new FormAdmin($db); - $formcompany = new FormCompany($db); - $formaccounting = new FormAccounting($db); - - $withentity = ''; - - foreach ($fieldlist as $field => $value) - { - if ($fieldlist[$field] == 'entity') { - $withentity = $obj->{$fieldlist[$field]}; - continue; - } - - if (in_array($fieldlist[$field], array('code', 'libelle', 'type')) && $tabname == MAIN_DB_PREFIX."c_actioncomm" && in_array($obj->type, array('system', 'systemauto'))) - { - $hidden = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''); - print ''; - print ''; - print $langs->trans($hidden); - print ''; - } - elseif ($fieldlist[$field] == 'country') - { - if (in_array('region_id', $fieldlist)) - { - print ''; - print ''; - continue; - } // For state page, we do not show the country input (we link to region, not country) - print ''; - $fieldname = 'country'; - print $form->select_country((!empty($obj->country_code) ? $obj->country_code : (!empty($obj->country) ? $obj->country : '')), $fieldname, '', 28, 'maxwidth150 maxwidthonsmartphone'); - print ''; - } - elseif ($fieldlist[$field] == 'country_id') - { - if (!in_array('country', $fieldlist)) // If there is already a field country, we don't show country_id (avoid duplicate) - { - $country_id = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]} : 0); - print ''; - print ''; - print ''; - } - } - elseif ($fieldlist[$field] == 'region') - { - print ''; - $formcompany->select_region($region_id, 'region'); - print ''; - } - elseif ($fieldlist[$field] == 'region_id') - { - $region_id = (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:0); - print ''; - print ''; - print ''; - } - elseif ($fieldlist[$field] == 'lang') - { - print ''; - print $formadmin->select_language($conf->global->MAIN_LANG_DEFAULT, 'lang'); - print ''; - } - // The type of the element (for contact types) - elseif ($fieldlist[$field] == 'element') - { - print ''; - print $form->selectarray('element', $elementList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); - print ''; - } - // The source of the element (for contact types) - elseif ($fieldlist[$field] == 'source') - { - print ''; - print $form->selectarray('source', $sourceList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); - print ''; - } - elseif ($fieldlist[$field] == 'private') - { - print ''; - print $form->selectyesno("private", (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); - print ''; - } - elseif ($fieldlist[$field] == 'type' && $tabname == MAIN_DB_PREFIX."c_actioncomm") - { - $type = (!empty($obj->type) ? $obj->type : 'user'); // Check if type is different of 'user' (external module) - print ''; - print $type.''; - print ''; - } - elseif ($fieldlist[$field] == 'type' && $tabname == MAIN_DB_PREFIX.'c_paiement') - { - print ''; - $select_list = array(0=>$langs->trans('PaymentTypeCustomer'), 1=>$langs->trans('PaymentTypeSupplier'), 2=>$langs->trans('PaymentTypeBoth')); - print $form->selectarray($fieldlist[$field], $select_list, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'2')); - print ''; - } - elseif ($fieldlist[$field] == 'recuperableonly' || $fieldlist[$field] == 'type_cdr' || $fieldlist[$field] == 'deductible' || $fieldlist[$field] == 'category_type') { - if ($fieldlist[$field] == 'type_cdr') print ''; - else print ''; - if ($fieldlist[$field] == 'type_cdr') { - print $form->selectarray($fieldlist[$field], array(0=>$langs->trans('None'), 1=>$langs->trans('AtEndOfMonth'), 2=>$langs->trans('CurrentNext')), (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); - } else { - print $form->selectyesno($fieldlist[$field], (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''), 1); - } - print ''; - } - elseif (in_array($fieldlist[$field], array('nbjour', 'decalage', 'taux', 'localtax1', 'localtax2'))) { - $class = "left"; - if (in_array($fieldlist[$field], array('taux', 'localtax1', 'localtax2'))) $class = "center"; // Fields aligned on right - print ''; - print ''; - print ''; - } - elseif (in_array($fieldlist[$field], array('libelle_facture'))) { - print ''; - $transfound = 0; - $transkey = ''; - // Special case for labels - if ($tabname == MAIN_DB_PREFIX.'c_payment_term') - { - $langs->load("bills"); - $transkey = "PaymentCondition".strtoupper($obj->code); - if ($langs->trans($transkey) != $transkey) - { - $transfound = 1; - print $form->textwithpicto($langs->trans($transkey), $langs->trans("GoIntoTranslationMenuToChangeThis")); - } - } - if (!$transfound) - { - print ''; - } - else { - print ''; - } - print ''; - } - elseif ($fieldlist[$field] == 'price' || preg_match('/^amount/i', $fieldlist[$field])) { - print ''; - } - elseif ($fieldlist[$field] == 'code' && isset($obj->{$fieldlist[$field]})) { - print ''; - } - elseif ($fieldlist[$field] == 'unit') { - print ''; - $units = array( - 'mm' => $langs->trans('SizeUnitmm'), - 'cm' => $langs->trans('SizeUnitcm'), - 'point' => $langs->trans('SizeUnitpoint'), - 'inch' => $langs->trans('SizeUnitinch') - ); - print $form->selectarray('unit', $units, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''), 0, 0, 0); - print ''; - } - // Le type de taxe locale - elseif ($fieldlist[$field] == 'localtax1_type' || $fieldlist[$field] == 'localtax2_type') - { - print ''; - print $form->selectarray($fieldlist[$field], $localtax_typeList, (!empty($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:'')); - print ''; - } - elseif ($fieldlist[$field] == 'accountancy_code' || $fieldlist[$field] == 'accountancy_code_sell' || $fieldlist[$field] == 'accountancy_code_buy') - { - print ''; - if (!empty($conf->accounting->enabled)) - { - $fieldname = $fieldlist[$field]; - $accountancy_account = (!empty($obj->$fieldname) ? $obj->$fieldname : 0); - print $formaccounting->select_account($accountancy_account, '.'.$fieldlist[$field], 1, '', 1, 1, 'maxwidth200 maxwidthonsmartphone'); - } - else - { - $fieldname = $fieldlist[$field]; - print ''; - } - print ''; - } - elseif ($fieldlist[$field] == 'fk_tva') - { - print ''; - print $form->load_tva('fk_tva', $obj->taux, $mysoc, new Societe($db), 0, 0, '', false, -1); - print ''; - } - elseif ($fieldlist[$field] == 'fk_c_exp_tax_cat') - { - print ''; - print $form->selectExpenseCategories($obj->fk_c_exp_tax_cat); - print ''; - } - elseif ($fieldlist[$field] == 'fk_range') - { - print ''; - print $form->selectExpenseRanges($obj->fk_range); - print ''; - } - else - { - $fieldValue = isset($obj->{$fieldlist[$field]}) ? $obj->{$fieldlist[$field]}:''; - - if ($fieldlist[$field] == 'sortorder') - { - $fieldlist[$field] = 'position'; - } - - $classtd = ''; $class = ''; - if ($fieldlist[$field] == 'code') $class = 'maxwidth100'; - if (in_array($fieldlist[$field], array('dayrule', 'day', 'month', 'year', 'pos', 'use_default', 'affect', 'delay', 'position', 'sortorder', 'sens', 'category_type'))) $class = 'maxwidth50'; - if (in_array($fieldlist[$field], array('libelle', 'label', 'tracking'))) $class = 'quatrevingtpercent'; - print ''; - $transfound = 0; - $transkey = ''; - if (in_array($fieldlist[$field], array('label', 'libelle'))) // For label - { - // Special case for labels - if ($tabname == MAIN_DB_PREFIX.'c_civility') { - $transkey = "Civility".strtoupper($obj->code); - } - if ($tabname == MAIN_DB_PREFIX.'c_payment_term') { - $langs->load("bills"); - $transkey = "PaymentConditionShort".strtoupper($obj->code); - } - if ($transkey && $langs->trans($transkey) != $transkey) - { - $transfound = 1; - print $form->textwithpicto($langs->trans($transkey), $langs->trans("GoIntoTranslationMenuToChangeThis")); - } - } - if (!$transfound) - { - print ''; - } - else { - print ''; - } - print ''; - } - } - - return $withentity; -} - function _handleActions($db) { $action = GETPOST('action', 'alpha'); if (empty($action)) $action = 'view'; @@ -408,88 +99,122 @@ function _handleActions($db) { call_user_func($callbackName, $db); } -function _actionAdd($db) { - -} - -function _actionModify($db) { - $id = GETPOST('id', 'int'); - _showDictionary($db, 'modify', $id); -} - -function _actionView($db) { - _showDictionary($db, 'view', intval(GETPOST('id', 'int')))); -} - -function _actionDelete($db) { +/** + * @param DoliDB $db + * @param string $mode + * @param int|null $targetId ID of the row targeted for edition, deletion, etc. + */ +function _mainView($db, $mode='view', $targetId=NULL) { global $langs; - global $delayedhtmlcontent; - $form = new Form($db); - $delayedhtmlcontent .= $form->formconfirm( - $_SERVER["PHP_SELF"].'?'.($page ? 'page='.$page.'&' : '').$paramwithsearch, - $langs->trans('DeleteLine'), - $langs->trans('ConfirmDeleteLine'), - 'confirm_delete', - '', - 0, - 1 - ); - _showDictionary($db, 'view', intval(GETPOST('id', 'int'))); -} - -function _actionConfirmDelete($db) { - $id = GETPOST('id', 'int'); - $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'multicurrency_rate' - .' WHERE rowid = ' . intval($id); - - _showDictionary($db, 'view'); -} - -function _showDictionary($db, $mode='view', $id=NULL) { - global $langs; - $form = new Form($db); - $formadmin = new FormAdmin($db); $title = $langs->trans('CurrencyRateSetup'); $limit = 123; + // column definition $TVisibleColumn = array( - 'rate.date_sync' => array('Date'), - 'rate.rate' => array('Number'), - 'currency.code' => array('CurrencyCode'), - 'rate.entity' => array(), + 'rate.date_sync' + => array('callback' => 'Date'), + 'rate.rate' + => array('callback' => 'Number'), + 'currency.code' + => array('callback' => 'CurrencyCode'), +// 'rate.entity' +// => array('callback' => 'Entity'), ); - foreach ($TVisibleColumn as $colSelect => &$colParam) { - $colParam['name'] = _columnAlias($colSelect); - } + foreach ($TVisibleColumn as $colSelect => &$colParam) { $colParam['name'] = _columnAlias($colSelect); } unset($colParam); - $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 1 = 1' - . ' LIMIT ' . intval($limit); + $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') . ')' + . ' ORDER BY rate.date_sync DESC' + . ' LIMIT ' . intval($limit); $resql = $db->query($sql); if (!$resql) { - llxFooter(); - return; + setEventMessages($db->lasterror, array(), 'errors'); + $num_rows = 0; + } else { + $num_rows = $db->num_rows($resql); } - $num_rows = $db->num_rows($resql); llxHeader(); - echo load_fiche_titre($title, $linkback, $titlepicto); + echo load_fiche_titre($title); + + echo ''; echo ''; - echo ''; - echo ''; + echo '' + . '' + . '' + . ''; - // titres + + // En-têtes de colonnes + echo ''; + echo ''; foreach ($TVisibleColumn as $colSelect => $colParam) { - echo ''; } - echo ''; + echo ''; + echo ''; + + // Formulaire des filtres de recherche + 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 ''; @@ -500,59 +225,296 @@ function _showDictionary($db, $mode='view', $id=NULL) { } 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 ''; + echo ''; foreach ($TVisibleColumn as $colSelect => $colParam) { $rawValue = $obj->{$colParam['name']}; - - // default callback for how to display the raw value - $cellContentCallback = '_getCellDefault'; - - // possible override in column definition - if (isset($colParam['callback'])) { - $cbName = $colParam['callback']; - // mandatory function name prefix: '_getCell' (some day, the callback may come from untrusted input) - if (strpos($cbName, '_getCell') !== 0) $cbName = '_getCell' . $cbName; - if (function_exists($cbName)) { $cellContentCallback = $cbName; } - } - - if ($mode === 'modify' && $obj->rowid === $id) { - $cellContent = call_user_func($cellContentCallback, $rawValue, 'modify', $colParam['name']); - } else { - $cellContent = call_user_func($cellContentCallback, $rawValue, 'view', $colParam['name']); - } - echo ''; + $displayMode = 'view'; + if ($row_is_in_edit_mode) { $displayMode = 'modify'; } + $cellContent = _getCellContent($rawValue, $colParam, $displayMode, $form_update_name); + echo ''; } - echo ''; + echo ''; echo ''; } echo ''; echo '
'; - echo $langs->trans('multicurrencyColumn' . _camel(ucfirst($colParam['name']))); + echo ''; + echo $langs->trans('Multicurrency' . _camel(ucfirst($colParam['name']))); echo '
'; + echo _getCellContent( + GETPOST('search_' . $colParam['name']), + $colParam, + 'search', + 'form-filter' + ); + echo '' + . '
' + . '' + . '' + . '
' + . '
'; + // show an empty input + echo _getCellContent('', $colParam, 'new', 'form-add-new'); + echo '' + .'
' + .'' + .'' + .'
' + .'
' . $cellContent . '' . $cellContent . '' - . '' . img_picto('edit', 'edit') . '' - . '' . img_picto('delete', 'delete') . '' - . ''; + // save form (for the row in edit mode) + if ($row_is_in_edit_mode) { + echo '
' + . '' + . '' + . '' + . '
'; + } + + // edit + delete buttons (for rows not in edit mode) + else { + echo '
' + . '' + . '' + . '' + . '
'; + echo '
' + . '' + . '' + . '' + . '
'; + } + 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': + return ''; + } + 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 _getcellDefault($rawValue, $mode='view', $inputName='') { - if ($mode === 'view') { - return dol_escape_htmltag($rawValue); - } elseif ($mode === 'modify') { - return ''; +function _getCellCurrencyCode($rawValue, $mode='view', $inputName='', $formId=NULL) { + global $db, $langs; + $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; } /** @@ -590,3 +552,137 @@ function _columnAlias($colSelect) { 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) { + _mainView($db, 'view', intval(GETPOST('id', 'int'))); +} + +/** + * Add a new currency rate + * @param DoliDB $db + */ +function _actionAdd($db) { + global $langs, $conf; + $dateSync = GETPOST('date_sync', 'alpha'); + $rate = GETPOST('rate', 'int'); + $code = GETPOST('code', 'aZ09'); + $entity = intval($conf->entity); + $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'multicurrency_rate (`date_sync`, `rate`, `fk_multicurrency`, `entity`)' + . ' VALUES (' + . ' "' . $db->escape($dateSync) . '"' + . ' ,"' . $db->escape($rate) . '"' + . ' , (SELECT rowid FROM llx_multicurrency WHERE code = "'. $db->escape($code) . '" AND entity IN ('. getEntity('multicurrency') .') LIMIT 1)' + . ' ,"' . $db->escape($entity) . '"' + .')'; + $resql = $db->query($sql); + if (!$resql) { + setEventMessages($langs->trans('TODOSaveFailed'), array(), 'errors'); + } + _mainView($db, 'view'); +} + +/** + * Show a currency rate in edit mode + * @param DoliDB $db + */ +function _actionModify($db) { + $id = intval(GETPOST('id', 'int')); + _mainView($db, 'modify', $id); +} + +/** + * Saves a currency rate + * @param $db + */ +function _actionUpdate($db) { + global $langs; + $id = intval(GETPOST('id', 'int')); + $dateSync = GETPOST('date_sync', 'alpha'); + $rate = GETPOST('rate', 'int'); + $date = date_parse($dateSync); + $date = dol_mktime( + $date['hour'], + $date['minute'], + $date['second'], + $date['month'], + $date['day'], + $date['year'] + ); + $sql = /** @lang SQL */ + 'UPDATE ' . MAIN_DB_PREFIX . 'multicurrency_rate SET' + . ' date_sync = "' . $db->idate($date) . '",' + . ' rate = "' . price2num($rate) . '"' + . ' WHERE rowid = ' . $id; + $resql = $db->query($sql); + if (!$resql) { + setEventMessages($langs->trans($db->lasterror), array(), 'errors'); + } else { + setEventMessages($langs->trans('Saved'), array(), 'mesgs'); + } + _mainView($db); +} + +/** + * Show a confirm form prior to deleting a currency rate + * @param DoliDB $db + */ +function _actionDelete($db) { + global $langs; + global $delayedhtmlcontent; + $id = intval(GETPOST('id', 'int')); + $form = new Form($db); + $formParams = array( + 'id' => $id, + ); + 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, 'view'); +} + +/** + * Delete a currency rate + * @param DoliDB $db + */ +function _actionConfirmDelete($db) { + global $langs; + $id = intval(GETPOST('id', 'int')); + if ($id === 0) { + setEventMessages($langs->trans('WrongID'), array(), 'errors'); + } else { + $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'multicurrency_rate' + . ' WHERE rowid = ' . $id; + $resql = $db->query($sql); + if (!$resql) { + setEventMessages($db->lasterror, array(), 'errors'); + } else { + setEventMessages($langs->trans('MulticurrencyRateDeleted'), array(), 'mesgs'); + } + } + _mainView($db, '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); + } +} From 592789c597b3db84ea5649607b395f17f24d12e6 Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Tue, 22 Sep 2020 18:34:59 +0200 Subject: [PATCH 07/77] Exchange rate editor: +filters --- htdocs/langs/en_US/multicurrency.lang | 1 + htdocs/langs/fr_FR/multicurrency.lang | 1 + htdocs/multicurrency/multicurrency_rates.php | 286 +++++++++++-------- 3 files changed, 171 insertions(+), 117 deletions(-) diff --git a/htdocs/langs/en_US/multicurrency.lang b/htdocs/langs/en_US/multicurrency.lang index 8e4f1c24502..6cfb6d4c62b 100644 --- a/htdocs/langs/en_US/multicurrency.lang +++ b/htdocs/langs/en_US/multicurrency.lang @@ -26,3 +26,4 @@ MulticurrencyDateSync=Date MulticurrencyRate=Exchange rate MulticurrencyCode=Currency code MulticurrencyEntity=Entity +UnknownAction=Unknown action : "%s" diff --git a/htdocs/langs/fr_FR/multicurrency.lang b/htdocs/langs/fr_FR/multicurrency.lang index b1418c780ee..e445066e6d8 100644 --- a/htdocs/langs/fr_FR/multicurrency.lang +++ b/htdocs/langs/fr_FR/multicurrency.lang @@ -26,3 +26,4 @@ MulticurrencyDateSync=Date MulticurrencyRate=Taux de change MulticurrencyCode=Code devise MulticurrencyEntity=Entité +UnknownAction=Action non reconnue : "%s" diff --git a/htdocs/multicurrency/multicurrency_rates.php b/htdocs/multicurrency/multicurrency_rates.php index e41f6ddc48b..0e8b2ecdf58 100644 --- a/htdocs/multicurrency/multicurrency_rates.php +++ b/htdocs/multicurrency/multicurrency_rates.php @@ -53,80 +53,70 @@ $hookmanager->initHooks(array('multicurrency_rates')); // Load translation files required by the page $action = GETPOST('action', 'alpha') ?GETPOST('action', 'alpha') : 'view'; -$confirm = GETPOST('confirm', 'alpha'); -$id = GETPOST('id', 'int'); -$rowid = GETPOST('rowid', 'alpha'); -$entity = GETPOST('entity', 'int'); -$code = GETPOST('code', 'alpha'); - -$acts =array(); $actl =array(); -$acts[0] = "activate"; -$acts[1] = "disable"; -$actl[0] = img_picto($langs->trans("Disabled"), 'switch_off'); -$actl[1] = img_picto($langs->trans("Activated"), 'switch_on'); - -$listoffset = GETPOST('listoffset'); -$listlimit = GETPOST('listlimit') > 0 ?GETPOST('listlimit') : 1000; // To avoid too long dictionaries -$active = 1; - -$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; -// TODO: sorting, filtering, paginating +// 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 */ - -_handleActions($db); +_completeColumns($db, $TVisibleColumn); +_handleActions($db, $TVisibleColumn); exit; -function _handleActions($db) { +function _handleActions($db, $TVisibleColumn) { + global $langs; $action = GETPOST('action', 'alpha'); if (empty($action)) $action = 'view'; $callbackName = '_action' . _camel($action); if (!function_exists($callbackName)) { - setEventMessages('UnknownAction', array(), 'errors'); + setEventMessages($langs->trans('UnknownAction', $action), array(), 'errors'); header('Location: ' . $_SERVER['PHP_SELF']); exit; } - call_user_func($callbackName, $db); + 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, $mode='view', $targetId=NULL) { +function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { global $langs; $title = $langs->trans('CurrencyRateSetup'); $limit = 123; - // 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'), - ); - foreach ($TVisibleColumn as $colSelect => &$colParam) { $colParam['name'] = _columnAlias($colSelect); } - unset($colParam); + $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); @@ -159,32 +149,22 @@ function _mainView($db, $mode='view', $targetId=NULL) { . ''; - // En-têtes de colonnes - echo ''; - echo ''; - foreach ($TVisibleColumn as $colSelect => $colParam) { - echo ''; - echo $langs->trans('Multicurrency' . _camel(ucfirst($colParam['name']))); - echo ''; - } - echo ''; - echo ''; - // Formulaire des filtres de recherche - echo ''; + echo ''; + echo ''; foreach ($TVisibleColumn as $colSelect => $colParam) { - echo ''; + echo ''; echo _getCellContent( - GETPOST('search_' . $colParam['name']), + $colParam['filter_value'], $colParam, 'search', 'form-filter' ); echo ''; } - echo '' + echo '' . '
' - . '' . '
' .'' .''; echo ''; @@ -307,9 +309,7 @@ function _getCellDefault($rawValue, $mode='view', $inputName='', $formId=NULL) { 'value' => $rawValue, 'name' => $inputName, ); - if ($formId !== NULL) { - $inputAttributes['form'] = $formId; - } + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} return _tagWithAttributes('input', $inputAttributes); case 'raw': return $rawValue; @@ -345,9 +345,7 @@ function _getCellDate($rawValue, $mode='view', $inputName='', $formId=NULL) { 'value' => preg_replace('/^(.*?) .*/', '$1', $rawValue), 'name' => $inputName, ); - if ($formId !== NULL) { - $inputAttributes['form'] = $formId; - } + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} return _tagWithAttributes('input', $inputAttributes); case 'raw': return $rawValue; @@ -391,9 +389,7 @@ function _getCellNumber($rawValue, $mode='view', $inputName='', $formId=NULL) { 'pattern' => '\d+(?:[.,]\d+)?', 'required' => 'required', ); - if ($formId !== NULL) { - $inputAttributes['form'] = $formId; - } + if ($formId !== NULL) {$inputAttributes['form'] = $formId;} return _tagWithAttributes('input', $inputAttributes); case 'raw': return $rawValue; From f97e9e4330352cb8074fbe83599ead4fed70df9d Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Wed, 23 Sep 2020 14:22:27 +0200 Subject: [PATCH 09/77] FIX: URL parameters vs. POST, tokens, use class CurrencyRate for CRUD --- .../core/modules/modMultiCurrency.class.php | 13 ++ htdocs/langs/en_US/multicurrency.lang | 5 + htdocs/langs/fr_FR/multicurrency.lang | 5 + .../class/multicurrency.class.php | 9 +- htdocs/multicurrency/multicurrency_rates.php | 163 +++++++++++++----- 5 files changed, 149 insertions(+), 46 deletions(-) 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 6cfb6d4c62b..0ff117a3f92 100644 --- a/htdocs/langs/en_US/multicurrency.lang +++ b/htdocs/langs/en_US/multicurrency.lang @@ -20,10 +20,15 @@ MulticurrencyPaymentAmount=Payment amount, original currency AmountToOthercurrency=Amount To (in currency of receiving account) 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 e445066e6d8..d6fd1cc71bd 100644 --- a/htdocs/langs/fr_FR/multicurrency.lang +++ b/htdocs/langs/fr_FR/multicurrency.lang @@ -20,10 +20,15 @@ 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 50288459a7d..32393cee9d9 100644 --- a/htdocs/multicurrency/class/multicurrency.class.php +++ b/htdocs/multicurrency/class/multicurrency.class.php @@ -762,7 +762,9 @@ 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 . '('; @@ -772,7 +774,7 @@ class CurrencyRate extends CommonObjectLine $sql .= ' entity'; $sql .= ') VALUES ('; $sql .= ' '.$this->rate.','; - $sql .= ' \'' . $now . '\','; + $sql .= ' \'' . $this->date_sync . '\','; $sql .= ' \'' . $fk_multicurrency . '\','; $sql .= ' \'' . $this->entity . '\''; $sql .= ')'; @@ -871,7 +873,8 @@ class CurrencyRate extends CommonObjectLine // Update request $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET'; - $sql .= ' rate='.$this->rate; + $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 index daf78227a15..2537fac4fe6 100644 --- a/htdocs/multicurrency/multicurrency_rates.php +++ b/htdocs/multicurrency/multicurrency_rates.php @@ -14,8 +14,6 @@ * along with this program. If not, see . */ -// FIXME: conflit entre paramètres d'URL et paramètres POST (action=filter vs. action=add) - /** * \file htdocs/multicurrency/multicurrency_rates.php * \ingroup multicurrency @@ -29,9 +27,10 @@ $res=@include("../main.inc.php"); // For root directory * @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.'/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( @@ -97,7 +96,7 @@ function _handleActions($db, $TVisibleColumn) { function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { global $langs; $title = $langs->trans('CurrencyRateSetup'); - $limit = 123; + $limit = 1000; $TSQLFilter = array(); foreach ($TVisibleColumn as $colSelect => $colParam) { @@ -139,7 +138,7 @@ function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { . ' cursor: pointer;' .'}' .'col.small-col {' - . ' width: 10%' + . ' width: 5%' .'}' . ''; @@ -165,6 +164,7 @@ function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { } echo '' . '
' + . '' . '' @@ -197,7 +197,8 @@ function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { } // entire form is inside cell because HTML does not allow forms inside tables unless they are inside cells echo '' - .'' + .'' + . _formHiddenInputs($TVisibleColumn) .'' @@ -234,6 +235,7 @@ function _mainView($db, $TVisibleColumn, $mode='view', $targetId=NULL) { // save form (for the row in edit mode) if ($row_is_in_edit_mode) { echo '' + . _formHiddenInputs($TVisibleColumn) . '' . '' . '' + . _formHiddenInputs($TVisibleColumn) . '' . '' . '' . '
'; echo '
' + . _formHiddenInputs($TVisibleColumn) . '' . '' . '