Browse Source

Merge remote-tracking branch 'dolibarr/develop' into develop_origin

Conflicts:
	htdocs/core/class/html.form.class.php

Change-Id: I87bdc02d611c2978a5a8b427f0e1b718b6453b59
Regis Houssin 8 years ago
parent
commit
6e40f9e700
100 changed files with 1142 additions and 634 deletions
  1. 64 4
      ChangeLog
  2. 9 4
      Dockerfile
  3. 4 4
      README-FR.md
  4. 5 5
      README.md
  5. 1 2
      build/debian/conf.php.install
  6. 2 2
      build/doxygen/doxygen_footer.html
  7. 1 4
      build/exe/doliwamp/dolibarr.conf.install
  8. 32 27
      build/exe/doliwamp/doliwamp.iss
  9. 3 0
      build/exe/doliwamp/httpd.conf.install
  10. 2 8
      build/exe/doliwamp/phpmyadmin.conf.install
  11. 1 0
      build/makepack-dolibarr.pl
  12. 2 1
      composer.json
  13. 1 1
      dev/initdata/README
  14. BIN
      dev/initdemo/documents_demo/doctemplates/invoices/template_invoice.odt
  15. BIN
      dev/initdemo/documents_demo/doctemplates/orders/template_order.odt
  16. BIN
      dev/initdemo/documents_demo/doctemplates/projects/template_project.odt
  17. BIN
      dev/initdemo/documents_demo/doctemplates/proposals/template_proposal.odt
  18. BIN
      dev/initdemo/documents_demo/doctemplates/tasks/template_task_summary.odt
  19. BIN
      dev/initdemo/documents_demo/doctemplates/thirdparties/template_thirdparty.odt
  20. BIN
      dev/initdemo/documents_demo/medias/image/myimagesforemailing/dolicloud_logo_white.png
  21. BIN
      dev/initdemo/documents_demo/produit/DOLICLOUD/dolicloud_logo.png
  22. BIN
      dev/initdemo/documents_demo/produit/DOLICLOUD/thumbs/dolicloud_logo_mini.png
  23. BIN
      dev/initdemo/documents_demo/produit/DOLICLOUD/thumbs/dolicloud_logo_small.png
  24. BIN
      dev/initdemo/documents_demo/societe/contact/10/eldy_php.jpg
  25. BIN
      dev/initdemo/documents_demo/societe/contact/10/photos/ldestailleur_200x200.jpg
  26. BIN
      dev/initdemo/documents_demo/societe/contact/10/photos/thumbs/ldestailleur_200x200_mini.jpg
  27. BIN
      dev/initdemo/documents_demo/societe/contact/10/photos/thumbs/ldestailleur_200x200_small.jpg
  28. BIN
      dev/initdemo/documents_demo/societe/contact/10/thumbs/eldy_php_mini.jpg
  29. BIN
      dev/initdemo/documents_demo/societe/contact/10/thumbs/eldy_php_small.jpg
  30. BIN
      dev/initdemo/documents_demo/supplier_proposal/RQ1607-0001/RQ1607-0001.pdf
  31. BIN
      dev/initdemo/documents_demo/users/9/1/eldy_php.jpg
  32. BIN
      dev/initdemo/documents_demo/users/9/1/thumbs/eldy_php_mini.jpg
  33. BIN
      dev/initdemo/documents_demo/users/9/1/thumbs/eldy_php_small.jpg
  34. 21 19
      dev/initdemo/initdemo.sh
  35. 52 0
      dev/initdemo/mysqldump_dolibarr_4.0.0.sql
  36. 0 1
      dev/initdemo/savedemo.sh
  37. 126 0
      dev/initdemo/updatedemo.php
  38. 7 5
      dev/skeletons/build_class_from_table.php
  39. 3 3
      dev/skeletons/skeleton_card.php
  40. 129 72
      dev/skeletons/skeleton_list.php
  41. BIN
      doc/images/dolibarr_screenshot10_1280x800.png
  42. BIN
      doc/images/dolibarr_screenshot2_1280x800.png
  43. BIN
      doc/images/dolibarr_screenshot4_1280x800.png
  44. BIN
      doc/images/dolibarr_screenshot5_1280x800.png
  45. BIN
      doc/images/dolibarr_screenshot6_1280x800.png
  46. BIN
      doc/images/dolibarr_screenshot7_1280x800.png
  47. BIN
      doc/images/dolibarr_screenshot8_1280x800.png
  48. BIN
      doc/images/dolibarr_screenshot9_1280x800.png
  49. BIN
      doc/images/dolimed.ico
  50. BIN
      doc/images/doliwampoff.ico
  51. BIN
      doc/images/doliwampon.ico
  52. 22 2
      htdocs/accountancy/admin/card.php
  53. 28 0
      htdocs/accountancy/admin/index.php
  54. 5 3
      htdocs/adherents/fiche_subscription.php
  55. 5 0
      htdocs/admin/commande.php
  56. 3 1
      htdocs/admin/dict.php
  57. 1 1
      htdocs/admin/fckeditor.php
  58. 5 1
      htdocs/admin/ihm.php
  59. 3 3
      htdocs/admin/menus/edit.php
  60. 1 1
      htdocs/admin/prelevement.php
  61. 2 0
      htdocs/admin/propal.php
  62. 2 2
      htdocs/admin/security_file.php
  63. 42 11
      htdocs/admin/stock.php
  64. 8 8
      htdocs/admin/supplier_order.php
  65. 1 1
      htdocs/admin/supplier_payment.php
  66. 3 2
      htdocs/admin/workflow.php
  67. 3 3
      htdocs/api/admin/explorer.php
  68. 8 3
      htdocs/api/class/api.class.php
  69. 3 3
      htdocs/api/index.php
  70. 1 1
      htdocs/bookmarks/bookmarks.lib.php
  71. 7 2
      htdocs/categories/index.php
  72. 3 3
      htdocs/comm/card.php
  73. 1 1
      htdocs/comm/mailing/card.php
  74. 1 0
      htdocs/comm/mailing/class/mailing.class.php
  75. 20 2
      htdocs/comm/mailing/list.php
  76. 10 13
      htdocs/comm/propal/card.php
  77. 204 54
      htdocs/comm/propal/list.php
  78. 5 32
      htdocs/commande/card.php
  79. 1 0
      htdocs/commande/class/api_deprecated_commande.class.php
  80. 8 7
      htdocs/commande/class/api_orders.class.php
  81. 90 76
      htdocs/commande/class/commande.class.php
  82. 16 11
      htdocs/commande/list.php
  83. 1 1
      htdocs/commande/orderstoinvoice.php
  84. 2 2
      htdocs/compta/bank/account.php
  85. 5 5
      htdocs/compta/bank/class/account.class.php
  86. 1 1
      htdocs/compta/bank/ligne.php
  87. 1 1
      htdocs/compta/bank/releve.php
  88. 1 1
      htdocs/compta/bank/virement.php
  89. 12 99
      htdocs/compta/facture.php
  90. 1 0
      htdocs/compta/facture/class/api_deprecated_invoice.class.php
  91. 17 6
      htdocs/compta/facture/class/api_invoices.class.php
  92. 84 65
      htdocs/compta/facture/class/facture.class.php
  93. 4 7
      htdocs/compta/facture/contact.php
  94. 4 7
      htdocs/compta/facture/document.php
  95. 5 2
      htdocs/compta/facture/fiche-rec.php
  96. 1 1
      htdocs/compta/facture/info.php
  97. 2 1
      htdocs/compta/facture/list.php
  98. 4 7
      htdocs/compta/facture/note.php
  99. 9 9
      htdocs/compta/facture/prelevement.php
  100. 11 10
      htdocs/compta/paiement_charge.php

+ 64 - 4
ChangeLog

@@ -23,7 +23,7 @@ Following changes may create regression for some external modules, but were nece
 Dolibarr better:
 - Function delete of class Facture (invoice) need the object $user as first parameter. Also you must
 check you make a fetch on object before calling the delete.
-
+- The old driver of "mysql" has been removed. Dolibarr use the new one (mysqli) by default.
 
 
 
@@ -175,7 +175,6 @@ So if you included it into your module, change your code like this to be compati
   if (! $res) include_once DOL_DOCUMENT_ROOT . '/core/tpl/document_actions_pre_headers.tpl.php';
 
 
-
 ***** ChangeLog for 3.9.3 compared to 3.9.2 *****
 FIX: #4383 $userid not defined
 FIX: #4448 $filebonprev is not used, $this->filename now
@@ -474,7 +473,68 @@ This is list of hooks modified:
 - Remove deprecated Product::hidden property
 
 
- 
+
+***** ChangeLog for 3.8.5 compared to 3.8.4 *****
+FIX: #3815 Call to undefined function local_by_date().
+FIX: #4424 Missing email of user popup in supplier orders area
+FIX: #4442 Missing translation in Banks menu
+FIX: #4448 $filebonprev is not used, $this->filename now
+FIX: #4455
+FIX: #4737 Bank transacion type selector translation is cropped
+FIX: #4742 Able to delete a supplier invoice with a registered payment
+FIX: #4743 UI glitch in project summary page
+FIX: #4747 Missing UI background when registering a supplier invoice payment
+FIX: #4748 Supplier invoice payment confirmation amount is not translated
+FIX: #4749
+FIX: #4756
+FIX: #4766 VAT not shown in supplier invoice popup
+FIX: #4809 - Duplicate functions with different content
+FIX: #4851 Project selector in supplier invoices shows the project label twice
+FIX: #4870
+FIX: #5008 SQL error when editing the reference of a supplier invoice that already exists
+FIX: #5048 Product supplier list display only one produc
+FIX: #5170 tva sign with INVOICE_POSITIVE_CREDIT_NOTE option
+FIX: #5203
+FIX: #5207
+FIX: #5338 use of not initialized var $aphour, $apmin, etc
+FIX: #5380
+FIX: #5383 bad object id on don delete
+FIX: #5474 Country_id of "Don" object is still empty
+FIX: Accountancy - 3.8 - Chart of accounts are limited on only one country
+FIX: Bad include and param for project numbering module call
+FIX: Box disabled because bugged
+FIX: bug on email template
+FIX: Can correct stock of lot using eatby or sell by date
+FIX: Can make a movement on "out of sell" products
+FIX: Can't create thirdparty or validate invoice if profid is mandatory and profid does not exists for other countries
+FIX: can't fetch by siret or siren because of first "if"
+FIX: Check stock of product by warehouse if $entrepot_id defined on shippings
+FIX: correct display of minimum buying price
+FIX: Creation of thumb image for size "small" was not done.
+FIX: Direction of movement lost if an error occurs
+FIX: dont retrieve new buying price on margin display
+FIX: Duplicate records into export
+FIX: Email templates not compatible with Multicompany
+FIX: end of select when no fournprice
+FIX: finished parameters not used
+FIX: hook on group card called but not initialized
+FIX: It doesn't check if there is enough stock to update the lines of orders/invoices
+FIX: large expense note
+FIX: missing column when module was installed before standard integration
+FIX: Missing database escaping on supplier price insert/update
+FIX: Not filtering correctly when come from dashboard
+FIX: PROPAL_MERGE_PDF with PRODUCT_USE_OLD_PATH
+FIX: real min buying price
+FIX: receiving link never works
+FIX: same page added several times on mergepropal option
+FIX: search on date into supplier invoice list dont work because of status -1
+FIX: Search supplier ref on contract
+FIX: SQL error function on getAvailableDiscounts function, on bill create mode if socid is empty
+FIX: systematic rounding causes prices to be updated without reason
+FIX: task ODT company object not correctly retrieved
+FIX: Template email must take care of positino column
+FIX: VAT rate can be negative. Example spain selling to morroco.
+
 ***** ChangeLog for 3.8.4 compared to 3.8.3 *****
 FIX: #3694
 FIX: #3798 #2519 Cron jobs would never be executed
@@ -887,7 +947,7 @@ NEW: add hook in send mail
 NEW: Add hooks on list of members to allow an external module to add more fields into list view.
 NEW: Add hooks to allow an external module to complete list of events into calendar views.
 NEW: Add opportunity amount on project card.
-NEW: Add option THEME_ELDY_DISABLE_IMAGE to disable images into menu eldy.
+NEW: Add option THEME_TOPMENU_DISABLE_IMAGE to disable images into menu eldy.
 NEW: add PDF icon on linked element into project
 NEW: add "productpricecard" hook and uniformize code
 NEW: Add ref and label of project into export

+ 9 - 4
Dockerfile

@@ -1,14 +1,19 @@
 FROM php:5.6-apache
 
-RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev \
+RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev libldap2-dev \
 	&& rm -rf /var/lib/apt/lists/* \
 	&& docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
 	&& docker-php-ext-install gd
-
-RUN docker-php-ext-install mysqli
+	&& docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ \
+        && docker-php-ext-install ldap \
+        && docker-php-ext-install mysqli \
+        && apt-get purge -y libpng12-dev libjpeg-dev libldap2-dev
 
 COPY htdocs/ /var/www/html/
 
 RUN chown -hR www-data:www-data /var/www/html
 
-EXPOSE 80
+VOLUME /var/www/html/conf
+VOLUME /var/www/html/documents
+
+EXPOSE 80

+ 4 - 4
README-FR.md

@@ -3,7 +3,7 @@
 Dolibarr ERP & CRM est un logiciel moderne pour gérer votre activité (société, association, auto-entrepreneurs, artisans). 
 Il est simple d'utilisation et modulaire, vous permettant de n'activez que les fonctions dont vous avez besoin (contacts, fournisseurs, factures, commandes, stocks, agenda, ...).
  
-![ScreenShot](http://www.dolibarr.org/images/dolibarr_screenshot1_640x480.png)
+![ScreenShot](https://www.dolibarr.org/images/dolibarr_screenshot1_640x480.png)
 
 
 
@@ -23,7 +23,7 @@ Ubuntu) ou DoliRpm (la version tout-en-un de Dolibarr pour Fedora, Redhat,
 OpenSuse, Mandriva ou Mageia).
 
 Vous pouvez les télécharger depuis la rubrique *download* du portail officiel: 
-http://www.dolibarr.org/
+https://www.dolibarr.org/
 
 Si vous avez déjà installé un serveur Web avec PHP et une base de donnée (Mysql),
 vous pouvez installer Dolibarr avec cette version de la manière suivante:
@@ -139,13 +139,13 @@ Suivez le projet Dolibarr project sur les réseaux francophones
 
 - Facebook: <https://www.facebook.com/dolibarr.fr>
 - Google+: <https://plus.google.com/+DolibarrFrance>
-- Twitter: <http://www.twitter.com/dolibarr_france>
+- Twitter: <https://www.twitter.com/dolibarr_france>
 
 ou sur les réseaux anglophones
 
 - [Facebook](https://www.facebook.com/dolibarr)
 - [Google+](https://plus.google.com/+DolibarrOrg)
-- [Twitter](http://www.twitter.com/dolibarr)
+- [Twitter](https://www.twitter.com/dolibarr)
 - [LinkedIn](https://www.linkedin.com/company/association-dolibarr)
 - [YouTube](https://www.youtube.com/user/DolibarrERPCRM)
 - [GitHub](https://github.com/Dolibarr/dolibarr)

+ 5 - 5
README.md

@@ -2,7 +2,7 @@
 
 ![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/develop.svg) ![Downloads per day](https://img.shields.io/sourceforge/dm/dolibarr.svg)
 
-Dolibarr ERP & CRM is a modern software to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda, ...).
+Dolibarr ERP & CRM is a modern software package to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda, ...).
 
 It's an Open Source software (wrote in PHP language) designed for small, medium or large companies, foundations and freelances.
 
@@ -10,7 +10,7 @@ You can freely use, study, modify or distribute it according to its Free Softwar
 
 You can use it as a standalone application or as a web application to be able to access it from the Internet or a LAN.
 
-![ScreenShot](http://www.dolibarr.org/images/dolibarr_screenshot1_640x400.png)
+![ScreenShot](https://www.dolibarr.org/images/dolibarr_screenshot1_640x400.png)
 
 ## LICENSE
 
@@ -24,7 +24,7 @@ Other licenses apply for some included dependencies. See [COPYRIGHT](https://git
 
 ### Download
 
-Releases can be downloaded from [official website](http://www.dolibarr.org/).
+Releases can be downloaded from [official website](https://www.dolibarr.org/).
 
 ### Simple setup
 
@@ -133,7 +133,7 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog)
 
 ### Extending
 
-Dolibarr can be extended with a lot of other external modules from third party developers available at the [DoliStore](http://www.dolistore.com).
+Dolibarr can be extended with a lot of other external modules from third party developers available at the [DoliStore](https://www.dolistore.com).
 
 ## FUTURE
 
@@ -165,7 +165,7 @@ Follow Dolibarr project on:
 
 - [Facebook](https://www.facebook.com/dolibarr)
 - [Google+](https://plus.google.com/+DolibarrOrg)
-- [Twitter](http://www.twitter.com/dolibarr)
+- [Twitter](https://www.twitter.com/dolibarr)
 - [LinkedIn](https://www.linkedin.com/company/association-dolibarr)
 - [YouTube](https://www.youtube.com/user/DolibarrERPCRM)
 - [GitHub](https://github.com/Dolibarr/dolibarr)

+ 1 - 2
build/debian/conf.php.install

@@ -100,9 +100,8 @@ $dolibarr_main_db_pass='';
 # This parameter contains the name of the driver used to access your
 # Dolibarr database.
 # Default value: none
-# Possible values: mysql, mysqli, pgsql
+# Possible values: mysqli, pgsql
 # Examples:
-# $dolibarr_main_db_type='mysql';
 # $dolibarr_main_db_type='mysqli';
 # $dolibarr_main_db_type='pgsql';
 #

+ 2 - 2
build/doxygen/doxygen_footer.html

@@ -19,7 +19,7 @@ File added into doxygen generated documentation
 
 <hr class="footer" />
 <address class="footer"><small>Generated on $datetime
-for <a href="http://www.dolibarr.org">$projectname</a> by Doxygen $doxygenversion </small></address>
+for <a href="https://www.dolibarr.org">$projectname</a> by Doxygen $doxygenversion </small></address>
 
 
 <!--  Google AdSense -->
@@ -32,7 +32,7 @@ google_ad_width = 468;
 google_ad_height = 60;
 //-->
 </script>
-<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
+<script type="text/javascript" src="https://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
 </div>
 <!-- End google adsense -->
 <br>

+ 1 - 4
build/exe/doliwamp/dolibarr.conf.install

@@ -15,10 +15,7 @@ Alias /dolibarr "WAMPROOT/www/dolibarr/htdocs/"
 # NOTE FOR APACHE 2.3:
 # To restrict access to dolibarr from outside set lines
 #
-#	<RequireAny>
-#	Require ip 127.0.0.1
-#	Require host localhost
-#   <RequireAny>
+#   Require local
 #
 # instead of
 #

+ 32 - 27
build/exe/doliwamp/doliwamp.iss

@@ -42,6 +42,7 @@ SolidCompression=yes
 WizardImageFile=build\exe\doliwamp\doliwamp.bmp
 WizardSmallImageFile=build\exe\doliwamp\doliwampsmall.bmp
 SetupIconFile=doc\images\dolibarr.ico
+;To say the installer must be ran as admin
 PrivilegesRequired=admin
 DisableProgramGroupPage=yes
 ChangesEnvironment=no
@@ -148,8 +149,12 @@ Name: "{userdesktop}\Dolibarr Help center"; Filename: "{app}\rundolihelp.bat"; W
 
 [Registry]
 ; Add "run as admin" flag. Same than command line: reg add "HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /v "<Path to your exe>" /t REG_SZ /d RUNASADMIN
-Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\"; ValueType: String; ValueName: "{app}\startdoliwamp.bat"; ValueData: "RUNASADMIN"; Flags: uninsdeletekeyifempty uninsdeletevalue;
-Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\"; ValueType: String; ValueName: "{app}\stopdoliwamp.bat"; ValueData: "RUNASADMIN"; Flags: uninsdeletekeyifempty uninsdeletevalue;
+Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\startdoliwamp.bat"; ValueData: "RUNASADMIN";
+Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\stopdoliwamp.bat"; ValueData: "RUNASADMIN";
+Root: "HKLM32"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\startdoliwamp.bat"; ValueData: "RUNASADMIN";
+Root: "HKLM32"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\stopdoliwamp.bat"; ValueData: "RUNASADMIN";
+Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\startdoliwamp.bat"; ValueData: "RUNASADMIN"; Check: IsWin64
+Root: "HKLM64"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\stopdoliwamp.bat"; ValueData: "RUNASADMIN"; Check: IsWin64
 
 
 [Code]
@@ -239,9 +244,9 @@ begin
         if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\MSCRM','SMTPServer', value) then
         begin
           if value <> '' then smtpServer:=value;
-        end
-      end
-    end
+        end;
+      end;
+    end;
   end;
 
   if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\NLTechno\DoliWamp','apachePort', value) then
@@ -419,7 +424,7 @@ begin
     begin
       themessage := FmtMessage(CustomMessage('FailedToDeleteLock'),[pathWithSlashes]);
  		  MsgBox(themessage,mbInformation,MB_OK);
-    end
+    end;
 
 
 		// Check if parameters already defined in conf.php file
@@ -592,7 +597,7 @@ begin
 	      StringChangeEx (srcContents, 'WAMPAPACHEPORT', myporta, True);
 	      StringChangeEx (srcContents, 'WAMPAPACHEPSSL', myportas, True);
 	      SaveStringToFile(destFileA,srcContents, False);
-	    end
+	    end;
 
 
       if MsgBox(CustomMessage('DoliWampWillStartApacheMysql'),mbConfirmation,MB_YESNO) = IDYES then
@@ -615,7 +620,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPPHPMYADMINVERSION', phpmyadminVersion, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		    DeleteFile(srcFile);
 		
 		
@@ -648,8 +653,8 @@ begin
 		        StringChangeEx (srcContents, 'WAMPMYSQLNEWPASSWORD', mypass, True);
 		
 		        SaveStringToFile(destFile, srcContents, False);
-		      end
-		    end
+		      end;
+		    end;
 		    DeleteFile(srcFile);
 		
 		
@@ -679,8 +684,8 @@ begin
 	            StringChangeEx (srcContents, 'WAMPMYSQLNEWPASSWORD', mypass, True);
 	            StringChangeEx (srcContents, 'WAMPMYSQLPORT', myport, True);
 	            SaveStringToFile(destFile,srcContents, False);
-		      end
-		    end
+		      end;
+		    end;
 		
 		
 		
@@ -703,7 +708,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPAPACHEPSSL', myportas, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -725,7 +730,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPMYSQLVERSION', mysqlVersion, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -757,7 +762,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPAPACHEPORT', myporta, True);
 		      StringChangeEx (srcContents, 'WAMPAPACHEPSSL', myportas, True);
 		      SaveStringToFile(destFile, srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -780,7 +785,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPMYSQLNEWPASSWORD', mypass, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -801,7 +806,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPAPACHEVERSION', apacheVersion, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -822,7 +827,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPAPACHEVERSION', apacheVersion, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -843,7 +848,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPMYSQLNEWPASSWORD', mypass, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		    //----------------------------------------------
@@ -864,7 +869,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPMYSQLPORT', myport, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		    //----------------------------------------------
@@ -885,7 +890,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPMYSQLPORT', myport, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		    
 		
 		    //----------------------------------------------
@@ -904,7 +909,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPAPACHEVERSION', apacheVersion, True);
 		
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 
 		
 		    //----------------------------------------------
@@ -921,7 +926,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPSMTP', mysmtp, True);
               StringChangeEx (srcContents, 'WAMPPHPVERSION', phpVersion, True);
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		    //----------------------------------------------
 		    // Create file php.ini in apache (if not exists)
@@ -937,7 +942,7 @@ begin
 		      StringChangeEx (srcContents, 'WAMPSMTP', mysmtp, True);
               StringChangeEx (srcContents, 'WAMPPHPVERSION', phpVersion, True);
 		      SaveStringToFile(destFile,srcContents, False);
-		    end
+		    end;
 		
 		
 		
@@ -979,7 +984,7 @@ begin
 
       	res := False;
 		  	
-		  end
+		  end;
       
     end
     else
@@ -989,9 +994,9 @@ begin
 		  	
 		  	res := False;
 
-    end
+    end;
     
-  end
+  end;
 
 
   Result := res;

+ 3 - 0
build/exe/doliwamp/httpd.conf.install

@@ -255,6 +255,9 @@ KeepAliveTimeout 30
     # Controls who can get stuff from this server.
     #
 #   onlineoffline tag - don't remove
+	
+	Require all granted
+    
     <RequireAny>
 		Require ip 127.0.0.1
 		Require host localhost

+ 2 - 8
build/exe/doliwamp/phpmyadmin.conf.install

@@ -19,17 +19,11 @@ Alias /phpmyadmin "WAMPROOT/apps/phpmyadminWAMPPHPMYADMINVERSION/"
 #
 # instead of
 #
-#	<RequireAny>
-#	Require ip 127.0.0.1
-#	Require host localhost
-#   <RequireAny>
+#	Require local
 #
 
 <Directory "WAMPROOT/apps/phpmyadminWAMPPHPMYADMINVERSION/">
     Options Indexes FollowSymLinks MultiViews
     AllowOverride all
-	<RequireAny>
-	Require ip 127.0.0.1
-	Require host localhost
-    </RequireAny>
+    Require local
 </Directory>

+ 1 - 0
build/makepack-dolibarr.pl

@@ -1117,6 +1117,7 @@ if ($nboftargetok) {
             print "Move $SOURCE/build/$FILENAMEEXEDOLIWAMP.exe to $NEWDESTI/$FILENAMEEXEDOLIWAMP.exe\n";
             $ret=`mv "$SOURCE/build/$FILENAMEEXEDOLIWAMP.exe" "$NEWDESTI/$FILENAMEEXEDOLIWAMP.exe"`;
             
+            print "Remove tmp file $SOURCE/build/exe/doliwamp/doliwamp.tmp.iss\n";
             $ret=`rm "$SOURCE/build/exe/doliwamp/doliwamp.tmp.iss"`;
             
     		next;

+ 2 - 1
composer.json

@@ -30,7 +30,8 @@
         "jakub-onderka/php-parallel-lint": "^0",
         "jakub-onderka/php-console-highlighter": "^0",
         "phpunit/phpunit": "^4",
-        "squizlabs/php_codesniffer": "^2"
+        "squizlabs/php_codesniffer": "^2",
+        "phpunit/phpunit-selenium": "^2"
     },
     "suggest": {
         "ext-mysqlnd": "To use with MySQL or MariaDB",

+ 1 - 1
dev/initdata/README

@@ -2,4 +2,4 @@ README
 ------
 
 Scripts in this directory can be used to load or purge data of a database instance.
-WARNING: This may erase data.
+WARNING: Some of this script may delete definitely data.

BIN
dev/initdemo/documents_demo/doctemplates/invoices/template_invoice.odt


BIN
dev/initdemo/documents_demo/doctemplates/orders/template_order.odt


BIN
dev/initdemo/documents_demo/doctemplates/projects/template_project.odt


BIN
dev/initdemo/documents_demo/doctemplates/proposals/template_proposal.odt


BIN
dev/initdemo/documents_demo/doctemplates/tasks/template_task_summary.odt


BIN
dev/initdemo/documents_demo/doctemplates/thirdparties/template_thirdparty.odt


BIN
dev/initdemo/documents_demo/medias/image/myimagesforemailing/dolicloud_logo_white.png


BIN
dev/initdemo/documents_demo/produit/DOLICLOUD/dolicloud_logo.png


BIN
dev/initdemo/documents_demo/produit/DOLICLOUD/thumbs/dolicloud_logo_mini.png


BIN
dev/initdemo/documents_demo/produit/DOLICLOUD/thumbs/dolicloud_logo_small.png


BIN
dev/initdemo/documents_demo/societe/contact/10/eldy_php.jpg


BIN
dev/initdemo/documents_demo/societe/contact/10/photos/ldestailleur_200x200.jpg


BIN
dev/initdemo/documents_demo/societe/contact/10/photos/thumbs/ldestailleur_200x200_mini.jpg


BIN
dev/initdemo/documents_demo/societe/contact/10/photos/thumbs/ldestailleur_200x200_small.jpg


BIN
dev/initdemo/documents_demo/societe/contact/10/thumbs/eldy_php_mini.jpg


BIN
dev/initdemo/documents_demo/societe/contact/10/thumbs/eldy_php_small.jpg


BIN
dev/initdemo/documents_demo/supplier_proposal/RQ1607-0001/RQ1607-0001.pdf


BIN
dev/initdemo/documents_demo/users/9/1/eldy_php.jpg


BIN
dev/initdemo/documents_demo/users/9/1/thumbs/eldy_php_mini.jpg


BIN
dev/initdemo/documents_demo/users/9/1/thumbs/eldy_php_small.jpg


+ 21 - 19
dev/initdemo/initdemo.sh

@@ -15,7 +15,7 @@
 
 
 export mydir=`echo "$0" | sed -e 's/initdemo.sh//'`;
-if [ "x$mydir" = "x" ]
+if [ "x$mydir" = 'x' -o "x$mydir" = 'x./' ]
 then
     export mydir="."
 fi
@@ -132,24 +132,7 @@ then
 	exit;;
 	esac
 	
-	# ---------------------------- chemin d'acces du repertoire documents
-	#DIALOG=${DIALOG=dialog}
-	#fichtemp=`tempfile 2>/dev/null` || fichtemp=/tmp/test$$
-	#trap "rm -f $fichtemp" 0 1 2 5 15
-	#$DIALOG --title "Init Dolibarr with demo values" --clear \
-	#        --inputbox "Full path to documents directory (ex: /var/www/dolibarr/documents)- no / at end :" 16 55 2> $fichtemp
-	
-	#valret=$?
-	
-	#case $valret in
-	#  0)
-	#docs=`cat $fichtemp`;;
-	#  1)
-	#exit;;
-	#  255)
-	#exit;;
-	#esac
-	
+
 	# ---------------------------- confirmation
 	DIALOG=${DIALOG=dialog}
 	$DIALOG --title "Init Dolibarr with demo values" --clear \
@@ -177,6 +160,25 @@ echo "mysql -P$port -u$admin -p***** $base < $mydir/$dumpfile"
 mysql -P$port -u$admin $passwd $base < $mydir/$dumpfile
 export res=$?
 
+
+# ---------------------------- copy demo files
+export documentdir=`cat $mydir/../../htdocs/conf/conf.php | grep '^\$dolibarr_main_data_root' | sed -e 's/$dolibarr_main_data_root=//' | sed -e 's/;//' | sed -e "s/'//g" `
+if [ "x$documentdir" != "x" ]
+then
+	echo cp -pr $mydir/documents_demo/* "$documentdir/"
+	cp -pr $mydir/documents_demo/* "$documentdir/"
+	echo cp -pr $mydir/../../htdocs/install/doctemplates/* "$documentdir/doctemplates/"
+	cp -pr $mydir/../../htdocs/install/doctemplates/* "$documentdir/doctemplates/"
+	mkdir -p "$documentdir/ecm/Administrative documents"
+	mkdir -p "$documentdir/ecm/Images"
+	echo cp -pr $mydir/../../doc/images/* "$documentdir/ecm/Images"
+	cp -pr $mydir/../../doc/images/* "$documentdir/ecm/Images"
+else
+	echo Detection of documents directory failed so demo files were not copied. 
+fi
+
+
+
 if [ "x$res" = "x0" ]
 then
 	echo "Success, file successfully loaded."

File diff suppressed because it is too large
+ 52 - 0
dev/initdemo/mysqldump_dolibarr_4.0.0.sql


+ 0 - 1
dev/initdemo/savedemo.sh

@@ -187,7 +187,6 @@ export list="
 	--ignore-table=$base.llx_bt_webseedfiles
 	--ignore-table=$base.llx_c_civilite
 	--ignore-table=$base.llx_c_dolicloud_plans
-	--ignore-table=$base.llx_c_lead_status
 	--ignore-table=$base.llx_c_source
 	--ignore-table=$base.llx_cabinetmed_c_banques
 	--ignore-table=$base.llx_cabinetmed_c_ccam

+ 126 - 0
dev/initdemo/updatedemo.php

@@ -0,0 +1,126 @@
+#!/usr/bin/env php
+<?php
+/* Copyright (C) 2016 Laurent Destailleur	<eldy@users.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * or see http://www.gnu.org/
+ * 
+ * Get a distant dump file and load it into a mysql database
+ */
+
+$sapi_type = php_sapi_name();
+$script_file = basename(__FILE__);
+$path=dirname(__FILE__).'/';
+
+// Test if batch mode
+if (substr($sapi_type, 0, 3) == 'cgi') {
+	echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
+	exit;
+}
+
+// Global variables
+$error=0;
+
+$confirm=isset($argv[1])?$argv[1]:'';
+
+// Include Dolibarr environment
+$res=0;
+if (! $res && file_exists($path."../../master.inc.php")) $res=@include($path."../../master.inc.php");
+if (! $res && file_exists($path."../../htdocs/master.inc.php")) $res=@include($path."../../htdocs/master.inc.php");
+if (! $res && file_exists("../master.inc.php")) $res=@include("../master.inc.php");
+if (! $res && file_exists("../../master.inc.php")) $res=@include("../../master.inc.php");
+if (! $res && file_exists("../../../master.inc.php")) $res=@include("../../../master.inc.php");
+if (! $res && preg_match('/\/nltechno([^\/]*)\//',$_SERVER["PHP_SELF"],$reg)) $res=@include($path."../../../dolibarr".$reg[1]."/htdocs/master.inc.php"); // Used on dev env only
+if (! $res && preg_match('/\/nltechno([^\/]*)\//',$_SERVER["PHP_SELF"],$reg)) $res=@include("../../../dolibarr".$reg[1]."/htdocs/master.inc.php"); // Used on dev env only
+if (! $res) die ("Failed to include master.inc.php file\n");
+include_once(DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php');
+
+
+/*
+ *	Main
+ */
+
+if (empty($confirm))
+{
+	print "Usage: $script_file confirm\n";
+	print "Return code: 0 if success, <>0 if error\n";
+	exit(-1);
+}
+
+
+$tmp=dol_getdate(dol_now());
+
+
+$tables=array(
+    'propal'=>array(0=>'datep', 1=>'fin_validite', 2=>'date_valid', 3=>'date_cloture'),
+    'commande'=>array(0=>'date_commande', 1=>'date_valid', 2=>'date_cloture'),
+    'facture'=>array(0=>'datef', 1=>'date_valid', 2=>'date_lim_reglement'),
+    'paiement'=>array(0=>'datep'),
+    'bank'=>array(0=>'datev', 1=>'dateo'),
+    'commande_fournisseur'=>array(0=>'date_commande', 1=>'date_valid', 3=>'date_creation', 4=>'date_approve', 5=>'date_approve2', 6=>'date_livraison'),
+    'supplier_proposal'=>array(0=>'datec', 1=>'date_valid', 2=>'date_cloture')
+);
+
+$year=2010;
+$currentyear=$tmp['year'];
+while ($year <= $currentyear) 
+{
+    //$year=2021;
+    $delta=($currentyear - $year);
+    //$delta=-1;
+
+    if ($delta)
+    {
+        foreach($tables as $tablekey => $tableval)
+        {
+            print "\nCorrect ".$tablekey." for year ".$year." and move them to current year ".$currentyear." "; 
+            $sql="select rowid from ".MAIN_DB_PREFIX.$tablekey." where ".$tableval[0]." between '".$year."-01-01' and '".$year."-12-31' and ".$tableval[0]." < DATE_ADD(NOW(), INTERVAL -1 YEAR)";
+            //$sql="select rowid from ".MAIN_DB_PREFIX.$tablekey." where ".$tableval[0]." between '".$year."-01-01' and '".$year."-12-31' and ".$tableval[0]." > NOW()";
+            $resql = $db->query($sql);
+            if ($resql)
+            {
+                $num = $db->num_rows($resql);
+                $i=0;
+                while ($i < $num)
+                {
+                    $obj=$db->fetch_object($resql);
+                    if ($obj)
+                    {
+                        print ".";
+                        $sql2="UPDATE ".MAIN_DB_PREFIX.$tablekey." set ";
+                        $j=0;
+                        foreach($tableval as $field)
+                        {
+                            if ($j) $sql2.=", ";
+                            $sql2.= $field." = DATE_ADD(".$field.", INTERVAL ".$delta." YEAR)";
+                            $j++;
+                        }
+                        $sql2.=" WHERE rowid = ".$obj->rowid;
+                        //print $sql2."\n";
+                        $resql2 = $db->query($sql2);
+                        if (! $resql2) dol_print_error($db);
+                    }            
+                    $i++;
+                }
+            }
+            else dol_print_error($db);
+        }
+    }
+    
+    $year++;
+}
+
+print "\n";
+
+exit(0);

+ 7 - 5
dev/skeletons/build_class_from_table.php

@@ -587,11 +587,10 @@ foreach ($skeletonfiles as $skeletonfile => $outfile)
     {
     	if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
     	{
-    	    $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print_liste_field_titre(\$arrayfields['t.".$prop['field']."']['label'],\$_SERVER['PHP_SELF'],'t.".$prop['field']."','',\$param,'',\$sortfield,\$sortorder);\n";
+    	    $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print_liste_field_titre(\$arrayfields['t.".$prop['field']."']['label'],\$_SERVER['PHP_SELF'],'t.".$prop['field']."','',\$params,'',\$sortfield,\$sortorder);\n";
     	}
     }
-    $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field1']['checked'])) print_liste_field_titre(\$langs->trans('field1'),\$_SERVER['PHP_SELF'],'t.field1','',\$param,'',\$sortfield,\$sortorder);",'/').'/', $varprop, $targetcontent);
-    $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field2']['checked'])) print_liste_field_titre(\$langs->trans('field2'),\$_SERVER['PHP_SELF'],'t.field2','',\$param,'',\$sortfield,\$sortorder);",'/').'/', '', $targetcontent);
+    $targetcontent=preg_replace('/LIST_OF_TD_TITLE_FIELDS/', $varprop, $targetcontent);
     
     // Substitute fields title search
     $varprop="\n";
@@ -603,8 +602,7 @@ foreach ($skeletonfiles as $skeletonfile => $outfile)
     	    $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print '<td class=\"liste_titre\"><input type=\"text\" class=\"flat\" name=\"search_".$prop['field']."\" value=\"'.\$search_".$prop['field'].".'\" size=\"10\"></td>';\n";
     	}
     }
-    $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field1']['checked'])) print '<td class=\"liste_titre\"><input type=\"text\" class=\"flat\" name=\"search_field1\" value=\"'.\$search_field1.'\" size=\"10\"></td>';",'/').'/', $varprop, $targetcontent);
-    $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field2']['checked'])) print '<td class=\"liste_titre\"><input type=\"text\" class=\"flat\" name=\"search_field2\" value=\"'.\$search_field2.'\" size=\"10\"></td>';",'/').'/', '', $targetcontent);
+    $targetcontent=preg_replace('/LIST_OF_TD_TITLE_SEARCH/', $varprop, $targetcontent);
     
     // Substitute where for <td>.fieldx.</td>
     $varprop="\n";
@@ -656,6 +654,10 @@ foreach ($skeletonfiles as $skeletonfile => $outfile)
     $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_VIEW/', $varprop, $targetcontent);
     
     
+    // LIST_OF_TD_FIELDS_LIST
+    
+    
+    
     // Build file
     $fp=fopen($outfile,"w");
     if ($fp)

+ 3 - 3
dev/skeletons/skeleton_card.php

@@ -322,9 +322,9 @@ if ($id && (empty($action) || $action == 'view' || $action == 'delete'))
 
 
 	// Example 2 : Adding links to objects
-	//$somethingshown=$form->showLinkedObjectBlock($object);
-	//$linktoelem = $form->showLinkToObjectBlock($object);
-	//if ($linktoelem) print '<br>'.$linktoelem;
+	// Show links to link elements
+	//$linktoelem = $form->showLinkToObjectBlock($object, null, array('skeleton'));
+	//$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
 
 }
 

+ 129 - 72
dev/skeletons/skeleton_list.php

@@ -91,30 +91,38 @@ $extrafields = new ExtraFields($db);
 $extralabels = $extrafields->fetch_name_optionals_label('mymodule');
 $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
 
-// Load object if id or ref is provided as parameter
-$object=new Skeleton_Class($db);
-if (($id > 0 || ! empty($ref)) && $action != 'add')
-{
-	$result=$object->fetch($id,$ref);
-	if ($result < 0) dol_print_error($db);
-}
+// List of fields to search into when doing a "search in all"
+$fieldstosearchall = array(
+    't.ref'=>'Ref',
+    't.note_public'=>'NotePublic',
+);
+if (empty($user->socid)) $fieldstosearchall["t.note_private"]="NotePrivate";
 
 // Definition of fields for list
 $arrayfields=array(
     't.field1'=>array('label'=>$langs->trans("Field1"), 'checked'=>1),
     't.field2'=>array('label'=>$langs->trans("Field2"), 'checked'=>1),
     //'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))),
-    't.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
+    't.datec'=>array('label'=>$langs->trans("DateCreationShort"), 'checked'=>0, 'position'=>500),
     't.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
     //'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
 );
 // Extra fields
 if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
 {
-   foreach($extrafields->attribute_label as $key => $val) 
-   {
-       $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
-   }
+    foreach($extrafields->attribute_label as $key => $val)
+    {
+        $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
+    }
+}
+
+
+// Load object if id or ref is provided as parameter
+$object=new Skeleton_Class($db);
+if (($id > 0 || ! empty($ref)) && $action != 'add')
+{
+	$result=$object->fetch($id,$ref);
+	if ($result < 0) dol_print_error($db);
 }
 
 
@@ -127,7 +135,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
 ********************************************************************/
 
 if (GETPOST('cancel')) { $action='list'; $massaction=''; }
-if (! GETPOST('confirmmassaction')) { $massaction=''; }
+if (! GETPOST('confirmmassaction') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; }
 
 $parameters=array();
 $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action);    // Note that $action and $object may have been modified by some hooks
@@ -135,65 +143,46 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e
 
 include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
 
+// Purge search criteria
 if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers
 {
 	$search_field1='';
 	$search_field2='';
 	$search_date_creation='';
 	$search_date_update='';
-	$search_array_options=array();
+    $toselect='';
+    $search_array_options=array();
 }
 
 
 if (empty($reshook))
 {
-    // Mass actions. Controls on number of lines checked
-    $maxformassaction=1000;
-    if (! empty($massaction) && count($toselect) < 1)
-    {
-        $error++;
-        setEventMessages($langs->trans("NoLineChecked"), null, "warnings");
-    }
-    if (! $error && count($toselect) > $maxformassaction)
-    {
-        setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors');
-        $error++;
-    }
-    
-	// Action to delete
-	if ($action == 'confirm_delete')
-	{
-		$result=$object->delete($user);
-		if ($result > 0)
-		{
-			// Delete OK
-			setEventMessages("RecordDeleted", null, 'mesgs');
-			header("Location: ".dol_buildpath('/mymodule/list.php',1));
-			exit;
-		}
-		else
-		{
-			if (! empty($object->errors)) setEventMessages(null,$object->errors,'errors');
-			else setEventMessages($object->error,null,'errors');
-		}
-	}
+    $objectclass='Skeleton';
+    $objectlabel='Skeleton';
+    $permtoread = $user->rights->skeleton->read;
+    $permtodelete = $user->rights->skeleton->delete;
+    $uploaddir = $conf->skeleton->dir_output;
+    include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
 }
 
 
 
-
 /***************************************************
 * VIEW
 *
 * Put here all code to build page
 ****************************************************/
 
-llxHeader('','MyPageName','');
+$now=dol_now();
 
 $form=new Form($db);
 
-// Put here content of your page
+//$help_url="EN:Module_Customers_Orders|FR:Module_Commandes_Clients|ES:Módulo_Pedidos_de_clientes";
+$help_url='';
 $title = $langs->trans('MyModuleListTitle');
+llxHeader('', $title, $help_url);
+
+// Put here content of your page
 
 // Example : Adding jquery code
 print '<script type="text/javascript" language="javascript">
@@ -215,8 +204,8 @@ $sql = "SELECT";
 $sql.= " t.rowid,";
 $sql.= " t.field1,";
 $sql.= " t.field2";
-// Add fields for extrafields
-foreach ($extrafields->attribute_list as $key => $val) $sql.=",ef.".$key.' as options_'.$key;
+// Add fields from extrafields
+foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : '');
 // Add fields from hooks
 $parameters=array();
 $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters);    // Note that $action and $object may have been modified by hook
@@ -265,7 +254,10 @@ if ($resql)
 {
     $num = $db->num_rows($resql);
     
+    $arrayofselected=is_array($toselect)?$toselect:array();
+    
     $params='';
+    if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage;
     if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
     if ($search_field1 != '') $params.= '&amp;search_field1='.urlencode($search_field1);
 	if ($search_field2 != '') $params.= '&amp;search_field2='.urlencode($search_field2);
@@ -278,9 +270,14 @@ if ($resql)
         if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val);
     } 
 
-    print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit);
-
-
+    $arrayofmassactions =  array(
+        'presend'=>$langs->trans("SendByMail"),
+        'builddoc'=>$langs->trans("PDFMerge"),
+    );
+    if ($user->rights->mymodule->supprimer) $arrayofmassactions['delete']=$langs->trans("Delete");
+    if ($massaction == 'presend') $arrayofmassactions=array();
+    $massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+    
 	print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
     if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
 	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
@@ -289,10 +286,12 @@ if ($resql)
 	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
 	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
 	
-    if ($sall)
+    print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit);
+	
+	if ($sall)
     {
         foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);
-        print $langs->trans("FilterOnInto", $all) . implode(', ',$fieldstosearchall);
+        print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall);
     }
     
     $moreforfilter = '';
@@ -300,25 +299,28 @@ if ($resql)
     $moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escpae_htmltag($search_myfield).'">';
     $moreforfilter.= '</div>';
     
+    $parameters=array();
+    $reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters);    // Note that $action and $object may have been modified by hook
+    if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
+    else $moreforfilter = $hookmanager->resPrint;
+    
 	if (! empty($moreforfilter))
 	{
 		print '<div class="liste_titre liste_titre_bydiv centpercent">';
 		print $moreforfilter;
-    	$parameters=array();
-    	$reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters);    // Note that $action and $object may have been modified by hook
-	    print $hookmanager->resPrint;
 	    print '</div>';
 	}
 
     $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
     $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
 	
-	print '<table class="liste '.($moreforfilter?"listwithfilterbefore":"").'">';
+	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 
     // Fields title
     print '<tr class="liste_titre">';
-    if (! empty($arrayfields['t.field1']['checked'])) print_liste_field_titre($arrayfields['t.field1']['label'],$_SERVER['PHP_SELF'],'t.field1','',$param,'',$sortfield,$sortorder);
-    if (! empty($arrayfields['t.field2']['checked'])) print_liste_field_titre($arrayfields['t.field2']['label'],$_SERVER['PHP_SELF'],'t.field2','',$param,'',$sortfield,$sortorder);
+    // LIST_OF_TD_TITLE_FIELDS
+    //if (! empty($arrayfields['t.field1']['checked'])) print_liste_field_titre($arrayfields['t.field1']['label'],$_SERVER['PHP_SELF'],'t.field1','',$params,'',$sortfield,$sortorder);
+    //if (! empty($arrayfields['t.field2']['checked'])) print_liste_field_titre($arrayfields['t.field2']['label'],$_SERVER['PHP_SELF'],'t.field2','',$params,'',$sortfield,$sortorder);
 	// Extra fields
 	if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
 	{
@@ -343,8 +345,9 @@ if ($resql)
 
     // Fields title search
 	print '<tr class="liste_titre">';
-	if (! empty($arrayfields['t.field1']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_field1" value="'.$search_field1.'" size="10"></td>';
-	if (! empty($arrayfields['t.field2']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_field2" value="'.$search_field2.'" size="10"></td>';
+	// LIST_OF_TD_TITLE_SEARCH
+	//if (! empty($arrayfields['t.field1']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_field1" value="'.$search_field1.'" size="10"></td>';
+	//if (! empty($arrayfields['t.field2']['checked'])) print '<td class="liste_titre"><input type="text" class="flat" name="search_field2" value="'.$search_field2.'" size="10"></td>';
 	// Extra fields
 	if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
 	{
@@ -393,7 +396,7 @@ if ($resql)
     }*/
     // Action column
 	print '<td class="liste_titre" align="right">';
-    $searchpitco=$form->showFilterAndCheckAddButtons(0);
+	$searchpitco=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1);
     print $searchpitco;
     print '</td>';
 	print '</tr>'."\n";
@@ -407,8 +410,12 @@ if ($resql)
         $obj = $db->fetch_object($resql);
         if ($obj)
         {
-            // You can use here results
-            print '<tr>';
+            $var = !$var;
+            
+            // Show here line of result
+            print '<tr '.$bc[$var].'>';
+            // LIST_OF_TD_FIELDS_LIST
+            /*
             if (! empty($arrayfields['t.field1']['checked'])) 
             {
                 print '<td>'.$obj->field1.'</td>';
@@ -418,7 +425,7 @@ if ($resql)
             {
                 print '<td>'.$obj->field2.'</td>';
     		    if (! $i) $totalarray['nbfield']++;
-            }
+            }*/
         	// Extra fields
     		if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
     		{
@@ -466,7 +473,14 @@ if ($resql)
             }*/
 
             // Action column
-            print '<td></td>';
+	        print '<td class="nowrap" align="center">';
+    	    if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+	        {
+    	        $selected=0;
+    			if (in_array($obj->rowid, $arrayofselected)) $selected=1;
+    			print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected?' checked="checked"':'').'>';
+	        }
+    	    print '</td>';
             if (! $i) $totalarray['nbfield']++;
 
             print '</tr>';
@@ -474,16 +488,59 @@ if ($resql)
         $i++;
     }
     
-    $db->free($resql);
+	// Show total line
+	if (isset($totalarray['totalhtfield']))
+	{
+	    print '<tr class="liste_total">';
+	    $i=0;
+	    while ($i < $totalarray['nbfield'])
+	    {
+	        $i++;
+	        if ($i == 1)
+	        {
+	            if ($num < $limit) print '<td align="left">'.$langs->trans("Total").'</td>';
+	            else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
+	        }
+	        elseif ($totalarray['totalhtfield'] == $i) print '<td align="right">'.price($totalarray['totalht']).'</td>';
+	        elseif ($totalarray['totalvatfield'] == $i) print '<td align="right">'.price($totalarray['totalvat']).'</td>';
+	        elseif ($totalarray['totalttcfield'] == $i) print '<td align="right">'.price($totalarray['totalttc']).'</td>';
+	        else print '<td></td>';
+	    }
+	    print '</tr>';
+	}
+    
+	$db->free($resql);
 
-	$parameters=array('sql' => $sql);
+	$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql);
 	$reshook=$hookmanager->executeHooks('printFieldListFooter',$parameters);    // Note that $action and $object may have been modified by hook
 	print $hookmanager->resPrint;
 
-	print "</table>\n";
-	print "</form>\n";
+	print '</table>'."\n";
+
+	print '</form>'."\n";
+	
+	
+	if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files)
+	{
+	    // Show list of available documents
+	    $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+	    $urlsource.=str_replace('&amp;','&',$param);
+	
+	    $filedir=$diroutputmassaction;
+	    $genallowed=$user->rights->facture->lire;
+	    $delallowed=$user->rights->facture->lire;
+	
+	    print '<br><a name="show_files"></a>';
+	    $paramwithoutshowfiles=preg_replace('/show_files=1&?/','',$param);
+	    $title=$langs->trans("MassFilesArea").' <a href="'.$_SERVER["PHP_SELF"].'?'.$paramwithoutshowfiles.'">('.$langs->trans("Hide").')</a>';
+	
+	    print $formfile->showdocuments('massfilesarea_orders','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'');
+	}
+	else
+	{
+	    print '<br><a name="show_files"></a><a href="'.$_SERVER["PHP_SELF"].'?show_files=1'.$param.'#show_files">'.$langs->trans("ShowTempMassFilesArea").'</a>';
+	}
 	
-	$db->free($result);
 }
 else
 {

BIN
doc/images/dolibarr_screenshot10_1280x800.png


BIN
doc/images/dolibarr_screenshot2_1280x800.png


BIN
doc/images/dolibarr_screenshot4_1280x800.png


BIN
doc/images/dolibarr_screenshot5_1280x800.png


BIN
doc/images/dolibarr_screenshot6_1280x800.png


BIN
doc/images/dolibarr_screenshot7_1280x800.png


BIN
doc/images/dolibarr_screenshot8_1280x800.png


BIN
doc/images/dolibarr_screenshot9_1280x800.png


BIN
doc/images/dolimed.ico


BIN
doc/images/doliwampoff.ico


BIN
doc/images/doliwampon.ico


+ 22 - 2
htdocs/accountancy/admin/card.php

@@ -58,7 +58,17 @@ if ($action == 'add') {
 		$obj = $db->fetch_object($result);
 
 		// Clean code
-		$account_number = clean_account(GETPOST('account_number')); // Accounting account without zero on the right
+
+		// To manage zero or not at the end of the accounting account
+		if($conf->global->ACCOUNTING_MANAGE_ZERO == 1)
+		{
+			$account_number = GETPOST('account_number');
+		}
+		else
+		{
+			$account_number = clean_account(GETPOST('account_number'));
+		}
+
 		if (GETPOST('account_category') <= 0) {
 			$account_parent = '';
 		} else {
@@ -98,7 +108,17 @@ if ($action == 'add') {
 		$obj = $db->fetch_object($result2);
 
 		// Clean code
-		$account_number = clean_account(GETPOST('account_number')); // Accounting account without zero on the right
+
+		// To manage zero or not at the end of the accounting account
+		if($conf->global->ACCOUNTING_MANAGE_ZERO == 1)
+		{
+			$account_number = GETPOST('account_number');
+		}
+		else
+		{
+			$account_number = clean_account(GETPOST('account_number'));
+		}
+
 		if (GETPOST('account_category') <= 0) {
 			$account_parent = '';
 		} else {

+ 28 - 0
htdocs/accountancy/admin/index.php

@@ -130,6 +130,7 @@ if ($action == 'update') {
 	}
 }
 
+// TO DO Mutualize code for yes/no constants
 if ($action == 'setlistsorttodo') {
 	$setlistsorttodo = GETPOST('value', 'int');
 	$res = dolibarr_set_const($db, "ACCOUNTING_LIST_SORT_VENTILATION_TODO", $setlistsorttodo, 'yesno', 0, '', $conf->entity);
@@ -155,6 +156,18 @@ if ($action == 'setlistsortdone') {
 	}
 }
 
+if ($action == 'setmanagezero') {
+	$setmanagezero = GETPOST('value', 'int');
+	$res = dolibarr_set_const($db, "ACCOUNTING_MANAGE_ZERO", $setmanagezero, 'yesno', 0, '', $conf->entity);
+	if (! $res > 0)
+		$error ++;
+	if (! $error) {
+		setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
+	} else {
+		setEventMessages($langs->trans("Error"), null, 'mesgs');
+	}
+}
+
 /*
  * View
  */
@@ -286,6 +299,7 @@ foreach ( $list_account as $key ) {
 	print '</td></tr>';
 }
 
+// TO DO Mutualize code for yes/no constants
 $var = ! $var;
 print "<tr " . $bc[$var] . ">";
 print '<td width="80%">' . $langs->trans("ACCOUNTING_LIST_SORT_VENTILATION_TODO") . '</td>';
@@ -314,6 +328,20 @@ if (! empty($conf->global->ACCOUNTING_LIST_SORT_VENTILATION_DONE)) {
 }
 print '</tr>';
 
+$var = ! $var;
+print "<tr " . $bc[$var] . ">";
+print '<td width="80%">' . $langs->trans("ACCOUNTING_MANAGE_ZERO") . '</td>';
+if (! empty($conf->global->ACCOUNTING_MANAGE_ZERO)) {
+	print '<td align="center" colspan="2"><a href="' . $_SERVER['PHP_SELF'] . '?action=setmanagezero&value=0">';
+	print img_picto($langs->trans("Activated"), 'switch_on');
+	print '</a></td>';
+} else {
+	print '<td align="center" colspan="2"><a href="' . $_SERVER['PHP_SELF'] . '?action=setmanagezero&value=1">';
+	print img_picto($langs->trans("Disabled"), 'switch_off');
+	print '</a></td>';
+}
+print '</tr>';
+
 print "</table>\n";
 
 dol_fiche_end();

+ 5 - 3
htdocs/adherents/fiche_subscription.php

@@ -419,14 +419,16 @@ if ($rowid && $action != 'edit')
     print $formfile->showdocuments('facture', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
     $somethingshown = $formfile->numoffiles;
     */
-    // Linked object block
-    $somethingshown = $form->showLinkedObjectBlock($object);
+	// Show links to link elements
+	//$linktoelem = $form->showLinkToObjectBlock($object, null, array('subscription'));
+    $somethingshown = $form->showLinkedObjectBlock($object, '');
     
     // Show links to link elements
     /*$linktoelem = $form->showLinkToObjectBlock($object,array('order'));
-    if ($linktoelem) print '<br>'.$linktoelem;
+	if ($linktoelem) print ($somethingshown?'':'<br>').$linktoelem;
     
     // Link for paypal payment
+    /*
     if (! empty($conf->paypal->enabled) && $object->statut != 0) {
         include_once DOL_DOCUMENT_ROOT . '/paypal/lib/paypal.lib.php';
         print showPaypalPaymentUrl('invoice', $object->ref);

+ 5 - 0
htdocs/admin/commande.php

@@ -602,6 +602,8 @@ print "</td></tr>\n";
 print '</form>';
 
 // Shippable Icon in List
+/* Kept as hidden feature for the moment, result seems bugged. 
+Whet is definition of "shippable" according to all different STOCK_CALCULATE_... options ?
 $var=!$var;
 print "<tr ".$bc[$var].">";
 print '<td>'.$langs->trans("ShippableOrderIconInList").'</td>';
@@ -617,6 +619,8 @@ if (!empty($conf->global->SHIPPABLE_ORDER_ICON_IN_LIST)) {
 print '</a></td>';
 print '</tr>';
 
+/* Seems to be not so used. So kept hidden for the moment to avoid dangerous options inflation.
+/*
 // Ask for payment bank during order
 if ($conf->banque->enabled)
 {
@@ -676,6 +680,7 @@ else
     print '<tr '.$bc[$var].'><td>';
     print $langs->trans("WAREHOUSE_ASK_WAREHOUSE_DURING_ORDER").'</td><td>&nbsp;</td><td align="center">'.$langs->trans('NotAvailable').'</td></tr>';
 }
+*/
 
 print '</table>';
 print '<br>';

+ 3 - 1
htdocs/admin/dict.php

@@ -596,7 +596,9 @@ if (GETPOST('actionadd') || GETPOST('actionmodify'))
         if ($value == 'color' && empty($_POST['color'])) continue;
 		if ($value == 'formula' && empty($_POST['formula'])) continue;
         if ((! isset($_POST[$value]) || $_POST[$value]=='')
-        	&& (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy')))  // Fields that are not mandatory
+        	&& (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy'))  // Fields that are not mandatory
+        	&& (! ($id == 10 && $listfield[$f] == 'code')) // Code is mandatory fir table 10
+        	)
 		)
         {
             $ok=0;

+ 1 - 1
htdocs/admin/fckeditor.php

@@ -56,7 +56,7 @@ $conditions = array(
 'SOCIETE' => 1,
 'PRODUCTDESC' => (! empty($conf->product->enabled) || ! empty($conf->service->enabled)),
 'MAILING' => ! empty($conf->mailing->enabled),
-'DETAILS' => (! empty($conf->facture->enabled) || ! empty($conf->propal->enabled) || ! empty($conf->commande->enabled)),
+'DETAILS' => (! empty($conf->facture->enabled) || ! empty($conf->propal->enabled) || ! empty($conf->commande->enabled) || ! empty($conf->supplier_proposal->enabled) || ! empty($conf->fournisseur->enabled)),
 'USERSIGN' => 1,
 'MAIL' => (! empty($conf->facture->enabled) || ! empty($conf->propal->enabled) || ! empty($conf->commande->enabled))
 );

+ 5 - 1
htdocs/admin/ihm.php

@@ -78,8 +78,12 @@ if ($action == 'update')
 
 	$val=(implode(',',(colorStringToArray(GETPOST('THEME_ELDY_BACKBODY'),array()))));
 	if ($val == '') dolibarr_del_const($db, 'THEME_ELDY_BACKBODY', $conf->entity);
-    else dolibarr_set_const($db, 'THEME_ELDY_BACKBODY', implode(',',colorStringToArray(GETPOST('THEME_ELDY_BACKBODY'),array())),'chaine',0,'',$conf->entity);
+	else dolibarr_set_const($db, 'THEME_ELDY_BACKBODY', implode(',',colorStringToArray(GETPOST('THEME_ELDY_BACKBODY'),array())),'chaine',0,'',$conf->entity);
 	
+	$val=GETPOST('THEME_TOPMENU_DISABLE_IMAGE');
+	if (! $val) dolibarr_del_const($db, 'THEME_TOPMENU_DISABLE_IMAGE', $conf->entity);
+    else dolibarr_set_const($db, 'THEME_TOPMENU_DISABLE_IMAGE', GETPOST('THEME_TOPMENU_DISABLE_IMAGE'),'chaine',0,'',$conf->entity);
+    
     $val=(implode(',',(colorStringToArray(GETPOST('THEME_ELDY_TOPMENU_BACK1'),array()))));
 	if ($val == '') dolibarr_del_const($db, 'THEME_ELDY_TOPMENU_BACK1', $conf->entity);
     else dolibarr_set_const($db, 'THEME_ELDY_TOPMENU_BACK1', implode(',',colorStringToArray(GETPOST('THEME_ELDY_TOPMENU_BACK1'),array())),'chaine',0,'',$conf->entity);

+ 3 - 3
htdocs/admin/menus/edit.php

@@ -370,7 +370,7 @@ if ($action == 'create')
     }
     else
     {
-        print '<td><input type="text" size="40" id="menuId" name="menuId" value="'.($_POST["menuId"]?$_POST["menuId"]:'').'"></td>';
+        print '<td><input type="text" size="48" 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';
@@ -458,7 +458,7 @@ elseif ($action == 'edit')
     $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="menuIdParent" value="'.$valtouse.'" size="40"></td>';
+    print '<td><input type="text" name="menuIdParent" value="'.$valtouse.'" size="48"></td>';
     print '<td>'.$langs->trans('DetailMenuIdParent');
     print ', '.$langs->trans("Example").': fk_mainmenu=abc&fk_leftmenu=def';
     print '</td></tr>';
@@ -470,7 +470,7 @@ elseif ($action == 'edit')
     print '<tr><td class="fieldrequired">'.$langs->trans('Title').'</td><td><input type="text" size="30" name="titre" value="'.$menu->titre.'"></td><td>'.$langs->trans('DetailTitre').'</td></tr>';
 
     // Url
-    print '<tr><td class="fieldrequired">'.$langs->trans('URL').'</td><td><input type="text" size="60" name="url" value="'.$menu->url.'"></td><td>'.$langs->trans('DetailUrl').'</td></tr>';
+    print '<tr><td class="fieldrequired">'.$langs->trans('URL').'</td><td><input type="text" class="quatrevingtpercent" name="url" value="'.$menu->url.'"></td><td>'.$langs->trans('DetailUrl').'</td></tr>';
 
     // Langs
     print '<tr><td>'.$langs->trans('LangFile').'</td><td><input type="text" size="30" name="langs" value="'.$menu->langs.'"></td><td>'.$langs->trans('DetailLangs').'</td></tr>';

+ 1 - 1
htdocs/admin/prelevement.php

@@ -141,7 +141,7 @@ print '</td></tr>';
 // ICS
 print '<tr class="pair"><td class="fieldrequired">'.$langs->trans("ICS").'</td>';
 print '<td align="left">';
-print '<input type="text" name="PRELEVEMENT_ICS" value="'.$conf->global->PRELEVEMENT_ICS.'" size="9" ></td>';
+print '<input type="text" name="PRELEVEMENT_ICS" value="'.$conf->global->PRELEVEMENT_ICS.'" size="15" ></td>';
 print '</td></tr>';
 
 //User

+ 2 - 0
htdocs/admin/propal.php

@@ -606,6 +606,7 @@ print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">'
 print "</td></tr>\n";
 print '</form>';
 
+/* Seems to be not so used. So kept hidden for the moment to avoid dangerous options inflation.
 if ($conf->banque->enabled)
 {
     $var=!$var;
@@ -634,6 +635,7 @@ else
     print '<tr '.$bc[$var].'><td>';
     print $langs->trans("BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL").'</td><td>&nbsp;</td><td align="center">'.$langs->trans('NotAvailable').'</td></tr>';
 }
+*/
 
 print '</table>';
 

+ 2 - 2
htdocs/admin/security_file.php

@@ -176,7 +176,7 @@ if (ini_get('safe_mode') && ! empty($conf->global->MAIN_ANTIVIRUS_COMMAND))
         dol_syslog("safe_mode is on, basedir is ".$basedir.", safe_mode_exec_dir is ".ini_get('safe_mode_exec_dir'), LOG_WARNING);
     }
 }
-print '<input type="text" name="MAIN_ANTIVIRUS_COMMAND" size="72" value="'.(! empty($conf->global->MAIN_ANTIVIRUS_COMMAND)?dol_htmlentities($conf->global->MAIN_ANTIVIRUS_COMMAND):'').'">';
+print '<input type="text" name="MAIN_ANTIVIRUS_COMMAND" class="minwidth500imp" value="'.(! empty($conf->global->MAIN_ANTIVIRUS_COMMAND)?dol_htmlentities($conf->global->MAIN_ANTIVIRUS_COMMAND):'').'">';
 print "</td>";
 print '</tr>';
 
@@ -187,7 +187,7 @@ print '<td colspan="2">'.$langs->trans("AntiVirusParam").'<br>';
 print $langs->trans("AntiVirusParamExample");
 print '</td>';
 print '<td>';
-print '<input type="text" name="MAIN_ANTIVIRUS_PARAM" size="72" value="'.(! empty($conf->global->MAIN_ANTIVIRUS_PARAM)?dol_htmlentities($conf->global->MAIN_ANTIVIRUS_PARAM):'').'">';
+print '<input type="text" name="MAIN_ANTIVIRUS_PARAM" class="minwidth500imp" value="'.(! empty($conf->global->MAIN_ANTIVIRUS_PARAM)?dol_htmlentities($conf->global->MAIN_ANTIVIRUS_PARAM):'').'">';
 print "</td>";
 print '</tr>';
 

+ 42 - 11
htdocs/admin/stock.php

@@ -73,12 +73,22 @@ if($action)
 	|| $action == 'STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER'
 	|| $action == 'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')
 	{
+		//Use variable cause empty(GETPOST()) do not work with php version < 5.4
+		$valdispatch=GETPOST('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER','alpha');
+
 		$res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_BILL", '','chaine',0,'',$conf->entity);
 		$res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER", '','chaine',0,'',$conf->entity);
 		$res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER", '','chaine',0,'',$conf->entity);
 		if ($action == 'STOCK_CALCULATE_ON_SUPPLIER_BILL')           $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_BILL", GETPOST('STOCK_CALCULATE_ON_SUPPLIER_BILL','alpha'),'chaine',0,'',$conf->entity);
 		if ($action == 'STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER", GETPOST('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER','alpha'),'chaine',0,'',$conf->entity);
-		if ($action == 'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER", GETPOST('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER','alpha'),'chaine',0,'',$conf->entity);
+		if ($action == 'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER", $valdispatch,'chaine',0,'',$conf->entity);
+		if (empty($valdispatch)) {
+			$res=dolibarr_set_const($db, "SUPPLIER_ORDER_USE_DISPATCH_STATUS", '','chaine',0,'',$conf->entity);
+		}
+	}
+
+	if($action == 'SUPPLIER_ORDER_USE_DISPATCH_STATUS') {
+		$res = dolibarr_set_const($db, "SUPPLIER_ORDER_USE_DISPATCH_STATUS", GETPOST('SUPPLIER_ORDER_USE_DISPATCH_STATUS','alpha'),'chaine',0,'',$conf->entity);
 	}
 
 	if($action == 'STOCK_USE_VIRTUAL_STOCK') {
@@ -163,7 +173,7 @@ if (! empty($conf->facture->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module30Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module30Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -183,7 +193,7 @@ if (! empty($conf->commande->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module25Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module25Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -205,7 +215,7 @@ if (! empty($conf->expedition->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -225,7 +235,7 @@ if (! empty($conf->expedition->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -267,7 +277,7 @@ if (! empty($conf->fournisseur->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -288,7 +298,7 @@ if (! empty($conf->fournisseur->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name"));
 }
 print "</td>\n</tr>\n";
 $found++;
@@ -308,9 +318,30 @@ if (! empty($conf->fournisseur->enabled))
 }
 else
 {
-    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); 
+    print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name"));
 }
 print "</td>\n</tr>\n";
+
+if (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
+	$var=!$var;
+	print "<tr ".$bc[$var].">";
+	print '<td width="60%">'.$langs->trans("UseDispatchStatus").'</td>';
+	print '<td width="160" align="right">';
+	if (! empty($conf->fournisseur->enabled))
+	{
+		print "<form method=\"post\" action=\"stock.php\">";
+		print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+		print "<input type=\"hidden\" name=\"action\" value=\"SUPPLIER_ORDER_USE_DISPATCH_STATUS\">";
+		print $form->selectyesno("SUPPLIER_ORDER_USE_DISPATCH_STATUS",$conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS,1,$disabled);
+		print '<input type="submit" class="button" value="'.$langs->trans("Modify").'"'.$disabled.'>';
+		print "</form>\n";
+	}
+	else
+	{
+		print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name"));
+	}
+	print "</td>\n</tr>\n";
+}
 $found++;
 
 /*if (! $found)
@@ -446,13 +477,13 @@ print '<br>';
 
 /* I keep the option/feature, but hidden to end users for the moment. If feature is used by module, no need to have users see it.
 If not used by a module, I still need to understand in which case user may need this now we can set rule on product page.
-if ($conf->global->PRODUIT_SOUSPRODUITS) 
+if ($conf->global->PRODUIT_SOUSPRODUITS)
 {
 	$var=!$var;
-	
+
 	print "<tr ".$bc[$var].">";
 	print '<td width="60%">'.$langs->trans("IndependantSubProductStock").'</td>';
-	
+
 	print '<td width="160" align="right">';
 	print "<form method=\"post\" action=\"stock.php\">";
 	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';

+ 8 - 8
htdocs/admin/supplier_order.php

@@ -169,11 +169,11 @@ else if ($action == 'addcat')
 else if ($action == 'set_SUPPLIER_ORDER_OTHER')
 {
     $freetext = GETPOST('SUPPLIER_ORDER_FREE_TEXT');	// No alpha here, we want exact string
-	$doubleapproval = GETPOST('SUPPLIER_ORDER_DOUBLE_APPROVAL','alpha');
+	$doubleapproval = GETPOST('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED','alpha');
 	$doubleapproval = price2num($doubleapproval );
 
     $res1 = dolibarr_set_const($db, "SUPPLIER_ORDER_FREE_TEXT",$freetext,'chaine',0,'',$conf->entity);
-    $res2 = dolibarr_set_const($db, "SUPPLIER_ORDER_DOUBLE_APPROVAL",$doubleapproval,'chaine',0,'',$conf->entity);
+    $res2 = dolibarr_set_const($db, "SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED",$doubleapproval,'chaine',0,'',$conf->entity);
 
     // TODO We add/delete permission here until permission can have a condition on a global var
     include_once DOL_DOCUMENT_ROOT.'/core/modules/modFournisseur.class.php';
@@ -189,7 +189,7 @@ else if ($action == 'set_SUPPLIER_ORDER_OTHER')
 	$newmodule->rights[$r][4] = 'commande';
 	$newmodule->rights[$r][5] = 'approve2';
 
-    if ($conf->global->SUPPLIER_ORDER_DOUBLE_APPROVAL)
+    if ($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED)
     {
     	$newmodule->insert_permissions(1);
     }
@@ -474,18 +474,18 @@ print '<td align="center" width="60">'.$langs->trans("Value").'</td>';
 print '<td width="80">&nbsp;</td>';
 print "</tr>\n";
 $var=false;
-if ($conf->global->MAIN_FEATURES_LEVEL > 0)
-{
+//if ($conf->global->MAIN_FEATURES_LEVEL > 0)
+//{
 	print '<tr '.$bc[$var].'><td>';
-	print $langs->trans("UseDoubleApproval").'<br>';
+	print $form->textwithpicto($langs->trans("UseDoubleApproval"), $langs->trans("Use3StepsApproval"), 1, 'help').'<br>';
 	print $langs->trans("IfSetToYesDontForgetPermission");
 	print '</td><td>';
-	print '<input type="text" size="3" name="SUPPLIER_ORDER_DOUBLE_APPROVAL" value="'.$conf->global->SUPPLIER_ORDER_DOUBLE_APPROVAL.'">';
+	print '<input type="text" size="3" name="SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED" value="'.$conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED.'">';
 	print '</td><td align="right">';
 	print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
 	print "</td></tr>\n";
 	$var=!$var;
-}
+//}
 
 print '<tr '.$bc[$var].'><td colspan="2">';
 print $langs->trans("FreeLegalTextOnOrders").' ('.$langs->trans("AddCRIfTooLong").')<br>';

+ 1 - 1
htdocs/admin/supplier_payment.php

@@ -94,7 +94,7 @@ dol_fiche_head($head, 'supplierpayment', $langs->trans("Suppliers"), 0, 'company
 
 if (empty($conf->global->SUPPLIER_PAYMENT_ADDON)) $conf->global->SUPPLIER_PAYMENT_ADDON = 'mod_supplier_payment_bronan';
     
-print load_fiche_titre($langs->trans("PaymentsNumberingModule"));
+print load_fiche_titre($langs->trans("PaymentsNumberingModule"), '', '');
 
 print '<table class="noborder" width="100%">';
 print '<tr class="liste_titre">';

+ 3 - 2
htdocs/admin/workflow.php

@@ -76,6 +76,7 @@ $workflowcodes=array(
 	'WORKFLOW_PROPAL_AUTOCREATE_ORDER'=>array('family'=>'create', 'position'=>10, 'enabled'=>'! empty($conf->propal->enabled) && ! empty($conf->commande->enabled)', 'picto'=>'order'),
 	'WORKFLOW_ORDER_AUTOCREATE_INVOICE'=>array('family'=>'create', 'position'=>20, 'enabled'=>'! empty($conf->commande->enabled) && ! empty($conf->facture->enabled)', 'picto'=>'bill'),
 	'WORKFLOW_ORDER_CLASSIFY_BILLED_PROPAL'=>array('family'=>'classify', 'position'=>30, 'enabled'=>'! empty($conf->propal->enabled) && ! empty($conf->commande->enabled)', 'picto'=>'order','warning'=>'WarningCloseAlways'),
+	'WORKFLOW_INVOICE_CLASSIFY_BILLED_PROPAL'=>array('family'=>'classify', 'position'=>30, 'enabled'=>'! empty($conf->propal->enabled) && ! empty($conf->facture->enabled)', 'picto'=>'order','warning'=>'WarningCloseAlways'),
 	// For the following 2 options, if module invoice is disabled, they does not exists, so "Classify billed" for order must be done manually from order card.
 	'WORKFLOW_INVOICE_CLASSIFY_BILLED_ORDER'=>array('family'=>'classify', 'position'=>40, 'enabled'=>'! empty($conf->facture->enabled) && ! empty($conf->commande->enabled)', 'picto'=>'bill','warning'=>'WarningCloseAlways'),
 	'WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_ORDER'=>array('family'=>'classify', 'position'=>50, 'enabled'=>'! empty($conf->facture->enabled) && ! empty($conf->commande->enabled)', 'picto'=>'bill','warning'=>'WarningCloseAlways'),
@@ -110,8 +111,8 @@ foreach($workflowcodes as $key => $params)
 		print '  <td align="center">'.$langs->trans("Status").'</td>';
 		print "</tr>\n";
 		$oldfamily = $family;
-   	}   	
-   	
+   	}
+
    	$var = !$var;
    	print "<tr ".$bc[$var].">\n";
    	print "<td>".img_object('', $picto).$langs->trans('desc'.$key);

+ 3 - 3
htdocs/api/admin/explorer.php

@@ -59,7 +59,7 @@ foreach ($modulesdir as $dir)
     /*
      * Search available module
      */
-    dol_syslog("Scan directory ".$dir." for API modules");
+    //dol_syslog("Scan directory ".$dir." for API modules");
 
     $handle=@opendir(dol_osencode($dir));
     if (is_resource($handle))
@@ -111,7 +111,7 @@ foreach ($modulesdir as $dir)
                                 require_once $dir_part.$file_searched;
                                 if (class_exists($classname))
                                 {
-                                    dol_syslog("Found deprecated API classname=".$classname);
+                                    dol_syslog("Found deprecated API classname=".$classname." into ".$dir);
                                     $api->r->addAPIClass($classname, '');
                                 }
                             }
@@ -121,7 +121,7 @@ foreach ($modulesdir as $dir)
                                 require_once $dir_part.$file_searched;
                                 if (class_exists($classname))
                                 {
-                                    dol_syslog("Found API classname=".$classname);
+                                    dol_syslog("Found API classname=".$classname." into ".$dir);
                                     $listofapis[] = $classname;
                                 }
                             }                                

+ 8 - 3
htdocs/api/class/api.class.php

@@ -86,8 +86,11 @@ class DolibarrApi
     function _cleanObjectDatas($object) {
 
         // Remove $db object property for object
-		unset($object->db);
-
+        unset($object->db);
+        
+        // Remove linkedObjects. We should already have linkedObjectIds that avoid huge responses
+        unset($object->linkedObjects);
+        
         // Remove the $oldcopy property because it is not supported by the JSON
         // encoder. The following error is generated when trying to serialize
         // it: "Error encoding/decoding JSON: Type is not supported"
@@ -109,13 +112,15 @@ class DolibarrApi
         }
 
         // If object has linked objects, remove $db property
+        /*
         if(isset($object->linkedObjects) && count($object->linkedObjects) > 0)  {
             foreach($object->linkedObjects as $type_object => $linked_object) {
                 foreach($linked_object as $object2clean) {
                     $this->_cleanObjectDatas($object2clean);
                 }
             }
-        }
+        }*/
+        
 		return $object;
     }
 

+ 3 - 3
htdocs/api/index.php

@@ -73,7 +73,7 @@ foreach ($modulesdir as $dir)
     /*
      * Search available module
      */
-    dol_syslog("Scan directory ".$dir." for API modules");
+    //dol_syslog("Scan directory ".$dir." for API modules");
 
     $handle=@opendir(dol_osencode($dir));
     if (is_resource($handle))
@@ -119,7 +119,7 @@ foreach ($modulesdir as $dir)
                                 require_once $dir_part.$file_searched;
                                 if (class_exists($classname))
                                 {
-                                    dol_syslog("Found deprecated API classname=".$classname);
+                                    dol_syslog("Found deprecated API classname=".$classname." into ".$dir);
                                     $api->r->addAPIClass($classname, '');
                                 }
                             }
@@ -129,7 +129,7 @@ foreach ($modulesdir as $dir)
                                 require_once $dir_part.$file_searched;
                                 if (class_exists($classname))
                                 {
-                                    dol_syslog("Found API classname=".$classname);
+                                    dol_syslog("Found API classname=".$classname." into ".$dir);
                                     $listofapis[] = $classname;
                                 }
                             }

+ 1 - 1
htdocs/bookmarks/bookmarks.lib.php

@@ -72,7 +72,7 @@ function printBookmarksList($aDb, $aLangs)
 			while ($i < $conf->global->BOOKMARKS_SHOW_IN_MENU && $obj = $db->fetch_object($resql))
 			{
 				$ret.='<div class="menu_contenu"><a class="vsmenu" title="'.$obj->title.'" href="'.$obj->url.'"'.($obj->target == 1?' target="_blank"':'').'>';
-				$ret.=' '.img_object('','bookmark').' ';
+				if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $ret.=' '.img_object('','bookmark').' ';
 				$ret.= dol_trunc($obj->title, 20).'</a><br></div>';
 				$i++;
 			}

+ 7 - 2
htdocs/categories/index.php

@@ -1,7 +1,7 @@
 <?php
 /* Copyright (C) 2005       Matthieu Valleton   <mv@seeschloss.org>
  * Copyright (C) 2005       Eric Seigne         <eric.seigne@ryxeo.com>
- * Copyright (C) 2006-2015  Laurent Destailleur <eldy@users.sourceforge.net>
+ * Copyright (C) 2006-2016  Laurent Destailleur <eldy@users.sourceforge.net>
  * Copyright (C) 2007       Patrick Raguin      <patrick.raguin@gmail.com>
  * Copyright (C) 2005-2012  Regis Houssin       <regis.houssin@capnetworks.com>
  * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
@@ -118,9 +118,14 @@ if ($catname || $id > 0)
 		$categstatic->ref=$cat->label;
 		$categstatic->label=$cat->label;
 		$categstatic->type=$cat->type;
+		$categstatic->color=$cat->color;
+		print '<span class="noborderoncategories" '.($categstatic->color?' style="background: #'.$categstatic->color.';"':' style="background: #aaa"').'>';
 		print $categstatic->getNomUrl(1,'');
+		print '</span>';
+		print "</td>\n";
+		print "\t\t<td>";
+		print $cat->description;
 		print "</td>\n";
-		print "\t\t<td>".$cat->description."</td>\n";
 		print "\t</tr>\n";
 	}
 	print "</table>";

+ 3 - 3
htdocs/comm/card.php

@@ -377,7 +377,7 @@ if ($id > 0)
 		print '<a href="'.DOL_URL_ROOT.'/comm/remise.php?id='.$object->id.'">'.img_edit($langs->trans("Modify")).'</a>';
 	}
 	print '</td></tr></table>';
-	print '</td><td colspan="3">'.($object->remise_percent?'<a href="'.DOL_URL_ROOT.'/comm/remise.php?id='.$object->id.'">'.$object->remise_percent.'%</a>':$langs->trans("DiscountNone")).'</td>';
+	print '</td><td colspan="3">'.($object->remise_percent?'<a href="'.DOL_URL_ROOT.'/comm/remise.php?id='.$object->id.'">'.$object->remise_percent.'%</a>':'').'</td>';
 	print '</tr>';
 
 	// Absolute discounts (Discounts-Drawbacks-Rebates)
@@ -396,7 +396,7 @@ if ($id > 0)
 	$amount_discount=$object->getAvailableDiscounts();
 	if ($amount_discount < 0) dol_print_error($db,$object->error);
 	if ($amount_discount > 0) print '<a href="'.DOL_URL_ROOT.'/comm/remx.php?id='.$object->id.'&backtopage='.urlencode($_SERVER["PHP_SELF"].'?socid='.$object->id).'">'.price($amount_discount,1,$langs,1,-1,-1,$conf->currency).'</a>';
-	else print $langs->trans("DiscountNone");
+	//else print $langs->trans("DiscountNone");
 	print '</td>';
 	print '</tr>';
 
@@ -408,7 +408,7 @@ if ($id > 0)
 		print '</td><td colspan="3">';
 		$limit_field_type = (! empty($conf->global->MAIN_USE_JQUERY_JEDITABLE)) ? 'numeric' : 'amount';
 		print $form->editfieldval("OutstandingBill",'outstanding_limit',$object->outstanding_limit,$object,$user->rights->societe->creer,$limit_field_type,($object->outstanding_limit != '' ? price($object->outstanding_limit) : ''));
-		if (empty($object->outstanding_limit)) print $langs->trans("NoLimit");
+		//if (empty($object->outstanding_limit)) print $langs->trans("NoLimit");
 		
 		print '</td>';
 		print '</tr>';

+ 1 - 1
htdocs/comm/mailing/card.php

@@ -876,7 +876,7 @@ else
 			{
 				print "\n\n<div class=\"tabsAction\">\n";
 
-				if (($object->statut == 0) && $user->rights->mailing->creer)
+				if (($object->statut == 0 || $object->statut == 1) && $user->rights->mailing->creer)
 				{
 					print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&amp;id='.$object->id.'">'.$langs->trans("EditMailing").'</a>';
 				}

+ 1 - 0
htdocs/comm/mailing/class/mailing.class.php

@@ -79,6 +79,7 @@ class Mailing extends CommonObject
 		$this->statuts[2] = 'MailingStatusSentPartialy';
 		$this->statuts[3] = 'MailingStatusSentCompletely';
 
+		$this->statut_dest[0] = 'MailingStatusNotSent';
 		$this->statut_dest[-1] = 'MailingStatusError';
 		$this->statut_dest[1] = 'MailingStatusSent';
 		$this->statut_dest[2] = 'MailingStatusRead';

+ 20 - 2
htdocs/comm/mailing/list.php

@@ -45,6 +45,19 @@ $sall=GETPOST("sall","alpha");
 $sref=GETPOST("sref","alpha");
 $filteremail=GETPOST('filteremail','alpha');
 
+// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array
+$hookmanager->initHooks(array('mailinglist'));
+$extrafields = new ExtraFields($db);
+
+// fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('mailing');
+$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+
+// List of fields to search into when doing a "search in all"
+$fieldstosearchall = array(
+    'm.titre'=>'Ref',
+);
+
 
 
 /*
@@ -107,6 +120,7 @@ if ($result)
 	if (! $filteremail) print_liste_field_titre($langs->trans("DateLastSend"),$_SERVER["PHP_SELF"],"m.date_envoi",$param,"",'align="center"',$sortfield,$sortorder);
 	else print_liste_field_titre($langs->trans("DateSending"),$_SERVER["PHP_SELF"],"mc.date_envoi",$param,"",'align="center"',$sortfield,$sortorder);
 	print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],($filteremail?"mc.statut":"m.statut"),$param,"",'align="right"',$sortfield,$sortorder);
+	print_liste_field_titre('', $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch ');
 	print "</tr>\n";
 
 	print '<tr class="liste_titre">';
@@ -120,8 +134,11 @@ if ($result)
 	print '<td class="liste_titre">&nbsp;</td>';
 	if (! $filteremail) print '<td class="liste_titre">&nbsp;</td>';
 	print '<td class="liste_titre">&nbsp;</td>';
-	print '<td class="liste_titre" align="right"><input class="liste_titre" type="image" src="'.img_picto($langs->trans("Search"),'search.png','','',1).'" value="'.dol_escape_htmltag($langs->trans("Search")).'" title="'.dol_escape_htmltag($langs->trans("Search")).'">';
-	print "</td>";
+	print '<td class="liste_titre">&nbsp;</td>';
+	print '<td class="liste_titre" align="right">';
+	$searchpitco=$form->showFilterAndCheckAddButtons(0);
+	print $searchpitco;
+	print '</td>';
 	print "</tr>\n";
 
 	$var=True;
@@ -172,6 +189,7 @@ if ($result)
 			print $email->LibStatut($obj->statut,5);
 		}
 		print '</td>';
+		print '<td></td>';
 		print "</tr>\n";
 		$i++;
 	}

+ 10 - 13
htdocs/comm/propal/card.php

@@ -5,7 +5,7 @@
  * Copyright (C) 2005      Marc Barilley / Ocebo <marc@ocebo.com>
  * Copyright (C) 2005-2012 Regis Houssin         <regis.houssin@capnetworks.com>
  * Copyright (C) 2006      Andre Cianfarani      <acianfa@free.fr>
- * Copyright (C) 2010-2014 Juanjo Menent         <jmenent@2byte.es>
+ * Copyright (C) 2010-2016 Juanjo Menent         <jmenent@2byte.es>
  * Copyright (C) 2010-2015 Philippe Grand        <philippe.grand@atoo-net.com>
  * Copyright (C) 2012-2013 Christophe Battarel   <christophe.battarel@altairis.fr>
  * Copyright (C) 2012      Cedric Salvador       <csalvador@gpcsolutions.fr>
@@ -1356,7 +1356,7 @@ if ($action == 'create')
 	print '</td></tr>';
 
     // Bank Account
-    if (! empty($conf->global->BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL) && $conf->banque->enabled) {
+    if (! empty($conf->global->BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL) && ! empty($conf->banque->enabled)) {
         print '<tr><td>' . $langs->trans('BankAccount') . '</td><td colspan="2">';
         $form->select_comptes($fk_account, 'fk_account', 0, '', 1);
         print '</td></tr>';
@@ -1717,7 +1717,7 @@ if ($action == 'create')
 	print '</tr></table>';
 	print '</td><td colspan="5">';
 	if ($user->rights->propal->creer && $action == 'refclient') {
-		print '<form action="propal.php?id=' . $object->id . '" method="post">';
+		print '<form action="'.$_SERVER["PHP_SELF"].'?id=' . $object->id . '" method="post">';
 		print '<input type="hidden" name="token" value="' . $_SESSION ['newtoken'] . '">';
 		print '<input type="hidden" name="action" value="set_ref_client">';
 		print '<input type="text" class="flat" size="20" name="ref_client" value="' . $object->ref_client . '">';
@@ -1836,9 +1836,9 @@ if ($action == 'create')
 	// Delivery date
 	$langs->load('deliveries');
 	print '<tr><td>';
-	print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->date_livraison, $object, $user->rights->propal->creer);
+	print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->date_livraison, $object, $user->rights->propal->creer, 'datepicker');
 	print '</td><td colspan="5">';
-	print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->date_livraison, $object, $user->rights->propal->creer, 'day');
+	print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->date_livraison, $object, $user->rights->propal->creer, 'datepicker');
 	print '</td>';
 	print '</tr>';
 
@@ -1939,7 +1939,7 @@ if ($action == 'create')
 		print '<tr>';
 		print '<td width="25%">';
 		print '<table class="nobordernopadding" width="100%"><tr><td>';
-		print fieldLabel('Rate','multicurrency_tx');
+		print fieldLabel('CurrencyRate','multicurrency_tx');
 		print '</td>';
 		if ($action != 'editmulticurrencyrate' && ! empty($object->brouillon))
 			print '<td align="right"><a href="' . $_SERVER["PHP_SELF"] . '?action=editmulticurrencyrate&amp;id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetMultiCurrencyCode'), 1) . '</a></td>';
@@ -2001,7 +2001,7 @@ if ($action == 'create')
 		print '</tr>';
 	}
 
-	if (! empty($conf->global->BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL) && $conf->banque->enabled)
+	if (! empty($conf->global->BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL) && ! empty($conf->banque->enabled))
 	{
 	    // Bank Account
 	    print '<tr><td>';
@@ -2320,13 +2320,10 @@ if ($action == 'create')
 
 		$somethingshown = $formfile->show_documents('propal', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', 0, '', $soc->default_lang);
 
-		// Linked object block
-		$somethingshown = $form->showLinkedObjectBlock($object);
-
 		// Show links to link elements
-		$linktoelem = $form->showLinkToObjectBlock($object);
-		if ($linktoelem) print '<br>'.$linktoelem;
-
+		$linktoelem = $form->showLinkToObjectBlock($object, null, array('propal'));
+		$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
+		
 
 		print '</div><div class="fichehalfright"><div class="ficheaddleft">';
 		// print '</td><td valign="top" width="50%">';

+ 204 - 54
htdocs/comm/propal/list.php

@@ -51,6 +51,12 @@ $langs->load('products');
 
 $socid=GETPOST('socid','int');
 
+$action=GETPOST('action','alpha');
+$massaction=GETPOST('massaction','alpha');
+$show_files=GETPOST('show_files','int');
+$confirm=GETPOST('confirm','alpha');
+$toselect = GETPOST('toselect', 'array');
+
 $search_user=GETPOST('search_user','int');
 $search_sale=GETPOST('search_sale','int');
 $search_ref=GETPOST('sf_ref')?GETPOST('sf_ref','alpha'):GETPOST('search_ref','alpha');
@@ -159,7 +165,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
  */
 
 if (GETPOST('cancel')) { $action='list'; $massaction=''; }
-if (! GETPOST('confirmmassaction')) { $massaction=''; }
+if (! GETPOST('confirmmassaction') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; }
 
 $parameters=array('socid'=>$socid);
 $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action);    // Note that $action and $object may have been modified by some hooks
@@ -192,28 +198,19 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP
     $day='';
 	$viewstatut='';
 	$object_statut='';
+	$toselect='';
     $search_array_options=array();
 }
 if ($object_statut != '') $viewstatut=$object_statut;
 
 if (empty($reshook))
 {
-    // Mass actions. Controls on number of lines checked
-    $maxformassaction=1000;
-    if (! empty($massaction) && count($toselect) < 1)
-    {
-        $error++;
-        setEventMessages($langs->trans("NoLineChecked"), null, "warnings");
-    }
-    if (! $error && count($toselect) > $maxformassaction)
-    {
-        setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors');
-        $error++;
-    }
-
-
-    
-    
+    $objectclass='Propal';
+    $objectlabel='Proposals';
+    $permtoread = $user->rights->propal->lire;
+    $permtodelete = $user->rights->propal->supprimer;
+    $uploaddir = $conf->propal->dir_output;
+	include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
 }
 
 
@@ -222,7 +219,7 @@ if (empty($reshook))
  * View
  */
 
-llxHeader('',$langs->trans('Proposal'),'EN:Commercial_Proposals|FR:Proposition_commerciale|ES:Presupuestos');
+$now=dol_now();
 
 $form = new Form($db);
 $formother = new FormOther($db);
@@ -231,14 +228,15 @@ $formpropal = new FormPropal($db);
 $companystatic=new Societe($db);
 $formcompany=new FormCompany($db);
 
-$now=dol_now();
+$help_url='EN:Commercial_Proposals|FR:Proposition_commerciale|ES:Presupuestos';
+llxHeader('',$langs->trans('Proposal'),$help_url);
 
 $sql = 'SELECT';
 if ($sall || $search_product_category > 0) $sql = 'SELECT DISTINCT';
-$sql.= ' s.rowid, s.nom as name, s.town, s.zip, s.fk_pays, s.client, s.code_client, ';
+$sql.= ' s.rowid as socid, s.nom as name, s.town, s.zip, s.fk_pays, s.client, s.code_client, ';
 $sql.= " typent.code as typent_code,";
 $sql.= " state.code_departement as state_code, state.nom as state_name,";
-$sql.= ' p.rowid as propalid, p.note_private, p.total_ht, p.tva as total_vat, p.total as total_ttc, p.localtax1, p.localtax2, p.ref, p.ref_client, p.fk_statut, p.fk_user_author, p.datep as dp, p.fin_validite as dfv,';
+$sql.= ' p.rowid, p.note_private, p.total_ht, p.tva as total_vat, p.total as total_ttc, p.localtax1, p.localtax2, p.ref, p.ref_client, p.fk_statut, p.fk_user_author, p.datep as dp, p.fin_validite as dfv,';
 $sql.= ' p.datec as date_creation, p.tms as date_update,';
 if (! $user->rights->societe->client->voir && ! $socid) $sql .= " sc.fk_soc, sc.fk_user,";
 $sql.= ' u.login';
@@ -340,19 +338,27 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
 
 $sql.= $db->plimit($limit+1, $offset);
 
-$result=$db->query($sql);
-if ($result)
+$resql=$db->query($sql);
+if ($resql)
 {
 	$objectstatic=new Propal($db);
 	$userstatic=new User($db);
-	$num = $db->num_rows($result);
 
- 	if ($socid)
+ 	if ($socid > 0)
 	{
 		$soc = new Societe($db);
-		 $soc->fetch($socid);
+		$soc->fetch($socid);
+		$title = $langs->trans('ListOfProposals') . ' - '.$soc->name;
 	}
+	else
+	{
+	    $title = $langs->trans('ListOfProposals');
+	}	
 
+	$num = $db->num_rows($resql);
+	
+	$arrayofselected=is_array($toselect)?$toselect:array();
+	
 	$param='&socid='.$socid.'&viewstatut='.$viewstatut;
     if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage;
 	if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
@@ -375,10 +381,17 @@ if ($result)
 	    if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val);
 	}
 	
-	//$massactionbutton=$form->selectMassAction('', $massaction == 'presend' ? array() : array('presend'=>$langs->trans("SendByMail"), 'builddoc'=>$langs->trans("PDFMerge")));
+	// List of mass actions available
+	$arrayofmassactions =  array(
+	    'presend'=>$langs->trans("SendByMail"),
+	    'builddoc'=>$langs->trans("PDFMerge"),
+	);
+	if ($user->rights->propal->supprimer) $arrayofmassactions['delete']=$langs->trans("Delete");
+	if ($massaction == 'presend') $arrayofmassactions=array();
+	$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
 	
 	// Lignes des champs de filtre
-	print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
+	print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
     if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
 	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 	print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
@@ -386,12 +399,107 @@ if ($result)
 	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
 	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
 
-	print_barre_liste($langs->trans('ListOfProposals').' '.($socid?'- '.$soc->name:''), $page, $_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords,'title_commercial.png', 0, '', '', $limit);
+	print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, '', '', $limit);
+	
+	if ($massaction == 'presend')
+	{
+	    $langs->load("mails");
+	
+	    if (! GETPOST('cancel'))
+	    {
+	        $objecttmp=new Propal($db);
+	        $listofselectedid=array();
+	        $listofselectedthirdparties=array();
+	        $listofselectedref=array();
+	        foreach($arrayofselected as $toselectid)
+	        {
+	            $result=$objecttmp->fetch($toselectid);
+	            if ($result > 0)
+	            {
+	                $listofselectedid[$toselectid]=$toselectid;
+	                $thirdpartyid=$objecttmp->fk_soc?$objecttmp->fk_soc:$objecttmp->socid;
+	                $listofselectedthirdparties[$thirdpartyid]=$thirdpartyid;
+	                $listofselectedref[$thirdpartyid][$toselectid]=$objecttmp->ref;
+	            }
+	        }
+	    }
+	
+	    print '<input type="hidden" name="massaction" value="confirm_presend">';
+	
+	    include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
+	    $formmail = new FormMail($db);
+	
+	    dol_fiche_head(null, '', '');
+	
+	    $topicmail="SendProposalRef";
+	    $modelmail="propal_send";
+	
+	    // Cree l'objet formulaire mail
+	    include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
+	    $formmail = new FormMail($db);
+	    $formmail->withform=-1;
+	    $formmail->fromtype = 'user';
+	    $formmail->fromid   = $user->id;
+	    $formmail->fromname = $user->getFullName($langs);
+	    $formmail->frommail = $user->email;
+	    if (! empty($conf->global->MAIN_EMAIL_ADD_TRACK_ID) && ($conf->global->MAIN_EMAIL_ADD_TRACK_ID & 1))	// If bit 1 is set
+	    {
+	        $formmail->trackid='ord'.$object->id;
+	    }
+	    if (! empty($conf->global->MAIN_EMAIL_ADD_TRACK_ID) && ($conf->global->MAIN_EMAIL_ADD_TRACK_ID & 2))	// If bit 2 is set
+	    {
+	        include DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+	        $formmail->frommail=dolAddEmailTrackId($formmail->frommail, 'ord'.$object->id);
+	    }
+	    $formmail->withfrom=1;
+	    $liste=$langs->trans("AllRecipientSelected");
+	    if (count($listofselectedthirdparties) == 1)
+	    {
+	        $liste=array();
+	        $thirdpartyid=array_shift($listofselectedthirdparties);
+	        $soc=new Societe($db);
+	        $soc->fetch($thirdpartyid);
+	        foreach ($soc->thirdparty_and_contact_email_array(1) as $key=>$value)
+	        {
+	            $liste[$key]=$value;
+	        }
+	        $formmail->withtoreadonly=0;
+	    }
+	    else
+	    {
+	        $formmail->withtoreadonly=1;
+	    }
+	    $formmail->withto=$liste;
+	    $formmail->withtofree=0;
+	    $formmail->withtocc=1;
+	    $formmail->withtoccc=$conf->global->MAIN_EMAIL_USECCC;
+	    $formmail->withtopic=$langs->transnoentities($topicmail, '__REF__', '__REFCLIENT__');
+	    $formmail->withfile=$langs->trans("OnlyPDFattachmentSupported");
+	    $formmail->withbody=1;
+	    $formmail->withdeliveryreceipt=1;
+	    $formmail->withcancel=1;
+	    // Tableau des substitutions
+	    $formmail->substit['__REF__']='__REF__';	// We want to keep the tag
+	    $formmail->substit['__SIGNATURE__']=$user->signature;
+	    $formmail->substit['__REFCLIENT__']='__REFCLIENT__';	// We want to keep the tag
+	    $formmail->substit['__PERSONALIZED__']='';
+	    $formmail->substit['__CONTACTCIVNAME__']='';
+	
+	    // Tableau des parametres complementaires du post
+	    $formmail->param['action']=$action;
+	    $formmail->param['models']=$modelmail;
+	    $formmail->param['models_id']=GETPOST('modelmailselected','int');
+	    $formmail->param['id']=join(',',$arrayofselected);
+	    //$formmail->param['returnurl']=$_SERVER["PHP_SELF"].'?id='.$object->id;
+	
+	    print $formmail->get_form();
+	
+	    dol_fiche_end();
+	}
 	
 	if ($sall)
     {
         foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);
-        //sort($fieldstosearchall);
         print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall);
     }
 	
@@ -426,6 +534,11 @@ if ($result)
 		$moreforfilter.=$form->selectarray('search_product_category', $cate_arbo, $search_product_category, 1, 0, 0, '', 0, 0, 0, 0, '', 1);
 		$moreforfilter.='</div>';
 	}
+	$parameters=array();
+	$reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters);    // Note that $action and $object may have been modified by hook
+	if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
+	else $moreforfilter = $hookmanager->resPrint;
+	
 	if (! empty($moreforfilter))
 	{
         print '<div class="liste_titre liste_titre_bydiv centpercent">';
@@ -438,6 +551,7 @@ if ($result)
 	
 	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 	
+	// Fields title
 	print '<tr class="liste_titre">';
 	if (! empty($arrayfields['p.ref']['checked']))            print_liste_field_titre($arrayfields['p.ref']['label'],$_SERVER["PHP_SELF"],'p.ref','',$param,'',$sortfield,$sortorder);
 	if (! empty($arrayfields['p.ref_client']['checked']))     print_liste_field_titre($arrayfields['p.ref_client']['label'],$_SERVER["PHP_SELF"],'p.ref_client','',$param,'',$sortfield,$sortorder);
@@ -473,8 +587,8 @@ if ($result)
 	if (! empty($arrayfields['p.tms']['checked']))       print_liste_field_titre($arrayfields['p.tms']['label'],$_SERVER["PHP_SELF"],"p.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder);
 	if (! empty($arrayfields['p.fk_statut']['checked'])) print_liste_field_titre($arrayfields['p.fk_statut']['label'],$_SERVER["PHP_SELF"],"p.fk_statut","",$param,'align="right"',$sortfield,$sortorder);
 	print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch ');
-	print "</tr>\n";
-
+	print '</tr>'."\n";
+	
 	print '<tr class="liste_titre">';
 	if (! empty($arrayfields['p.ref']['checked']))            
 	{
@@ -610,7 +724,7 @@ if ($result)
 	}
 	// Action column
 	print '<td class="liste_titre" align="middle">';
-	$searchpitco=$form->showFilterAndCheckAddButtons(0);
+	$searchpitco=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1);
 	print $searchpitco;
 	print '</td>';
 	
@@ -622,37 +736,44 @@ if ($result)
 	$totalarray=array();
 	while ($i < min($num,$limit))
 	{
-		$obj = $db->fetch_object($result);
+		$obj = $db->fetch_object($resql);
 		$var=!$var;
+		
+    	$objectstatic->id=$obj->rowid;
+    	$objectstatic->ref=$obj->ref;
+    		
 		print '<tr '.$bc[$var].'>';
 		
 		if (! empty($arrayfields['p.ref']['checked']))
 		{
     		print '<td class="nowrap">';
     
-    		$objectstatic->id=$obj->propalid;
-    		$objectstatic->ref=$obj->ref;
-    
     		print '<table class="nobordernopadding"><tr class="nocellnopadd">';
+            // Picto + Ref
     		print '<td class="nobordernopadding nowrap">';
     		print $objectstatic->getNomUrl(1);
     		print '</td>';
-    
-    		print '<td style="min-width: 20px" class="nobordernopadding nowrap">';
-    		if ($obj->fk_statut == 1 && $db->jdate($obj->dfv) < ($now - $conf->propal->cloture->warning_delay)) print img_warning($langs->trans("Late"));
+            // Warning
+            $warnornote='';
+    		if ($obj->fk_statut == 1 && $db->jdate($obj->dfv) < ($now - $conf->propal->cloture->warning_delay)) $warnornote.=img_warning($langs->trans("Late"));
     		if (! empty($obj->note_private))
     		{
-    			print ' <span class="note">';
-    			print '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$obj->propalid.'">'.img_picto($langs->trans("ViewPrivateNote"),'object_generic').'</a>';
-    			print '</span>';
+    			$warnornote.=($warnornote?' ':'');
+    			$warnornote.= '<span class="note">';
+    			$warnornote.= '<a href="note.php?id='.$obj->rowid.'">'.img_picto($langs->trans("ViewPrivateNote"),'object_generic').'</a>';
+    			$warnornote.= '</span>';
     		}
-    		print '</td>';
-    
-    		// Ref
+    		if ($warnornote)
+    		{
+    			print '<td style="min-width: 20px" class="nobordernopadding nowrap">';
+    			print $warnornote;
+    			print '</td>';
+    		}
+    		// Other picto tool
     		print '<td width="16" align="right" class="nobordernopadding hideonsmartphone">';
     		$filename=dol_sanitizeFileName($obj->ref);
     		$filedir=$conf->propal->dir_output . '/' . dol_sanitizeFileName($obj->ref);
-    		$urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->propalid;
+    		$urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->rowid;
     		print $formfile->getDocumentsLink($objectstatic->element, $filename, $filedir);
     		print '</td></tr></table>';
     
@@ -669,9 +790,7 @@ if ($result)
     		if (! $i) $totalarray['nbfield']++;
 		}
 		
-		$url = DOL_URL_ROOT.'/comm/card.php?socid='.$obj->rowid;
-
-		$companystatic->id=$obj->rowid;
+		$companystatic->id=$obj->socid;
 		$companystatic->name=$obj->name;
 		$companystatic->client=$obj->client;
 		$companystatic->code_client=$obj->code_client;
@@ -833,7 +952,14 @@ if ($result)
             if (! $i) $totalarray['nbfield']++;
         }
         // Action column
-        print '<td></td>';
+        print '<td class="nowrap" align="center">';
+        if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+        {
+            $selected=0;
+    		if (in_array($obj->rowid, $arrayofselected)) $selected=1;
+    		print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected?' checked="checked"':'').'>';
+        }
+        print '</td>';
         if (! $i) $totalarray['nbfield']++;
 
 		print "</tr>\n";
@@ -863,15 +989,39 @@ if ($result)
 		
 	}
 
-	$db->free($result);
+	$db->free($resql);
 	
 	$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql);
 	$reshook=$hookmanager->executeHooks('printFieldListFooter',$parameters);    // Note that $action and $object may have been modified by hook
 	print $hookmanager->resPrint;
 				
-	print '</table>';
+	print '</table>'."\n";
 
-	print '</form>';
+	print '</form>'."\n";
+	
+	if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files)
+	{
+	    /*
+	     * Show list of available documents
+	     */
+	    $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+	    $urlsource.=str_replace('&amp;','&',$param);
+	
+	    $filedir=$diroutputmassaction;
+	    $genallowed=$user->rights->propal->lire;
+	    $delallowed=$user->rights->propal->lire;
+	
+	    print '<br><a name="show_files"></a>';
+	    $paramwithoutshowfiles=preg_replace('/show_files=1&?/','',$param);
+	    $title=$langs->trans("MassFilesArea").' <a href="'.$_SERVER["PHP_SELF"].'?'.$paramwithoutshowfiles.'">('.$langs->trans("Hide").')</a>';
+	
+	    print $formfile->showdocuments('massfilesarea_proposals','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'');
+	}
+	else
+	{
+	    print '<br><a name="show_files"></a><a href="'.$_SERVER["PHP_SELF"].'?show_files=1'.$param.'#show_files">'.$langs->trans("ShowTempMassFilesArea").'</a>';
+	}
+	
 }
 else
 {

+ 5 - 32
htdocs/commande/card.php

@@ -376,32 +376,7 @@ if (empty($reshook))
 							$error++;
 						}
 
-						// Now we create same links to contact than the ones found on origin object
-						if (! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN))
-						{
-						    $originforcontact = $object->origin;
-						    $originidforcontact = $object->origin_id;
-						    if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
-						    {
-						        $originforcontact=$srcobject->origin;
-						        $originidforcontact=$srcobject->origin_id;
-						    }
-						    $sqlcontact = "SELECT code, fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
-						    $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
-						    	
-						    $resqlcontact = $db->query($sqlcontact);
-						    if ($resqlcontact)
-						    {
-						        while($objcontact = $db->fetch_object($resqlcontact))
-						        {
-						            //print $objcontact->code.'-'.$objcontact->fk_socpeople."\n";
-						            $object->add_contact($objcontact->fk_socpeople, $objcontact->code);
-						        }
-						    }
-						    else dol_print_error($resqlcontact);
-						}
-							
-						// Hooks
+    					// Hooks
 						$parameters = array('objFrom' => $srcobject);
 						$reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
 						// modified by hook
@@ -2208,7 +2183,7 @@ if ($action == 'create' && $user->rights->commande->creer)
 			print '<tr>';
 			print '<td width="25%">';
 			print '<table class="nobordernopadding" width="100%"><tr><td>';
-			print fieldLabel('Rate','multicurrency_tx');
+			print fieldLabel('CurrencyRate','multicurrency_tx');
 			print '</td>';
 			if ($action != 'editmulticurrencyrate' && ! empty($object->brouillon))
 				print '<td align="right"><a href="' . $_SERVER["PHP_SELF"] . '?action=editmulticurrencyrate&amp;id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetMultiCurrencyCode'), 1) . '</a></td>';
@@ -2610,13 +2585,11 @@ if ($action == 'create' && $user->rights->commande->creer)
 			$delallowed = $user->rights->commande->supprimer;
 			$somethingshown = $formfile->show_documents('commande', $comref, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
 
-			// Linked object block
-			$somethingshown = $form->showLinkedObjectBlock($object);
 
 			// Show links to link elements
-			//$linktoelem = $form->showLinkToObjectBlock($object);
-			//if ($linktoelem) print '<br>'.$linktoelem;
-
+			$linktoelem = $form->showLinkToObjectBlock($object, null, array('order'));
+			$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
+			
 
 			print '</div><div class="fichehalfright"><div class="ficheaddleft">';
 

+ 1 - 0
htdocs/commande/class/api_deprecated_commande.class.php

@@ -160,6 +160,7 @@ class CommandeApi extends DolibarrApi
         
         if ($result)
         {
+        	$i=0;
             $num = $db->num_rows($result);
             while ($i < $num)
             {

+ 8 - 7
htdocs/commande/class/api_orders.class.php

@@ -156,7 +156,7 @@ class Orders extends DolibarrApi
             throw new RestException(503, 'Error when retrieve commande list');
         }
         if( ! count($obj_ret)) {
-            throw new RestException(404, 'No commande found');
+            throw new RestException(404, 'No order found');
         }
 		return $obj_ret;
     }
@@ -178,15 +178,16 @@ class Orders extends DolibarrApi
         foreach($request_data as $field => $value) {
             $this->commande->$field = $value;
         }
-        if (isset($request_data["lines"])) {
+        /*if (isset($request_data["lines"])) {
           $lines = array();
           foreach ($request_data["lines"] as $line) {
             array_push($lines, (object) $line);
           }
           $this->commande->lines = $lines;
-        }
-        if(! $this->commande->create(DolibarrApiAccess::$user) ) {
-            throw new RestException(500, "Error while creating order");
+        }*/
+        if ($this->commande->create(DolibarrApiAccess::$user) <= 0) {
+            $errormsg = $this->commande->error;
+            throw new RestException(500, $errormsg ? $errormsg : "Error while creating order");
         }
         
         return $this->commande->id;
@@ -208,7 +209,7 @@ class Orders extends DolibarrApi
         
       $result = $this->commande->fetch($id);
       if( ! $result ) {
-         throw new RestException(404, 'Commande not found');
+         throw new RestException(404, 'Order not found');
       }
 		
 		  if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {
@@ -239,7 +240,7 @@ class Orders extends DolibarrApi
         
       $result = $this->commande->fetch($id);
       if( ! $result ) {
-         throw new RestException(404, 'Commande not found');
+         throw new RestException(404, 'Order not found');
       }
 		
 		  if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {

+ 90 - 76
htdocs/commande/class/commande.class.php

@@ -693,8 +693,8 @@ class Commande extends CommonOrder
      *	Note that this->ref can be set or empty. If empty, we will use "(PROV)"
      *
      *	@param		User	$user 		Objet user that make creation
-     *	@param		int	$notrigger	Disable all triggers
-     *	@return 	int			<0 if KO, >0 if OK
+     *	@param		int	    $notrigger	Disable all triggers
+     *	@return 	int			        <0 if KO, >0 if OK
      */
     function create($user, $notrigger=0)
     {
@@ -808,37 +808,43 @@ class Commande extends CommonOrder
                  */
                 for ($i=0;$i<$num;$i++)
                 {
+                	$line = $this->lines[$i];
+                	
+                	// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
+				    //if (! is_object($line)) $line=json_decode(json_encode($line), FALSE);  // convert recursively array into object.
+                	if (! is_object($line)) $line = (object) $line;
+                	 
                     // Reset fk_parent_line for no child products and special product
-                    if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
+                    if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
                         $fk_parent_line = 0;
                     }
 
                     $result = $this->addline(
-                        $this->lines[$i]->desc,
-                        $this->lines[$i]->subprice,
-                        $this->lines[$i]->qty,
-                        $this->lines[$i]->tva_tx,
-                        $this->lines[$i]->localtax1_tx,
-                        $this->lines[$i]->localtax2_tx,
-                        $this->lines[$i]->fk_product,
-                        $this->lines[$i]->remise_percent,
-                        $this->lines[$i]->info_bits,
-                        $this->lines[$i]->fk_remise_except,
+                        $line->desc,
+                        $line->subprice,
+                        $line->qty,
+                        $line->tva_tx,
+                        $line->localtax1_tx,
+                        $line->localtax2_tx,
+                        $line->fk_product,
+                        $line->remise_percent,
+                        $line->info_bits,
+                        $line->fk_remise_except,
                         'HT',
                         0,
-                        $this->lines[$i]->date_start,
-                        $this->lines[$i]->date_end,
-                        $this->lines[$i]->product_type,
-                        $this->lines[$i]->rang,
-                        $this->lines[$i]->special_code,
+                        $line->date_start,
+                        $line->date_end,
+                        $line->product_type,
+                        $line->rang,
+                        $line->special_code,
                         $fk_parent_line,
-                        $this->lines[$i]->fk_fournprice,
-                        $this->lines[$i]->pa_ht,
-                    	$this->lines[$i]->label,
-                    	$this->lines[$i]->array_options,
-	                    $this->lines[$i]->fk_unit,
+                        $line->fk_fournprice,
+                        $line->pa_ht,
+                    	$line->label,
+                    	$line->array_options,
+	                    $line->fk_unit,
                         $this->element,
-                        $this->lines[$i]->id
+                        $line->id
                     );
                     if ($result < 0)
                     {
@@ -851,7 +857,7 @@ class Commande extends CommonOrder
                         return -1;
                     }
                     // Defined the new fk_parent_line
-                    if ($result > 0 && $this->lines[$i]->product_type == 9) {
+                    if ($result > 0 && $line->product_type == 9) {
                         $fk_parent_line = $result;
                     }
                 }
@@ -879,30 +885,40 @@ class Commande extends CommonOrder
                         			$error++;
                         		}
 
-                        		// TODO mutualiser
-                        		if ($origin == 'propal' && $origin_id)
+                        		if (! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN))
                         		{
-                        			// On recupere les differents contact interne et externe
-                        			$prop = new Propal($this->db);
-									$prop->fetch($origin_id);
-
-                        			// We get ids of sales representatives of proposal
-                        			$this->userid = $prop->getIdcontact('internal', 'SALESREPFOLL');
-
-                        			if ($this->userid)
-                        			{
-                        				//On passe le commercial suivi propale en commercial suivi commande
-                        				$this->add_contact($this->userid[0], 'SALESREPFOLL', 'internal');
-                        			}
-
-                        			// We get ids of customer follower of proposal
-                        			$this->contactid = $prop->getIdcontact('external', 'CUSTOMER');
-
-                        			if ($this->contactid)
-                        			{
-                        				//On passe le contact client suivi propale en contact client suivi commande
-                        				$this->add_contact($this->contactid[0], 'CUSTOMER', 'external');
-                        			}
+                        		    $originforcontact = $origin;
+                        		    $originidforcontact = $origin_id;
+                        		    if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
+                        		    {
+                        		        require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
+                        		        $exp = new Expedition($db);
+                        		        $exp->fetch($origin_id);
+                        		        $exp->fetchObjectLinked();
+                        		        if (count($exp->linkedObjectsIds['commande']) > 0)
+                        		        {
+                        		            foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
+                        		            {
+                        		                $originforcontact = 'commande';
+                        		                $originidforcontact = $value->id;
+                        		                break; // We take first one
+                        		            }
+                        		        }
+                        		    }
+                        		    	
+                        		    $sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
+                        		    $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
+                        		    	
+                        		    $resqlcontact = $this->db->query($sqlcontact);
+                        		    if ($resqlcontact)
+                        		    {
+                        		        while($objcontact = $this->db->fetch_object($resqlcontact))
+                        		        {
+                					        //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
+                        		            $this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source);    // May failed because of duplicate key or because code of contact type does not exists for new object
+                        		        }
+                        		    }
+                        		    else dol_print_error($resqlcontact);
                         		}
                         	}
                         }
@@ -910,22 +926,8 @@ class Commande extends CommonOrder
 
                     if (! $error)
                     {
-                    	//$action='create';
-
-	                    // Actions on extra fields (by external module or standard code)
-	                    // TODO le hook fait double emploi avec le trigger !!
-	                    /*$hookmanager->initHooks(array('orderdao'));
-	                    $parameters=array('socid'=>$this->id);
-	                    $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
-	                    if (empty($reshook))
-	                    {
-	                    	if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
-	                    	{*/
-	                    		$result=$this->insertExtraFields();
-	                    		if ($result < 0) $error++;
-	                    /*	}
-	                    }
-	                    else if ($reshook < 0) $error++;*/
+                   		$result=$this->insertExtraFields();
+                   		if ($result < 0) $error++;
                     }
 
                     if (! $error && ! $notrigger)
@@ -3327,7 +3329,7 @@ class Commande extends CommonOrder
      */
     function getNomUrl($withpicto=0,$option=0,$max=0,$short=0)
     {
-        global $conf, $langs;
+        global $conf, $langs, $user;
 
         $result='';
 
@@ -3337,17 +3339,29 @@ class Commande extends CommonOrder
         if ($short) return $url;
 
         $picto = 'order';
-        $label = '<u>' . $langs->trans("ShowOrder") . '</u>';
-        if (! empty($this->ref))
-            $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
-        if (! empty($this->ref_client))
-            $label.= '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_client;
-        if (! empty($this->total_ht))
-            $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
-        if (! empty($this->total_tva))
-            $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
-        if (! empty($this->total_ttc))
-            $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
+        $label = '';
+
+		if ($user->rights->commande->lire) {
+			$label = '<u>'.$langs->trans("ShowOrder").'</u>';
+			if (!empty($this->ref)) {
+				$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
+			}
+			if (!empty($this->ref_client)) {
+				$label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
+			}
+			if (!empty($this->total_ht)) {
+				$label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1,
+						$conf->currency);
+			}
+			if (!empty($this->total_tva)) {
+				$label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,
+						$conf->currency);
+			}
+			if (!empty($this->total_ttc)) {
+				$label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1,
+						$conf->currency);
+			}
+		}
 
         $linkstart = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
         $linkend='</a>';

+ 16 - 11
htdocs/commande/list.php

@@ -163,7 +163,7 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e
 include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
 
 // Purge search criteria
-if (GETPOST("button_removefilter_x") || 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_categ='';
     $search_user='';
@@ -196,6 +196,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP
 if (empty($reshook))
 {
     $objectclass='Commande';
+    $objectlabel='Orders';
     $permtoread = $user->rights->commande->lire;
     $permtodelete = $user->rights->commande->supprimer;
     $uploaddir = $conf->commande->dir_output;
@@ -337,6 +338,7 @@ $sql.=$hookmanager->resPrint;
 
 $sql.= $db->order($sortfield,$sortorder);
 
+// Count total nb of records
 $nbtotalofrecords = 0;
 if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
 {
@@ -410,6 +412,7 @@ if ($resql)
 	    if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val);
 	}
 	
+	// List of mass actions available
 	$arrayofmassactions =  array(
 	    'presend'=>$langs->trans("SendByMail"),
 	    'builddoc'=>$langs->trans("PDFMerge"),
@@ -419,7 +422,7 @@ if ($resql)
 	$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
 
 	// Lignes des champs de filtre
-	print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
+	print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
     if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
 	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 	print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
@@ -561,10 +564,11 @@ if ($resql)
 		$moreforfilter.=$form->selectarray('search_product_category', $cate_arbo, $search_product_category, 1, 0, 0, '', 0, 0, 0, 0, '', 1);
 		$moreforfilter.='</div>';
 	}
-    	$parameters=array();
-    	$reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters);    // Note that $action and $object may have been modified by hook
+	$parameters=array();
+	$reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters);    // Note that $action and $object may have been modified by hook
 	if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
 	else $moreforfilter = $hookmanager->resPrint;
+	
 	if (! empty($moreforfilter))
 	{
 		print '<div class="liste_titre liste_titre_bydiv centpercent">';
@@ -577,6 +581,7 @@ if ($resql)
 	
 	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 
+	// Fields title
 	print '<tr class="liste_titre">';
 	if (! empty($arrayfields['c.ref']['checked']))            print_liste_field_titre($arrayfields['c.ref']['label'],$_SERVER["PHP_SELF"],'c.ref','',$param,'',$sortfield,$sortorder);
 	if (! empty($arrayfields['c.ref_client']['checked']))     print_liste_field_titre($arrayfields['c.ref_client']['label'],$_SERVER["PHP_SELF"],'c.ref_client','',$param,'',$sortfield,$sortorder);
@@ -612,7 +617,7 @@ if ($resql)
 	if (! empty($arrayfields['c.fk_statut']['checked'])) print_liste_field_titre($arrayfields['c.fk_statut']['label'],$_SERVER["PHP_SELF"],"c.fk_statut","",$param,'align="right"',$sortfield,$sortorder);
 	if (! empty($arrayfields['c.facture']['checked']))   print_liste_field_titre($arrayfields['c.facture']['label'],$_SERVER["PHP_SELF"],'c.facture','',$param,'align="center"',$sortfield,$sortorder,'');
 	print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch ');
-	print '</tr>';
+	print '</tr>'."\n";
 
 	print '<tr class="liste_titre">';
 	// Ref
@@ -1115,8 +1120,8 @@ if ($resql)
         print '</td>';
         if (! $i) $totalarray['nbfield']++;
 		
-		print '</tr>';
-
+		print "</tr>\n";
+        
 		$total+=$obj->total_ht;
 		$subtotal+=$obj->total_ht;
 		$i++;
@@ -1149,7 +1154,7 @@ if ($resql)
 	$reshook=$hookmanager->executeHooks('printFieldListFooter',$parameters);    // Note that $action and $object may have been modified by hook
 	print $hookmanager->resPrint;
 				
-	print '</table>';
+	print '</table>'."\n";
 
 	print '</form>'."\n";
 
@@ -1164,8 +1169,8 @@ if ($resql)
 	    $urlsource.=str_replace('&amp;','&',$param);
 	
 	    $filedir=$diroutputmassaction;
-	    $genallowed=$user->rights->facture->lire;
-	    $delallowed=$user->rights->facture->lire;
+	    $genallowed=$user->rights->commande->lire;
+	    $delallowed=$user->rights->commande->lire;
 	
 	    print '<br><a name="show_files"></a>';
 	    $paramwithoutshowfiles=preg_replace('/show_files=1&?/','',$param);
@@ -1184,6 +1189,6 @@ else
 	dol_print_error($db);
 }
 
+// End of page
 llxFooter();
-
 $db->close();

+ 1 - 1
htdocs/commande/orderstoinvoice.php

@@ -665,7 +665,7 @@ if (($action != 'create' && $action != 'add') || ($action == 'create' && $error)
 
 			print '<table class="nobordernopadding"><tr class="nocellnopadd">';
 			print '<td class="nobordernopadding nowrap">';
-			print $generic_commande->getNomUrl(1,$objp->fk_statut);
+			print $generic_commande->getNomUrl(1,0);
 			print '</td>';
 
 			print '<td width="20" class="nobordernopadding nowrap">';

+ 2 - 2
htdocs/compta/bank/account.php

@@ -734,7 +734,7 @@ if ($id > 0 || ! empty($ref))
 						{
 							$banklinestatic->fetch($links[$key]['url_id']);
 							$bankstatic->id=$banklinestatic->fk_account;
-							$bankstatic->label=$banklinestatic->bank_account_label;
+							$bankstatic->label=$banklinestatic->bank_account_ref;
 							print ' ('.$langs->trans("TransferFrom").' ';
 							print $bankstatic->getNomUrl(1,'transactions');
 							print ' '.$langs->trans("toward").' ';
@@ -752,7 +752,7 @@ if ($id > 0 || ! empty($ref))
 							print ' '.$langs->trans("toward").' ';
 							$banklinestatic->fetch($links[$key]['url_id']);
 							$bankstatic->id=$banklinestatic->fk_account;
-							$bankstatic->label=$banklinestatic->bank_account_label;
+							$bankstatic->label=$banklinestatic->bank_account_ref;
 							print $bankstatic->getNomUrl(1,'transactions');
 							print ')';
 						}

+ 5 - 5
htdocs/compta/bank/class/account.class.php

@@ -1298,9 +1298,9 @@ class Account extends CommonObject
     }
 
     /**
-     * Return 1 is IBAN is need for UE country
+     * Return 1 if IBAN / BIC is mandatory (otherwise option)
      *
-     * @return		int        1 yes / 0 No
+     * @return		int        1 = mandatory / 0 = Not mandatory
      */
     function needIBAN()
     {
@@ -1342,7 +1342,7 @@ class Account extends CommonObject
     			//'CH',	// Switzerland - No. Swizerland in not in EEC
     	);
 
-    	if (in_array($country_code,$country_code_in_EEC)) return 1; // France, Spain, Gabon, ...
+    	if (in_array($country_code,$country_code_in_EEC)) return 1; // France, Spain, ...
     	return 0;
     }
 
@@ -1388,10 +1388,10 @@ class Account extends CommonObject
 			$fieldarray=self::getAccountNumberOrder();
 		}
 
-		if ($this->needIBAN()) {
+		//if ($this->needIBAN()) {    // return always IBAN and BIC (this was old behaviour)
 			$fieldarray[]='IBAN';
 			$fieldarray[]='BIC';
-		}
+		//}
 
 		//Get the order the properties are shown
 		return $fieldarray;

+ 1 - 1
htdocs/compta/bank/ligne.php

@@ -320,7 +320,7 @@ if ($result)
                     print '</a>';
                 }
                 else if ($links[$key]['type']=='sc') {
-                    print '<a href="'.DOL_URL_ROOT.'/compta/sociales/charges.php?id='.$links[$key]['url_id'].'">';
+                    print '<a href="'.DOL_URL_ROOT.'/compta/sociales/card.php?id='.$links[$key]['url_id'].'">';
                     print img_object($langs->trans('ShowSocialContribution'),'bill').' ';
                     print $langs->trans("SocialContribution").($links[$key]['label']?' - '.$links[$key]['label']:'');
                     print '</a>';

+ 1 - 1
htdocs/compta/bank/releve.php

@@ -469,7 +469,7 @@ else
 					$newline=0;
 				}
 				elseif ($links[$key]['type']=='sc') {
-					print '<a href="'.DOL_URL_ROOT.'/compta/sociales/charges.php?id='.$links[$key]['url_id'].'">';
+					print '<a href="'.DOL_URL_ROOT.'/compta/sociales/card.php?id='.$links[$key]['url_id'].'">';
 					print img_object($langs->trans('ShowBill'),'bill').' ';
 					print $langs->trans("SocialContribution");
 					print '</a>';

+ 1 - 1
htdocs/compta/bank/virement.php

@@ -152,7 +152,7 @@ if($error)
 	$amount = GETPOST('amount','int');
 }
 
-print load_fiche_titre($langs->trans("BankTransfer"), '', 'title_bank.png');
+print load_fiche_titre($langs->trans("MenuBankInternalTransfer"), '', 'title_bank.png');
 
 print $langs->trans("TransferDesc");
 print "<br><br>";

+ 12 - 99
htdocs/compta/facture.php

@@ -134,14 +134,6 @@ if (empty($reshook))
 
 	include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php';	// Must be include, not include_once
 
-	// Link invoice to order
-	if (GETPOST('linkedOrder') && empty($cancel) && $id > 0)
-	{
-	    $object->fetch($id);
-	    $object->fetch_thirdparty();
-	    $result = $object->add_object_linked('commande', GETPOST('linkedOrder'));
-	}
-	
 	// Action clone object
 	if ($action == 'confirm_clone' && $confirm == 'yes' && $user->rights->facture->creer) {
 	//	if (1 == 0 && empty($_REQUEST["clone_content"]) && empty($_REQUEST["clone_receivers"])) {
@@ -864,7 +856,7 @@ if (empty($reshook))
 			}
 		}
 
-		// Standard invoice or Deposit invoice created from a Predefined template invoice
+		// Standard invoice or Deposit invoice, created from a Predefined template invoice
 		if (($_POST['type'] == Facture::TYPE_STANDARD || $_POST['type'] == Facture::TYPE_DEPOSIT) && GETPOST('fac_rec') > 0)
 		{
 			$dateinvoice = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']);
@@ -904,6 +896,7 @@ if (empty($reshook))
 				$object->fac_rec = GETPOST('fac_rec');
 
 				$id = $object->create($user);
+				var_dump('ggg');
 			}
 		}
 
@@ -1011,8 +1004,8 @@ if (empty($reshook))
 						$object->linked_objects = array_merge($object->linked_objects, $_POST['other_linked_objects']);
 					}
 
-					$id = $object->create($user);
-
+					$id = $object->create($user);      // This include class to add_object_linked() and add add_contact()
+					
 					if ($id > 0)
 					{
 						dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
@@ -1183,32 +1176,7 @@ if (empty($reshook))
 								$error ++;
 							}
 						}
-
-						// Now we create same links to contact than the ones found on origin object
-						if (! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN))
-						{
-    						$originforcontact = $object->origin;
-    						$originidforcontact = $object->origin_id;
-    						if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
-    						{
-    						    $originforcontact=$srcobject->origin;
-    						    $originidforcontact=$srcobject->origin_id;
-    						}
-    						$sqlcontact = "SELECT code, fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
-    						$sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
-
-    						$resqlcontact = $db->query($sqlcontact);
-    						if ($resqlcontact)
-    						{
-                                while($objcontact = $db->fetch_object($resqlcontact))
-                                {
-                                    //print $objcontact->code.'-'.$objcontact->fk_socpeople."\n";
-                                    $object->add_contact($objcontact->fk_socpeople, $objcontact->code);
-                                }
-    						}
-    						else dol_print_error($resqlcontact);					
-						}
-						
+												
 						// Hooks
 						$parameters = array('objFrom' => $srcobject);
 						$reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
@@ -1887,7 +1855,7 @@ if (! empty($conf->projet->enabled)) { $formproject = new FormProjets($db); }
 
 $now = dol_now();
 
-llxHeader('', $langs->trans('Bill'), 'EN:Customers_Invoices|FR:Factures_Clients|ES:Facturas_a_clientes');
+llxHeader('', $langs->trans('InvoiceCustomer'), 'EN:Customers_Invoices|FR:Factures_Clients|ES:Facturas_a_clientes');
 
 
 // Mode creation
@@ -2544,58 +2512,6 @@ if ($action == 'create')
 			print '<tr><td>' . $langs->trans('MulticurrencyTotalTTC') . '</td><td colspan="2">' . price($objectsrc->multicurrency_total_ttc) . "</td></tr>";	
 		}
 	}
-	else
-	{
-		// Show deprecated optional form to add product line here
-		if (! empty($conf->global->PRODUCT_SHOW_WHEN_CREATE)) {
-			print '<tr><td colspan="3">';
-
-			// Zone de choix des produits predefinis a la creation
-			print '<table class="noborder" width="100%">';
-			print '<tr>';
-			print '<td>' . $langs->trans('ProductsAndServices') . '</td>';
-			print '<td>' . $langs->trans('Qty') . '</td>';
-			print '<td>' . $langs->trans('ReductionShort') . '</td>';
-			print '<td> &nbsp; &nbsp; </td>';
-			if (! empty($conf->service->enabled)) {
-				print '<td>' . $langs->trans('ServiceLimitedDuration') . '</td>';
-			}
-			print '</tr>';
-			for($i = 1; $i <= $NBLINES; $i ++) {
-				print '<tr>';
-				print '<td>';
-				// multiprix
-				if (! empty($conf->global->PRODUIT_MULTIPRICES))
-					$form->select_produits('', 'idprod' . $i, '', $conf->product->limit_size, $soc->price_level);
-				else
-					$form->select_produits('', 'idprod' . $i, '', $conf->product->limit_size);
-				print '</td>';
-				print '<td><input type="text" size="2" name="qty' . $i . '" value="1"></td>';
-				print '<td class="nowrap"><input type="text" size="1" name="remise_percent' . $i . '" value="' . $soc->remise_percent . '">%</td>';
-				print '<td>&nbsp;</td>';
-				// Si le module service est actif, on propose des dates de debut et fin a la ligne
-				if (! empty($conf->service->enabled)) {
-					print '<td class="nowrap">';
-					print '<table class="nobordernopadding"><tr class="nocellnopadd">';
-					print '<td class="nobordernopadding nowrap">';
-					print $langs->trans('From') . ' ';
-					print '</td><td class="nobordernopadding nowrap">';
-					print $form->select_date('', 'date_start' . $i, $usehm, $usehm, 1, "add", 1, 0, 1);
-					print '</td></tr>';
-					print '<td class="nobordernopadding nowrap">';
-					print $langs->trans('to') . ' ';
-					print '</td><td class="nobordernopadding nowrap">';
-					print $form->select_date('', 'date_end' . $i, $usehm, $usehm, 1, "add", 1, 0, 1);
-					print '</td></tr></table>';
-					print '</td>';
-				}
-				print "</tr>\n";
-			}
-
-			print '</table>';
-			print '</td></tr>';
-		}
-	}
 
 	print "</table>\n";
 
@@ -2931,7 +2847,7 @@ else if ($id > 0 || ! empty($ref))
 	$linkback = '<a href="' . DOL_URL_ROOT . '/compta/facture/list.php' . (! empty($socid) ? '?socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
 
 	// Ref
-	print '<tr><td width="20%">' . $langs->trans('Ref') . '</td>';
+	print '<tr><td class="titlefield">' . $langs->trans('Ref') . '</td>';
 	print '<td colspan="5">';
 	$morehtmlright = '';
 	$discount = new DiscountAbsolute($db);
@@ -2946,7 +2862,7 @@ else if ($id > 0 || ! empty($ref))
 	print '</td></tr>';
 
 	// Ref customer
-	print '<tr><td width="20%">';
+	print '<tr><td>';
 	print '<table class="nobordernopadding" width="100%"><tr><td>';
 	print $langs->trans('RefCustomer');
 	print '</td>';
@@ -3559,7 +3475,7 @@ else if ($id > 0 || ! empty($ref))
 		print '<tr>';
 		print '<td>';
 		print '<table class="nobordernopadding" width="100%"><tr><td>';
-		print fieldLabel('Rate','multicurrency_tx');
+		print fieldLabel('CurrencyRate','multicurrency_tx');
 		print '</td>';
 		if ($action != 'editmulticurrencyrate' && ! empty($object->brouillon))
 			print '<td align="right"><a href="' . $_SERVER["PHP_SELF"] . '?action=editmulticurrencyrate&amp;id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetMultiCurrencyCode'), 1) . '</a></td>';
@@ -4102,13 +4018,10 @@ else if ($id > 0 || ! empty($ref))
 		print $formfile->showdocuments('facture', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
 		$somethingshown = $formfile->numoffiles;
 
-		// Linked object block
-		$somethingshown = $form->showLinkedObjectBlock($object);
-
 		// Show links to link elements
-		$linktoelem = $form->showLinkToObjectBlock($object,array('order'));
-		if ($linktoelem) print '<br>'.$linktoelem;
-
+		$linktoelem = $form->showLinkToObjectBlock($object, null, array('invoice'));
+		$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
+		
 		// Link for paypal payment
 		if (! empty($conf->paypal->enabled) && $object->statut != 0) {
 			include_once DOL_DOCUMENT_ROOT . '/paypal/lib/paypal.lib.php';

+ 1 - 0
htdocs/compta/facture/class/api_deprecated_invoice.class.php

@@ -158,6 +158,7 @@ class InvoiceApi extends DolibarrApi
         $result = $db->query($sql);
         if ($result)
         {
+            $i=0;
             $num = $db->num_rows($result);
             while ($i < $num)
             {

+ 17 - 6
htdocs/compta/facture/class/api_invoices.class.php

@@ -68,7 +68,7 @@ class Invoices extends DolibarrApi
 			
         $result = $this->invoice->fetch($id);
         if( ! $result ) {
-            throw new RestException(404, 'Facture not found');
+            throw new RestException(404, 'Invoice not found');
         }
 		
 		if( ! DolibarrApi::_checkAccessToResource('facture',$this->invoice->id)) {
@@ -128,6 +128,7 @@ class Invoices extends DolibarrApi
             $sql .= " AND sc.fk_user = ".$search_sale;
         }
         
+        // TODO remove this, useless for WS
         $nbtotalofrecords = 0;
         if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
         {
@@ -178,7 +179,7 @@ class Invoices extends DolibarrApi
     function post($request_data = NULL)
     {
         if(! DolibarrApiAccess::$user->rights->facture->creer) {
-			throw new RestException(401);
+			throw new RestException(401, "Insuffisant rights");
 		}
         // Check mandatory fields
         $result = $this->_validate($request_data);
@@ -189,8 +190,18 @@ class Invoices extends DolibarrApi
         if(! array_keys($request_data,'date')) {
             $this->invoice->date = dol_now();
         }
-        if( ! $this->invoice->create(DolibarrApiAccess::$user)) {
-            throw new RestException(500);
+        /* We keep lines as an array
+         if (isset($request_data["lines"])) {
+            $lines = array();
+            foreach ($request_data["lines"] as $line) {
+                array_push($lines, (object) $line);
+            }
+            $this->invoice->lines = $lines;
+        }*/
+        
+        if ($this->invoice->create(DolibarrApiAccess::$user) <= 0) {
+            $errormsg = $this->invoice->error;
+            throw new RestException(500, $errormsg ? $errormsg : "Error while creating order");
         }
         return $this->invoice->id;
     }
@@ -210,7 +221,7 @@ class Invoices extends DolibarrApi
         
         $result = $this->invoice->fetch($id);
         if( ! $result ) {
-            throw new RestException(404, 'Facture not found');
+            throw new RestException(404, 'Invoice not found');
         }
 		
 		if( ! DolibarrApi::_checkAccessToResource('facture',$this->invoice->id)) {
@@ -240,7 +251,7 @@ class Invoices extends DolibarrApi
 		}
         $result = $this->invoice->fetch($id);
         if( ! $result ) {
-            throw new RestException(404, 'Facture not found');
+            throw new RestException(404, 'Invoice not found');
         }
 		
 		if( ! DolibarrApi::_checkAccessToResource('facture',$this->facture->id)) {

+ 84 - 65
htdocs/compta/facture/class/facture.class.php

@@ -408,30 +408,40 @@ class Facture extends CommonInvoice
 						$error++;
 					}
 
-					// TODO mutualiser
-					if ($origin == 'commande')
+					if (! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN))
 					{
-						// On recupere les differents contact interne et externe
-						$order = new Commande($this->db);
-						$order->id = $origin_id;
-
-						// On recupere le commercial suivi propale
-						$this->userid = $order->getIdcontact('internal', 'SALESREPFOLL');
-
-						if ($this->userid)
-						{
-							//On passe le commercial suivi commande en commercial suivi paiement
-							$this->add_contact($this->userid[0], 'SALESREPFOLL', 'internal');
-						}
-
-						// On recupere le contact client facturation commande
-						$this->contactid = $order->getIdcontact('external', 'BILLING');
-
-						if ($this->contactid)
-						{
-							//On passe le contact client facturation commande en contact client facturation
-							$this->add_contact($this->contactid[0], 'BILLING', 'external');
-						}
+    					$originforcontact = $origin;
+    					$originidforcontact = $origin_id;
+    					if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
+    					{
+    					    require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
+    					    $exp = new Expedition($db);
+    					    $exp->fetch($origin_id);
+    					    $exp->fetchObjectLinked();
+    					    if (count($exp->linkedObjectsIds['commande']) > 0) 
+    					    {
+    					        foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
+    					        {
+    					            $originforcontact = 'commande';
+    					            $originidforcontact = $value->id;
+    					            break; // We take first one
+    					        }
+    					    }
+    					}
+    					
+    					$sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
+    					$sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
+			
+    					$resqlcontact = $this->db->query($sqlcontact);
+    					if ($resqlcontact)
+    					{
+    					    while($objcontact = $this->db->fetch_object($resqlcontact))
+    					    {
+    					        //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
+    					        $this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source);    // May failed because of duplicate key or because code of contact type does not exists for new object
+    					    }
+    					}
+    					else dol_print_error($resqlcontact);
 					}
 				}
 			}
@@ -439,7 +449,7 @@ class Facture extends CommonInvoice
 			/*
 			 *  Insert lines of invoices into database
 			 */
-			if (count($this->lines) && is_object($this->lines[0]))	// If this->lines is array on InvoiceLines (preferred mode)
+			if (count($this->lines) && is_object($this->lines[0]))	// If this->lines is array of InvoiceLines (preferred mode)
 			{
 				$fk_parent_line = 0;
 
@@ -448,8 +458,8 @@ class Facture extends CommonInvoice
 				{
 					$newinvoiceline=$this->lines[$i];
 					$newinvoiceline->fk_facture=$this->id;
-                    $newinvoiceline->origin = $this->element;
-                    $newinvoiceline->origin_id = $this->lines[$i]->id;
+                    $newinvoiceline->origin = $this->element;           // TODO This seems not used. Here we but origin 'facture' but after
+                    $newinvoiceline->origin_id = $this->lines[$i]->id;  // we put an id of object !
 					if ($result >= 0 && ($newinvoiceline->info_bits & 0x01) == 0)	// We keep only lines with first bit = 0
 					{
 						// Reset fk_parent_line for no child products and special product
@@ -473,49 +483,56 @@ class Facture extends CommonInvoice
 					}
 				}
 			}
-			else	// If this->lines is not object of invoice lines
+			else	// If this->lines is an array of invoice line arrays
 			{
 				$fk_parent_line = 0;
 
 				dol_syslog("There is ".count($this->lines)." lines that are array lines");
+
 				foreach ($this->lines as $i => $val)
 				{
-					if (($this->lines[$i]->info_bits & 0x01) == 0)	// We keep only lines with first bit = 0
+                	$line = $this->lines[$i];
+                	
+                	// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
+				    //if (! is_object($line)) $line=json_decode(json_encode($line), FALSE);  // convert recursively array into object.
+                	if (! is_object($line)) $line = (object) $line;
+				    
+				    if (($line->info_bits & 0x01) == 0)	// We keep only lines with first bit = 0
 					{
 						// Reset fk_parent_line for no child products and special product
-						if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
+						if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
 							$fk_parent_line = 0;
 						}
 
 						$result = $this->addline(
-							$this->lines[$i]->desc,
-							$this->lines[$i]->subprice,
-							$this->lines[$i]->qty,
-							$this->lines[$i]->tva_tx,
-							$this->lines[$i]->localtax1_tx,
-							$this->lines[$i]->localtax2_tx,
-							$this->lines[$i]->fk_product,
-							$this->lines[$i]->remise_percent,
-							$this->lines[$i]->date_start,
-							$this->lines[$i]->date_end,
-							$this->lines[$i]->fk_code_ventilation,
-							$this->lines[$i]->info_bits,
-							$this->lines[$i]->fk_remise_except,
+							$line->desc,
+							$line->subprice,
+							$line->qty,
+							$line->tva_tx,
+							$line->localtax1_tx,
+							$line->localtax2_tx,
+							$line->fk_product,
+							$line->remise_percent,
+							$line->date_start,
+							$line->date_end,
+							$line->fk_code_ventilation,
+							$line->info_bits,
+							$line->fk_remise_except,
 							'HT',
 							0,
-							$this->lines[$i]->product_type,
-							$this->lines[$i]->rang,
-							$this->lines[$i]->special_code,
+							$line->product_type,
+							$line->rang,
+							$line->special_code,
                             $this->element,
-                            $this->lines[$i]->id,
+                            $line->id,
 							$fk_parent_line,
-							$this->lines[$i]->fk_fournprice,
-							$this->lines[$i]->pa_ht,
-							$this->lines[$i]->label,
-							$this->lines[$i]->array_options,
-							$this->lines[$i]->situation_percent,
-							$this->lines[$i]->fk_prev_id,
-							$this->lines[$i]->fk_unit
+							$line->fk_fournprice,
+							$line->pa_ht,
+							$line->label,
+							$line->array_options,
+							$line->situation_percent,
+							$line->fk_prev_id,
+							$line->fk_unit
 						);
 						if ($result < 0)
 						{
@@ -526,7 +543,7 @@ class Facture extends CommonInvoice
 						}
 
 						// Defined the new fk_parent_line
-						if ($result > 0 && $this->lines[$i]->product_type == 9) {
+						if ($result > 0 && $line->product_type == 9) {
 							$fk_parent_line = $result;
 						}
 					}
@@ -595,21 +612,22 @@ class Facture extends CommonInvoice
 
 					// Actions on extra fields (by external module or standard code)
 					// TODO le hook fait double emploi avec le trigger !!
+					/*
 					$hookmanager->initHooks(array('invoicedao'));
 					$parameters=array('invoiceid'=>$this->id);
 					$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
 					if (empty($reshook))
 					{
 						if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
-						{
-							$result=$this->insertExtraFields();
-							if ($result < 0)
-							{
-								$error++;
-							}
-						}
+						{*/
+					if (! $error)
+					{
+					    $result=$this->insertExtraFields();
+					    if ($result < 0) $error++;
+					}
+						/*}
 					}
-					else if ($reshook < 0) $error++;
+					else if ($reshook < 0) $error++;*/
 
                     // Call trigger
                     $result=$this->call_trigger('BILL_CREATE',$user);
@@ -1720,7 +1738,7 @@ class Facture extends CommonInvoice
 	 *	@param  string	$close_note	Commentaire renseigne si on classe a payee alors que paiement incomplet (cas escompte par exemple)
 	 *  @return int         		<0 if KO, >0 if OK
 	 */
-	function set_paid($user,$close_code='',$close_note='')
+	function set_paid($user, $close_code='', $close_note='')
 	{
 		$error=0;
 
@@ -2944,9 +2962,10 @@ class Facture extends CommonInvoice
 			$field2='fk_paiementfourn';
 		}
 
-		$sql = 'SELECT pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, t.code';
+		$sql = 'SELECT pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code';
 		$sql.= ' FROM '.MAIN_DB_PREFIX.$table.' as pf, '.MAIN_DB_PREFIX.$table2.' as p, '.MAIN_DB_PREFIX.'c_paiement as t';
 		$sql.= ' WHERE pf.'.$field.' = '.$this->id;
+		//$sql.= ' WHERE pf.'.$field.' = 1';
 		$sql.= ' AND pf.'.$field2.' = p.rowid';
 		$sql.= ' AND p.fk_paiement = t.id';
 		if ($filtertype) $sql.=" AND t.code='PRE'";
@@ -2960,7 +2979,7 @@ class Facture extends CommonInvoice
 			while ($i < $num)
 			{
 				$obj = $this->db->fetch_object($resql);
-				$retarray[]=array('amount'=>$obj->amount,'type'=>$obj->code, 'date'=>$obj->datep);
+				$retarray[]=array('amount'=>$obj->amount,'type'=>$obj->code, 'date'=>$obj->datep, 'num'=>$obj->num);
 				$i++;
 			}
 			$this->db->free($resql);

+ 4 - 7
htdocs/compta/facture/contact.php

@@ -114,7 +114,7 @@ else if ($action == 'deletecontact' && $user->rights->facture->creer)
  * View
  */
 
-llxHeader('', $langs->trans("Bill"), "Facture");
+llxHeader('', $langs->trans("InvoiceCustomer"));
 
 $form = new Form($db);
 $formcompany = new FormCompany($db);
@@ -146,7 +146,7 @@ if ($id > 0 || ! empty($ref))
 		$linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 		// Ref
-		print '<tr><td width="20%">'.$langs->trans('Ref').'</td>';
+		print '<tr><td class="titlefield">'.$langs->trans('Ref').'</td>';
 		print '<td colspan="3">';
 		$morehtmlref='';
 		$discount=new DiscountAbsolute($db);
@@ -163,13 +163,10 @@ if ($id > 0 || ! empty($ref))
 		print '</td></tr>';
 
 		// Ref customer
-		print '<tr><td width="20%">';
-        print '<table class="nobordernopadding" width="100%"><tr><td>';
+		print '<tr><td>';
         print $langs->trans('RefCustomer');
         print '</td>';
-        print '</tr></table>';
-        print '</td>';
-        print '<td colspan="5">';
+        print '<td colspan="3">';
         print $object->ref_client;
 		print '</td></tr>';
 

+ 4 - 7
htdocs/compta/facture/document.php

@@ -82,7 +82,7 @@ include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php';
  * View
  */
 
-llxHeader();
+llxHeader('', $langs->trans("InvoiceCustomer"));
 
 $form = new Form($db);
 
@@ -113,7 +113,7 @@ if ($id > 0 || ! empty($ref))
 		$linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 		// Ref
-		print '<tr><td width="30%">'.$langs->trans('Ref').'</td>';
+		print '<tr><td class="titlefield">'.$langs->trans('Ref').'</td>';
 		print '<td colspan="3">';
 		$morehtmlref='';
 		$discount=new DiscountAbsolute($db);
@@ -130,13 +130,10 @@ if ($id > 0 || ! empty($ref))
 		print '</td></tr>';
 
 		// Ref customer
-		print '<tr><td width="20%">';
-		print '<table class="nobordernopadding" width="100%"><tr><td>';
+		print '<tr><td>';
 		print $langs->trans('RefCustomer');
 		print '</td>';
-		print '</tr></table>';
-		print '</td>';
-		print '<td colspan="5">';
+		print '<td colspan="3">';
 		print $object->ref_client;
 		print '</td></tr>';
 

+ 5 - 2
htdocs/compta/facture/fiche-rec.php

@@ -1533,8 +1533,11 @@ else
 		print '<div class="fichecenter"><div class="fichehalfleft">';
 		print '<a name="builddoc"></a>'; // ancre
 		
-		// Linked object block
-		$somethingshown = $form->showLinkedObjectBlock($object);
+		
+		// Show links to link elements
+		//$linktoelem = $form->showLinkToObjectBlock($object, null, array('order'));
+		$somethingshown = $form->showLinkedObjectBlock($object, '');
+		
 		
         print '</div></div>';
 

+ 1 - 1
htdocs/compta/facture/info.php

@@ -35,7 +35,7 @@ $langs->load("bills");
  * View
  */
 
-llxHeader();
+llxHeader('', $langs->trans("InvoiceCustomer"));
 
 $fac = new Facture($db);
 $fac->fetch($_GET["facid"]);

+ 2 - 1
htdocs/compta/facture/list.php

@@ -219,6 +219,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter") || GETPOS
 if (empty($reshook))
 {
 	$objectclass='Facture';
+	$objectlabel='Invoices';
     $permtoread = $user->rights->facture->lire;
 	$permtodelete = $user->rights->facture->supprimer;
 	$uploaddir = $conf->facture->dir_output;
@@ -366,7 +367,7 @@ $sql.=$hookmanager->resPrint;
 
 if (! $sall)
 {
-    $sql.= ' GROUP BY f.rowid, f.facnumber, ref_client, f.type, f.note_private, f.note_public, f.increment, f.total, f.tva, f.total_ttc,';
+    $sql.= ' GROUP BY f.rowid, f.facnumber, ref_client, f.type, f.note_private, f.note_public, f.increment, fk_mode_reglement, f.total, f.tva, f.total_ttc,';
     $sql.= ' f.datef, f.date_lim_reglement,';
     $sql.= ' f.paye, f.fk_statut,';
     $sql.= ' f.datec, f.tms,';

+ 4 - 7
htdocs/compta/facture/note.php

@@ -60,7 +60,7 @@ include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php';	// Must be include,
  * View
  */
 
-llxHeader();
+llxHeader('', $langs->trans("InvoiceCustomer"));
 
 $form = new Form($db);
 
@@ -81,7 +81,7 @@ if ($id > 0 || ! empty($ref))
     $linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 	// Ref
-	print '<tr><td width="25%">'.$langs->trans('Ref').'</td>';
+	print '<tr><td class="titlefield">'.$langs->trans('Ref').'</td>';
 	print '<td colspan="3">';
 	$morehtmlref='';
 	$discount=new DiscountAbsolute($db);
@@ -98,13 +98,10 @@ if ($id > 0 || ! empty($ref))
 	print '</td></tr>';
 
 	// Ref customer
-	print '<tr><td width="20%">';
-	print '<table class="nobordernopadding" width="100%"><tr><td>';
+	print '<tr><td>';
 	print $langs->trans('RefCustomer');
 	print '</td>';
-	print '</tr></table>';
-	print '</td>';
-	print '<td colspan="5">';
+	print '<td colspan="3">';
 	print $object->ref_client;
 	print '</td></tr>';
 

+ 9 - 9
htdocs/compta/facture/prelevement.php

@@ -106,7 +106,7 @@ if ($action == "delete")
 
 $now=dol_now();
 
-llxHeader('', $langs->trans("Bill"));
+llxHeader('', $langs->trans("InvoiceCustomer"));
 
 $form = new Form($db);
 
@@ -155,7 +155,7 @@ if ($object->id > 0)
 	$linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 	// Ref
-	print '<tr><td width="20%">'.$langs->trans("Ref").'</td><td colspan="5">';
+	print '<tr><td class="titlefield">'.$langs->trans("Ref").'</td><td colspan="3">';
 	$morehtmlref='';
 	$discount=new DiscountAbsolute($db);
 	$result=$discount->fetch(0,$object->id);
@@ -171,24 +171,24 @@ if ($object->id > 0)
 	print "</td></tr>";
 
 	// Ref customer
-	print '<tr><td width="20%">';
+	print '<tr><td>';
 	print '<table class="nobordernopadding" width="100%"><tr><td>';
 	print $langs->trans('RefCustomer');
 	print '</td>';
 	print '</tr></table>';
 	print '</td>';
-	print '<td colspan="5">';
+	print '<td colspan="3">';
 	print $object->ref_client;
 	print '</td></tr>';
 
 	// Third party
 	print '<tr><td>'.$langs->trans('Company').'</td>';
-	print '<td colspan="5">'.$object->thirdparty->getNomUrl(1,'compta');
+	print '<td colspan="3">'.$object->thirdparty->getNomUrl(1,'compta');
 	print ' &nbsp; (<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$object->socid.'">'.$langs->trans('OtherBills').'</a>)</td>';
 	print '</tr>';
 
 	// Type
-	print '<tr><td>'.$langs->trans('Type').'</td><td colspan="5">';
+	print '<tr><td>'.$langs->trans('Type').'</td><td colspan="3">';
 	print $object->getLibType();
 	if ($object->type == Facture::TYPE_REPLACEMENT)
 	{
@@ -229,7 +229,7 @@ if ($object->id > 0)
 	print '</td></tr>';
 
 	// Discounts
-	print '<tr><td>'.$langs->trans('Discounts').'</td><td colspan="5">';
+	print '<tr><td>'.$langs->trans('Discounts').'</td><td colspan="3">';
 	if ($object->thirdparty->remise_percent) print $langs->trans("CompanyHasRelativeDiscount",$object->thirdparty->remise_percent);
 	else print $langs->trans("CompanyHasNoRelativeDiscount");
 	print '. ';
@@ -443,7 +443,7 @@ if ($object->id > 0)
 	print '<tr><td>'.$langs->trans('Status').'</td>';
 	print '<td align="left" colspan="3">'.($object->getLibStatut(4,$totalpaye)).'</td></tr>';
 
-	print '<tr><td>'.$langs->trans("RIB").'</td><td colspan="5">';
+	print '<tr><td>'.$langs->trans("RIB").'</td><td colspan="3">';
 	print $object->thirdparty->display_rib();
 	print '</td></tr>';
 
@@ -509,7 +509,7 @@ if ($object->id > 0)
 	{
 		if ($num == 0)
 		{
-			if ($object->statut > Facture::STATUS_DRAFT) print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->trans("AlreadyPayed")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
+			if ($object->statut > Facture::STATUS_DRAFT) print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->trans("AlreadyPaid")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
 			else print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->trans("Draft")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
 		}
 		else print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->trans("RequestAlreadyDone")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';

+ 11 - 10
htdocs/compta/paiement_charge.php

@@ -29,8 +29,8 @@ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
 
 $langs->load("bills");
 
-$chid=GETPOST("id");
-$action=GETPOST('action');
+$chid=GETPOST("id", 'int');
+$action=GETPOST('action', 'alpha');
 $amounts = array();
 
 // Security check
@@ -45,13 +45,13 @@ if ($user->societe_id > 0)
  * Actions
  */
 
-if ($action == 'add_payment')
+if ($action == 'add_payment' || ($action == 'confirm_paiement' && $confirm=='yes'))
 {
 	$error=0;
 
 	if ($_POST["cancel"])
 	{
-		$loc = DOL_URL_ROOT.'/compta/sociales/charges.php?id='.$chid;
+		$loc = DOL_URL_ROOT.'/compta/sociales/card.php?id='.$chid;
 		header("Location: ".$loc);
 		exit;
 	}
@@ -109,7 +109,7 @@ if ($action == 'add_payment')
 
     		if (! $error)
     		{
-    		    $paymentid = $paiement->create($user);
+    		    $paymentid = $paiement->create($user, (GETPOST('closepaidcontrib')=='on'?1:0));
                 if ($paymentid < 0)
                 {
                     $errmsg=$paiement->error;
@@ -130,7 +130,7 @@ if ($action == 'add_payment')
     	    if (! $error)
             {
                 $db->commit();
-                $loc = DOL_URL_ROOT.'/compta/sociales/charges.php?id='.$chid;
+                $loc = DOL_URL_ROOT.'/compta/sociales/card.php?id='.$chid;
                 header('Location: '.$loc);
                 exit;
             }
@@ -155,7 +155,7 @@ $form=new Form($db);
 
 
 // Formulaire de creation d'un paiement de charge
-if ($_GET["action"] == 'create')
+if ($action == 'create')
 {
 
 	$charge = new ChargeSociales($db);
@@ -185,7 +185,7 @@ if ($_GET["action"] == 'create')
 
 	print "<tr class=\"liste_titre\"><td colspan=\"2\">".$langs->trans("SocialContribution")."</td></tr>";
 
-	print '<tr><td>'.$langs->trans("Ref").'</td><td><a href="'.DOL_URL_ROOT.'/compta/sociales/charges.php?id='.$chid.'">'.$chid.'</a></td></tr>';
+	print '<tr><td>'.$langs->trans("Ref").'</td><td><a href="'.DOL_URL_ROOT.'/compta/sociales/card.php?id='.$chid.'">'.$chid.'</a></td></tr>';
 	print '<tr><td>'.$langs->trans("Type")."</td><td>".$charge->type_libelle."</td></tr>\n";
 	print '<tr><td>'.$langs->trans("Period")."</td><td>".dol_print_date($charge->periode,'day')."</td></tr>\n";
 	print '<tr><td>'.$langs->trans("Label").'</td><td>'.$charge->lib."</td></tr>\n";
@@ -317,8 +317,9 @@ if ($_GET["action"] == 'create')
 
 	print "</table>";
 
-	print '<br><div class="center">';
-	print '<input type="submit" class="button" name="save" value="'.$langs->trans("Save").'">';
+	// Bouton Save payment
+	print '<br><div class="center"><input type="checkbox" checked name="closepaidcontrib"> '.$langs->trans("ClosePaidContributionsAutomatically");
+	print '<br><input type="submit" class="button" name="save" value="'.$langs->trans('ToMakePayment').'">';
 	print '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
 	print '<input type="submit" class="button" name="cancel" value="'.$langs->trans("Cancel").'">';
 	print '</div>';

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