Преглед изворни кода

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

Laurent Destailleur пре 9 година
родитељ
комит
a57a41a07d
65 измењених фајлова са 766 додато и 323 уклоњено
  1. 1 1
      COPYRIGHT
  2. 1 1
      ChangeLog
  3. 2 1
      build/exe/doliwamp/doliwamp.iss
  4. 20 2
      build/makepack-dolibarr.pl
  5. 2 0
      build/rpm/dolibarr_fedora.spec
  6. 2 0
      build/rpm/dolibarr_generic.spec
  7. 2 0
      build/rpm/dolibarr_mandriva.spec
  8. 2 0
      build/rpm/dolibarr_opensuse.spec
  9. 3 0
      dev/translation/sanity_check_en_langfiles.php
  10. 3 4
      htdocs/accountancy/journal/purchasesjournal.php
  11. 1 1
      htdocs/accountancy/journal/sellsjournal.php
  12. 32 30
      htdocs/adherents/card.php
  13. 24 24
      htdocs/adherents/card_subscriptions.php
  14. 44 21
      htdocs/adherents/class/adherent.class.php
  15. 1 1
      htdocs/adherents/class/cotisation.class.php
  16. 4 2
      htdocs/admin/company.php
  17. 36 5
      htdocs/admin/menus/edit.php
  18. 1 1
      htdocs/admin/menus/index.php
  19. 38 12
      htdocs/admin/tools/export.php
  20. 1 1
      htdocs/cashdesk/include/environnement.php
  21. 2 1
      htdocs/categories/categorie.php
  22. 8 8
      htdocs/categories/class/categorie.class.php
  23. 1 1
      htdocs/comm/action/class/ical.class.php
  24. 2 1
      htdocs/comm/propal/class/propal.class.php
  25. 5 15
      htdocs/commande/class/commande.class.php
  26. 1 1
      htdocs/commande/orderstoinvoice.php
  27. 12 14
      htdocs/compta/bank/card.php
  28. 24 18
      htdocs/compta/bank/index.php
  29. 108 2
      htdocs/compta/facture/class/facture-rec.class.php
  30. 2 0
      htdocs/compta/facture/class/facture.class.php
  31. 163 3
      htdocs/compta/facture/fiche-rec.php
  32. 23 16
      htdocs/conf/conf.php.example
  33. 2 2
      htdocs/contrat/class/contrat.class.php
  34. 7 5
      htdocs/core/actions_printing.inc.php
  35. 1 2
      htdocs/core/class/commonobject.class.php
  36. 1 1
      htdocs/core/class/conf.class.php
  37. 2 1
      htdocs/core/class/extrafields.class.php
  38. 3 1
      htdocs/core/class/interfaces.class.php
  39. 1 1
      htdocs/core/class/translate.class.php
  40. 6 7
      htdocs/core/db/pgsql.class.php
  41. 3 3
      htdocs/core/lib/functions.lib.php
  42. 10 2
      htdocs/core/menus/standard/auguria.lib.php
  43. 35 16
      htdocs/core/menus/standard/eldy.lib.php
  44. 2 2
      htdocs/core/tpl/objectline_create.tpl.php
  45. 2 1
      htdocs/expedition/class/expedition.class.php
  46. 1 1
      htdocs/expensereport/card.php
  47. 2 2
      htdocs/exports/class/export.class.php
  48. 2 1
      htdocs/fourn/class/fournisseur.facture.class.php
  49. 0 2
      htdocs/fourn/class/fournisseur.product.class.php
  50. 8 6
      htdocs/fourn/commande/orderstoinvoice.php
  51. 1 1
      htdocs/fourn/facture/card.php
  52. 11 5
      htdocs/install/step1.php
  53. 3 0
      htdocs/langs/en_US/admin.lang
  54. 1 0
      htdocs/langs/en_US/bills.lang
  55. 2 0
      htdocs/langs/en_US/printing.lang
  56. 7 7
      htdocs/langs/en_US/products.lang
  57. 2 1
      htdocs/livraison/class/livraison.class.php
  58. 1 1
      htdocs/projet/list.php
  59. 2 1
      htdocs/supplier_proposal/class/supplier_proposal.class.php
  60. 1 1
      htdocs/theme/eldy/style.css.php
  61. 48 46
      htdocs/user/card.php
  62. 15 9
      htdocs/user/class/user.class.php
  63. 3 4
      scripts/product/migrate_picture_path.php
  64. 1 1
      test/phpunit/CategorieTest.php
  65. 9 3
      test/phpunit/PgsqlTest.php

+ 1 - 1
COPYRIGHT

@@ -40,7 +40,7 @@ jQuery blockUI         2.70.0        GPL and MIT License         Yes
 jQuery Colorpicker     1.1           MIT License                 Yes             JS library for color picker for a defined list of colors
 jQuery DataTables      1.9.4         BSD                         Yes             JS library for tables output
 jQuery FileUpload      5.0.3         GPL and MIT License         Yes             JS library to upload files
-jQuery Flot            0.7           MIT License                 Yes             JS library to build graph
+jQuery Flot            0.8.3         MIT License                 Yes             JS library to build graph
 jQuery JCrop           0.9.8         GPL and MIT License         Yes             JS library plugin Crop (to crop images)
 jQuery Jeditable       1.7.1         GPL and MIT License         Yes             JS library plugin jeditable (to edit in place)
 jQuery jNotify         1.1.00        Apache Software License 2.0 Yes             JS library plugin jNotify (to use ajax popups)

+ 1 - 1
ChangeLog

@@ -12,7 +12,7 @@ Upgrading to any other version or any other database system is abolutely require
 make a Dolibarr upgrade.
 
 
-***** ChangeLog for 4.0 compared to 3.9.* *****
+***** ChangeLog for 4.0.0 compared to 3.9.* *****
 For users:
 
 NEW: Add reccuring invoice feature and automatic generation of invoices.

+ 2 - 1
build/exe/doliwamp/doliwamp.iss

@@ -101,6 +101,7 @@ Source: "build\exe\doliwamp\UsedPort.exe"; DestDir: "{app}\"; Flags: ignoreversi
 ; Put here path of Wampserver applications
 ; Value OK: apache 2.2.6, php 5.2.5 (5.2.11, 5.3.0 and 5.3.1 fails if php_exif, php_pgsql, php_zip is on), mysql 5.0.45 or 5.1.36
 ; Value OK: apache 2.2.11, php 5.3.0 (if no php_exif, php_pgsql, php_zip), mysql 5.0.45 or 5.1.36
+; Value ???: apache 2.4.19, php 5.5.12, mysql 5.6.17
 Source: "C:\Program Files\Wamp\apps\phpmyadmin4.1.14\*.*"; DestDir: "{app}\apps\phpmyadmin4.1.14"; Flags: ignoreversion recursesubdirs; Excludes: "config.inc.php,wampserver.conf,*.log,*_log,darkblue_orange"
 Source: "C:\Program Files\Wamp\bin\apache\apache2.4.9\*.*"; DestDir: "{app}\bin\apache\apache2.4.9"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,httpd.conf,wampserver.conf,*.log,*_log"
 Source: "C:\Program Files\Wamp\bin\php\php5.5.12\*.*"; DestDir: "{app}\bin\php\php5.5.12"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,phpForApache.ini,wampserver.conf,*.log,*_log"
@@ -109,7 +110,7 @@ Source: "C:\Program Files\Wamp\bin\mysql\mysql5.6.17\*.*"; DestDir: "{app}\bin\m
 Source: "build\exe\doliwamp\mysql\*.*"; DestDir: "{app}\bin\mysql\data\mysql"; Flags: onlyifdoesntexist ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db"
 ; Dolibarr
 Source: "htdocs\*.*"; DestDir: "{app}\www\dolibarr\htdocs"; Flags: ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,custom\*,custom2\*,documents\*,includes\ckeditor\_source\*,includes\savant\*,includes\phpmailer\*,jquery\plugins\template\*,nltechno*\*,PHPExcel\Shared\PDF\*,PHPExcel\Shared\PCLZip\*,tcpdf\fonts\dejavu-fonts-ttf-2.33\*,tcpdf\fonts\freefont-20100919\*,tcpdf\fonts\utils\*,*\conf.php,*\conf.php.mysql,*\conf.php.old,*\conf.php.postgres,*\conf.php.sav,*\install.forced.php"
-Source: "dev\*.*"; DestDir: "{app}\www\dolibarr\dev"; Flags: ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,dbmodel\*,fpdf\*,initdata\*,iso-normes\*,licence\*,phpcheckstyle\*,phpunit\*,samples\*,test\*,uml\*,vagrant\*,xdebug\*"
+Source: "dev\*.*"; DestDir: "{app}\www\dolibarr\dev"; Flags: ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,dbmodel\*,fpdf\*,initdata\*,initdemo\*,iso-normes\*,licence\*,phpcheckstyle\*,phpunit\*,samples\*,test\*,uml\*,vagrant\*,xdebug\*"
 Source: "doc\*.*"; DestDir: "{app}\www\dolibarr\doc"; Flags: ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,wiki\*,plaquette\*,dev\*,images\dolibarr_screenshot2.png,images\dolibarr_screenshot3.png,images\dolibarr_screenshot4.png,images\dolibarr_screenshot5.png,images\dolibarr_screenshot6.png,images\dolibarr_screenshot7.png,images\dolibarr_screenshot8.png,images\dolibarr_screenshot9.png,images\dolibarr_screenshot10.png,images\dolibarr_screenshot11.png,images\dolibarr_screenshot12.png"
 Source: "scripts\*.*"; DestDir: "{app}\www\dolibarr\scripts"; Flags: ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,product\materiel.net.php,product\import-product.php"
 Source: "*.*"; DestDir: "{app}\www\dolibarr"; Flags: ignoreversion; Excludes: ".gitignore,.project,CVS\*,Thumbs.db,default.properties,install.lock"

+ 20 - 2
build/makepack-dolibarr.pl

@@ -53,7 +53,7 @@ if (-d "/usr/src/RPM")      { $RPMDIR="/usr/src/RPM"; } # mandrake
 
 
 use vars qw/ $REVISION $VERSION /;
-$VERSION="3.3";
+$VERSION="4.0";
 
 
 
@@ -356,6 +356,7 @@ if ($nboftargetok) {
 		# Test that the ChangeLog is ok
 		$TMPBUILDTOCHECKCHANGELOG=$BUILD;
 		$TMPBUILDTOCHECKCHANGELOG =~ s/\-rc\d*//;
+		$TMPBUILDTOCHECKCHANGELOG =~ s/\-beta\d*//;
 		print "Check if ChangeLog is ok for version $MAJOR.$MINOR\.$TMPBUILDTOCHECKCHANGELOG\n";
 		$ret=`grep "ChangeLog for $MAJOR.$MINOR\.$TMPBUILDTOCHECKCHANGELOG" "$SOURCE/ChangeLog" 2>&1`;
 		if (! $ret)
@@ -458,6 +459,7 @@ if ($nboftargetok) {
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/codetemplates`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/dbmodel`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/initdata`;
+		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/initdemo`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/iso-normes`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/ldap`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/dev/licence`;
@@ -1121,7 +1123,7 @@ if ($nboftargetok) {
 	{
 		if ($CHOOSEDPUBLISH{$target} < 0) { next; }
 	
-		print "\nList of files to publish\n";
+		print "\nList of files to publish (BUILD=$BUILD)\n";
 		%filestoscansf=(
 			"$DESTI/package_rpm_generic/$FILENAMERPM"=>'Dolibarr installer for Fedora-Redhat-Mandriva-Opensuse (DoliRpm)',
 			"$DESTI/package_debian-ubuntu/${FILENAMEDEB}_all.deb"=>'Dolibarr installer for Debian-Ubuntu (DoliDeb)',
@@ -1136,6 +1138,22 @@ if ($nboftargetok) {
 			"$DESTI/standard/$FILENAMETGZ.tgz"=>'standard',
 			"$DESTI/standard/$FILENAMETGZ.zip"=>'standard'
 		);
+		if ($target eq 'ASSO' && $BUILD =~ /[a-z]/i)   { 	# Not stable
+			%filestoscansf=(
+				"$DESTI/$FILENAMERPM"=>'Dolibarr installer for Fedora-Redhat-Mandriva-Opensuse (DoliRpm)',
+				"$DESTI/${FILENAMEDEB}_all.deb"=>'Dolibarr installer for Debian-Ubuntu (DoliDeb)',
+				"$DESTI/$FILENAMEEXEDOLIWAMP.exe"=>'Dolibarr installer for Windows (DoliWamp)',
+				"$DESTI/$FILENAMETGZ.tgz"=>'Dolibarr ERP-CRM',
+				"$DESTI/$FILENAMETGZ.zip"=>'Dolibarr ERP-CRM'
+			);
+			%filestoscanstableasso=(
+				"$DESTI/$FILENAMERPM"=>'',
+				"$DESTI/${FILENAMEDEB}_all.deb"=>'',
+				"$DESTI/$FILENAMEEXEDOLIWAMP.exe"=>'',
+				"$DESTI/$FILENAMETGZ.tgz"=>'',
+				"$DESTI/$FILENAMETGZ.zip"=>''
+			);
+		}
 
 		use POSIX qw/strftime/;
 		foreach my $file (sort keys %filestoscansf)

+ 2 - 0
build/rpm/dolibarr_fedora.spec

@@ -192,6 +192,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/loan
 %_datadir/dolibarr/htdocs/mailmanspip
 %_datadir/dolibarr/htdocs/margin
+%_datadir/dolibarr/htdocs/multicurrency
 %_datadir/dolibarr/htdocs/opensurvey
 %_datadir/dolibarr/htdocs/paybox
 %_datadir/dolibarr/htdocs/paypal
@@ -206,6 +207,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/theme
 %_datadir/dolibarr/htdocs/user
 %_datadir/dolibarr/htdocs/webservices
+%_datadir/dolibarr/htdocs/websites
 %_datadir/dolibarr/htdocs/*.ico
 %_datadir/dolibarr/htdocs/*.patch
 %_datadir/dolibarr/htdocs/*.php

+ 2 - 0
build/rpm/dolibarr_generic.spec

@@ -272,6 +272,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/loan
 %_datadir/dolibarr/htdocs/mailmanspip
 %_datadir/dolibarr/htdocs/margin
+%_datadir/dolibarr/htdocs/multicurrency
 %_datadir/dolibarr/htdocs/opensurvey
 %_datadir/dolibarr/htdocs/paybox
 %_datadir/dolibarr/htdocs/paypal
@@ -286,6 +287,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/theme
 %_datadir/dolibarr/htdocs/user
 %_datadir/dolibarr/htdocs/webservices
+%_datadir/dolibarr/htdocs/websites
 %_datadir/dolibarr/htdocs/*.ico
 %_datadir/dolibarr/htdocs/*.patch
 %_datadir/dolibarr/htdocs/*.php

+ 2 - 0
build/rpm/dolibarr_mandriva.spec

@@ -189,6 +189,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/loan
 %_datadir/dolibarr/htdocs/mailmanspip
 %_datadir/dolibarr/htdocs/margin
+%_datadir/dolibarr/htdocs/multicurrency
 %_datadir/dolibarr/htdocs/opensurvey
 %_datadir/dolibarr/htdocs/paybox
 %_datadir/dolibarr/htdocs/paypal
@@ -203,6 +204,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/theme
 %_datadir/dolibarr/htdocs/user
 %_datadir/dolibarr/htdocs/webservices
+%_datadir/dolibarr/htdocs/websites
 %_datadir/dolibarr/htdocs/*.ico
 %_datadir/dolibarr/htdocs/*.patch
 %_datadir/dolibarr/htdocs/*.php

+ 2 - 0
build/rpm/dolibarr_opensuse.spec

@@ -200,6 +200,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/loan
 %_datadir/dolibarr/htdocs/mailmanspip
 %_datadir/dolibarr/htdocs/margin
+%_datadir/dolibarr/htdocs/multicurrency
 %_datadir/dolibarr/htdocs/opensurvey
 %_datadir/dolibarr/htdocs/paybox
 %_datadir/dolibarr/htdocs/paypal
@@ -214,6 +215,7 @@ done >>%{name}.lang
 %_datadir/dolibarr/htdocs/theme
 %_datadir/dolibarr/htdocs/user
 %_datadir/dolibarr/htdocs/webservices
+%_datadir/dolibarr/htdocs/websites
 %_datadir/dolibarr/htdocs/*.ico
 %_datadir/dolibarr/htdocs/*.patch
 %_datadir/dolibarr/htdocs/*.php

+ 3 - 0
dev/translation/sanity_check_en_langfiles.php

@@ -316,6 +316,9 @@ if ((! empty($_REQUEST['unused']) && $_REQUEST['unused'] == 'true') || (isset($a
 	    if (preg_match('/^EMailText/', $value)) $qualifiedforclean=0;
 	    if (preg_match('/ById$/', $value)) $qualifiedforclean=0;
 	    if (preg_match('/ByLogin$/', $value)) $qualifiedforclean=0;
+	    // printing
+	    if (preg_match('/PrintingDriverDesc$/', $value)) $qualifiedforclean=0;
+	    if (preg_match('/PrintTestDesc$/', $value)) $qualifiedforclean=0;
 	    // products
 	    if (preg_match('/GlobalVariableUpdaterType$/', $value)) $qualifiedforclean=0;
 	    if (preg_match('/GlobalVariableUpdaterHelp$/', $value)) $qualifiedforclean=0;

+ 3 - 4
htdocs/accountancy/journal/purchasesjournal.php

@@ -166,14 +166,13 @@ if ($action == 'writebookkeeping') {
 	$now = dol_now();
 	$error = 0;
 
-	foreach ( $tabfac as $key => $val ) {
-
+	foreach ($tabfac as $key => $val) 
+	{
 		$companystatic = new Societe($db);
 		$invoicestatic = new FactureFournisseur($db);
 
 		$invoicestatic->id = $key;
-		$invoicestatic->ref = $val["ref"];
-		$invoicestatic->ref = $val["refsologest"];
+		$invoicestatic->ref = (string) $val["refsologest"];
 		$invoicestatic->refsupplier = $val["refsuppliersologest"];
 		$invoicestatic->type = $val["type"];
 		$invoicestatic->description = html_entity_decode(dol_trunc($val["description"], 32));

+ 1 - 1
htdocs/accountancy/journal/sellsjournal.php

@@ -207,7 +207,7 @@ if ($action == 'writebookkeeping') {
 		$companystatic->client = $tabcompany[$key]['code_client'];
 
 		$invoicestatic->id = $key;
-		$invoicestatic->ref = $val["ref"];
+		$invoicestatic->ref = (string) $val["ref"];
 
 		foreach ( $tabttc[$key] as $k => $mt ) {
 			$bookkeeping = new BookKeeping($db);

+ 32 - 30
htdocs/adherents/card.php

@@ -404,11 +404,7 @@ if (empty($reshook))
 			}
 			else
 			{
-				if ($object->error) {
-					setEventMessages($object->error, $object->errors, 'errors');
-				} else {
-					setEventMessages($object->error, $object->errors, 'errors');
-				}
+				setEventMessages($object->error, $object->errors, 'errors');
 				$action='';
 			}
 		}
@@ -1443,6 +1439,12 @@ else
 		if (empty($conf->global->ADHERENT_LOGIN_NOT_REQUIRED))
 		{
 			print '<tr><td>'.$langs->trans("Password").'</td><td>'.preg_replace('/./i','*',$object->pass);
+			if ($object->pass) print preg_replace('/./i','*',$object->pass);
+			else
+			{
+			    if ($user->admin) print $langs->trans("Crypted").': '.$object->pass_indatabase_crypted;
+			    else print $langs->trans("Hidden");
+			}
 			if ((! empty($object->pass) || ! empty($object->pass_crypted)) && empty($object->user_id))
 			{
 			    $langs->load("errors");
@@ -1451,7 +1453,7 @@ else
 			}
 			print '</td></tr>';
 		}
-
+		
         print '</table>';
         
         print '</div>';
@@ -1483,6 +1485,30 @@ else
 			print $object->showOptionals($extrafields, 'view', $parameters);
 		}
 
+        // Date end subscription
+        print '<tr><td>'.$langs->trans("SubscriptionEndDate").'</td><td class="valeur">';
+        if ($object->datefin)
+        {
+            print dol_print_date($object->datefin,'day');
+            if ($object->hasDelay()) {
+                print " ".img_warning($langs->trans("Late"));
+            }
+        }
+        else
+        {
+	        if (! $adht->cotisation)
+	        {
+	        	print $langs->trans("SubscriptionNotRecorded");
+		        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // displays delay Pictogram only if not a draft and not terminated
+	        }
+	        else
+	        {
+	            print $langs->trans("SubscriptionNotReceived");
+	            if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // displays delay Pictogram only if not a draft and not terminated
+	        }
+        }
+        print '</td></tr>';
+		
 		// Third party Dolibarr
 		if (! empty($conf->societe->enabled))
 		{
@@ -1553,30 +1579,6 @@ else
 		}
 		print '</td></tr>';
 
-        // Date end subscription
-        print '<tr><td>'.$langs->trans("SubscriptionEndDate").'</td><td class="valeur">';
-        if ($object->datefin)
-        {
-            print dol_print_date($object->datefin,'day');
-            if ($object->hasDelay()) {
-                print " ".img_warning($langs->trans("Late"));
-            }
-        }
-        else
-        {
-	        if (! $adht->cotisation)
-	        {
-	        	print $langs->trans("SubscriptionNotRecorded");
-		        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // displays delay Pictogram only if not a draft and not terminated
-	        }
-	        else
-	        {
-	            print $langs->trans("SubscriptionNotReceived");
-	            if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // displays delay Pictogram only if not a draft and not terminated
-	        }
-        }
-        print '</td></tr>';
-
 		print "</table>\n";
 
 		print "</div></div></div>\n";

+ 24 - 24
htdocs/adherents/card_subscriptions.php

@@ -652,6 +652,30 @@ if ($rowid > 0)
 		print $object->showOptionals($extrafields, 'view', $parameters);
 	}
 
+	// Date end subscription
+	print '<tr><td>'.$langs->trans("SubscriptionEndDate").'</td><td class="valeur">';
+	if ($object->datefin)
+	{
+	    print dol_print_date($object->datefin,'day');
+	    if ($object->hasDelay()) {
+	        print " ".img_warning($langs->trans("Late"));
+	    }
+	}
+	else
+	{
+	    if (! $adht->cotisation)
+	    {
+	        print $langs->trans("SubscriptionNotRecorded");
+	        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie
+	    }
+	    else
+	    {
+	        print $langs->trans("SubscriptionNotReceived");
+	        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie
+	    }
+	}
+	print '</td></tr>';
+	
 	// Third party Dolibarr
 	if (! empty($conf->societe->enabled))
 	{
@@ -722,30 +746,6 @@ if ($rowid > 0)
 	}
 	print '</td></tr>';
 
-    // Date end subscription
-    print '<tr><td>'.$langs->trans("SubscriptionEndDate").'</td><td class="valeur">';
-    if ($object->datefin)
-    {
-        print dol_print_date($object->datefin,'day');
-        if ($object->hasDelay()) {
-            print " ".img_warning($langs->trans("Late"));
-        }
-    }
-    else
-    {
-	    if (! $adht->cotisation)
-	    {
-	     	print $langs->trans("SubscriptionNotRecorded");
-	        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie
-	    }
-	    else
-	    {
-	    	print $langs->trans("SubscriptionNotReceived");
-	        if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie
-	    }
-    }
-    print '</td></tr>';
-
     print "</table>\n";
 
 	print "</div></div></div>\n";

+ 44 - 21
htdocs/adherents/class/adherent.class.php

@@ -47,8 +47,15 @@ class Adherent extends CommonObject
     var $mesgs;
 
     var $login;
-    var $pass;
-    var $societe;
+
+	//! Clear password in memory
+	var $pass;
+	//! Clear password in database (defined if DATABASE_PWD_ENCRYPTED=0)
+	var $pass_indatabase;
+	//! Encrypted password in database (always defined)
+	var $pass_indatabase_crypted;
+
+	var $societe;
     var $company;
     var $address;
     var $zip;
@@ -309,7 +316,7 @@ class Adherent extends CommonObject
             if ($id > 0)
             {
                 $this->id=$id;
-                $this->ref=$id;
+                $this->ref=(string) $id;
 
                 // Update minor fields
                 $result=$this->update($user,1,1,0,0,'add'); // nosync is 1 to avoid update data of user
@@ -482,8 +489,10 @@ class Adherent extends CommonObject
                 dol_syslog(get_class($this)."::update update password");
                 if ($this->pass != $this->pass_indatabase && $this->pass != $this->pass_indatabase_crypted)
                 {
-                    // Si mot de passe saisi et different de celui en base
-                    $result=$this->setPassword($user,$this->pass,0,$notrigger,$nosyncuserpass);
+                    $isencrypted = empty($conf->global->DATABASE_PWD_ENCRYPTED)?0:1;
+                    
+                    // If password to set differs from the one found into database
+                    $result=$this->setPassword($user,$this->pass,$isencrypted,$notrigger,$nosyncuserpass);
                     if (! $nbrowsaffected) $nbrowsaffected++;
                 }
             }
@@ -509,7 +518,7 @@ class Adherent extends CommonObject
             if (! $error && $nbrowsaffected)	// If something has change in main data
             {
                 // Update information on linked user if it is an update
-                if ($this->user_id > 0 && ! $nosyncuser)
+                if (! $error && $this->user_id > 0 && ! $nosyncuser)
                 {
                     require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
 
@@ -552,7 +561,7 @@ class Adherent extends CommonObject
                 }
 
                 // Update information on linked thirdparty if it is an update
-                if ($this->fk_soc > 0 && ! $nosyncthirdparty)
+                if (! $error && $this->fk_soc > 0 && ! $nosyncthirdparty)
                 {
                     require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
 
@@ -810,13 +819,11 @@ class Adherent extends CommonObject
             $password=getRandomPassword(false);
         }
 
-        // Cryptage mot de passe
-        if ($isencrypted)
-        {
-            // Encryption
-            $password_indatabase = dol_hash($password);
-        }
-        else
+        // Crypt password
+        $password_crypted = dol_hash($password);
+
+        $password_indatabase = '';
+        if (! $isencrypted)
         {
             $password_indatabase = $password;
         }
@@ -824,7 +831,17 @@ class Adherent extends CommonObject
         $this->db->begin();
 
         // Mise a jour
-        $sql = "UPDATE ".MAIN_DB_PREFIX."adherent SET pass = '".$this->db->escape($password_indatabase)."'";
+        $sql = "UPDATE ".MAIN_DB_PREFIX."adherent";
+        $sql.= " SET pass_crypted = '".$this->db->escape($password_crypted)."'";
+        //if (! empty($conf->global->DATABASE_PWD_ENCRYPTED))
+        if ($isencrypted)
+        {
+            $sql.= ", pass = null";
+        }
+        else
+        {
+            $sql.= ", pass = '".$this->db->escape($password_indatabase)."'";
+        }
         $sql.= " WHERE rowid = ".$this->id;
 
         //dol_syslog("Adherent::Password sql=hidden");
@@ -838,7 +855,8 @@ class Adherent extends CommonObject
             {
                 $this->pass=$password;
                 $this->pass_indatabase=$password_indatabase;
-
+                $this->pass_indatabase_crypted=$password_crypted;
+                
                 if ($this->user_id && ! $nosyncuser)
                 {
                     require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
@@ -1042,7 +1060,7 @@ class Adherent extends CommonObject
 
         $sql = "SELECT d.rowid, d.ref_ext, d.civility as civility_id, d.firstname, d.lastname, d.societe as company, d.fk_soc, d.statut, d.public, d.address, d.zip, d.town, d.note_private,";
         $sql.= " d.note_public,";
-        $sql.= " d.email, d.skype, d.phone, d.phone_perso, d.phone_mobile, d.login, d.pass,";
+        $sql.= " d.email, d.skype, d.phone, d.phone_perso, d.phone_mobile, d.login, d.pass, d.pass_crypted,";
         $sql.= " d.photo, d.fk_adherent_type, d.morphy, d.entity,";
         $sql.= " d.datec as datec,";
         $sql.= " d.tms as datem,";
@@ -1087,7 +1105,6 @@ class Adherent extends CommonObject
                 $this->firstname		= $obj->firstname;
                 $this->lastname			= $obj->lastname;
                 $this->login			= $obj->login;
-                $this->pass				= $obj->pass;
                 $this->societe			= $obj->company;
                 $this->company			= $obj->company;
                 $this->fk_soc			= $obj->fk_soc;
@@ -1095,6 +1112,10 @@ class Adherent extends CommonObject
                 $this->zip				= $obj->zip;
                 $this->town				= $obj->town;
 
+                $this->pass				= $obj->pass;
+                $this->pass_indatabase  = $obj->pass;
+                $this->pass_indatabase_crypted = $obj->pass_crypted;
+                
                 $this->state_id			= $obj->state_id;
                 $this->state_code		= $obj->state_id?$obj->state_code:'';
                 $this->state			= $obj->state_id?$obj->state:'';
@@ -1423,9 +1444,10 @@ class Adherent extends CommonObject
         $err=0;
 
         // mailman
-        if (! empty($conf->global->ADHERENT_USE_MAILMAN))
+        if (! empty($conf->global->ADHERENT_USE_MAILMAN) && ! empty($conf->mailmanspip->enabled))
         {
             $result=$mailmanspip->add_to_mailman($this);
+         
             if ($result < 0)
             {
             	if (! empty($mailmanspip->error)) $this->errors[]=$mailmanspip->error;
@@ -1444,7 +1466,7 @@ class Adherent extends CommonObject
         }
 
         // spip
-        if ($conf->global->ADHERENT_USE_SPIP && ! empty($conf->mailmanspip->enabled))
+        if (! empty($conf->global->ADHERENT_USE_SPIP) && ! empty($conf->mailmanspip->enabled))
         {
             $result=$mailmanspip->add_to_spip($this);
             if ($result < 0)
@@ -1458,7 +1480,7 @@ class Adherent extends CommonObject
             return -$err;
         }
         else
-       {
+        {
             return 1;
         }
     }
@@ -1556,6 +1578,7 @@ class Adherent extends CommonObject
             $label.= '<br><b>' . $langs->trans('Name') . ':</b> ' . $this->getFullName($langs);
         $linkclose = '" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
 
+        $link=''; $linkend='';
         if ($option == 'card')
         {
             $link = '<a href="'.DOL_URL_ROOT.'/adherents/card.php?rowid='.$this->id.$linkclose;

+ 1 - 1
htdocs/adherents/class/cotisation.class.php

@@ -213,7 +213,7 @@ class Cotisation extends CommonObject
 				$result=$member->fetch($this->fk_adherent);
 				$result=$member->update_end_date($user);
 
-				if ($accountline->id > 0)						// If we found bank account line (this means this->fk_bank defined)
+				if (is_object($accountline) && $accountline->id > 0)						// If we found bank account line (this means this->fk_bank defined)
 				{
 					$result=$accountline->delete($user);		// Return false if refused because line is conciliated
 					if ($result > 0)

+ 4 - 2
htdocs/admin/company.php

@@ -308,10 +308,12 @@ if ($action == 'edit' || $action == 'updateedit')
 	print '<table class="noborder" width="100%">';
 	print '<tr class="liste_titre"><th width="35%">'.$langs->trans("CompanyInfo").'</th><th>'.$langs->trans("Value").'</th></tr>'."\n";
 
+	// Name
 	$var=!$var;
 	print '<tr '.$bc[$var].'><td class="fieldrequired"><label for="name">'.$langs->trans("CompanyName").'</label></td><td>';
 	print '<input name="nom" id="name" size="30" value="'. ($conf->global->MAIN_INFO_SOCIETE_NOM?$conf->global->MAIN_INFO_SOCIETE_NOM:$_POST["nom"]) . '" autofocus="autofocus"></td></tr>'."\n";
 
+	// Addresse
 	$var=!$var;
 	print '<tr '.$bc[$var].'><td><label for="address">'.$langs->trans("CompanyAddress").'</label></td><td>';
 	print '<textarea name="address" id="address" class="quatrevingtpercent" rows="'.ROWS_3.'">'. ($conf->global->MAIN_INFO_SOCIETE_ADDRESS?$conf->global->MAIN_INFO_SOCIETE_ADDRESS:$_POST["address"]) . '</textarea></td></tr>'."\n";
@@ -392,7 +394,7 @@ if ($action == 'edit' || $action == 'updateedit')
 	// Note
 	$var=!$var;
 	print '<tr '.$bc[$var].'><td valign="top"><label for="note">'.$langs->trans("Note").'</label></td><td>';
-	print '<textarea class="flat" name="note" id="note" cols="80" rows="'.ROWS_5.'">'.(! empty($conf->global->MAIN_INFO_SOCIETE_NOTE) ? $conf->global->MAIN_INFO_SOCIETE_NOTE : '').'</textarea></td></tr>';
+	print '<textarea class="flat quatrevingtpercent" name="note" id="note" rows="'.ROWS_5.'">'.(! empty($conf->global->MAIN_INFO_SOCIETE_NOTE) ? $conf->global->MAIN_INFO_SOCIETE_NOTE : '').'</textarea></td></tr>';
 	print '</td></tr>';
 
 	print '</table>';
@@ -531,7 +533,7 @@ if ($action == 'edit' || $action == 'updateedit')
 	// Object of the company
 	$var=!$var;
 	print '<tr '.$bc[$var].'><td width="35%"><label for="object">'.$langs->trans("CompanyObject").'</label></td><td>';
-	print '<textarea class="flat" name="object" id="object" cols="80" rows="'.ROWS_5.'">'.(! empty($conf->global->MAIN_INFO_SOCIETE_OBJECT) ? $conf->global->MAIN_INFO_SOCIETE_OBJECT : '').'</textarea></td></tr>';
+	print '<textarea class="flat quatrevingtpercent" name="object" id="object" rows="'.ROWS_5.'">'.(! empty($conf->global->MAIN_INFO_SOCIETE_OBJECT) ? $conf->global->MAIN_INFO_SOCIETE_OBJECT : '').'</textarea></td></tr>';
 	print '</td></tr>';
 
 	print '</table>';

+ 36 - 5
htdocs/admin/menus/edit.php

@@ -62,6 +62,23 @@ if ($action == 'update')
 {
     if (! $_POST['cancel'])
     {
+        $leftmenu=''; $mainmenu='';
+        if (! empty($_POST['menuIdParent']) && ! is_numeric($_POST['menuIdParent']))
+        {
+            $tmp=explode('&',$_POST['menuIdParent']);
+            foreach($tmp as $s)
+            {
+                if (preg_match('/fk_mainmenu=/',$s))
+                {
+                    $mainmenu=preg_replace('/fk_mainmenu=/','',$s);
+                }
+                if (preg_match('/fk_leftmenu=/',$s))
+                {
+                    $leftmenu=preg_replace('/fk_leftmenu=/','',$s);
+                }
+            }
+        }
+        
         $menu = new Menubase($db);
         $result=$menu->fetch($_POST['menuId']);
         if ($result > 0)
@@ -75,7 +92,18 @@ if ($action == 'update')
             $menu->perms=$_POST['perms'];
             $menu->target=$_POST['target'];
             $menu->user=$_POST['user'];
-            $menu->fk_menu=$_POST['fk_menu'];
+            if (is_numeric($_POST['menuIdParent']))
+            {
+            	$menu->fk_menu=$_POST['menuIdParent'];
+            }
+            else
+            {
+    	       	if ($_POST['type'] == 'top') $menu->fk_menu=0;
+    	       	else $menu->fk_menu=-1;
+            	$menu->fk_mainmenu=$mainmenu;
+            	$menu->fk_leftmenu=$leftmenu;
+            }
+
             $result=$menu->update($user);
             if ($result > 0)
             {
@@ -342,7 +370,7 @@ if ($action == 'create')
     }
     else
     {
-        print '<td><input type="text" size="20" id="menuId" name="menuId" value="'.($_POST["menuId"]?$_POST["menuId"]:'').'"></td>';
+        print '<td><input type="text" size="40" id="menuId" name="menuId" value="'.($_POST["menuId"]?$_POST["menuId"]:'').'"></td>';
     }
     print '<td>'.$langs->trans('DetailMenuIdParent');
     print ', '.$langs->trans("Example").': fk_mainmenu=abc&fk_leftmenu=def';
@@ -425,12 +453,15 @@ elseif ($action == 'edit')
     print '<tr><td class="fieldrequired">'.$langs->trans('Type').'</td><td>'.$langs->trans(ucfirst($menu->type)).'</td><td>'.$langs->trans('DetailType').'</td></tr>';
 
     // MenuId Parent
-    print '<tr><td class="fieldrequired">'.$langs->trans('MenuIdParent').'</td>';
+    print '<tr><td class="fieldrequired">'.$langs->trans('MenuIdParent');
+    print '</td>';
     $valtouse=$menu->fk_menu;
     if ($menu->fk_mainmenu) $valtouse='fk_mainmenu='.$menu->fk_mainmenu;
     if ($menu->fk_leftmenu) $valtouse.='&fk_leftmenu='.$menu->fk_leftmenu;
-    print '<td><input type="text" name="fk_menu" value="'.$valtouse.'" size="10"></td>';
-    print '<td>'.$langs->trans('DetailMenuIdParent').'</td></tr>';
+    print '<td><input type="text" name="menuIdParent" value="'.$valtouse.'" size="40"></td>';
+    print '<td>'.$langs->trans('DetailMenuIdParent');
+    print ', '.$langs->trans("Example").': fk_mainmenu=abc&fk_leftmenu=def';
+    print '</td></tr>';
 
     // Niveau
     //print '<tr><td>'.$langs->trans('Level').'</td><td>'.$menu->level.'</td><td>'.$langs->trans('DetailLevel').'</td></tr>';

+ 1 - 1
htdocs/admin/menus/index.php

@@ -361,7 +361,7 @@ if ($conf->use_javascript_ajax)
 	
 	if (count($remainingdata))
 	{
-    	print '<table class="border" width="100%">';
+    	print '<table class="noborder centpercent">';
     	
     	print '<tr class="liste_titre">';
     	print '<td>'.$langs->trans("NotTopTreeMenuPersonalized").'</td>';

+ 38 - 12
htdocs/admin/tools/export.php

@@ -53,6 +53,8 @@ if ($file && ! $what)
     exit;
 }
 
+$errormsg='';
+
 
 /*
  * Actions
@@ -120,16 +122,37 @@ if ($what == 'mysql')
 {
     
     $cmddump=GETPOST("mysqldump");	// Do not sanitize here with 'alpha', will be sanitize later by escapeshellarg
-    if ($cmddump)
+    if (! empty($dolibarr_main_restrict_os_commands))
+    {
+        $arrayofallowedcommand=explode(',', $dolibarr_main_restrict_os_commands);
+        $ok=0;
+        dol_syslog("Command are restricted to ".$dolibarr_main_restrict_os_commands.". We check that on of this command is inside ".$cmddump);
+        foreach($arrayofallowedcommand as $allowedcommand)
+        {
+            if (preg_match('/'.preg_quote($allowedcommand,'/').'/', $cmddump))
+            {
+                $ok=1;
+                break;
+            }
+        }
+        if (! $ok)
+        {
+            $errormsg=$langs->trans('CommandIsNotInsideAllowedCommands');
+        }
+    }
+    
+    if (! $errormsg && $cmddump)
     {
         dolibarr_set_const($db, 'SYSTEMTOOLS_MYSQLDUMP', $cmddump,'chaine',0,'',$conf->entity);
     }
 
-    $utils->dumpDatabase(GETPOST('compression','alpha'), $what, 0, $file);
-    
-    $errormsg=$utils->error;
-    $_SESSION["commandbackuplastdone"]=$utils->result['commandbackuplastdone'];
-    $_SESSION["commandbackuptorun"]=$utils->result['commandbackuptorun'];
+    if (! $errormsg) 
+    {
+        $utils->dumpDatabase(GETPOST('compression','alpha'), $what, 0, $file);
+        $errormsg=$utils->error;
+        $_SESSION["commandbackuplastdone"]=$utils->result['commandbackuplastdone'];
+        $_SESSION["commandbackuptorun"]=$utils->result['commandbackuptorun'];
+    }
 }
 
 // MYSQL NO BIN
@@ -146,16 +169,19 @@ if ($what == 'mysqlnobin')
 if ($what == 'postgresql')
 {
     $cmddump=GETPOST("postgresqldump");	// Do not sanitize here with 'alpha', will be sanitize later by escapeshellarg
-    if ($cmddump)
+    
+    if (! $errormsg && $cmddump)
     {
         dolibarr_set_const($db, 'SYSTEMTOOLS_POSTGRESQLDUMP', $cmddump,'chaine',0,'',$conf->entity);
     }
 
-    $utils->dumpDatabase(GETPOST('compression','alpha'), $what, 0, $file);
-    
-    $errormsg=$utils->error;
-    $_SESSION["commandbackuplastdone"]=$utils->result['commandbackuplastdone'];
-    $_SESSION["commandbackuptorun"]=$utils->result['commandbackuptorun'];
+    if (! $errormsg) 
+    {
+        $utils->dumpDatabase(GETPOST('compression','alpha'), $what, 0, $file);
+        $errormsg=$utils->error;
+        $_SESSION["commandbackuplastdone"]=$utils->result['commandbackuplastdone'];
+        $_SESSION["commandbackuptorun"]=$utils->result['commandbackuptorun'];
+    }
 
     $what='';   // Clear to show message to run command
 }

+ 1 - 1
htdocs/cashdesk/include/environnement.php

@@ -44,7 +44,7 @@ $conf_fkaccount_cb = (! empty($_SESSION["CASHDESK_ID_BANKACCOUNT_CB"]))?$_SESSIO
 
 
 // View parameters
-$conf_taille_listes = (empty($conf->global->PRODUIT_LIMIT_SIZE)?500:$conf->global->PRODUIT_LIMIT_SIZE);	// Nombre max de lignes a afficher dans les listes
+$conf_taille_listes = (empty($conf->global->PRODUIT_LIMIT_SIZE)?1000:$conf->global->PRODUIT_LIMIT_SIZE);	// Nombre max de lignes a afficher dans les listes
 $conf_nbr_car_listes = 60;	// Nombre max de caracteres par ligne dans les listes
 
 // Add hidden option to force decrease of stock whatever is user setup

+ 2 - 1
htdocs/categories/categorie.php

@@ -617,13 +617,14 @@ else if ($id || $ref)
  * 	@param		Object		$object				Object we want to see categories it can be classified into
  * 	@param		int			$typeid				Type of category (0, 1, 2, 3)
  *  @param		int			$socid				Id thirdparty
- *  @param		int		$showclassifyform	1=Add form to 'Classify', 0=Do not show form to 'Classify'
+ *  @param		int		    $showclassifyform	1=Add form to 'Classify', 0=Do not show form to 'Classify'
  *  @return		int			0
  */
 function formCategory($db,$object,$typeid,$socid=0,$showclassifyform=1)
 {
 	global $user,$langs,$form,$bc;
 
+	$title='NotDefined';
 	if ($typeid == Categorie::TYPE_PRODUCT)     $title = $langs->trans("ProductsCategoriesShort");
 	if ($typeid == Categorie::TYPE_SUPPLIER)    $title = $langs->trans("SuppliersCategoriesShort");
 	if ($typeid == Categorie::TYPE_CUSTOMER)    $title = $langs->trans("CustomersProspectsCategoriesShort");

+ 8 - 8
htdocs/categories/class/categorie.class.php

@@ -43,13 +43,13 @@ require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
 class Categorie extends CommonObject
 {
 	// Categories types
-	const TYPE_PRODUCT = 0;
-	const TYPE_SUPPLIER = 1;
-	const TYPE_CUSTOMER = 2;
-	const TYPE_MEMBER = 3;
-	const TYPE_CONTACT = 4;
-	const TYPE_USER = 4;    // categorie contact and user are same !
-    const TYPE_ACCOUNT = 5; // bank account
+	const TYPE_PRODUCT = 0;    // TODO Replace with value 'product'
+	const TYPE_SUPPLIER = 1;   // TODO Replace this value with 'supplier'
+	const TYPE_CUSTOMER = 2;   // TODO Replace this value with 'customer'
+	const TYPE_MEMBER = 3;     // TODO Replace this value with 'member'
+	const TYPE_CONTACT = 4;    // TODO Replace this value with 'contact'
+	const TYPE_USER = 4;       // categorie contact and user are same !   TODO Replace this value with 'user'
+    const TYPE_ACCOUNT = 5;    // for bank account TODO Replace this value with 'account'
 
 	/**
 	 * @var array ID mapping from type string
@@ -63,7 +63,7 @@ class Categorie extends CommonObject
 		'member'   => 3,
 		'contact'  => 4,
 		'user'     => 4,
-        'account' => 5,
+        'account'  => 5,
 	);
 	/**
 	 * @var array Foreign keys mapping from type string

+ 1 - 1
htdocs/comm/action/class/ical.class.php

@@ -208,7 +208,7 @@ class ICal
 
         //print 'type='.$type.' key='.$key.' value='.$value.'<br>'."\n";
 
-        if ($key == false)
+        if (empty($key))
         {
             $key = $this->last_key;
             switch ($type)

+ 2 - 1
htdocs/comm/propal/class/propal.class.php

@@ -2611,7 +2611,8 @@ class Propal extends CommonObject
     {
         global $langs;
 
-        // Charge tableau des produits prodids
+        // Load array of products prodids
+        $num_prods = 0;
         $prodids = array();
         $sql = "SELECT rowid";
         $sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 5 - 15
htdocs/commande/class/commande.class.php

@@ -449,6 +449,8 @@ class Commande extends CommonOrder
             // If stock is decremented on validate order, we must reincrement it
             if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
             {
+                $result = 0;
+                
                 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
                 $langs->load("agenda");
 
@@ -460,22 +462,9 @@ class Commande extends CommonOrder
                         $mouvP = new MouvementStock($this->db);
                         // We increment stock of product (and sub-products)
                         $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr",$this->ref));
-                        if ($result < 0) { $error++; }
+                        if ($result < 0) { $error++; $this->error=$mouvP->error; break; }
                     }
                 }
-
-                if (!$error)
-                {
-                    $this->statut=self::STATUS_DRAFT;
-                    $this->db->commit();
-                    return $result;
-                }
-                else
-                {
-                    $this->error=$mouvP->error;
-                    $this->db->rollback();
-                    return $result;
-                }
             }
 
             if (!$error) {
@@ -3258,7 +3247,8 @@ class Commande extends CommonOrder
 
         dol_syslog(get_class($this)."::initAsSpecimen");
 
-        // Charge tableau des produits prodids
+        // Load array of products prodids
+        $num_prods = 0;
         $prodids = array();
         $sql = "SELECT rowid";
         $sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 1 - 1
htdocs/commande/orderstoinvoice.php

@@ -72,7 +72,7 @@ $date_endy = dol_mktime(23,59,59,$_REQUEST["date_end_delymonth"],$_REQUEST["date
 
 if ($action == 'create')
 {
-	if (is_array($selected) == false)
+	if (! is_array($selected))
 	{
 		$error++;
 		setEventMessages($langs->trans('Error_OrderNotChecked'), null, 'errors');

+ 12 - 14
htdocs/compta/bank/card.php

@@ -89,8 +89,7 @@ if ($_POST["action"] == 'add')
     $account->proprio 	      = trim($_POST["proprio"]);
     $account->owner_address   = trim($_POST["owner_address"]);
 
-	if (GETPOST('account_number') <= 0) { $accountancy_code_number = ''; } else { $accountancy_code_number = GETPOST('account_number'); }
-    $account->account_number  = $accountancy_code_number;
+    $account->account_number  = GETPOST('account_number');
 	$account->accountancy_journal  = trim($_POST["accountancy_journal"]);
 
     $account->solde           = $_POST["solde"];
@@ -172,8 +171,7 @@ if ($_POST["action"] == 'update' && ! $_POST["cancel"])
     $account->proprio 	      = trim($_POST["proprio"]);
     $account->owner_address   = trim($_POST["owner_address"]);
 
-    if (GETPOST('account_number') <= 0) { $accountancy_code_number = ''; } else { $accountancy_code_number = GETPOST('account_number'); }
-    $account->account_number  = $accountancy_code_number;
+    $account->account_number  = GETPOST('account_number');
 	$account->accountancy_journal = trim($_POST["accountancy_journal"]);
 
     $account->currency_code   = trim($_POST["account_currency_code"]);
@@ -287,7 +285,7 @@ if ($action == 'create')
 
 	// Ref
 	print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans("Ref").'</td>';
-	print '<td colspan="3"><input size="8" type="text" class="flat" name="ref" value="'.($_POST["ref"]?$_POST["ref"]:$account->ref).'" maxlength="12"></td></tr>';
+	print '<td colspan="3"><input size="8" type="text" class="flat" name="ref" value="'.(GETPOST("ref")?GETPOST("ref",'alpha'):$account->ref).'" maxlength="12"></td></tr>';
 
 	// Label
 	print '<tr><td class="fieldrequired">'.$langs->trans("LabelBankCashAccount").'</td>';
@@ -393,10 +391,10 @@ if ($action == 'create')
 	print '</td></tr>';
 
 	print '<tr><td>'.$langs->trans("BalanceMinimalAllowed").'</td>';
-	print '<td colspan="3"><input size="12" type="text" class="flat" name="account_min_allowed" value="'.($_POST["account_min_allowed"]?$_POST["account_min_allowed"]:$account->min_allowed).'"></td></tr>';
+	print '<td colspan="3"><input size="12" type="text" class="flat" name="account_min_allowed" value="'.(GETPOST("account_min_allowed")?GETPOST("account_min_allowed"):$account->min_allowed).'"></td></tr>';
 
 	print '<tr><td>'.$langs->trans("BalanceMinimalDesired").'</td>';
-	print '<td colspan="3"><input size="12" type="text" class="flat" name="account_min_desired" value="'.($_POST["account_min_desired"]?$_POST["account_min_desired"]:$account->min_desired).'"></td></tr>';
+	print '<td colspan="3"><input size="12" type="text" class="flat" name="account_min_desired" value="'.(GETPOST("account_min_desired")?GETPOST("account_min_desired"):$account->min_desired).'"></td></tr>';
 
 	print '</table>';
 	print '<br>';
@@ -407,7 +405,7 @@ if ($action == 'create')
 
 		// If bank account
 		print '<tr><td class="titlefieldcreate">'.$langs->trans("BankName").'</td>';
-		print '<td colspan="3"><input size="30" type="text" class="flat" name="bank" value="'.$account->bank.'"></td>';
+		print '<td colspan="3"><input size="30" type="text" class="flat" name="bank" value="'.(GETPOST('bank')?GETPOST('bank','alpha'):$account->bank).'"></td>';
 		print '</tr>';
 
 		// Show fields of bank account
@@ -431,7 +429,7 @@ if ($action == 'create')
 			}
 
 			print '<td>'.$langs->trans($val).'</td>';
-			print '<td><input size="'.$size.'" type="text" class="flat" name="'.$name.'" value="'.$content.'"></td>';
+			print '<td><input size="'.$size.'" type="text" class="flat" name="'.$name.'" value="'.(GETPOST($name)?GETPOST($name,'alpha'):$content).'"></td>';
 			print '</tr>';
 		}
 		$ibankey = FormBank::getIBANLabel($account);
@@ -440,23 +438,23 @@ if ($action == 'create')
 
 		// IBAN
 		print '<tr><td>'.$langs->trans($ibankey).'</td>';
-		print '<td colspan="3"><input size="34" maxlength="34" type="text" class="flat" name="iban" value="'.$account->iban.'"></td></tr>';
+		print '<td colspan="3"><input size="34" maxlength="34" type="text" class="flat" name="iban" value="'.(GETPOST('iban')?GETPOST('iban','alpha'):$account->iban).'"></td></tr>';
 
 		print '<tr><td>'.$langs->trans($bickey).'</td>';
-		print '<td colspan="3"><input size="11" maxlength="11" type="text" class="flat" name="bic" value="'.$account->bic.'"></td></tr>';
+		print '<td colspan="3"><input size="11" maxlength="11" type="text" class="flat" name="bic" value="'.(GETPOST('bic')?GETPOST('bic','alpha'):$account->bic).'"></td></tr>';
 
 		print '<tr><td>'.$langs->trans("BankAccountDomiciliation").'</td><td colspan="3">';
 		print "<textarea class=\"flat\" name=\"domiciliation\" rows=\"2\" cols=\"40\">";
-		print $account->domiciliation;
+		print (GETPOST('domiciliation')?GETPOST('domiciliation'):$account->domiciliation);
 		print "</textarea></td></tr>";
 
 		print '<tr><td>'.$langs->trans("BankAccountOwner").'</td>';
-		print '<td colspan="3"><input size="30" type="text" class="flat" name="proprio" value="'.$account->proprio.'">';
+		print '<td colspan="3"><input size="30" type="text" class="flat" name="proprio" value="'.(GETPOST('proprio')?GETPOST('proprio','alpha'):$account->proprio).'">';
 		print '</td></tr>';
 
 		print '<tr><td class="tdtop">'.$langs->trans("BankAccountOwnerAddress").'</td><td colspan="3">';
 		print "<textarea class=\"flat\" name=\"owner_address\" rows=\"2\" cols=\"40\">";
-		print $account->owner_address;
+		print (GETPOST('owner_address')?GETPOST('owner_address','alpha'):$account->owner_address);
 		print "</textarea></td></tr>";
 
 		print '</table>';

+ 24 - 18
htdocs/compta/bank/index.php

@@ -45,16 +45,9 @@ $statut=GETPOST('statut');
  * View
  */
 
-$help_url='EN:Module_Banks_and_Cash|FR:Module_Banques_et_Caisses|ES:M&oacute;dulo_Bancos_y_Cajas';
-llxHeader('',$langs->trans('AccountsArea'),$help_url);
-
-$link='';
-if ($statut == '') $link='<a href="'.$_SERVER["PHP_SELF"].'?statut=all">'.$langs->trans("IncludeClosedAccount").'</a>';
-if ($statut == 'all') $link='<a href="'.$_SERVER["PHP_SELF"].'">'.$langs->trans("OnlyOpenedAccount").'</a>';
-print load_fiche_titre($langs->trans("AccountsArea"),$link, 'title_bank.png');
+$title=$langs->trans('BankAccounts');
 
-
-// On charge tableau des comptes financiers (ouverts par defaut)
+// Load array of financial accounts (opened by default)
 $accounts = array();
 
 $sql  = "SELECT rowid, courant, rappro";
@@ -66,17 +59,30 @@ $sql.= $db->order('label', 'ASC');
 $resql = $db->query($sql);
 if ($resql)
 {
-	$num = $db->num_rows($resql);
-	$i = 0;
-	while ($i < $num)
-	{
-		$objp = $db->fetch_object($resql);
-		$accounts[$objp->rowid] = $objp->courant;
-		$i++;
-	}
-	$db->free($resql);
+    $num = $db->num_rows($resql);
+    $i = 0;
+    while ($i < $num)
+    {
+        $objp = $db->fetch_object($resql);
+        $accounts[$objp->rowid] = $objp->courant;
+        $i++;
+    }
+    $db->free($resql);
 }
 
+$nbtotalofrecords = $num;
+
+
+$help_url='EN:Module_Banks_and_Cash|FR:Module_Banques_et_Caisses|ES:M&oacute;dulo_Bancos_y_Cajas';
+llxHeader('',$title,$help_url);
+
+$link='';
+if ($statut == '') $link='<a href="'.$_SERVER["PHP_SELF"].'?statut=all">'.$langs->trans("IncludeClosedAccount").'</a>';
+if ($statut == 'all') $link='<a href="'.$_SERVER["PHP_SELF"].'">'.$langs->trans("OnlyOpenedAccount").'</a>';
+
+print_barre_liste($title,$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,$link,$num,$nbtotalofrecords,'title_bank.png',0,'','',$limit, 1);
+
+
 
 /*
  * Comptes courants (courant = 1)

+ 108 - 2
htdocs/compta/facture/class/facture-rec.class.php

@@ -509,7 +509,7 @@ class FactureRec extends CommonInvoice
 	    
 		$facid=$this->id;
 
-		dol_syslog("FactureRec::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,fk_product=$fk_product,remise_percent=$remise_percent,date_start=$date_start,date_end=$date_end,ventil=$ventil,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit", LOG_DEBUG);
+		dol_syslog(get_class($this)."::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit", LOG_DEBUG);
 		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
 
 		// Check parameters
@@ -604,6 +604,111 @@ class FactureRec extends CommonInvoice
 		}
 	}
 
+	/**
+	 * 	Update a line to invoice
+	 *
+	 *  @param     	int			$rowid           	Id of line to update
+	 *	@param    	string		$desc            	Description de la ligne
+	 *	@param    	double		$pu_ht              Prix unitaire HT (> 0 even for credit note)
+	 *	@param    	double		$qty             	Quantite
+	 *	@param    	double		$txtva           	Taux de tva force, sinon -1
+	 *	@param    	int			$fk_product      	Id du produit/service predefini
+	 *	@param    	double		$remise_percent  	Pourcentage de remise de la ligne
+	 *	@param		string		$price_base_type	HT or TTC
+	 *	@param    	int			$info_bits			Bits de type de lignes
+	 *	@param    	int			$fk_remise_except	Id remise
+	 *	@param    	double		$pu_ttc             Prix unitaire TTC (> 0 even for credit note)
+	 *	@param		int			$type				Type of line (0=product, 1=service)
+	 *	@param      int			$rang               Position of line
+	 *	@param		int			$special_code		Special code
+	 *	@param		string		$label				Label of the line
+	 *	@param		string		$fk_unit			Unit
+	 *	@return    	int             				<0 if KO, Id of line if OK
+	 */
+	function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null)
+	{
+	    global $mysoc;
+	     
+	    $facid=$this->id;
+	
+	    dol_syslog(get_class($this)."::updateline facid=".$facid." rowid=$rowid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit", LOG_DEBUG);
+	    include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
+	
+	    // Check parameters
+	    if ($type < 0) return -1;
+	
+	    if ($this->brouillon)
+	    {
+	        // Clean parameters
+	        $remise_percent=price2num($remise_percent);
+	        $qty=price2num($qty);
+	        if (! $qty) $qty=1;
+	        if (! $info_bits) $info_bits=0;
+	        $pu_ht=price2num($pu_ht);
+	        $pu_ttc=price2num($pu_ttc);
+	        $txtva=price2num($txtva);
+	
+	        if ($price_base_type=='HT')
+	        {
+	            $pu=$pu_ht;
+	        }
+	        else
+	        {
+	            $pu=$pu_ttc;
+	        }
+	
+	        // Calcul du total TTC et de la TVA pour la ligne a partir de
+	        // qty, pu, remise_percent et txtva
+	        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
+	        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
+	        $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, 0, 0, 0, $price_base_type, $info_bits, $type, $mysoc);
+	        $total_ht  = $tabprice[0];
+	        $total_tva = $tabprice[1];
+	        $total_ttc = $tabprice[2];
+	        	
+	        $product_type=$type;
+	        if ($fk_product)
+	        {
+	            $product=new Product($this->db);
+	            $result=$product->fetch($fk_product);
+	            $product_type=$product->type;
+	        }
+	
+	        $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet_rec SET ";
+	        $sql.= "fk_facture = '".$facid."'";
+	        $sql.= ", label=".(! empty($label)?"'".$this->db->escape($label)."'":"null");
+	        $sql.= ", description='".$this->db->escape($desc)."'";
+	        $sql.= ", price=".price2num($pu_ht);
+	        $sql.= ", qty=".price2num($qty);
+	        $sql.= ", tva_tx=".price2num($txtva);
+	        $sql.= ", fk_product=".(! empty($fk_product)?"'".$fk_product."'":"null");
+	        $sql.= ", product_type=".$product_type;
+	        $sql.= ", remise_percent='".price2num($remise_percent)."'";
+	        $sql.= ", subprice='".price2num($pu_ht)."'";
+	        $sql.= ", total_ht='".price2num($total_ht)."'";
+	        $sql.= ", total_tva='".price2num($total_tva)."'";
+	        $sql.= ", total_ttc='".price2num($total_ttc)."'";
+	        $sql.= ", rang=".$rang;
+	        $sql.= ", special_code=".$special_code;
+	        $sql.= ", fk_unit=".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
+	        $sql.= " WHERE rowid = ".$rowid;
+	        
+	        dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
+	        if ($this->db->query($sql))
+	        {
+	            $this->id=$facid;
+	            $this->update_price();
+	            return 1;
+	        }
+	        else
+	        {
+	            $this->error=$this->db->lasterror();
+	            return -1;
+	        }
+	    }
+	}	
+	
+	
 	/**
 	 * Return the next date of 
 	 * 
@@ -760,8 +865,9 @@ class FactureRec extends CommonInvoice
 		$arraynow=dol_getdate($now);
 		$nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
 
-		$prodids = array();
+        // Load array of products prodids
 		$num_prods = 0;
+		$prodids = array();
 		
 		$sql = "SELECT rowid";
 		$sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 2 - 0
htdocs/compta/facture/class/facture.class.php

@@ -3537,6 +3537,8 @@ class Facture extends CommonInvoice
 		$arraynow=dol_getdate($now);
 		$nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
 
+        // Load array of products prodids
+		$num_prods = 0;
 		$prodids = array();
 		$sql = "SELECT rowid";
 		$sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 163 - 3
htdocs/compta/facture/fiche-rec.php

@@ -552,9 +552,9 @@ if ($action == 'addline' && $user->rights->facture->creer)
 
             if ($result > 0)
             {
-                // Define output language
                 /*if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE))
                 {
+                    // Define output language
                     $outputlangs = $langs;
                     $newlang = '';
                     if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id')) $newlang = GETPOST('lang_id','alpha');
@@ -615,8 +615,168 @@ if ($action == 'addline' && $user->rights->facture->creer)
     }
 }
 
+elseif ($action == 'updateligne' && $user->rights->facture->creer && ! GETPOST('cancel'))
+{
+    if (! $object->fetch($id) > 0)	dol_print_error($db);
+    $object->fetch_thirdparty();
+
+    // Clean parameters
+    $date_start = '';
+    $date_end = '';
+    //$date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), GETPOST('date_startsec'), GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
+    //$date_end = dol_mktime(GETPOST('date_endhour'), GETPOST('date_endmin'), GETPOST('date_endsec'), GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear'));
+    $description = dol_htmlcleanlastbr(GETPOST('product_desc') ? GETPOST('product_desc') : GETPOST('desc'));
+    $pu_ht = GETPOST('price_ht');
+    $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
+    $qty = GETPOST('qty');
+
+    // Define info_bits
+    $info_bits = 0;
+    if (preg_match('/\*/', $vat_rate))
+        $info_bits |= 0x01;
+
+        // Define vat_rate
+        $vat_rate = str_replace('*', '', $vat_rate);
+        $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty);
+        $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty);
+
+        // Add buying price
+        $fournprice = price2num(GETPOST('fournprice') ? GETPOST('fournprice') : '');
+        $buyingprice = price2num(GETPOST('buying_price') != '' ? GETPOST('buying_price') : '');       // If buying_price is '0', we muste keep this value
+
+        // Extrafields
+        $extrafieldsline = new ExtraFields($db);
+        $extralabelsline = $extrafieldsline->fetch_name_optionals_label($object->table_element_line);
+        $array_options = $extrafieldsline->getOptionalsFromPost($extralabelsline);
+        // Unset extrafield
+        if (is_array($extralabelsline)) {
+            // Get extra fields
+            foreach ($extralabelsline as $key => $value) {
+                unset($_POST["options_" . $key]);
+            }
+        }
+
+        // Define special_code for special lines
+        $special_code=GETPOST('special_code');
+        if (! GETPOST('qty')) $special_code=3;
+
+        /*$line = new FactureLigne($db);
+        $line->fetch(GETPOST('lineid'));
+        $percent = $line->get_prev_progress($object->id);
+
+        if (GETPOST('progress') < $percent)
+        {
+            $mesg = '<div class="warning">' . $langs->trans("CantBeLessThanMinPercent") . '</div>';
+            setEventMessages($mesg, null, 'warnings');
+            $error++;
+            $result = -1;
+        }*/
+
+        // Check minimum price
+        $productid = GETPOST('productid', 'int');
+        if (! empty($productid))
+        {
+            $product = new Product($db);
+            $product->fetch($productid);
+
+            $type = $product->type;
+
+            $price_min = $product->price_min;
+            if (! empty($conf->global->PRODUIT_MULTIPRICES) && ! empty($object->thirdparty->price_level))
+                $price_min = $product->multiprices_min [$object->thirdparty->price_level];
+
+                $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
+
+                // Check price is not lower than minimum (check is done only for standard or replacement invoices)
+                if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT) && $price_min && (price2num($pu_ht) * (1 - price2num(GETPOST('remise_percent')) / 100) < price2num($price_min))) {
+                    setEventMessages($langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, - 1, $conf->currency)), null, 'errors');
+                    $error ++;
+                }
+        } else {
+            $type = GETPOST('type');
+            $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
+
+            // Check parameters
+            if (GETPOST('type') < 0) {
+                setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
+                $error ++;
+            }
+        }
+        if ($qty < 0) {
+            $langs->load("errors");
+            setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
+            $error ++;
+        }
+
+        // Update line
+        if (! $error) {
+            $result = $object->updateline(GETPOST('lineid'), $description, $pu_ht, $qty,
+                $vat_rate, GETPOST('productid'), GETPOST('remise_percent'), 'HT', $info_bits, 0, 0, $type,
+                0, $special_code, $label, GETPOST('units'));
+
+            if ($result >= 0) {
+                /*if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
+                    // Define output language
+                    $outputlangs = $langs;
+                    $newlang = '';
+                    if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id'))
+                        $newlang = GETPOST('lang_id');
+                        if ($conf->global->MAIN_MULTILANGS && empty($newlang))
+                            $newlang = $object->thirdparty->default_lang;
+                            if (! empty($newlang)) {
+                                $outputlangs = new Translate("", $conf);
+                                $outputlangs->setDefaultLang($newlang);
+                            }
+
+                            $ret = $object->fetch($id); // Reload to get new records
+                            $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
+                }*/
+
+                $object->fetch($object->id);    // Reload lines
+                
+                unset($_POST['qty']);
+                unset($_POST['type']);
+                unset($_POST['productid']);
+                unset($_POST['remise_percent']);
+                unset($_POST['price_ht']);
+                unset($_POST['multicurrency_price_ht']);
+                unset($_POST['price_ttc']);
+                unset($_POST['tva_tx']);
+                unset($_POST['product_ref']);
+                unset($_POST['product_label']);
+                unset($_POST['product_desc']);
+                unset($_POST['fournprice']);
+                unset($_POST['buying_price']);
+                unset($_POST['np_marginRate']);
+                unset($_POST['np_markRate']);
+
+                unset($_POST['dp_desc']);
+                unset($_POST['idprod']);
+                unset($_POST['units']);
+
+                unset($_POST['date_starthour']);
+                unset($_POST['date_startmin']);
+                unset($_POST['date_startsec']);
+                unset($_POST['date_startday']);
+                unset($_POST['date_startmonth']);
+                unset($_POST['date_startyear']);
+                unset($_POST['date_endhour']);
+                unset($_POST['date_endmin']);
+                unset($_POST['date_endsec']);
+                unset($_POST['date_endday']);
+                unset($_POST['date_endmonth']);
+                unset($_POST['date_endyear']);
+
+                unset($_POST['situations']);
+                unset($_POST['progress']);
+            } else {
+                setEventMessages($object->error, $object->errors, 'errors');
+            }
+        }
+}
+
 // Do we click on purge search criteria ?
-if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers
+if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All test are required to be compatible with all browsers
 {
     $search_ref='';
     $search_societe='';
@@ -1302,7 +1462,7 @@ else
 		// Show object lines
 		if (! empty($object->lines))
 		{
-		    $disableedit=1;
+		    //$disableedit=1;
 		    //$disablemove=1;
 		    $ret = $object->printObjectLines($action, $mysoc, $soc, $lineid, 0);      // No date selector for template invoice
 		}

+ 23 - 16
htdocs/conf/conf.php.example

@@ -220,6 +220,25 @@ $dolibarr_main_authentication='dolibarr';
 //
 $dolibarr_main_force_https='0';
 
+// dolibarr_main_prod
+// When this parameter is defined, all errors messages are not reported.
+// This feature exists for production usage to avoid to give any information to hackers.
+// Default value: 0
+// Possible values: 0 or 1
+// Examples:
+// $dolibarr_main_prod='0';
+//
+$dolibarr_main_prod='0';
+
+// $dolibarr_main_restrict_os_commands
+// To restrict commands you can execute by the backup feature, enter allowed command here.
+// Note: If you can, defining permission on OS linux (using SELinux for example) may be a better choice.
+// Default value: 'mysqldump, mysql, pg_dump, pgrestore'
+// Examples:
+// $dolibarr_main_restrict_os_commands='mysqldump, /usr/local/bin/otherdumptool';
+//
+$dolibarr_main_restrict_os_commands='mysqldump, mysql, pg_dump, pgrestore';
+
 // dolibarr_nocsrfcheck
 // This parameter can be used to disable CSRF protection.
 // This might be required if you access Dolibarr behind a proxy that make
@@ -231,15 +250,11 @@ $dolibarr_main_force_https='0';
 //
 $dolibarr_nocsrfcheck='0';
 
-// dolibarr_main_prod
-// When this parameter is defined, all errors messages are not reported.
-// This feature exists for production usage to avoid to give any information to hackers.
-// Default value: 0
-// Possible values: 0 or 1
+// dolibarr_mailing_limit_sendbyweb
+// Can set a limit for mailing send by web. Can be used for a restricted mode.
+// Default value: 0 (use database value if exist)
 // Examples:
-// $dolibarr_main_prod='0';
-//
-$dolibarr_main_prod='0';
+// $dolibarr_mailing_limit_sendbyweb='0';
 
 
 
@@ -251,8 +266,6 @@ $dolibarr_main_prod='0';
 // This parameter contains prefix of Dolibarr database. 'llx_' if not defined.
 // Examples:
 // $dolibarr_main_db_prefix='llx_';
-//
-$dolibarr_main_db_prefix='';
 
 // dolibarr_main_limit_users
 // Can set a limit on the number of users it will be possible to create
@@ -261,12 +274,6 @@ $dolibarr_main_db_prefix='';
 // Examples:
 // $dolibarr_main_limit_users='0';
 
-// dolibarr_mailing_limit_sendbyweb
-// Can set a limit for mailing send by web. Can be used for a restricted mode.
-// Default value: 0 (use database value if exist)
-// Examples:
-// $dolibarr_mailing_limit_sendbyweb='0';
-
 // dolibarr_strict_mode
 // Set this to 1 to enable the PHP strict mode. For dev environment only.
 // Default value: 0 (use database value if exist)

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

@@ -2098,6 +2098,8 @@ class Contrat extends CommonObject
 	{
 		global $user,$langs,$conf;
 
+        // Load array of products prodids
+		$num_prods = 0;
 		$prodids = array();
 		$sql = "SELECT rowid";
 		$sql.= " FROM ".MAIN_DB_PREFIX."product";
@@ -2116,8 +2118,6 @@ class Contrat extends CommonObject
 			}
 		}
 
-
-
 		// Initialise parametres
 		$this->id=0;
 		$this->specimen=1;

+ 7 - 5
htdocs/core/actions_printing.inc.php

@@ -1,6 +1,6 @@
 <?php
-/* Copyright (C) 2014 Laurent Destailleur  <eldy@users.sourceforge.net>
- * Copyright (C) 2014 Frederic France      <frederic.france@free.fr>
+/* Copyright (C) 2014-2016 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2014      Frederic France      <frederic.france@free.fr>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -44,7 +44,8 @@ if ($action == 'print_file' and $user->rights->printing->read)
             $printer = new $classname($db);
             //print '<pre>'.print_r($printer, true).'</pre>';
 
-            if (! empty($conf->global->{$printer->active})) {
+            if (! empty($conf->global->{$printer->active})) 
+            {
                 $subdir=(GETPOST('printer', 'alpha')=='expedition'?'sending':'');
                 $module = GETPOST('printer', 'alpha');
                 if ($module =='commande_fournisseur') {
@@ -56,10 +57,11 @@ if ($action == 'print_file' and $user->rights->printing->read)
                     //print '<pre>'.print_r($printer->errors, true).'</pre>';
                     setEventMessages($printer->error, $printer->errors, 'errors');
                 }
-                if ($ret==0) {
+                if ($ret==0) 
+                {
                     //print '<pre>'.print_r($printer->errors, true).'</pre>';
                     setEventMessages($printer->error, $printer->errors);
-                    setEventMessages($langs->trans("FileWasSentToPrinter", basename(GETPOST('file'))).' '.$langs->trans("ViaModule").' '.$printer->name, null);
+                    setEventMessages($langs->trans("FileWasSentToPrinter", basename(GETPOST('file'))).' '.$langs->transnoentitiesnoconv("ViaModule").' '.$printer->name, null);
                     $printed++;
                 }
             }

+ 1 - 2
htdocs/core/class/commonobject.class.php

@@ -3998,14 +3998,13 @@ abstract class CommonObject
     	{
     		if (!empty($this->errors))
     		{
-    			$this->errors=array_merge($this->errors,$interface->errors);
+    			$this->errors=array_unique(array_merge($this->errors,$interface->errors));   // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
     		}
     		else
     		{
     			$this->errors=$interface->errors;
     		}
     	}
-
     	return $result;
     }
 

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

@@ -429,7 +429,7 @@ class Conf
 		$this->liste_limit=$this->global->MAIN_SIZE_LISTE_LIMIT;
 
 		// conf->product->limit_size = constante de taille maximale des select de produit
-		if (! isset($this->global->PRODUIT_LIMIT_SIZE)) $this->global->PRODUIT_LIMIT_SIZE=100;
+		if (! isset($this->global->PRODUIT_LIMIT_SIZE)) $this->global->PRODUIT_LIMIT_SIZE=1000;
 		$this->product->limit_size=$this->global->PRODUIT_LIMIT_SIZE;
 
 		// conf->theme et $this->css

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

@@ -633,7 +633,8 @@ class ExtraFields
 		}
 		else
 		{
-			print dol_print_error($this->db);
+			$this->error=$this->db->lasterror();
+			dol_syslog(get_class($this)."::fetch_name_optionals_label ".$this->error, LOG_ERR);
 		}
 
 		return $array_name_label;

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

@@ -191,10 +191,12 @@ class Interfaces
                 if ($result < 0)
                 {
                     // Action KO
+                    //dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
                     $nbtotal++;
                     $nbko++;
                     if (! empty($objMod->errors)) $this->errors=array_merge($this->errors,$objMod->errors);
                     else if (! empty($objMod->error))  $this->errors[]=$objMod->error;
+                    //dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($this->errors), LOG_ERR);
                 }
             }
             else
@@ -205,7 +207,7 @@ class Interfaces
 
         if ($nbko)
         {
-            dol_syslog(get_class($this)."::run_triggers action=".$action." Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_ERR);
+            dol_syslog(get_class($this)."::run_triggers action=".$action." Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko." - Nb of error string returned in this->errors = ".count($this->errors), LOG_ERR);
             return -$nbko;
         }
         else

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

@@ -167,7 +167,7 @@ class Translate
 		if (empty($domain))
 		{
 			dol_print_error('',get_class($this)."::Load ErrorWrongParameters");
-			exit;
+			return -1;
 		}
 		if ($this->defaultlang == 'none_NONE') return 0;    // Special language code to not translate keys
 

+ 6 - 7
htdocs/core/db/pgsql.class.php

@@ -163,10 +163,13 @@ class DoliDBPgsql extends DoliDB
 		}
 		if ($line != "")
 		{
-			// group_concat support (PgSQL >= 9.1)
-			$line = preg_replace('/GROUP_CONCAT/i', 'STRING_AGG', $line);
+			// group_concat support (PgSQL >= 9.0)
+			// Replace group_concat(x) or group_concat(x SEPARATOR ',') with string_agg(x, ',')
+		    $line = preg_replace('/GROUP_CONCAT/i', 'STRING_AGG', $line);
 			$line = preg_replace('/ SEPARATOR/i', ',', $line);
-
+			$line = preg_replace('/STRING_AGG\(([^,\)]+)\)/i', 'STRING_AGG(\\1, \',\')', $line);
+			//print $line."\n";
+					
 		    if ($type == 'auto')
 		    {
               if (preg_match('/ALTER TABLE/i',$line)) $type='dml';
@@ -315,10 +318,6 @@ class DoliDBPgsql extends DoliDB
 				}
 			}
 
-			// Replace group_concat(x) with string_agg(x, ',')
-			$line=preg_replace('/GROUP_CONCAT\(([^\)]+)\)/i','STRING_AGG(\\1, \',\')',$line);
-			//print $line."\n";
-			
 			// Remove () in the tables in FROM if 1 table
 			$line=preg_replace('/FROM\s*\((([a-z_]+)\s+as\s+([a-z_]+)\s*)\)/i','FROM \\1',$line);
 			//print $line."\n";

+ 3 - 3
htdocs/core/lib/functions.lib.php

@@ -4548,9 +4548,9 @@ function dol_textishtml($msg,$option=0)
 		if (preg_match('/<html/i',$msg))				return true;
 		elseif (preg_match('/<body/i',$msg))			return true;
 		elseif (preg_match('/<(b|em|i)>/i',$msg))		return true;
-		elseif (preg_match('/<(br|div|font|li|span|strong|table)>/i',$msg)) 	  return true;
-		elseif (preg_match('/<(br|div|font|li|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
-		elseif (preg_match('/<(br|div|font|li|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
+		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
+		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
+		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
 		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
 		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
 		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;

+ 10 - 2
htdocs/core/menus/standard/auguria.lib.php

@@ -78,11 +78,19 @@ function print_auguria_menu($db,$atarget,$type_user,&$tabMenu,&$menu,$noout=0,$m
 			
 			if (! preg_match("/^(http:\/\/|https:\/\/)/i",$newTabMenu[$i]['url']))
 			{
-				$tmp=explode('?',$newTabMenu[$i]['url'],2);
+			    $tmp=explode('?',$newTabMenu[$i]['url'],2);
 				$url = $shorturl = $tmp[0];
 				$param = (isset($tmp[1])?$tmp[1]:'');
 
-				if (! preg_match('/mainmenu/i',$param) || ! preg_match('/leftmenu/i',$param)) $param.=($param?'&':'').'mainmenu='.$newTabMenu[$i]['mainmenu'].'&amp;leftmenu=';
+				// Complete param to force leftmenu to '' to closed opend menu when we click on a link with no leftmenu defined.
+			    if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && ! empty($newTabMenu[$i]['url'])) 
+			    {
+			        $param.=($param?'&':'').'mainmenu='.$newTabMenu[$i]['url'].'&leftmenu=';
+			    }
+			    if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && empty($newTabMenu[$i]['url'])) 
+			    {
+			        $param.=($param?'&':'').'leftmenu=';
+			    }
 				//$url.="idmenu=".$newTabMenu[$i]['rowid'];    // Already done by menuLoad
 				$url = dol_buildpath($url,1).($param?'?'.$param:'');
 				$shorturl = $shorturl.($param?'?'.$param:'');

+ 35 - 16
htdocs/core/menus/standard/eldy.lib.php

@@ -857,8 +857,8 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 			{
 				$langs->load("donations");
 				$newmenu->add("/don/index.php?leftmenu=donations&amp;mainmenu=accountancy",$langs->trans("Donations"), 0, $user->rights->don->lire, '', $mainmenu, 'donations');
-				if (empty($leftmenu) || $leftmenu=="donations") $newmenu->add("/don/card.php?action=create",$langs->trans("NewDonation"), 1, $user->rights->don->creer);
-				if (empty($leftmenu) || $leftmenu=="donations") $newmenu->add("/don/list.php",$langs->trans("List"), 1, $user->rights->don->lire);
+				if (empty($leftmenu) || $leftmenu=="donations") $newmenu->add("/don/card.php?leftmenu=donations&amp;action=create",$langs->trans("NewDonation"), 1, $user->rights->don->creer);
+				if (empty($leftmenu) || $leftmenu=="donations") $newmenu->add("/don/list.php?leftmenu=donations",$langs->trans("List"), 1, $user->rights->don->lire);
 				// if ($leftmenu=="donations") $newmenu->add("/don/stats/index.php",$langs->trans("Statistics"), 1, $user->rights->don->lire);
 			}
 
@@ -1058,6 +1058,7 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				$newmenu->add("/compta/bank/index.php?leftmenu=bank&amp;mainmenu=bank",$langs->trans("MenuBankCash"),0,$user->rights->banque->lire, '', $mainmenu, 'bank');
 
 				$newmenu->add("/compta/bank/card.php?action=create",$langs->trans("MenuNewFinancialAccount"),1,$user->rights->banque->configurer);
+				$newmenu->add("/compta/bank/index.php?leftmenu=bank&amp;mainmenu=bank",$langs->trans("List"),1,$user->rights->banque->lire, '', $mainmenu, 'bank');
 				$newmenu->add("/compta/bank/search.php",$langs->trans("ListTransactions"),1,$user->rights->banque->lire);
 				$newmenu->add("/compta/bank/budget.php",$langs->trans("ListTransactionsByCategory"),1,$user->rights->banque->lire);
 
@@ -1184,9 +1185,9 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				$langs->load("projects");
 
 				// Project affected to user
-				$newmenu->add("/projet/index.php?leftmenu=projects&mode=mine", $langs->trans("MyProjects"), 0, $user->rights->projet->lire, '', $mainmenu, 'myprojects');
-				$newmenu->add("/projet/card.php?leftmenu=projects&action=create&mode=mine", $langs->trans("NewProject"), 1, $user->rights->projet->creer);
-				$newmenu->add("/projet/list.php?leftmenu=projects&mode=mine&search_status=1", $langs->trans("List"), 1, $user->rights->projet->lire);
+				$newmenu->add("/projet/index.php?leftmenu=myprojects&mode=mine", $langs->trans("MyProjects"), 0, $user->rights->projet->lire, '', $mainmenu, 'myprojects');
+				$newmenu->add("/projet/card.php?leftmenu=myprojects&action=create&mode=mine", $langs->trans("NewProject"), 1, $user->rights->projet->creer);
+				$newmenu->add("/projet/list.php?leftmenu=myprojects&mode=mine&search_status=1", $langs->trans("List"), 1, $user->rights->projet->lire);
 
 				// All project i have permission on
 				$newmenu->add("/projet/index.php?leftmenu=projects", $langs->trans("Projects"), 0, $user->rights->projet->lire && $user->rights->projet->lire, '', $mainmenu, 'projects');
@@ -1197,10 +1198,10 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				if (empty($conf->global->PROJECT_HIDE_TASKS))
 				{
 					// Project affected to user
-					$newmenu->add("/projet/activity/index.php?mode=mine", $langs->trans("MyActivities"), 0, $user->rights->projet->lire);
-					$newmenu->add("/projet/tasks.php?action=create", $langs->trans("NewTask"), 1, $user->rights->projet->creer);
-					$newmenu->add("/projet/tasks/list.php?mode=mine", $langs->trans("List"), 1, $user->rights->projet->lire);
-					$newmenu->add("/projet/activity/perweek.php?mode=mine", $langs->trans("NewTimeSpent"), 1, $user->rights->projet->creer);
+					$newmenu->add("/projet/activity/index.php?leftmenu=mytasks&mode=mine", $langs->trans("MyActivities"), 0, $user->rights->projet->lire);
+					$newmenu->add("/projet/tasks.php?leftmenu=mytasks&action=create", $langs->trans("NewTask"), 1, $user->rights->projet->creer);
+					$newmenu->add("/projet/tasks/list.php?leftmenu=mytasks&mode=mine", $langs->trans("List"), 1, $user->rights->projet->lire);
+					$newmenu->add("/projet/activity/perweek.php?leftmenu=mytasks&mode=mine", $langs->trans("NewTimeSpent"), 1, $user->rights->projet->creer);
 
 					// All project i have permission on
 					$newmenu->add("/projet/activity/index.php", $langs->trans("Activities"), 0, $user->rights->projet->lire && $user->rights->projet->lire);
@@ -1452,15 +1453,33 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				}
 			}
 
-			// For external modules
-			$tmp=explode('?',$menu_array[$i]['url'],2);
-			$url = $tmp[0];
-			$param = (isset($tmp[1])?$tmp[1]:'');
-			$url = dol_buildpath($url,1).($param?'?'.$param:'');
-
+			$url = $shorturl = $menu_array[$i]['url'];
+			
+			if (! preg_match("/^(http:\/\/|https:\/\/)/i",$menu_array[$i]['url']))
+			{
+			    $tmp=explode('?',$menu_array[$i]['url'],2);
+			    $url = $shorturl = $tmp[0];
+			    $param = (isset($tmp[1])?$tmp[1]:'');    // params in url of the menu link
+
+			    // Complete param to force leftmenu to '' to closed opend menu when we click on a link with no leftmenu defined.
+			    if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && ! empty($menu_array[$i]['mainmenu'])) 
+			    {
+			        $param.=($param?'&':'').'mainmenu='.$menu_array[$i]['mainmenu'].'&leftmenu=';
+			    }
+			    if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && empty($menu_array[$i]['mainmenu'])) 
+			    {
+			        $param.=($param?'&':'').'leftmenu=';
+			    }
+			    //$url.="idmenu=".$menu_array[$i]['rowid'];    // Already done by menuLoad
+			    $url = dol_buildpath($url,1).($param?'?'.$param:'');
+			    $shorturl = $shorturl.($param?'?'.$param:'');
+			}
+				
 			$url=preg_replace('/__LOGIN__/',$user->login,$url);
+			$shorturl=preg_replace('/__LOGIN__/',$user->login,$shorturl);
 			$url=preg_replace('/__USERID__/',$user->id,$url);
-
+			$shorturl=preg_replace('/__USERID__/',$user->id,$shorturl);
+				
 			print '<!-- Process menu entry with mainmenu='.$menu_array[$i]['mainmenu'].', leftmenu='.$menu_array[$i]['leftmenu'].', level='.$menu_array[$i]['level'].' enabled='.$menu_array[$i]['enabled'].', position='.$menu_array[$i]['position'].' -->'."\n";
 
 			// Menu niveau 0

+ 2 - 2
htdocs/core/tpl/objectline_create.tpl.php

@@ -694,17 +694,17 @@ function setforpredef() {
 	jQuery("#prod_entry_mode_free").prop('checked',false);
 	jQuery("#prod_entry_mode_predef").prop('checked',true);
 	jQuery("#price_ht").hide();
-	jQuery("#title_up_ht").hide();
 	jQuery("#price_ttc").hide();	// May no exists
 	jQuery("#tva_tx").hide();
 	jQuery("#buying_price").show();
+	//jQuery("#fournprice_predef").show(); // management somewhere else
 	jQuery("#title_vat").hide();
+	jQuery("#title_up_ht").hide();
 	jQuery("#title_up_ttc").hide();
 	jQuery("#np_marginRate").hide();	// May no exists
 	jQuery("#np_markRate").hide();	// May no exists
 	jQuery(".np_marginRate").hide();	// May no exists
 	jQuery(".np_markRate").hide();	// May no exists
-	jQuery(".np_markRate").hide();	// May no exists
 	jQuery("#units, #title_units").hide();
 }
 

+ 2 - 1
htdocs/expedition/class/expedition.class.php

@@ -1549,7 +1549,8 @@ class Expedition extends CommonObject
 
 		dol_syslog(get_class($this)."::initAsSpecimen");
 
-		// Charge tableau des produits prodids
+        // Load array of products prodids
+		$num_prods = 0;
 		$prodids = array();
 		$sql = "SELECT rowid";
 		$sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 1 - 1
htdocs/expensereport/card.php

@@ -1432,7 +1432,7 @@ else
 				{
 					$num = $db->num_rows($resql);
 					$i = 0; $total = 0;
-					print '<table class="nobordernopadding" width="100%">';
+					print '<table class="nobordernopadding paymenttable" width="100%">';
 					print '<tr class="liste_titre">';
 					print '<td>'.$langs->trans("RefPayment").'</td>';
 					print '<td>'.$langs->trans("Date").'</td>';

+ 2 - 2
htdocs/exports/class/export.class.php

@@ -241,7 +241,7 @@ class Export
 			// Loop on each condition to add
 			foreach ($array_filterValue as $key => $value)
 			{
-			    if (preg_match('/group_concat/', $key)) continue;
+			    if (preg_match('/GROUP_CONCAT/i', $key)) continue;
 				if ($value != '') $sqlWhere.=" and ".$this->build_filterQuery($this->array_export_TypeFields[$indice][$key], $key, $array_filterValue[$key]);
 			}
 			$sql.=$sqlWhere;
@@ -256,7 +256,7 @@ class Export
 		    // Loop on each condition to add
 		    foreach ($array_filterValue as $key => $value)
 		    {
-		        if (preg_match('/group_concat/', $key) and $value != '') $sql.=" HAVING ".$this->build_filterQuery($this->array_export_TypeFields[$indice][$key], $key, $array_filterValue[$key]);
+		        if (preg_match('/GROUP_CONCAT/i', $key) and $value != '') $sql.=" HAVING ".$this->build_filterQuery($this->array_export_TypeFields[$indice][$key], $key, $array_filterValue[$key]);
 		    }
 		}
 		

+ 2 - 1
htdocs/fourn/class/fournisseur.facture.class.php

@@ -1647,7 +1647,8 @@ class FactureFournisseur extends CommonInvoice
 
         $now = dol_now();
 
-        // Charge tableau des produits prodids
+        // Load array of products prodids
+        $num_prods = 0;
         $prodids = array();
 
         $sql = "SELECT rowid";

+ 0 - 2
htdocs/fourn/class/fournisseur.product.class.php

@@ -6,8 +6,6 @@
  * Copyright (C) 2012		Christophe Battarel	<christophe.battarel@altairis.fr>
  * Copyright (C) 2015		Marcos García           <marcosgdf@gmail.com>
  * Copyright (C) 2016		Charlie Benke           <charlie@patas-monkey.com>
- 
- status
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 8 - 6
htdocs/fourn/commande/orderstoinvoice.php

@@ -71,11 +71,12 @@ $date_end = dol_mktime(23, 59, 59, $_REQUEST["date_endmonth"], $_REQUEST["date_e
 $date_starty = dol_mktime(0, 0, 0, $_REQUEST["date_start_delymonth"], $_REQUEST["date_start_delyday"], $_REQUEST["date_start_delyyear"]); // Date for local PHP server
 $date_endy = dol_mktime(23, 59, 59, $_REQUEST["date_end_delymonth"], $_REQUEST["date_end_delyday"], $_REQUEST["date_end_delyyear"]);
 
-if ($action == 'create') {
-	if (is_array($selected) == false) {
-		$mesgs = array (
-				'<div class="error">' . $langs->trans('Error_OrderNotChecked') . '</div>'
-		);
+if ($action == 'create') 
+{
+	if (! is_array($selected)) 
+	{
+		$error++;
+		setEventMessages($langs->trans('Error_OrderNotChecked'), null, 'errors');
 	} else {
 		$origin = GETPOST('origin');
 		$originid = GETPOST('originid');
@@ -87,11 +88,12 @@ include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
 $hookmanager = new HookManager($db);
 $hookmanager->initHooks(array('orderstoinvoicesupplier'));
 
+
 /*
  * Actions
  */
 
-if (($action == 'create' || $action == 'add') && empty($mesgs)) {
+if (($action == 'create' || $action == 'add') && ! $error) {
 
 	require_once DOL_DOCUMENT_ROOT . '/core/lib/fourn.lib.php';
 	if (! empty($conf->projet->enabled))

+ 1 - 1
htdocs/fourn/facture/card.php

@@ -1736,7 +1736,7 @@ else
         {
             $num = $db->num_rows($result);
             $i = 0; $totalpaye = 0;
-            print '<table class="nobordernopadding" width="100%">';
+            print '<table class="nobordernopadding paymenttable" width="100%">';
             print '<tr class="liste_titre">';
             print '<td>'.$langs->trans('Payments').'</td>';
 			print '<td>'.$langs->trans('Date').'</td>';

+ 11 - 5
htdocs/install/step1.php

@@ -853,24 +853,30 @@ function write_conf_file($conffile)
 		fputs($fp,"\n");
 
 		/* Authentication */
+		fputs($fp, '// Authentication settings');
+        fputs($fp,"\n");
+
 		fputs($fp, '$dolibarr_main_authentication=\'dolibarr\';');
 		fputs($fp,"\n\n");
 
-		fputs($fp, '// Specific settings');
-        fputs($fp,"\n");
-
         fputs($fp, '//$dolibarr_main_demo=\'autologin,autopass\';');
         fputs($fp,"\n");
 
-        fputs($fp, '$dolibarr_main_prod=\'0\';');
+		fputs($fp, '// Security settings');
         fputs($fp,"\n");
 
-        fputs($fp, '$dolibarr_nocsrfcheck=\'0\';');
+        fputs($fp, '$dolibarr_main_prod=\'0\';');
         fputs($fp,"\n");
 
         fputs($fp, '$dolibarr_main_force_https=\''.$main_force_https.'\';');
 		fputs($fp,"\n");
 
+        fputs($fp, '$dolibarr_main_restrict_os_commands=\'mysqldump, mysql, pg_dump, pgrestore\';');
+		fputs($fp,"\n");
+		
+        fputs($fp, '$dolibarr_nocsrfcheck=\'0\';');
+        fputs($fp,"\n");
+
 		fputs($fp, '$dolibarr_main_cookie_cryptkey=\''.$key.'\';');
 		fputs($fp,"\n");
 

+ 3 - 0
htdocs/langs/en_US/admin.lang

@@ -512,6 +512,8 @@ Module5000Name=Multi-company
 Module5000Desc=Allows you to manage multiple companies
 Module6000Name=Workflow
 Module6000Desc=Workflow management
+Module10000Name=Websites
+Module10000Desc=Create public websites with a WYSIWG editor. Just setup your web server to point to the dedicated directory to have it online on the Internet.
 Module20000Name=Leave Requests management
 Module20000Desc=Declare and follow employees leaves requests
 Module39000Name=Product lot
@@ -1589,3 +1591,4 @@ DetectionNotPossible=Detection not possible
 UrlToGetKeyToUseAPIs=Url to get token to use API (once token has been received it is saved on database user table and will be checked on each future access) 
 ListOfAvailableAPIs=List of available APIs
 activateModuleDependNotSatisfied=Module "%s" depends on module "%s" that is missing, so module "%1$s" may not work correclty. Please install module "%2$s" or disable module "%1$s" if you want to be safe from any surprise
+CommandIsNotInsideAllowedCommands=The command you try to run is not inside list of allowed commands defined into parameter <strong>$dolibarr_main_restrict_os_commands</strong> into <strong>conf.php</strong> file.

+ 1 - 0
htdocs/langs/en_US/bills.lang

@@ -323,6 +323,7 @@ NextDateToExecution=Date for next invoice generation
 DateLastGeneration=Date of latest generation
 MaxPeriodNumber=Max nb of invoice generation
 NbOfGenerationDone=Nb of invoice generation already done
+MaxGenerationReached=Maximum nb of generations reached
 InvoiceAutoValidate=Validate invoices automatically
 GeneratedFromRecurringInvoice=Generated from template recurring invoice %s
 DateIsNotEnough=Date not reached yet

+ 2 - 0
htdocs/langs/en_US/printing.lang

@@ -51,3 +51,5 @@ IPP_Supported=Type of media
 DirectPrintingJobsDesc=This page lists printing jobs found for available printers.
 GoogleAuthNotConfigured=Google OAuth setup not done. Enable module OAuth and set a Google ID/Secret.
 GoogleAuthConfigured=Google OAuth credentials found into setup of module OAuth.
+PrintingDriverDescprintgcp=Configuration variables for printing driver Google Cloud Print.
+PrintTestDescprintgcp=List of Printers for Google Cloud Print.

+ 7 - 7
htdocs/langs/en_US/products.lang

@@ -83,7 +83,7 @@ SetDefaultBarcodeType=Set barcode type
 BarcodeValue=Barcode value
 NoteNotVisibleOnBill=Note (not visible on invoices, proposals...)
 ServiceLimitedDuration=If product is a service with limited duration:
-MultiPricesAbility=Several level of prices per product/service
+MultiPricesAbility=Several segment of prices per product/service (each customer is in one segment)
 MultiPricesNumPrices=Number of prices
 AssociatedProductsAbility=Activate the package feature
 AssociatedProducts=Package product
@@ -174,13 +174,13 @@ AlwaysUseNewPrice=Always use current price of product/service
 AlwaysUseFixedPrice=Use the fixed price
 PriceByQuantity=Different prices by quantity
 PriceByQuantityRange=Quantity range
-MultipriceRules=Price level rules
-UseMultipriceRules=Use price level rules (defined into product module setup) to autocalculate prices of all other level according to first level
+MultipriceRules=Price segment rules
+UseMultipriceRules=Use price segment rules (defined into product module setup) to autocalculate prices of all other segment according to first segment
 PercentVariationOver=%% variation over %s
 PercentDiscountOver=%% discount over %s
 ### composition fabrication
 Build=Produce
-ProductsMultiPrice=Products and prices for each price level
+ProductsMultiPrice=Products and prices for each price segment
 ProductsOrServiceMultiPrice=Customer prices (of products or services, multi-prices)
 ProductSellByQuarterHT=Products turnover quarterly before tax
 ServiceSellByQuarterHT=Services turnover quarterly before tax
@@ -201,9 +201,9 @@ DefinitionOfBarCodeForThirdpartyNotComplete=Definition of type or value of bar c
 BarCodeDataForProduct=Barcode information of product %s :
 BarCodeDataForThirdparty=Barcode information of thirdparty %s :
 ResetBarcodeForAllRecords=Define barcode value for all records (this will also reset barcode value already defined with new values)
-PriceByCustomer=Different price for each customer
-PriceCatalogue=Unique price per product/service
-PricingRule=Rules for customer prices
+PriceByCustomer=Different prices for each customer
+PriceCatalogue=A single sell price per product/service
+PricingRule=Rules for sell prices
 AddCustomerPrice=Add price by customer
 ForceUpdateChildPriceSoc=Set same price on customer subsidiaries
 PriceByCustomerLog=Log of previous customer prices

+ 2 - 1
htdocs/livraison/class/livraison.class.php

@@ -833,7 +833,8 @@ class Livraison extends CommonObject
 
 		$now=dol_now();
 
-		// Charge tableau des produits prodids
+        // Load array of products prodids
+		$num_prods = 0;
 		$prodids = array();
 		$sql = "SELECT rowid";
 		$sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 1 - 1
htdocs/projet/list.php

@@ -416,7 +416,7 @@ if ($resql)
     if (! empty($arrayfields['p.opp_amount']['checked']))    print_liste_field_titre($arrayfields['p.opp_amount']['label'],$_SERVER["PHP_SELF"],'p.opp_amount',"",$param,'align="right"',$sortfield,$sortorder);
 	if (! empty($arrayfields['p.fk_opp_status']['checked'])) print_liste_field_titre($arrayfields['p.fk_opp_status']['label'],$_SERVER["PHP_SELF"],'p.fk_opp_status',"",$param,'align="center"',$sortfield,$sortorder);
     if (! empty($arrayfields['p.opp_percent']['checked']))   print_liste_field_titre($arrayfields['p.opp_percent']['label'],$_SERVER["PHP_SELF"],'p.opp_percent',"",$param,'align="right"',$sortfield,$sortorder);
-    if (! empty($arrayfields['p.budget_amount']['checked'])) print_liste_field_titre($arrayfields['p.budget_amount']['label'],$_SERVER["PHP_SELF"],'p.budget_amount',"",$param,'align="center"',$sortfield,$sortorder);
+    if (! empty($arrayfields['p.budget_amount']['checked'])) print_liste_field_titre($arrayfields['p.budget_amount']['label'],$_SERVER["PHP_SELF"],'p.budget_amount',"",$param,'align="right"',$sortfield,$sortorder);
     // Extra fields
 	if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
 	{

+ 2 - 1
htdocs/supplier_proposal/class/supplier_proposal.class.php

@@ -2186,7 +2186,8 @@ class SupplierProposal extends CommonObject
     {
         global $user,$langs,$conf;
 
-        // Charge tableau des produits prodids
+        // Load array of products prodids
+        $num_prods = 0; 
         $prodids = array();
         $sql = "SELECT rowid";
         $sql.= " FROM ".MAIN_DB_PREFIX."product";

+ 1 - 1
htdocs/theme/eldy/style.css.php

@@ -4093,7 +4093,7 @@ select {
     /* display: inline-block; */	/* We can't set this. This disable ability to make */
     /* TODO modified by jmobile, replace jmobile with pure css*/
     overflow:hidden;
-    white-space: nowrap;
+    white-space: nowrap;			/* Enabling this make behaviour strange when selecting the empty value if this empty value is '' instead of '&nbsp;' */
     text-overflow: ellipsis;
 }
 .fiche .ui-controlgroup {

+ 48 - 46
htdocs/user/card.php

@@ -54,6 +54,7 @@ $mode		= GETPOST('mode','alpha');
 $confirm	= GETPOST('confirm','alpha');
 $subaction	= GETPOST('subaction','alpha');
 $group		= GETPOST("group","int",3);
+$cancel     = GETPOST('cancel');
 
 // Define value to know what current user can do on users
 $canadduser=(! empty($user->admin) || $user->rights->user->user->creer);
@@ -299,7 +300,8 @@ if (empty($reshook)) {
 		}
 	}
 
-	if ($action == 'update' && !$_POST["cancel"]) {
+	if ($action == 'update' && ! $cancel) 
+	{
 		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
 
 		if ($caneditfield)    // Case we can edit all field
@@ -395,7 +397,7 @@ if (empty($reshook)) {
 					if (!$error) {
 						$ret = $object->update($user);
 						if ($ret < 0) {
-							$error ++;
+							$error++;
 							if ($db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
 								$langs->load("errors");
 								setEventMessages($langs->trans("ErrorLoginAlreadyExists", $object->login), null, 'errors');
@@ -1440,6 +1442,49 @@ else
 				print '</td></tr>';
 		    }
 
+		    // Multicompany
+		    // TODO This should be done with hook formObjectOption
+		    if (is_object($mc))
+		    {
+		        if (! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode) && $conf->entity == 1 && $user->admin && ! $user->entity)
+		        {
+		            print '<tr><td>'.$langs->trans("Entity").'</td><td>';
+		            if (empty($object->entity))
+		            {
+		                print $langs->trans("AllEntities");
+		            }
+		            else
+		            {
+		                $mc->getInfo($object->entity);
+		                print $mc->label;
+		            }
+		            print "</td></tr>\n";
+		        }
+		    }
+		    
+		    if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER))
+		    {
+		        print '<tr><td>'.$langs->trans("OpenIDURL").'</td>';
+		        print '<td>'.$object->openid.'</td>';
+		        print "</tr>\n";
+		    }
+		    
+		    print '<tr><td class="titlefield">'.$langs->trans("LastConnexion").'</td>';
+		    print '<td>'.dol_print_date($object->datelastlogin,"dayhour").'</td>';
+		    print "</tr>\n";
+		    
+		    print '<tr><td>'.$langs->trans("PreviousConnexion").'</td>';
+		    print '<td>'.dol_print_date($object->datepreviouslogin,"dayhour").'</td>';
+		    print "</tr>\n";
+		    
+		    // Other attributes
+		    $parameters=array();
+		    $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$object,$action);    // Note that $action and $object may have been modified by hook
+		    if (empty($reshook) && ! empty($extrafields->attribute_label))
+		    {
+		        print $object->showOptionals($extrafields);
+		    }
+		    
             // Company / Contact
             if (! empty($conf->societe->enabled))
             {
@@ -1488,49 +1533,6 @@ else
                 print '</tr>'."\n";
             }
 
-            // Multicompany
-            // TODO This should be done with hook formObjectOption
-            if (is_object($mc))
-            {
-	            if (! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode) && $conf->entity == 1 && $user->admin && ! $user->entity)
-	            {
-	            	print '<tr><td>'.$langs->trans("Entity").'</td><td>';
-	            	if (empty($object->entity))
-	            	{
-	            		print $langs->trans("AllEntities");
-	            	}
-	            	else
-	            	{
-	            		$mc->getInfo($object->entity);
-	            		print $mc->label;
-	            	}
-	            	print "</td></tr>\n";
-	            }
-            }
-
-            if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER))
-            {
-                print '<tr><td>'.$langs->trans("OpenIDURL").'</td>';
-                print '<td>'.$object->openid.'</td>';
-                print "</tr>\n";
-            }
-
-            print '<tr><td class="titlefield">'.$langs->trans("LastConnexion").'</td>';
-            print '<td>'.dol_print_date($object->datelastlogin,"dayhour").'</td>';
-            print "</tr>\n";
-
-            print '<tr><td>'.$langs->trans("PreviousConnexion").'</td>';
-            print '<td>'.dol_print_date($object->datepreviouslogin,"dayhour").'</td>';
-            print "</tr>\n";
-
-            // Other attributes
-			$parameters=array();
-			$reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$object,$action);    // Note that $action and $object may have been modified by hook
-			if (empty($reshook) && ! empty($extrafields->attribute_label))
-			{
-				print $object->showOptionals($extrafields);
-			}
-
 			print "</table>\n";
 			print '</div>';
 
@@ -2222,7 +2224,7 @@ else
 			print '<td>';
 			$cate_arbo = $form->select_all_categories( Categorie::TYPE_CONTACT, null, null, null, null, 1 );
 			$c = new Categorie( $db );
-			$cats = $c->containing( $object->id, 'user' );
+			$cats = $c->containing($object->id, Categorie::TYPE_USER);
 			foreach ($cats as $cat) {
 				$arrayselected[] = $cat->id;
 			}

+ 15 - 9
htdocs/user/class/user.class.php

@@ -1332,14 +1332,14 @@ class User extends CommonObject
 			// If user is linked to a member, remove old link to this member
 			if ($this->fk_member > 0)
 			{
-				$sql = "UPDATE ".MAIN_DB_PREFIX."user SET fk_member = NULL where fk_member = ".$this->fk_member;
-				dol_syslog(get_class($this)."::update", LOG_DEBUG);
+				dol_syslog(get_class($this)."::update remove link with member. We will recreate it later", LOG_DEBUG);
+			    $sql = "UPDATE ".MAIN_DB_PREFIX."user SET fk_member = NULL where fk_member = ".$this->fk_member;
 				$resql = $this->db->query($sql);
 				if (! $resql) { $this->error=$this->db->error(); $this->db->rollback(); return -5; }
 			}
 			// Set link to user
+			dol_syslog(get_class($this)."::update set link with member", LOG_DEBUG);
 			$sql = "UPDATE ".MAIN_DB_PREFIX."user SET fk_member =".($this->fk_member>0?$this->fk_member:'null')." where rowid = ".$this->id;
-			dol_syslog(get_class($this)."::update", LOG_DEBUG);
 			$resql = $this->db->query($sql);
 			if (! $resql) { $this->error=$this->db->error(); $this->db->rollback(); return -5; }
 
@@ -1347,6 +1347,8 @@ class User extends CommonObject
 			{
 				if ($this->fk_member > 0 && ! $nosyncmember)
 				{
+				    dol_syslog(get_class($this)."::update user is linked with a member. We try to update member too.", LOG_DEBUG);
+				    
 					require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
 
 					// This user is linked with a member, so we also update members informations
@@ -1360,7 +1362,9 @@ class User extends CommonObject
 						$adh->lastname=$this->lastname;
 						$adh->login=$this->login;
 						$adh->gender=$this->gender;
+						
 						$adh->pass=$this->pass;
+						
 						$adh->societe=(empty($adh->societe) && $this->societe_id ? $this->societe_id : $adh->societe);
 
 						$adh->email=$this->email;
@@ -1373,17 +1377,19 @@ class User extends CommonObject
 						$adh->user_id=$this->id;
 						$adh->user_login=$this->login;
 
-						$result=$adh->update($user,0,1);
-						if ($result < 0)
+						$result=$adh->update($user,0,1,0);
+                        if ($result < 0)
 						{
-							$this->error=$luser->error;
-							dol_syslog(get_class($this)."::update ".$this->error,LOG_ERR);
+						    $this->error=$adh->error;
+						    $this->errors=$adh->errors;
+							dol_syslog(get_class($this)."::update error after calling adh->update to sync it with user: ".$this->error, LOG_ERR);
 							$error++;
 						}
 					}
 					else
 					{
 						$this->error=$adh->error;
+						$this->errors=$adh->errors;
 						$error++;
 					}
 				}
@@ -1495,7 +1501,7 @@ class User extends CommonObject
 			$password=getRandomPassword(false);
 		}
 
-		// Crypte avec md5
+		// Crypt password
 		$password_crypted = dol_hash($password);
 
 		// Mise a jour
@@ -1539,7 +1545,7 @@ class User extends CommonObject
 
 						if ($result >= 0)
 						{
-							$result=$adh->setPassword($user,$this->pass,0,1);	// Cryptage non gere dans module adherent
+							$result=$adh->setPassword($user,$this->pass,(empty($conf->global->DATABASE_PWD_ENCRYPTED)?0:1),1);	// Cryptage non gere dans module adherent
 							if ($result < 0)
 							{
 								$this->error=$adh->error;

+ 3 - 4
scripts/product/migrate_picture_path.php

@@ -1,6 +1,6 @@
 #!/usr/bin/env php
 <?php
-/* Copyright (C) 2007-2013 Laurent Destailleur  <eldy@users.sourceforge.net>
+/* Copyright (C) 2007-2016 Laurent Destailleur  <eldy@users.sourceforge.net>
  * Copyright (C) 2015 Jean Heimburger  <http://tiaris.eu>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,6 @@
  *      \file       scripts/product/migrate_picture_path.php
  *		\ingroup    scripts
  *      \brief      Migrate pictures from old system prior to 3.7 to new path for 3.7+
- *
  */
 
 $sapi_type = php_sapi_name();
@@ -119,7 +118,7 @@ function migrate_product_photospath($product)
 		$handle=opendir($origin_osencoded);
         if (is_resource($handle))
         {
-        	while (($file = readdir($handle)) != false)
+        	while (($file = readdir($handle)) !== false)
     		{
      			if ($file != '.' && $file != '..' && is_dir($origin_osencoded.'/'.$file))
     			{
@@ -127,7 +126,7 @@ function migrate_product_photospath($product)
     				if (is_resource($thumbs))
         			{
 	     				dol_mkdir($destin.'/'.$file);
-	     				while (($thumb = readdir($thumbs)) != false)
+	     				while (($thumb = readdir($thumbs)) !== false)
 		    			{
 		    				dol_move($origin.'/'.$file.'/'.$thumb, $destin.'/'.$file.'/'.$thumb);
 		    			}

+ 1 - 1
test/phpunit/CategorieTest.php

@@ -200,7 +200,7 @@ class CategorieTest extends PHPUnit_Framework_TestCase
 
         // Get list of categories for product
         $localcateg=new Categorie($this->savdb);
-        $listofcateg=$localcateg->containing($localobject2->id, 'product', 'label');
+        $listofcateg=$localcateg->containing($localobject2->id, Categorie::TYPE_PRODUCT, 'label');
         $this->assertTrue(in_array('Specimen Category for product',$listofcateg), 'Categ not found linked to product when it should');
 
         return $id;

+ 9 - 3
test/phpunit/PgsqlTest.php

@@ -162,11 +162,17 @@ class PgsqlTest extends PHPUnit_Framework_TestCase
         print __METHOD__." result=".$result."\n";
     	$this->assertEquals($result, $sql.' DEFERRABLE INITIALLY IMMEDIATE;');
 
-        // Create a constraint
-		$sql='SELECT a.b, GROUP_CONCAT(a.c) FROM table GROUP BY a.b';
+        // Test GROUP_CONCAT (without SEPARATOR)
+		$sql="SELECT a.b, GROUP_CONCAT(a.c) FROM table GROUP BY a.b";
+		$result=DoliDBPgsql::convertSQLFromMysql($sql);
+        print __METHOD__." result=".$result."\n";
+    	$this->assertEquals($result, "SELECT a.b, STRING_AGG(a.c, ',') FROM table GROUP BY a.b", 'Test GROUP_CONCAT (without SEPARATOR)');
+    	
+        // Test GROUP_CONCAT (with SEPARATOR)
+		$sql="SELECT a.b, GROUP_CONCAT(a.c SEPARATOR ',') FROM table GROUP BY a.b";
 		$result=DoliDBPgsql::convertSQLFromMysql($sql);
         print __METHOD__." result=".$result."\n";
-    	$this->assertEquals($result, "SELECT a.b, STRING_AGG(a.c, ',') FROM table GROUP BY a.b");
+    	$this->assertEquals($result, "SELECT a.b, STRING_AGG(a.c, ',') FROM table GROUP BY a.b", 'Test GROUP_CONCAT (with SEPARATOR)');
     	
     	return $result;
     }