Browse Source

Merge branch '17.0' of git@github.com:Dolibarr/dolibarr.git into develop

Laurent Destailleur 2 years ago
parent
commit
fe35f5ec28

+ 1 - 1
dev/dolibarr_changes.txt

@@ -212,7 +212,7 @@ with
 * Fix by replacing 
 	if ($res[0] == PDF_TYPE_OBJECT)
 with
-	if ($res && $res[0] == PDF_TYPE_OBJECT)
+	if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
 
 
 

+ 1 - 2
dev/resources/iso-normes/qr-bar-codes/QR code for invoices.txt

@@ -23,7 +23,6 @@ Method to encode/decode ZATCA string is available in test/phpunit/BarcodeTest.ph
 
 
 * FOR QR-Bill in switzerland - Facture-QR
------------------------------------------
-Syntax of QR Code https://www.swiss-qr-invoice.org/fr/
+Syntax of QR Code - See file ig-qr-bill-v2.2-fr.pdf  (more doc on https://www.swiss-qr-invoice.org/downloads/)
 Syntax of complentary field named "structured information of invoice S1": https://www.swiss-qr-invoice.org/downloads/qr-bill-s1-syntax-fr.pdf
 To test/validate: https://www.swiss-qr-invoice.org/validator/

File diff suppressed because it is too large
+ 3676 - 0
dev/resources/iso-normes/qr-bar-codes/ig-qr-bill-v2.2-fr.pdf


+ 5 - 1
htdocs/commande/card.php

@@ -5,7 +5,7 @@
  * Copyright (C) 2005-2015	Regis Houssin			<regis.houssin@inodbox.com>
  * Copyright (C) 2006		Andre Cianfarani		<acianfa@free.fr>
  * Copyright (C) 2010-2013	Juanjo Menent			<jmenent@2byte.es>
- * Copyright (C) 2011-2022	Philippe Grand			<philippe.grand@atoo-net.com>
+ * Copyright (C) 2011-2023	Philippe Grand			<philippe.grand@atoo-net.com>
  * Copyright (C) 2012-2013	Christophe Battarel		<christophe.battarel@altairis.fr>
  * Copyright (C) 2012-2016	Marcos García			<marcosgdf@gmail.com>
  * Copyright (C) 2012       Cedric Salvador      	<csalvador@gpcsolutions.fr>
@@ -660,6 +660,10 @@ if (empty($reshook)) {
 		$price_ht_devise = '';
 		$price_ttc = '';
 		$price_ttc_devise = '';
+		$pu_ht = '';
+		$pu_ttc = '';
+		$pu_ht_devise = '';
+		$pu_ttc_devise  = '';
 
 		if (GETPOST('price_ht') !== '') {
 			$price_ht = price2num(GETPOST('price_ht'), 'MU', 2);

+ 5 - 7
htdocs/contact/list.php

@@ -453,28 +453,26 @@ if ($search_priv != '0' && $search_priv != '1') {
 }
 
 
-// Search Categories
-
- // Search Contact Categories
+// Search Contact Categories
 $searchCategoryContactList = $search_categ ? array($search_categ) : array();
 $searchCategoryContactOperator = 0;
- // Search for tag/category ($searchCategoryContactList is an array of ID)
+// Search for tag/category ($searchCategoryContactList is an array of ID)
 if (!empty($searchCategoryContactList)) {
 	$searchCategoryContactSqlList = array();
 	$listofcategoryid = '';
 	foreach ($searchCategoryContactList as $searchCategoryContact) {
 		if (intval($searchCategoryContact) == -2) {
-			$searchCategoryContactSqlList[] = "NOT EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople)";
+			$searchCategoryContactSqlList[] = "NOT EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople)";
 		} elseif (intval($searchCategoryContact) > 0) {
 			if ($searchCategoryContactOperator == 0) {
-				$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople AND ck.fk_categorie = ".((int) $searchCategoryContact).")";
+				$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople AND ck.fk_categorie = ".((int) $searchCategoryContact).")";
 			} else {
 				$listofcategoryid .= ($listofcategoryid ? ', ' : '') .((int) $searchCategoryContact);
 			}
 		}
 	}
 	if ($listofcategoryid) {
-		$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople AND ck.fk_categorie IN (".$db->sanitize($listofcategoryid)."))";
+		$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople AND ck.fk_categorie IN (".$db->sanitize($listofcategoryid)."))";
 	}
 	if ($searchCategoryContactOperator == 1) {
 		if (!empty($searchCategoryContactSqlList)) {

+ 1 - 1
htdocs/core/class/commondocgenerator.class.php

@@ -1119,7 +1119,7 @@ abstract class CommonDocGenerator
 	public function getColumnContentXStart($colKey)
 	{
 		$colDef = $this->cols[$colKey];
-		return  (isset($colDef['xStartPos']) ? $colDef['xStartPos'] : 0) + $colDef['content']['padding'][3];
+		return (isset($colDef['xStartPos']) ? $colDef['xStartPos'] : 0) + $colDef['content']['padding'][3];
 	}
 
 	/**

+ 31 - 14
htdocs/core/class/commoninvoice.class.php

@@ -1735,6 +1735,7 @@ abstract class CommonInvoice extends CommonObject
 			$complementaryinfo .= '/30/'.$this->thirdparty->tva_intra;
 		}
 
+		include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
 		$bankaccount = new Account($this->db);
 
 		// Header
@@ -1742,25 +1743,36 @@ abstract class CommonInvoice extends CommonObject
 		$s .= "SPC\n";
 		$s .= "0200\n";
 		$s .= "1\n";
-		// Info seller
+		// Info Seller ("Compte / Payable à")
 		if ($this->fk_account > 0) {
-			// Bank BAN if country is LI or CH
-			// TODO Add test on bank iban
+			// Bank BAN if country is LI or CH.  TODO Add a test to check than IBAN start with CH or LI
 			$bankaccount->fetch($this->fk_account);
 			$s .= $bankaccount->iban."\n";
 		} else {
 			$s .= "\n";
 		}
-		// Seller
-		$s .= "S\n";
-		$s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
-		$addresslinearray = explode("\n", $mysoc->address);
-		$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n";		// address line 1
-		$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n";		// address line 2
-		$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
-		$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
-		$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
-		// Final seller
+		if ($bankaccount->id > 0 && getDolGlobalString('PDF_SWISS_QRCODE_USE_OWNER_OF_ACCOUNT_AS_CREDITOR')) {
+			// If a bank account is prodived and we ask to use it as creditor, we use the bank address
+			// TODO In a future, we may always use this address, and if name/address/zip/town/country differs from $mysoc, we can use the address of $mysoc into the final seller field ?
+			$s .= "S\n";
+			$s .= dol_trunc($bankaccount->proprio, 70, 'right', 'UTF-8', 1)."\n";
+			$addresslinearray = explode("\n", $bankaccount->owner_address);
+			$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n";		// address line 1
+			$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n";		// address line 2
+			/*$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
+			$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
+			$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";*/
+		} else {
+			$s .= "S\n";
+			$s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
+			$addresslinearray = explode("\n", $mysoc->address);
+			$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n";		// address line 1
+			$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n";		// address line 2
+			$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
+			$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
+			$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
+		}
+		// Final seller (Ultimate seller) ("Créancier final" = "En faveur de")
 		$s .= "\n";
 		$s .= "\n";
 		$s .= "\n";
@@ -1782,13 +1794,18 @@ abstract class CommonInvoice extends CommonObject
 		$s .= dol_trunc($this->thirdparty->country_code, 2, 'right', 'UTF-8', 1)."\n";
 		// ID of payment
 		$s .= "NON\n";			// NON or QRR
-		$s .= "\n";				// QR Code if previous field is QRR
+		$s .= "\n";				// QR Code reference if previous field is QRR
+		// Free text
 		if ($complementaryinfo) {
 			$s .= $complementaryinfo."\n";
 		} else {
 			$s .= "\n";
 		}
 		$s .= "EPD\n";
+		// More text, complementary info
+		if ($complementaryinfo) {
+			$s .= $complementaryinfo."\n";
+		}
 		$s .= "\n";
 		//var_dump($s);exit;
 		return $s;

+ 3 - 1
htdocs/core/class/extrafields.class.php

@@ -1975,6 +1975,8 @@ class ExtraFields
 		if (!empty($extrafield_param) && is_array($extrafield_param)) {
 			$extrafield_param_list = array_keys($extrafield_param['options']);
 		}
+
+		// Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
 		$extrafield_collapse_display_value = -1;
 		$expand_display = false;
 		if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
@@ -1993,7 +1995,7 @@ class ExtraFields
 		$out .= '<'.$tagtype_dyn.' '.(!empty($colspan)?'colspan="' . $colspan . '"':'').'>';
 		// Some js code will be injected here to manage the collapsing of extrafields
 		// Output the picto
-		$out .= '<span class="cursorpointer '.($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-'.(($expand_display ? 'minus' : 'plus').'-square')).'"></span>';
+		$out .= '<span class="'.($extrafield_collapse_display_value ? 'cursorpointer ' : '').($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-'.(($expand_display ? 'minus' : 'plus').'-square')).'"></span>';
 		$out .= '&nbsp;';
 		$out .= '<strong>';
 		$out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);

+ 1 - 1
htdocs/core/js/lib_notification.js.php

@@ -102,7 +102,7 @@ function check_events() {
 		var newToken = 'notrequired';
 		const allMeta = document.getElementsByTagName("meta");
 		for (let i = 0; i < allMeta.length; i++) {
-			if (allMeta[i].getAttribute("name") == 'anti-csrf-token') {
+			if (allMeta[i].getAttribute("name") == 'anti-csrf-currenttoken') {
 				newToken = allMeta[i].getAttribute('content');
 				console.log("newToken in page = "+newToken);
 			}

+ 22 - 14
htdocs/core/lib/functions.lib.php

@@ -1328,6 +1328,10 @@ function dol_string_unaccent($str)
 {
 	global $conf;
 
+	if (is_null($str)) {
+		return '';
+	}
+
 	if (utf8_check($str)) {
 		if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
 			$transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
@@ -7175,6 +7179,10 @@ function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
  */
 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
 {
+	if (is_null($stringtoencode)) {
+		return '';
+	}
+
 	if (!$nl2brmode) {
 		return nl2br($stringtoencode, $forxml);
 	} else {
@@ -8948,17 +8956,17 @@ function verifCond($strToEvaluate)
  */
 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
 {
-	try {
-		// Only global variables can be changed by eval function and returned to caller
-		global $db, $langs, $user, $conf, $website, $websitepage;
-		global $action, $mainmenu, $leftmenu;
-		global $rights;
-		global $object;
-		global $mysoc;
+	// Only global variables can be changed by eval function and returned to caller
+	global $db, $langs, $user, $conf, $website, $websitepage;
+	global $action, $mainmenu, $leftmenu;
+	global $rights;
+	global $object;
+	global $mysoc;
 
-		global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
-		global $soc; // For backward compatibility
+	global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
+	global $soc; // For backward compatibility
 
+	try {
 		// Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
 		if ($onlysimplestring == '1') {
 			// We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
@@ -9053,11 +9061,11 @@ function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1'
 			}
 		}
 	} catch (Error $e) {
-			$error = 'Caught error : ';
-			$error .= $e->getMessage() . ', ';
-			$error .= 'Trace : ';
-			$error .= json_encode($e->getTrace());
-			error_log($error, 1);
+		$error = 'Caught error : ';
+		$error .= $e->getMessage() . ', ';
+		$error .= 'Trace : ';
+		$error .= json_encode($e->getTrace());
+		error_log($error, 1);
 	}
 }
 

+ 2 - 2
htdocs/core/lib/project.lib.php

@@ -2867,13 +2867,13 @@ function getTaskProgressView($task, $label = true, $progressNumber = true, $hide
 		// good
 		$out .= '        <div class="progress-bar '.$progressBarClass.'" style="width: '.floatval($task->progress).'%" title="'.floatval($task->progress).'%">';
 		if (!empty($task->progress)) {
-			$out .= '        <div class="progress-bar progress-bar-consumed" style="width: '.floatval($progressCalculated / $task->progress * 100).'%" title="'.floatval($progressCalculated).'%"></div>';
+			$out .= '        <div class="progress-bar progress-bar-consumed" style="width: '.floatval($progressCalculated / (floatval($task->progress) === 0 ? 1 : $task->progress) * 100).'%" title="'.floatval($progressCalculated).'%"></div>';
 		}
 		$out .= '        </div>';
 	} else {
 		// bad
 		$out .= '        <div class="progress-bar progress-bar-consumed-late" style="width: '.floatval($progressCalculated).'%" title="'.floatval($progressCalculated).'%">';
-		$out .= '        <div class="progress-bar '.$progressBarClass.'" style="width: '.($task->progress ? floatval($task->progress / $progressCalculated * 100).'%' : '1px').'" title="'.floatval($task->progress).'%"></div>';
+		$out .= '        <div class="progress-bar '.$progressBarClass.'" style="width: '.($task->progress ? floatval($task->progress / (floatval($progressCalculated) === 0 ? 1 : $progressCalculated) * 100).'%' : '1px').'" title="'.floatval($task->progress).'%"></div>';
 		$out .= '        </div>';
 	}
 	$out .= '    </div>';

+ 1 - 1
htdocs/core/lib/sendings.lib.php

@@ -50,7 +50,7 @@ function shipping_prepare_head($object)
 	if ($conf->delivery_note->enabled && $user->rights->expedition->delivery->lire) {
 		// delivery link
 		$object->fetchObjectLinked($object->id, $object->element);
-		if (is_array($object->linkedObjectsIds['delivery']) && count($object->linkedObjectsIds['delivery']) > 0) {        // If there is a delivery
+		if (isset($object->linkedObjectsIds['delivery']) && is_array($object->linkedObjectsIds['delivery']) && count($object->linkedObjectsIds['delivery']) > 0) {        // If there is a delivery
 			// Take first one element of array
 			$tmp = reset($object->linkedObjectsIds['delivery']);
 

+ 3 - 3
htdocs/delivery/card.php

@@ -283,7 +283,7 @@ if ($action == 'create') {
 			print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 			print '<input type="hidden" name="token" value="'.newToken().'">';
 			print '<input type="hidden" name="action" value="update_extras_line">';
-			print '<input type="hidden" name="origin" value="'.$origin.'">';
+			print '<input type="hidden" name="origin" value="'.$object->origin.'">';
 			print '<input type="hidden" name="id" value="'.$object->id.'">';
 			print '<input type="hidden" name="ref" value="'.$object->ref.'">';
 
@@ -568,7 +568,7 @@ if ($action == 'create') {
 						$description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($object->lines[$i]->description));
 						//print $description;
 						print $form->textwithtooltip($text, $description, 3, '', '', $i);
-						print_date_range($object->lines[$i]->date_start, $object->lines[$i]->date_end);
+						//print_date_range($object->lines[$i]->date_start, $object->lines[$i]->date_end);
 						if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
 							print (!empty($object->lines[$i]->description) && $object->lines[$i]->description != $object->lines[$i]->product_label) ? '<br>'.dol_htmlentitiesbr($object->lines[$i]->description) : '';
 						}
@@ -587,7 +587,7 @@ if ($action == 'create') {
 							print $text.' '.nl2br($object->lines[$i]->description);
 						}
 
-						print_date_range($objp->date_start, $objp->date_end);
+						//print_date_range($objp->date_start, $objp->date_end);
 						print "</td>\n";
 					}
 

+ 2 - 2
htdocs/delivery/class/delivery.class.php

@@ -3,7 +3,7 @@
  * Copyright (C) 2005-2014 Regis Houssin         <regis.houssin@inodbox.com>
  * Copyright (C) 2006-2007 Laurent Destailleur   <eldy@users.sourceforge.net>
  * Copyright (C) 2007      Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
- * Copyright (C) 2011-2018 Philippe Grand	     <philippe.grand@atoo-net.com>
+ * Copyright (C) 2011-2023 Philippe Grand	     <philippe.grand@atoo-net.com>
  * Copyright (C) 2013      Florian Henry	     <florian.henry@open-concept.pro>
  * Copyright (C) 2014-2015 Marcos García         <marcosgdf@gmail.com>
  *
@@ -819,7 +819,7 @@ class Delivery extends CommonObject
 				$line->product_type		= $obj->fk_product_type;
 				$line->fk_origin_line = $obj->fk_origin_line;
 
-				$line->price = $obj->price;
+				$line->price = $obj->subprice;
 				$line->total_ht = $obj->total_ht;
 
 				// units

+ 1 - 1
htdocs/expedition/card.php

@@ -337,7 +337,7 @@ if (empty($reshook)) {
 			// Extrafields
 			$array_options[$i] = $extrafields->getOptionalsFromPost($object->table_element_line, $i);
 			// Unset extrafield
-			if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
+			if (isset($extrafields->attributes[$object->table_element_line]['label']) && is_array($extrafields->attributes[$object->table_element_line]['label'])) {
 				// Get extra fields
 				foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
 					unset($_POST["options_".$key]);

+ 2 - 2
htdocs/includes/tcpdi/tcpdi_parser.php

@@ -1365,7 +1365,7 @@ class tcpdi_parser {
     	$obj = $this->getObjectVal($obj);
     	if (isset ($obj[1][1]['/Rotate'])) {
     		$res = $this->getObjectVal($obj[1][1]['/Rotate']);
-    		if ($res[0] == PDF_TYPE_OBJECT)
+    		if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
                 return $res[1];
             return $res;
     	} else {
@@ -1373,7 +1373,7 @@ class tcpdi_parser {
     			return false;
     		} else {
                 $res = $this->_getPageRotation($obj[1][1]['/Parent']);
-                if ($res && $res[0] == PDF_TYPE_OBJECT)
+                if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
                     return $res[1];
                 return $res;
     		}

+ 1 - 1
htdocs/install/mysql/data/llx_20_c_departements.sql

@@ -1228,7 +1228,7 @@ INSERT INTO llx_c_departements (fk_region, code_departement, ncc, nom) VALUES (6
 INSERT INTO llx_c_departements (fk_region, code_departement, ncc, nom) VALUES (601, 'ZH','ZURICH','Zürich');
 
 
--- Taiwan Divisions / Provinces / Counties (rowid country=886)
+-- Taiwan Divisions / Provinces / Counties (id country=213)
 INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-KLU', 'KLU', NULL, '基隆市');
 INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-TPE', 'TPE', NULL, '臺北市');
 INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-TPH', 'TPH', NULL, '新北市');

+ 3 - 0
htdocs/install/mysql/migration/16.0.0-17.0.0.sql

@@ -141,6 +141,9 @@ ALTER TABLE llx_societe_rib ADD COLUMN state_id integer AFTER default_rib;
 ALTER TABLE llx_societe_rib ADD COLUMN fk_country integer AFTER state_id;
 ALTER TABLE llx_societe_rib ADD COLUMN currency_code varchar(3) AFTER fk_country;
 
+DELETE FROM llx_societe_rib WHERE fk_soc = 0;
+ALTER TABLE llx_societe_rib ADD CONSTRAINT llx_societe_rib_fk_societe FOREIGN KEY (fk_soc) REFERENCES llx_societe(rowid);
+
 ALTER TABLE llx_user_rib ADD COLUMN state_id integer AFTER owner_address;
 ALTER TABLE llx_user_rib ADD COLUMN fk_country integer AFTER state_id;
 ALTER TABLE llx_user_rib ADD COLUMN currency_code varchar(3) AFTER fk_country;

+ 1 - 1
htdocs/install/mysql/tables/llx_product_attribute_combination_price_level.key.sql

@@ -17,5 +17,5 @@
 -- ============================================================================
 
 
-ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE( fk_product_attribute_combination, fk_price_level);
+ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE INDEX uk_prod_att_comb_price_level(fk_product_attribute_combination, fk_price_level);
 

+ 3 - 2
htdocs/main.inc.php

@@ -501,7 +501,7 @@ if ((!empty($conf->global->MAIN_VERSION_LAST_UPGRADE) && ($conf->global->MAIN_VE
 
 // Creation of a token against CSRF vulnerabilities
 if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
-	// No token renewal on .css.php, .js.php and .json.php
+	// No token renewal on .css.php, .js.php and .json.php (even if the NOTOKENRENEWAL was not provided)
 	if (!preg_match('/\.(css|js|json)\.php$/', $_SERVER["PHP_SELF"])) {
 		// Rolling token at each call ($_SESSION['token'] contains token of previous page)
 		if (isset($_SESSION['newtoken'])) {
@@ -1589,7 +1589,8 @@ function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arr
 		print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
 		print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
 		print '<meta name="author" content="Dolibarr Development Team">'."\n";
-		print '<meta name="anti-csrf-token" content="'.newToken().'">'."\n";
+		print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
+		print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
 		if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
 			print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
 		}

+ 1 - 1
htdocs/public/payment/newpayment.php

@@ -2241,7 +2241,7 @@ if (preg_match('/^dopayment/', $action)) {			// If we choosed/click on the payme
 
 			if (!empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
 				$noidempotency_key = (GETPOSTISSET('noidempotency') ? GETPOST('noidempotency', 'int') : 0); // By default noidempotency is unset, so we must use a different tag/ref for each payment. If set, we can pay several times the same tag/ref.
-				$paymentintent = $stripe->getPaymentIntent($amount, $currency, $tag, 'Stripe payment: '.$fulltag.(is_object($object) ? ' ref='.$object->ref : ''), $object, $stripecu, $stripeacc, $servicestatus, 0, 'automatic', false, null, 0, $noidempotency_key);
+				$paymentintent = $stripe->getPaymentIntent($amount, $currency, ($tag ? $tag : $fulltag), 'Stripe payment: '.$fulltag.(is_object($object) ? ' ref='.$object->ref : ''), $object, $stripecu, $stripeacc, $servicestatus, 0, 'automatic', false, null, 0, $noidempotency_key);
 				// The paymentintnent has status 'requires_payment_method' (even if paymentintent was already paid)
 				//var_dump($paymentintent);
 				if ($stripe->error) {

+ 4 - 2
htdocs/stripe/class/stripe.class.php

@@ -465,12 +465,14 @@ class Stripe extends CommonObject
 				"currency" => $currency_code,
 				"payment_method_types" => $paymentmethodtypes,
 				"description" => $description,
-				"statement_descriptor_suffix" => $descriptor, // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
-				"statement_descriptor" => $descriptor, // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
 				//"save_payment_method" => true,
 				"setup_future_usage" => "on_session",
 				"metadata" => $metadata
 			);
+			if ($descriptor) {
+				$dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
+				$dataforintent["statement_descriptor"] = $descriptor; 	// For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
+			}
 			if (!is_null($customer)) {
 				$dataforintent["customer"] = $customer;
 			}

+ 1 - 2
htdocs/theme/eldy/global.inc.php

@@ -304,9 +304,8 @@ section.setupsection {
 div.tabBar textarea:focus {
 	border: 1px solid #aaa !important;
 }
-input:focus:not(.button):not(.buttonwebsite):not(.select2-search__field):not(#top-bookmark-search-input):not(.search_component_input):not(.input-search-takepos),
+input:focus:not(.button):not(.buttonwebsite):not(.buttonreset):not(.select2-search__field):not(#top-bookmark-search-input):not(.search_component_input):not(.input-search-takepos),
  select:focus, .select2-container--open [aria-expanded="false"].select2-selection--single {
-/* div.tabBar input:focus, div.tabBar select:focus { */
 	border-bottom: 1px solid #666 !important;
 	border-bottom-left-radius: 0 !important;
 	border-bottom-right-radius: 0 !important;

+ 12 - 6
htdocs/website/index.php

@@ -345,7 +345,7 @@ if ($mode == 'replacesite') {
 
 $usercanedit = $user->rights->website->write;
 $permissiontoadd = $user->rights->website->write;	// Used by the include of actions_addupdatedelete.inc.php and actions_linkedfiles
-$permissiontodelete = $user->rights->website->delete;
+$permissiontodelete = $user->hasRight('website', 'delete');
 
 
 /*
@@ -2930,14 +2930,20 @@ if (!GETPOST('hide_websitemenu')) {
 			print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("CloneSite")).'" name="createfromclone">';
 
 			// Delete website
-			if ($website->status == $website::STATUS_VALIDATED) {
+			if (!$permissiontodelete) {
 				$disabled = ' disabled="disabled"';
-				$title = $langs->trans("WebsiteMustBeDisabled", $langs->transnoentitiesnoconv($website->LibStatut(0, 0)));
+				$title = $langs->trans("NotEnoughPermissions");
 				$url = '#';
 			} else {
-				$disabled = '';
-				$title = $langs->trans("Delete");
-				$url = $_SERVER["PHP_SELF"].'?action=deletesite&token='.newToken().'&website='.urlencode($website->ref);
+				if ($website->status == $website::STATUS_VALIDATED) {
+					$disabled = ' disabled="disabled"';
+					$title = $langs->trans("WebsiteMustBeDisabled", $langs->transnoentitiesnoconv($website->LibStatut(0, 0)));
+					$url = '#';
+				} else {
+					$disabled = '';
+					$title = $langs->trans("Delete");
+					$url = $_SERVER["PHP_SELF"].'?action=deletesite&token='.newToken().'&website='.urlencode($website->ref);
+				}
 			}
 			print '<a href="'.$url.'" class="buttonDelete bordertransp'.($disabled ? ' disabled' : '').'"'.$disabled.' title="'.dol_escape_htmltag($title).'">'.img_picto('', 'delete', 'class=""').'<span class="hideonsmartphone paddingleft">'.$langs->trans("Delete").'</span></a>';
 

Some files were not shown because too many files changed in this diff