diff --git a/ChangeLog b/ChangeLog index 46a7de5eee1..ccc028cf949 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,44 @@ The following changes may create regressions for some external modules, but were * If you setup the API to update multicurrency rate from internet, you may need to re-enter your API key (so API key will be crypted in database). +***** ChangeLog for 21.0.1 compared to 21.0.0 ***** + +FIX: #33360 +FIX: #33365 Global search for single shipment (#33401) +FIX: #33404 - to keep the method findNearest an agnostic method. +FIX: #33435 Warnings +FIX: Accountancy simplified - with multiple entities, amount of the entry is multiplied by the number of entities (#33370) +FIX: Add a new email for notification +FIX: Bad link to download tax vat document +FIX: Blank page on agenda event per user +FIX: blank page on smartphone for bank SEPA direct transfer page +FIX: close all services on contract will close all lines (#33466) +FIX: Count on supplier invoice list does not match count in DB (#33351) +FIX: CR on script output +FIX: CSS center end CSS in total +FIX: Duplicate load of extrafield ->fetch_optionals() +FIX: fatal error in notification sending email when error array is empty +FIX: Link to country setup on company setup page +FIX: Loading of deliveries in shipping card was loading everything +FIX: Missing ref_ext in group by in list of product +FIX: Must make different redirect in paymentok/ko according to frame or not. +FIX: PAIEMENT Wrong field displayed for DateChequeReceived (#33390) +FIX: picto for unknown mime type +FIX: Replace compromised tj-actions/changed-files (#33481) +FIX: Report by custom group was empty +FIX: Responsive +FIX: shipment dispatch origin line (#33415) +FIX: Show the default duration of a membership type. +FIX: Sort and search Ref Project column was missing (#33539) +FIX: syntax error on list of intervention for external users +FIX: text in tooltip on buttons when pb is not a permission problem +FIX: Translation of column in list of invoice +FIX: warnings (#33423) +FIX: Warning when getNomUrl is called before top_httphead +FIX: we must retrieve linked order_supplier and no other object (#33602) +SEC FIX: Reflected XSS reported by 柏天浩 + + ***** ChangeLog for 21.0.0 compared to 20.0 ***** For users: diff --git a/dev/build/makepack-dolibarr.pl b/dev/build/makepack-dolibarr.pl index 456ced926c6..98282dda0cf 100755 --- a/dev/build/makepack-dolibarr.pl +++ b/dev/build/makepack-dolibarr.pl @@ -19,7 +19,7 @@ use Term::ANSIColor; # Change this to defined target for option 98 and 99 $PROJECT="dolibarr"; -$PUBLISHBETARC="$ENV{'DESTIASSLOGIN'}\@vmprod1.dolibarr.org:/home/dolibarr/asso.dolibarr.org/dolibarr_documents/website/www.dolibarr.org/files"; +$PUBLISHBETARC="$ENV{'DESTIASSOLOGIN'}\@vmprod1.dolibarr.org:/home/dolibarr/asso.dolibarr.org/dolibarr_documents/website/www.dolibarr.org/files"; $PUBLISHSTABLE="$ENV{'DESTISFLOGIN'}\@frs.sourceforge.net:/home/frs/project/dolibarr"; #@LISTETARGET=("TGZ","ZIP","RPM_GENERIC","RPM_FEDORA","RPM_MANDRIVA","RPM_OPENSUSE","DEB","EXEDOLIWAMP","SNAPSHOT"); # Possible packages @@ -435,6 +435,10 @@ if ($nboftargetok) { print $ret."\n"; # Copy to final dir $NEWDESTI=$DESTI; + if ( !-d "$NEWDESTI/signatures" ) { + use File::Path qw( make_path ); + make_path "$NEWDESTI/signatures" or die "Failed to create path: $NEWDESTI/signatures"; + } print "Copy \"$SOURCE/htdocs/install/filelist-$MAJOR.$MINOR.$BUILD.xml\" to $NEWDESTI/signatures/filelist-$MAJOR.$MINOR.$BUILD.xml\n"; use File::Copy qw(copy); copy "$SOURCE/htdocs/install/filelist-$MAJOR.$MINOR.$BUILD.xml", "$NEWDESTI/signatures/filelist-$MAJOR.$MINOR.$BUILD.xml"; @@ -991,6 +995,11 @@ if ($nboftargetok) { # Removed files we don't need (already removed) #$ret=`rm -fr $BUILDROOT/$PROJECT.tmp/htdocs/includes/ckeditor/ckeditor/_source`; + $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.codeclimate.yml`; + $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.pre-commit-config.yaml`; + $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.vscode`; + $ret=`find $BUILDROOT/$PROJECT.tmp/ -type f -name '.editorconfig' -exec rm {} \\;`; + $ret=`find $BUILDROOT/$PROJECT.tmp/ -type f -name '.travis.yml' -exec rm {} \\;`; # Rename upstream changelog to match debian rules $ret=`mv $BUILDROOT/$PROJECT.tmp/ChangeLog $BUILDROOT/$PROJECT.tmp/changelog`; @@ -1195,9 +1204,15 @@ if ($nboftargetok) { "$DESTI/package_debian-ubuntu/${FILENAMEDEB}_all.deb"=>'package_debian-ubuntu', "$DESTI/package_debian-ubuntu/${FILENAMEDEB}_amd64.changes"=>'package_debian-ubuntu', "$DESTI/package_debian-ubuntu/${FILENAMEDEB}.dsc"=>'package_debian-ubuntu', - "$DESTI/package_debian-ubuntu/${FILENAMEDEB}.debian.tar.xz"=>'package_debian-ubuntu', + #"$DESTI/package_debian-ubuntu/${FILENAMEDEB}.debian.tar.xz"=>'package_debian-ubuntu', + "$DESTI/package_debian-ubuntu/${FILENAMEDEB}.debian.tar.gz"=>'package_debian-ubuntu', "$DESTI/package_debian-ubuntu/${FILENAMEDEBSHORT}.orig.tar.gz"=>'package_debian-ubuntu', - "$DESTI/package_windows/$FILENAMEEXEDOLIWAMP.exe"=>'package_windows', + "$DESTI/package_ $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.codeclimate.yml`; + $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.pre-commit-config.yaml`; + $ret=`rm -fr $BUILDROOT/$PROJECT.tmp/.vscode`; + $ret=`find $BUILDROOT/$PROJECT.tmp/ -type f -name '.editorconfig' -exec rm {} \\;`; + $ret=`find $BUILDROOT/$PROJECT.tmp/ -type f -name '.travis.yml' -exec rm {} \\;`; +windows/$FILENAMEEXEDOLIWAMP.exe"=>'package_windows', "$DESTI/standard/$FILENAMETGZ.tgz"=>'standard', "$DESTI/standard/$FILENAMETGZ.zip"=>'standard' ); diff --git a/dev/build/tgz/tar_exclude.txt b/dev/build/tgz/tar_exclude.txt index 0269130f090..59eef245a44 100644 --- a/dev/build/tgz/tar_exclude.txt +++ b/dev/build/tgz/tar_exclude.txt @@ -8,6 +8,8 @@ .idea .editorconfig .codeclimate.yml +.pre-commit-config.yaml +.mailmap Thumbs.db dev/build/exe dev/build/html diff --git a/dev/build/zip/zip_exclude.txt b/dev/build/zip/zip_exclude.txt index 11a17d808d8..0eb6025f703 100644 --- a/dev/build/zip/zip_exclude.txt +++ b/dev/build/zip/zip_exclude.txt @@ -22,3 +22,9 @@ dolibarr*.deb dolibarr*.zip cvschangelogbuilder_dolibarr* dolibarr_install.log +.travis.yml +.vscode +.idea +.editorconfig +.codeclimate.yml +.pre-commit-config.yaml diff --git a/htdocs/accountancy/class/accountancycategory.class.php b/htdocs/accountancy/class/accountancycategory.class.php index 4460153dbea..4ab7a8a65ee 100644 --- a/htdocs/accountancy/class/accountancycategory.class.php +++ b/htdocs/accountancy/class/accountancycategory.class.php @@ -618,7 +618,7 @@ class AccountancyCategory // extends CommonObject } /** - * Function to show result of an accounting account from the ledger with a direction and a period + * Function to set the property ->sdc (and ->sdcperaccount) that is the result of an accounting account from the ledger with a direction and a period * * @param int|array $cpt Accounting account or array of accounting account * @param int $date_start Date start @@ -712,7 +712,7 @@ class AccountancyCategory // extends CommonObject * Function to get an array of all active custom groups (llx_c_accunting_categories) with their accounts from the chart of account (ll_accounting_acount) * * @param int $catid Custom group ID - * @return array>|int<-1,-1> Result in table (array), -1 if KO + * @return array>|int<-1,-1> Result in table (array), -1 if KO * @see getCats(), getCptsCat() */ public function getCatsCpts($catid = 0) @@ -756,6 +756,7 @@ class AccountancyCategory // extends CommonObject 'category_type' => $obj->category_type, 'formula' => $obj->formula, 'sens' => $obj->sens, + 'dc' => $obj->sens, 'account_number' => $obj->account_number, 'account_label' => $obj->account_label ); @@ -776,7 +777,7 @@ class AccountancyCategory // extends CommonObject * @param int $categorytype -1=All, 0=Only non computed groups, 1=Only computed groups * @param int $active 1= active, 0=not active * @param int $id_report id of the report - * @return never|array|int Array of groups or -1 if error + * @return never|array|int Array of groups or -1 if error * @see getCatsCpts(), getCptsCat() */ public function getCats($categorytype = -1, $active = 1, $id_report = 1) @@ -784,11 +785,11 @@ class AccountancyCategory // extends CommonObject global $conf, $mysoc; if (empty($mysoc->country_id)) { - dol_print_error(null, 'Call to select_accounting_account with mysoc country not yet defined'); + dol_print_error(null, 'Call to getCats with mysoc country not yet defined'); exit(); } - $sql = "SELECT c.rowid, c.code, c.label, c.formula, c.position, c.category_type, c.sens"; + $sql = "SELECT c.rowid, c.code, c.label, c.formula, c.position, c.category_type, c.sens, c.fk_report"; $sql .= " FROM ".$this->db->prefix().$this->table_element." as c"; $sql .= " WHERE c.active = " . (int) $active; $sql .= " AND c.fk_report=".((int) $id_report); @@ -817,7 +818,7 @@ class AccountancyCategory // extends CommonObject 'category_type' => $obj->category_type, 'formula' => $obj->formula, 'sens' => $obj->sens, - 'bc' => $obj->sens + 'dc' => $obj->sens ); $i++; } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 3d99fcfd966..e5dfdba9a7f 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -2860,7 +2860,7 @@ if (empty($reshook)) { // Invoice situation if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) { $previousprogress = $line->getAllPrevProgress($line->fk_facture); - $fullprogress = price2num(GETPOST('progress', 'alpha'), 2); + $fullprogress = (float) price2num(GETPOST('progress', 'alpha'), 2); if ($fullprogress < $previousprogress) { $error++; diff --git a/htdocs/compta/resultat/result.php b/htdocs/compta/resultat/result.php index 1c4c360289e..8bae3c88c65 100644 --- a/htdocs/compta/resultat/result.php +++ b/htdocs/compta/resultat/result.php @@ -47,6 +47,9 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancyreport.class.php'; $langs->loadLangs(array('compta', 'bills', 'donation', 'salaries', 'accountancy')); $id_report = GETPOSTINT('id_report'); +if ($id_report <= 0) { + $id_report = 1; +} $error = 0; @@ -337,6 +340,14 @@ if ($modecompta == 'CREANCES-DETTES') { $totPerAccount = array(); if (!is_array($cats) && $cats < 0) { setEventMessages(null, $AccCat->errors, 'errors'); + } elseif (is_array($cats) && count($cats) == 0) { + print ''; + print ''; + print ''; + print $langs->trans("ErrorNoAccountingCategoryForThisCountry", $mysoc->country_code, $langs->transnoentitiesnoconv("Accountancy"), $langs->transnoentitiesnoconv("Setup"), $langs->transnoentitiesnoconv("AccountingCategory")); + print ''; + print ''; + print ''; } elseif (is_array($cats) && count($cats) > 0) { // Loop on each custom group of accounts foreach ($cats as $cat) { diff --git a/htdocs/core/class/commonobjectline.class.php b/htdocs/core/class/commonobjectline.class.php index 173cd852be1..2d846af6547 100644 --- a/htdocs/core/class/commonobjectline.class.php +++ b/htdocs/core/class/commonobjectline.class.php @@ -226,6 +226,13 @@ abstract class CommonObjectLine extends CommonObject * @var float */ public $subprice; + + /** + * Unit price including taxes + * @var float + */ + public $subprice_ttc; + /** * @var float|string */ @@ -242,10 +249,15 @@ abstract class CommonObjectLine extends CommonObject public $multicurrency_code; /** - * @var float Multicurrency subprice + * @var float Multicurrency subprice without taxes */ public $multicurrency_subprice; + /** + * @var float Multicurrency subprice including taxes + */ + public $multicurrency_subprice_ttc; + /** * @var float Multicurrency total without tax */ diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 244e40c6a85..e8645acb0cd 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -3151,7 +3151,7 @@ class Form // include search in supplier ref if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) { - $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' OR ' : ''; + $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ':''; $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'"; } $sql .= ")"; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index ecb6ac0b2d1..88b89e01ea8 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -2566,14 +2566,57 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max ); - $remoteip = getUserRemoteIP(); // Get ip when page run on a web server + // For log, we want the reliable IP first. + $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server if (!empty($remoteip)) { $data['ip'] = $remoteip; // This is when server run behind a reverse proxy - if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) { - $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip']; - } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) { - $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip']; + // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..." + // $data['ip'] is last + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + $data['ip'] = ''; + $foundremoteip = 0; + $j = 0; + foreach ($tmpips as $tmpip) { + $tmpip = trim($tmpip); + if (strtolower($tmpip) == strtolower($remoteip)) { + $foundremoteip = 1; + } + if (empty($data['ip'])) { + $data['ip'] = $tmpip; + } else { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip; + } + } + if (!$foundremoteip) { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip; + } + $data['ip'] .= (($j > 0) ? ']' : ''); + } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) { + $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']); + $data['ip'] = ''; + $foundremoteip = 0; + $j = 0; + foreach ($tmpips as $tmpip) { + $tmpip = trim($tmpip); + if (strtolower($tmpip) == strtolower($remoteip)) { + $foundremoteip = 1; + } + if (empty($data['ip'])) { + $data['ip'] = $tmpip; + } else { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip; + } + } + if (!$foundremoteip) { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip; + } + $data['ip'] .= (($j > 0) ? ']' : ''); } } elseif (!empty($_SERVER['SERVER_ADDR'])) { // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache) @@ -4748,27 +4791,39 @@ function dol_print_ip($ip, $mode = 0) } /** - * Return the IP of remote user. + * Return the real IP of remote user. * Take HTTP_X_FORWARDED_FOR (defined when using proxy) * Then HTTP_CLIENT_IP if defined (rare) - * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy) + * Then REMOTE_ADDR (the last seerver ip in proxy chain). + * REMOTE_ADDR can't be modified by client and may be the IP of a proxy. + * Note: that if Apache module "remoteip" module is on, $_SERVER["REMOTE_ADDR"] may be replaced y HTTP_X_FORWARDED_FOR directly + * from CF-Connecting-IP, but only if remote is in trusted cloudflare list. * - * @return string Ip of remote user. + * @param int $trusted 0=Default, 1=Trusted value (the last IP that was not altered by client) + * @return string Real IP of remote user. */ -function getUserRemoteIP() +function getUserRemoteIP($trusted = 0) { - if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { - if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) { + if ($trusted) { // Return only IP we can rely on (not spoofable by the client) + $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy + // Note that if apache module remoteip has been enabled, REMOTE_ADDR can contain the real client (the value from cloudFlare HTTP_CF_CONNECTING_IP for example) + // if the proxy were added in the list of trusted proxy. + return $ip; + } + + // Try to guess the real IP of client (but this may not be reliable) + if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { + if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) { if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) { - $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client + $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client } else { $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client } } else { - $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy + $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy } } else { - $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy + $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy } return $ip; } diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 683fb2f58a9..6160d8327d2 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -1907,8 +1907,9 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef $i++; } } else { - // Should not happen. Entries are added - $newmenu->add('', $langs->trans("NoReportDefined"), 3, $user->hasRight('accounting', 'comptarapport', 'lire')); + // Should not happen. We keep a link in case it happen to go to the page to explain how to create custom groups. + $newmenu->add("/compta/resultat/result.php?mainmenu=accountancy&leftmenu=accountancy_report", $langs->trans("ByPersonalizedAccountGroups"), 3, $user->hasRight('accounting', 'comptarapport', 'lire')); + //$newmenu->add('', $langs->trans("NoReportDefined"), 3, 0); } } else { dol_print_error($db); diff --git a/htdocs/fichinter/list.php b/htdocs/fichinter/list.php index 1d00faf5f5d..3f277de7a23 100644 --- a/htdocs/fichinter/list.php +++ b/htdocs/fichinter/list.php @@ -70,6 +70,12 @@ $toselect = GETPOST('toselect', 'array'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'interventionlist'; $mode = GETPOST('mode', 'alpha'); +if (getDolGlobalInt('MAIN_SEE_SUBORDINATES')) { + $userschilds = $user->getAllChildIds(); +} else { + $userschilds = array(); +} + $search_ref = GETPOST('search_ref') ? GETPOST('search_ref', 'alpha') : GETPOST('search_inter', 'alpha'); $search_ref_client = GETPOST('search_ref_client', 'alpha'); $search_company = GETPOST('search_company', 'alpha'); @@ -168,6 +174,7 @@ if ($user->socid) { } $result = restrictedArea($user, 'ficheinter', $id, 'fichinter'); +$permissiontoreadallthirdparty = $user->hasRight('societe', 'client', 'voir'); $permissiontoread = $user->hasRight('ficheinter', 'lire'); $permissiontoadd = $user->hasRight('ficheinter', 'creer'); $permissiontodelete = $user->hasRight('ficheinter', 'supprimer'); @@ -349,12 +356,18 @@ if (!getDolGlobalString('FICHINTER_DISABLE_DETAILS') && $atleastonefieldinlines) $sql .= " AND fd.date <= '".$db->idate($search_date_end)."'"; } } -if (!$user->hasRight('societe', 'client', 'voir')) { - $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id); -} -if ($socid) { +if ($socid > 0) { $sql .= " AND s.rowid = ".((int) $socid); } +// Restriction on sale representative +if (empty($user->socid) && !$permissiontoreadallthirdparty) { + $sql .= " AND (EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = c.fk_soc AND sc.fk_user = ".((int) $user->id).")"; + if (getDolGlobalInt('MAIN_SEE_SUBORDINATES') && $userschilds) { + $sql .= " OR EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = c.fk_soc AND sc.fk_user IN (".$db->sanitize(implode(',', $userschilds))."))"; + } + $sql .= ")"; +} + if ($search_all) { $sql .= natural_search(array_keys($fieldstosearchall), $search_all); } diff --git a/htdocs/fourn/class/fournisseur.orderline.class.php b/htdocs/fourn/class/fournisseur.orderline.class.php index eba7a008ab2..7e438005cdf 100644 --- a/htdocs/fourn/class/fournisseur.orderline.class.php +++ b/htdocs/fourn/class/fournisseur.orderline.class.php @@ -440,34 +440,36 @@ class CommandeFournisseurLigne extends CommonOrderLine $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET"; $sql .= " description='".$this->db->escape($this->desc)."'"; $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'"; - $sql .= ", subprice='".price2num($this->subprice)."'"; - //$sql.= ",remise='".price2num($remise)."'"; - $sql .= ", remise_percent='".price2num($this->remise_percent)."'"; - - $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'"; - $sql .= ", tva_tx='".price2num($this->tva_tx)."'"; - $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'"; - $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'"; - $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'"; - $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'"; - $sql .= ", qty='".price2num($this->qty)."'"; - $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null"); - $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null"); - $sql .= ", info_bits='".$this->db->escape((string) $this->info_bits)."'"; - $sql .= ", total_ht='".price2num($this->total_ht)."'"; - $sql .= ", total_tva='".price2num($this->total_tva)."'"; - $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'"; - $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'"; - $sql .= ", total_ttc='".price2num($this->total_ttc)."'"; - $sql .= ", product_type=".$this->product_type; - $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0); - $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape((string) $this->fk_unit)."'" : ", fk_unit=null"); - + $sql .= ", subprice = ".((float) price2num($this->subprice)); + $sql .= ", subprice_ttc = ".((float) price2num($this->subprice_ttc)); + $sql .= ", remise_percent = ".((float) price2num($this->remise_percent)); + $sql .= ", vat_src_code = '".$this->db->escape((string) $this->vat_src_code)."'"; + $sql .= ", tva_tx = ".((float) price2num($this->tva_tx)); + $sql .= ", localtax1_tx = ".((float) price2num($this->localtax1_tx)); + $sql .= ", localtax2_tx = ".((float) price2num($this->localtax2_tx)); + $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'"; + $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'"; + $sql .= ", qty = ".((float) price2num($this->qty)); + $sql .= ", date_start = ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null"); + $sql .= ", date_end = ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null"); + $sql .= ", info_bits = ".((int) $this->info_bits); + $sql .= ", total_ht = ".((float) price2num($this->total_ht)); + $sql .= ", total_tva = ".((float) price2num($this->total_tva)); + $sql .= ", total_localtax1 = ".((float) price2num($this->total_localtax1)); + $sql .= ", total_localtax2 = ".((float) price2num($this->total_localtax2)); + $sql .= ", total_ttc = ".((float) price2num($this->total_ttc)); + $sql .= ", product_type = ".((int) $this->product_type); + $sql .= ", special_code = ".((int) $this->special_code); + $sql .= ", fk_unit = ".($this->fk_unit ? ((int) $this->fk_unit) : "null"); + if (!empty($this->rang)) { + $sql .= ", rang=".((int) $this->rang); + } // Multicurrency - $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice); - $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht); - $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva); - $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc); + $sql .= ", multicurrency_subprice = ".((float) price2num($this->multicurrency_subprice)); + $sql .= ", multicurrency_subprice = ".((float) price2num($this->multicurrency_subprice_ttc)); + $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht)); + $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva)); + $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc)); $sql .= " WHERE rowid = ".((int) $this->id); diff --git a/htdocs/install/mysql/migration/19.0.0-20.0.0.sql b/htdocs/install/mysql/migration/19.0.0-20.0.0.sql index cf2697e3567..5b575cafd66 100644 --- a/htdocs/install/mysql/migration/19.0.0-20.0.0.sql +++ b/htdocs/install/mysql/migration/19.0.0-20.0.0.sql @@ -244,6 +244,9 @@ ALTER TABLE llx_knowledgemanagement_knowledgerecord MODIFY COLUMN answer longtex ALTER TABLE llx_commande_fournisseur_dispatch_extrafields RENAME TO llx_receptiondet_batch_extrafields; ALTER TABLE llx_commande_fournisseur_dispatch RENAME TO llx_receptiondet_batch; +-- VPGSQL8.2 ALTER SEQUENCE llx_commande_fournisseur_dispatch_extrafields_rowid_seq RENAME TO llx_receptiondet_batch_extrafields_rowid_seq; +-- VPGSQL8.2 ALTER SEQUENCE llx_commande_fournisseur_dispatch_rowid_seq RENAME TO llx_receptiondet_batch_rowid_seq; + -- Rename const to add customer categories on not customer/prospect third-party if enabled UPDATE llx_const SET name = 'THIRDPARTY_CAN_HAVE_CUSTOMER_CATEGORY_EVEN_IF_NOT_CUSTOMER_PROSPECT' WHERE name = 'THIRDPARTY_CAN_HAVE_CATEGORY_EVEN_IF_NOT_CUSTOMER_PROSPECT_SUPPLIER'; diff --git a/htdocs/mrp/mo_production.php b/htdocs/mrp/mo_production.php index 00fc992d23f..76f47de4319 100644 --- a/htdocs/mrp/mo_production.php +++ b/htdocs/mrp/mo_production.php @@ -1022,7 +1022,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea if (empty($costprice)) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; $productFournisseur = new ProductFournisseur($db); - if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) { + if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product, $line->qty) > 0) { $costprice = $productFournisseur->fourn_unitprice; } else { $costprice = 0; diff --git a/htdocs/public/payment/paymentko.php b/htdocs/public/payment/paymentko.php index 1336cec30f2..146ec30845b 100644 --- a/htdocs/public/payment/paymentko.php +++ b/htdocs/public/payment/paymentko.php @@ -119,14 +119,14 @@ if (empty($paymentmethod)) { dol_print_error(null, 'The back url does not contain a parameter fulltag that should help us to find the payment method used'); exit; } else { - dol_syslog("paymentmethod=".$paymentmethod); + dol_syslog("paymentko.php: paymentmethod=".$paymentmethod, LOG_DEBUG, 0, '_payment'); } // Detect $ws $reg_ws = array(); $ws = preg_match('/WS=([^\.]+)/', $FULLTAG, $reg_ws) ? $reg_ws[1] : 0; if ($ws) { - dol_syslog("Paymentko.php page is invoked from a website with ref ".$ws.". It performs actions and then redirects back to this website. A page with ref paymentko must be created for this website.", LOG_DEBUG, 0, '_payment'); + dol_syslog("paymentko.php: page is invoked from a website with ref ".$ws.". It performs actions and then redirects back to this website. A page with ref paymentko must be created for this website.", LOG_DEBUG, 0, '_payment'); } @@ -145,6 +145,7 @@ $error = 0; // Check if we have redirtodomain to do. $ws_virtuelhost = null; +$ws_id = 0; $doactionsthenredirect = 0; if ($ws) { $doactionsthenredirect = 1; @@ -153,6 +154,7 @@ if ($ws) { $result = $website->fetch(0, $ws); if ($result > 0) { $ws_virtuelhost = $website->virtualhost; + $ws_id = $website->id; } } @@ -359,5 +361,15 @@ if (!empty($doactionsthenredirect)) { $ext_urlko = DOL_URL_ROOT.'/public/website/index.php?website='.urlencode($ws).'&pageref=paymentko&fulltag='.$FULLTAG; } - print ""; + if (getDolGlobalInt('MARKETPLACE_PAYMENT_IN_FRAME') == 1) { // TODO Use a property in website module + dol_syslog("Now do a redirect in iframe mode in js to ".$ext_urlko, LOG_DEBUG, 0, '_payment'); + + // Redirect in js is not reliable + print ""; + } else { + dol_syslog("Now do a redirect using Location : ".$ext_urlko, LOG_DEBUG, 0, '_payment'); + + header("Location: ".$ext_urlko); + exit; + } } diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index b54c722c24e..9564ced0a2b 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -175,6 +175,7 @@ $error = 0; // Check if we have redirtodomain to do. $ws_virtuelhost = null; +$ws_id = 0; $doactionsthenredirect = 0; if ($ws) { $doactionsthenredirect = 1; @@ -183,6 +184,7 @@ if ($ws) { $result = $website->fetch(0, $ws); if ($result > 0) { $ws_virtuelhost = $website->virtualhost; + $ws_id = $website->id; } } @@ -1891,6 +1893,8 @@ if ($ispaymentok) { } } +dol_syslog("ispaymentok=".$ispaymentok." ispostactionok=".$ispostactionok." doactionsthenredirect=".$doactionsthenredirect, LOG_DEBUG, 0, '_payment'); + if ($ispaymentok) { // Get on url call $onlinetoken = empty($PAYPALTOKEN) ? $_SESSION['onlinetoken'] : $PAYPALTOKEN; @@ -2153,7 +2157,18 @@ if (!empty($doactionsthenredirect)) { } else { $ext_urlok = DOL_URL_ROOT.'/public/website/index.php?website='.urlencode($ws).'&pageref=paymentok&fulltag='.$FULLTAG; } - print ""; + + if (getDolGlobalInt('MARKETPLACE_PAYMENT_IN_FRAME') == 1) { // TODO Use a property in website module + dol_syslog("Now do a redirect in iframe mode in js to ".$ext_urlok, LOG_DEBUG, 0, '_payment'); + + // Redirect in js is not reliable + print ""; + } else { + dol_syslog("Now do a redirect using a Location: ".$ext_urlok, LOG_DEBUG, 0, '_payment'); + + header("Location: ".$ext_urlok); + exit; + } } else { // Redirect to an error page // Paymentko page must be created for the specific website @@ -2162,6 +2177,17 @@ if (!empty($doactionsthenredirect)) { } else { $ext_urlko = DOL_URL_ROOT.'/public/website/index.php?website='.urlencode($ws).'&pageref=paymentko&fulltag='.$FULLTAG; } - print ""; + + if (getDolGlobalInt('MARKETPLACE_PAYMENT_IN_FRAME') == 1) { // TODO Use a property in website module + dol_syslog("Now do a redirect in iframe mode in js to ".$ext_urlko, LOG_DEBUG, 0, '_payment'); + + // Redirect in js is not reliable + print ""; + } else { + dol_syslog("Now do a redirect using a Location:".$ext_urlko, LOG_DEBUG, 0, '_payment'); + + header("Location: ".$ext_urlko); + exit; + } } } diff --git a/htdocs/reception/class/reception.class.php b/htdocs/reception/class/reception.class.php index 1516689de63..7c0960fd68e 100644 --- a/htdocs/reception/class/reception.class.php +++ b/htdocs/reception/class/reception.class.php @@ -435,7 +435,7 @@ class Reception extends CommonObject $sql .= ', e.fk_incoterms, e.location_incoterms'; $sql .= ', i.libelle as label_incoterms'; $sql .= " FROM ".MAIN_DB_PREFIX."reception as e"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."' AND el.sourcetype = 'order_supplier'"; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid'; $sql .= " WHERE e.entity IN (".getEntity('reception').")"; if ($id) { diff --git a/htdocs/supplier_proposal/list.php b/htdocs/supplier_proposal/list.php index 7e5662acfc3..77db9b5cb85 100644 --- a/htdocs/supplier_proposal/list.php +++ b/htdocs/supplier_proposal/list.php @@ -58,7 +58,7 @@ if (isModEnabled('project')) { */ // Load translation files required by the page -$langs->loadLangs(array('companies', 'propal', 'supplier_proposal', 'compta', 'bills', 'orders', 'products')); +$langs->loadLangs(array('companies', 'propal', 'supplier_proposal', 'compta', 'bills', 'orders', 'products', 'projects')); $socid = GETPOSTINT('socid'); @@ -106,6 +106,7 @@ $search_multicurrency_tx = GETPOST('search_multicurrency_tx', 'alpha'); $search_multicurrency_montant_ht = GETPOST('search_multicurrency_montant_ht', 'alpha'); $search_multicurrency_montant_vat = GETPOST('search_multicurrency_montant_vat', 'alpha'); $search_multicurrency_montant_ttc = GETPOST('search_multicurrency_montant_ttc', 'alpha'); +$search_project_ref = GETPOST('search_project_ref', 'alpha'); $search_status = GETPOST('search_status', 'intcomma'); $search_product_category = GETPOST('search_product_category', 'int'); $search_all = trim(GETPOST('search_all', 'alphanohtml')); @@ -166,12 +167,17 @@ $search_array_options = $extrafields->getOptionalsFromPost($object->table_elemen // List of fields to search into when doing a "search in all" -$fieldstosearchall = array( - 'sp.ref' => 'Ref', - 's.nom' => 'Supplier', - 'pd.description' => 'Description', - 'sp.note_public' => 'NotePublic', -); +$fieldstosearchall = array(); +foreach ($object->fields as $key => $val) { + if (!empty($val['searchall'])) { + $fieldstosearchall['sp.'.$key] = $val['label']; + } +} +$fieldstosearchall['pd.description'] = 'Description'; +$fieldstosearchall['s.nom'] = "ThirdParty"; +$fieldstosearchall['s.name_alias'] = "AliasNameShort"; +$fieldstosearchall['s.zip'] = "Zip"; +$fieldstosearchall['s.town'] = "Town"; if (empty($user->socid)) { $fieldstosearchall["p.note_private"] = "NotePrivate"; } @@ -196,6 +202,7 @@ $arrayfields = array( 'sp.multicurrency_total_ht' => array('label' => 'MulticurrencyAmountHT', 'checked' => '0', 'enabled' => (!isModEnabled("multicurrency") ? '0' : '1')), 'sp.multicurrency_total_vat' => array('label' => 'MulticurrencyAmountVAT', 'checked' => '0', 'enabled' => (!isModEnabled("multicurrency") ? '0' : '1')), 'sp.multicurrency_total_ttc' => array('label' => 'MulticurrencyAmountTTC', 'checked' => '0', 'enabled' => (!isModEnabled("multicurrency") ? '0' : '1')), + 'sp.fk_projet' => array('label' => $langs->trans("RefProject"), 'checked' => 1,'enabled' => (!isModEnabled("project") ? 0 : 1)), 'u.login' => array('label' => $langs->trans("Author"), 'checked' => '1', 'position' => 10), 'sp.datec' => array('label' => $langs->trans("DateCreation"), 'checked' => '0', 'position' => 500), 'sp.tms' => array('label' => $langs->trans("DateModificationShort"), 'checked' => '0', 'position' => 500), @@ -256,6 +263,7 @@ if (empty($reshook)) { $search_multicurrency_montant_ht = ''; $search_multicurrency_montant_vat = ''; $search_multicurrency_montant_ttc = ''; + $search_project_ref = ''; $search_login = ''; $search_product_category = ''; $search_town = ''; @@ -331,7 +339,7 @@ $sql .= " state.code_departement as state_code, state.nom as state_name,"; $sql .= ' sp.rowid, sp.note_public, sp.note_private, sp.total_ht, sp.total_tva, sp.total_ttc, sp.localtax1, sp.localtax2, sp.ref, sp.fk_statut as status, sp.fk_user_author, sp.date_valid, sp.date_livraison as dp,'; $sql .= ' sp.fk_multicurrency, sp.multicurrency_code, sp.multicurrency_tx, sp.multicurrency_total_ht, sp.multicurrency_total_tva as multicurrency_total_vat, sp.multicurrency_total_ttc,'; $sql .= ' sp.datec as date_creation, sp.tms as date_modification,'; -$sql .= " p.rowid as project_id, p.ref as project_ref,"; +$sql .= " p.rowid as project_id, p.ref as project_ref, p.title as project_title,"; $sql .= " u.firstname, u.lastname, u.photo, u.login, u.statut as ustatus, u.admin, u.employee, u.email as uemail"; // Add fields from extrafields if (!empty($extrafields->attributes[$object->table_element]['label'])) { @@ -417,6 +425,9 @@ if ($search_multicurrency_montant_vat != '') { if ($search_multicurrency_montant_ttc != '') { $sql .= natural_search('sp.multicurrency_total_ttc', $search_multicurrency_montant_ttc, 1); } +if ($search_project_ref != '') { + $sql .= natural_search("p.ref", $search_project_ref); +} if ($search_all) { $sql .= natural_search(array_keys($fieldstosearchall), $search_all); } @@ -634,6 +645,9 @@ if ($resql) { if ($search_status != '') { $param .= '&search_status='.urlencode($search_status); } + if ($search_project_ref >= 0) { + $param .= "&search_project_ref=".urlencode($search_project_ref); + } if ($optioncss != '') { $param .= '&optioncss='.urlencode($optioncss); } @@ -758,6 +772,12 @@ if ($resql) { print ''; print ''; } + // Project ref + if (!empty($arrayfields['sp.fk_projet']['checked'])) { + print ''; + print ''; + print ''; + } if (!empty($arrayfields['s.nom']['checked'])) { print ''; print ''; @@ -914,6 +934,12 @@ if ($resql) { print_liste_field_titre($arrayfields['sp.ref']['label'], $_SERVER["PHP_SELF"], 'sp.ref', '', $param, '', $sortfield, $sortorder); $totalarray['nbfield']++; } + + if (!empty($arrayfields['sp.fk_projet']['checked'])) { + print_liste_field_titre($arrayfields['sp.fk_projet']['label'], $_SERVER["PHP_SELF"], "p.ref", "", $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; + } + if (!empty($arrayfields['s.nom']['checked'])) { print_liste_field_titre($arrayfields['s.nom']['label'], $_SERVER["PHP_SELF"], 's.nom', '', $param, '', $sortfield, $sortorder); $totalarray['nbfield']++; @@ -1016,10 +1042,13 @@ if ($resql) { $i = 0; $total = 0; $subtotal = 0; + + $userstatic = new User($db); + $objectstatic = new SupplierProposal($db); + $projectstatic = new Project($db); + $savnbfield = $totalarray['nbfield']; - $totalarray = array(); - $totalarray['nbfield'] = 0; - $totalarray['val'] = array(); + $totalarray = array('nbfield' => 0, 'val' => array(), 'pos' => array()); $totalarray['val']['sp.total_ht'] = 0; $totalarray['val']['sp.total_tva'] = 0; $totalarray['val']['sp.total_ttc'] = 0; @@ -1099,6 +1128,21 @@ if ($resql) { } } + // Project + if (!empty($arrayfields['sp.fk_projet']['checked'])) { + $projectstatic->id = $obj->project_id; + $projectstatic->ref = $obj->project_ref; + $projectstatic->title = $obj->project_title; + print ''; + if ($obj->project_id > 0) { + print $projectstatic->getNomUrl(1); + } + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + // Thirdparty if (!empty($arrayfields['s.nom']['checked'])) { print ''; diff --git a/htdocs/takepos/index.php b/htdocs/takepos/index.php index b0c2fc2f776..25706ebbb6e 100644 --- a/htdocs/takepos/index.php +++ b/htdocs/takepos/index.php @@ -1588,7 +1588,7 @@ if (getDolGlobalString('TAKEPOS_WEIGHING_SCALE')) { if ($count == ($MAXPRODUCT - 1)) { ?> onclick="MoreProducts('more')" > getAllChildIds(1); // For later, test on salary visibility +$childids = $user->getAllChildIds(1); // For test on hrm fields (like salary visibility) $object = new User($db); $extrafields = new ExtraFields($db); @@ -125,15 +125,15 @@ $feature2 = 'user'; $result = restrictedArea($user, 'user', $id, 'user', $feature2); // Define value to know what current user can do on users. A test on logged user is done later to complete -$permissiontoadd = (!empty($user->admin) || $user->hasRight("user", "user", "write")); -$permissiontoread = (!empty($user->admin) || $user->hasRight("user", "user", "read")); -$permissiontoedit = (!empty($user->admin) || $user->hasRight("user", "user", "write")); -$permissiontodisable = (!empty($user->admin) || $user->hasRight("user", "user", "delete")); +$permissiontoadd = (!empty($user->admin) || $user->hasRight("user", "user", "write")) && (empty($user->socid) || $user->socid == $object->socid); +$permissiontoread = (!empty($user->admin) || $user->hasRight("user", "user", "read")) && (empty($user->socid) || $user->socid == $object->socid); +$permissiontoedit = (!empty($user->admin) || $user->hasRight("user", "user", "write")) && (empty($user->socid) || $user->socid == $object->socid); +$permissiontodisable = (!empty($user->admin) || $user->hasRight("user", "user", "delete")) && (empty($user->socid) || $user->socid == $object->socid); $permissiontoreadgroup = $permissiontoread; $permissiontoeditgroup = $permissiontoedit; if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) { - $permissiontoreadgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "read")); - $permissiontoeditgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "write")); + $permissiontoreadgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "read")) && (empty($user->socid) || $user->socid == $object->socid); + $permissiontoeditgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "write")) && (empty($user->socid) || $user->socid == $object->socid); } $permissiontoclonesuperadmin = ($permissiontoadd && empty($user->entity)); @@ -158,9 +158,9 @@ $permissiontoeditpasswordandsee = false; $permissiontoeditpasswordandsend = false; if ($id > 0) { // $user is the current logged user, $id is the user we want to edit - $permissiontoedit = ((($user->id == $id) && $user->hasRight("user", "self", "write")) || (($user->id != $id) && $user->hasRight("user", "user", "write"))); - $permissiontoeditpasswordandsee = ((($user->id == $id) && $user->hasRight("user", "self", "password")) || (($user->id != $id) && $user->hasRight("user", "user", "password") && $user->admin)); - $permissiontoeditpasswordandsend = ((($user->id == $id) && $user->hasRight("user", "self", "password")) || (($user->id != $id) && $user->hasRight("user", "user", "password"))); + $permissiontoedit = ((($user->id == $id) && $user->hasRight("user", "self", "write")) || (($user->id != $id) && $user->hasRight("user", "user", "write"))) && (empty($user->socid) || $user->socid == $object->socid); + $permissiontoeditpasswordandsee = ((($user->id == $id) && $user->hasRight("user", "self", "password")) || (($user->id != $id) && $user->hasRight("user", "user", "password") && $user->admin))&& (empty($user->socid) || $user->socid == $object->socid); + $permissiontoeditpasswordandsend = ((($user->id == $id) && $user->hasRight("user", "self", "password")) || (($user->id != $id) && $user->hasRight("user", "user", "password")))&& (empty($user->socid) || $user->socid == $object->socid); } $passwordismodified = false;